CVE-2020-18750 (C, published 2021-02-05T19:15Z)
Description: value: Buffer overflow in pdf2json 0.69 allows local users to execute arbitrary code by converting a crafted PDF file.
Severity: MEDIUM (CVSS v3 base score: 7.8)
Weakness: CWE-120 \u2014 Buffer Copy without Checking Size of Input ('Classic Buffer Overflow')
The product copies an input buffer to an output buffer without verifying that the size of the input buffer is less than the size of the output buffer, leading to a buffer overflow.
Repository: https://github.com/flexpaper/pdf2json
Files changed:
.gitignore
AUTHORS
CHANGES
Makefile.in
README.md
aclocal.m4
aconf-win32.h
aconf.h.in
configure
configure.ac
doc/pdffonts.1
doc/pdffonts.cat
doc/pdffonts.hlp
doc/pdfimages.1
doc/pdfimages.cat
doc/pdfimages.hlp
doc/pdfinfo.1
doc/pdfinfo.cat
doc/pdfinfo.hlp
doc/pdftoppm.1
doc/pdftoppm.cat
doc/pdftoppm.hlp
doc/pdftops.1
doc/pdftops.cat
doc/pdftops.hlp
doc/pdftotext.1
doc/pdftotext.cat
doc/pdftotext.hlp
doc/sample-xpdfrc
doc/xpdf.1
doc/xpdf.cat
doc/xpdf.hlp
doc/xpdfrc.5
doc/xpdfrc.cat
doc/xpdfrc.hlp
fofi/FoFiBase.cc
fofi/FoFiBase.h
fofi/FoFiEncodings.cc
fofi/FoFiEncodings.h
fofi/FoFiTrueType.cc
fofi/FoFiTrueType.h
fofi/FoFiType1.cc
fofi/FoFiType1.h
fofi/FoFiType1C.cc
fofi/FoFiType1C.h
fofi/Makefile.in
freetype.win32/include/freetype/config/ftconfig.h
freetype.win32/include/freetype/config/ftheader.h
freetype.win32/include/freetype/config/ftmodule.h
freetype.win32/include/freetype/config/ftoption.h
freetype.win32/include/freetype/config/ftstdlib.h
freetype.win32/include/freetype/freetype.h
freetype.win32/include/freetype/ftadvanc.h
freetype.win32/include/freetype/ftbbox.h
freetype.win32/include/freetype/ftbdf.h
freetype.win32/include/freetype/ftbitmap.h
freetype.win32/include/freetype/ftcache.h
freetype.win32/include/freetype/ftchapters.h
freetype.win32/include/freetype/ftcid.h
freetype.win32/include/freetype/fterrdef.h
freetype.win32/include/freetype/fterrors.h
freetype.win32/include/freetype/ftgasp.h
freetype.win32/include/freetype/ftglyph.h
freetype.win32/include/freetype/ftgxval.h
freetype.win32/include/freetype/ftgzip.h
freetype.win32/include/freetype/ftimage.h
freetype.win32/include/freetype/ftincrem.h
freetype.win32/include/freetype/ftlcdfil.h
freetype.win32/include/freetype/ftlist.h
freetype.win32/include/freetype/ftlzw.h
freetype.win32/include/freetype/ftmac.h
freetype.win32/include/freetype/ftmm.h
freetype.win32/include/freetype/ftmodapi.h
freetype.win32/include/freetype/ftmoderr.h
freetype.win32/include/freetype/ftotval.h
freetype.win32/include/freetype/ftoutln.h
freetype.win32/include/freetype/ftpfr.h
freetype.win32/include/freetype/ftrender.h
freetype.win32/include/freetype/ftsizes.h
freetype.win32/include/freetype/ftsnames.h
freetype.win32/include/freetype/ftstroke.h
freetype.win32/include/freetype/ftsynth.h
freetype.win32/include/freetype/ftsystem.h
freetype.win32/include/freetype/fttrigon.h
freetype.win32/include/freetype/fttypes.h
freetype.win32/include/freetype/ftwinfnt.h
freetype.win32/include/freetype/ftxf86.h
freetype.win32/include/freetype/internal/autohint.h
freetype.win32/include/freetype/internal/ftcalc.h
freetype.win32/include/freetype/internal/ftdebug.h
freetype.win32/include/freetype/internal/ftdriver.h
freetype.win32/include/freetype/internal/ftgloadr.h
freetype.win32/include/freetype/internal/ftmemory.h
freetype.win32/include/freetype/internal/ftobjs.h
freetype.win32/include/freetype/internal/ftpic.h
freetype.win32/include/freetype/internal/ftrfork.h
freetype.win32/include/freetype/internal/ftserv.h
freetype.win32/include/freetype/internal/ftstream.h
freetype.win32/include/freetype/internal/fttrace.h
freetype.win32/include/freetype/internal/ftvalid.h
freetype.win32/include/freetype/internal/internal.h
freetype.win32/include/freetype/internal/pcftypes.h
freetype.win32/include/freetype/internal/psaux.h
freetype.win32/include/freetype/internal/pshints.h
freetype.win32/include/freetype/internal/services/svbdf.h
freetype.win32/include/freetype/internal/services/svcid.h
freetype.win32/include/freetype/internal/services/svgldict.h
freetype.win32/include/freetype/internal/services/svgxval.h
freetype.win32/include/freetype/internal/services/svkern.h
freetype.win32/include/freetype/internal/services/svmm.h
freetype.win32/include/freetype/internal/services/svotval.h
freetype.win32/include/freetype/internal/services/svpfr.h
freetype.win32/include/freetype/internal/services/svpostnm.h
freetype.win32/include/freetype/internal/services/svpscmap.h
freetype.win32/include/freetype/internal/services/svpsinfo.h
freetype.win32/include/freetype/internal/services/svsfnt.h
freetype.win32/include/freetype/internal/services/svttcmap.h
freetype.win32/include/freetype/internal/services/svtteng.h
freetype.win32/include/freetype/internal/services/svttglyf.h
freetype.win32/include/freetype/internal/services/svwinfnt.h
freetype.win32/include/freetype/internal/services/svxf86nm.h
freetype.win32/include/freetype/internal/sfnt.h
freetype.win32/include/freetype/internal/t1types.h
freetype.win32/include/freetype/internal/tttypes.h
freetype.win32/include/freetype/t1tables.h
freetype.win32/include/freetype/ttnameid.h
freetype.win32/include/freetype/tttables.h
freetype.win32/include/freetype/tttags.h
freetype.win32/include/freetype/ttunpat.h
freetype.win32/include/ft2build.h
goo/FixedPoint.cc
goo/FixedPoint.h
goo/GHash.cc
goo/GHash.h
goo/GList.cc
goo/GList.h
goo/GMutex.h
goo/GString.cc
goo/GString.cc.fixed
goo/GString.h
goo/Makefile.in
goo/gfile.cc
goo/gfile.h
goo/gmem.cc
goo/gmem.h
goo/gmempp.cc
goo/gtypes.h
goo/parseargs.c
goo/parseargs.h
goo/vms_directory.c
goo/vms_dirent.h
goo/vms_make.com
goo/vms_sys_dirent.h
goo/vms_unix_time.h
goo/vms_unix_times.c
goo/vms_unlink.c
ms_make.bat
splash/Makefile.in
splash/Splash.cc
splash/Splash.h
splash/SplashBitmap.cc
splash/SplashBitmap.h
splash/SplashClip.cc
splash/SplashClip.h
splash/SplashErrorCodes.h
splash/SplashFTFont.cc
splash/SplashFTFont.h
splash/SplashFTFontEngine.cc
splash/SplashFTFontEngine.h
splash/SplashFTFontFile.cc
splash/SplashFTFontFile.h
splash/SplashFont.cc
splash/SplashFont.h
splash/SplashFontEngine.cc
splash/SplashFontEngine.h
splash/SplashFontFile.cc
splash/SplashFontFile.h
splash/SplashFontFileID.cc
splash/SplashFontFileID.h
splash/SplashGlyphBitmap.h
splash/SplashMath.h
splash/SplashPath.cc
splash/SplashPath.h
splash/SplashPattern.cc
splash/SplashPattern.h
splash/SplashScreen.cc
splash/SplashScreen.h
splash/SplashState.cc
splash/SplashState.h
splash/SplashT1Font.cc
splash/SplashT1Font.h
splash/SplashT1FontEngine.cc
splash/SplashT1FontEngine.h
splash/SplashT1FontFile.cc
splash/SplashT1FontFile.h
splash/SplashTypes.h
splash/SplashXPath.cc
splash/SplashXPath.h
splash/SplashXPathScanner.cc
splash/SplashXPathScanner.h
src/GVector.h
src/ImgOutputDev.cc
src/ImgOutputDev.h
src/Makefile.in
src/XmlFonts.cc
src/XmlFonts.h
src/XmlLinks.cc
src/XmlLinks.h
src/pdf2json.cc
src/pdf2xml.dtd
src/pdf2xmljson.dtd
xpdf/Annot.cc
xpdf/Annot.h
xpdf/Array.cc
xpdf/Array.h
xpdf/BuiltinFont.cc
xpdf/BuiltinFont.h
xpdf/BuiltinFontTables.cc
xpdf/BuiltinFontTables.h
xpdf/CMap.cc
xpdf/CMap.h
xpdf/Catalog.cc
xpdf/Catalog.h
xpdf/CharCodeToUnicode.cc
xpdf/CharCodeToUnicode.h
xpdf/CharTypes.h
xpdf/CompactFontTables.h
xpdf/CoreOutputDev.cc
xpdf/CoreOutputDev.h
xpdf/Decrypt.cc
xpdf/Decrypt.h
xpdf/Dict.cc
xpdf/Dict.h
xpdf/Error.cc
xpdf/Error.h
xpdf/ErrorCodes.h
xpdf/FontEncodingTables.cc
xpdf/FontEncodingTables.h
xpdf/Function.cc
xpdf/Function.h
xpdf/Gfx.cc
xpdf/Gfx.h
xpdf/GfxFont.cc
xpdf/GfxFont.h
xpdf/GfxState.cc
xpdf/GfxState.h
xpdf/GlobalParams.cc
xpdf/GlobalParams.cc.old
xpdf/GlobalParams.h
xpdf/GlobalParams.h.old
xpdf/GooLikely.h
xpdf/ImageOutputDev.cc
xpdf/ImageOutputDev.h
xpdf/JArithmeticDecoder.cc
xpdf/JArithmeticDecoder.h
xpdf/JBIG2Stream.cc
xpdf/JBIG2Stream.h
xpdf/JPXStream.cc
xpdf/JPXStream.h
xpdf/Lexer.cc
xpdf/Lexer.h
xpdf/Link.cc
xpdf/Link.cc.old
xpdf/Link.h
xpdf/Link.h.old
xpdf/Makefile.in
xpdf/NameToCharCode.cc
xpdf/NameToCharCode.h
xpdf/NameToUnicodeTable.h
xpdf/Object.cc
xpdf/Object.h
xpdf/Outline.cc
xpdf/Outline.h
xpdf/OutputDev.cc
xpdf/OutputDev.h
xpdf/PDFCore.cc
xpdf/PDFCore.h
xpdf/PDFDoc.cc
xpdf/PDFDoc.h
xpdf/PDFDocEncoding.cc
xpdf/PDFDocEncoding.h
xpdf/PSOutputDev.cc
xpdf/PSOutputDev.h
xpdf/PSTokenizer.cc
xpdf/PSTokenizer.h
xpdf/Page.cc
xpdf/Page.h
xpdf/Parser.cc
xpdf/Parser.h
xpdf/PreScanOutputDev.cc
xpdf/PreScanOutputDev.h
xpdf/SecurityHandler.cc
xpdf/SecurityHandler.h
xpdf/SplashOutputDev.cc
xpdf/SplashOutputDev.h
xpdf/Stream-CCITT.h
xpdf/Stream.cc
xpdf/Stream.h
xpdf/TextOutputDev.cc
xpdf/TextOutputDev.h
xpdf/UTF8.h
xpdf/UnicodeMap.cc
xpdf/UnicodeMap.cc.old
xpdf/UnicodeMap.h
xpdf/UnicodeMap.h.old
xpdf/UnicodeMapTables.h
xpdf/UnicodeTypeTable.cc
xpdf/UnicodeTypeTable.h
xpdf/XPDFApp.cc
xpdf/XPDFApp.h
xpdf/XPDFCore.cc
xpdf/XPDFCore.h
xpdf/XPDFTree.cc
xpdf/XPDFTree.h
xpdf/XPDFTreeP.h
xpdf/XPDFViewer.cc
xpdf/XPDFViewer.h
xpdf/XRef.cc
xpdf/XRef.h
xpdf/XpdfPluginAPI.cc
xpdf/XpdfPluginAPI.h
xpdf/about-text.h
xpdf/about.xbm
xpdf/backArrow.xbm
xpdf/backArrowDis.xbm
xpdf/config.h
xpdf/dblLeftArrow.xbm
xpdf/dblLeftArrowDis.xbm
xpdf/dblRightArrow.xbm
xpdf/dblRightArrowDis.xbm
xpdf/find.xbm
xpdf/findDis.xbm
xpdf/forwardArrow.xbm
xpdf/forwardArrowDis.xbm
xpdf/leftArrow.xbm
xpdf/leftArrowDis.xbm
xpdf/pdffonts.cc
xpdf/pdfimages.cc
xpdf/pdfinfo.cc
xpdf/pdftoppm.cc
xpdf/pdftops.cc
xpdf/pdftotext.cc
xpdf/print.xbm
xpdf/printDis.xbm
xpdf/rightArrow.xbm
xpdf/rightArrowDis.xbm
xpdf/vms_make.com
xpdf/xpdf.cc
xpdf/xpdfIcon.xpm
Commit message: Fix for heap vulnerability


Vulnerable code:

Fixed code:
# object files
*.o

# library files
*.a

# files generated by autoconf
config.log
config.status
Makefile
aconf.h

# binary files
pdf2json
pdffonts
pdfimages
pdfinfo
pdftops
pdftotext
PRIMARY AUTHORS AND CONTRIBUTORS TO PDF2IMAGE:

Erik Engstrom
http://flexpaper.devaldi.com/pdf2image/

Based on the PDFTOHTML Project. 
Original authors of PDFTOHTML:
Gueorgui Ovtcharov
Rainer Dorsch <http://www.ra.informatik.uni-stuttgart.de/~rainer/>

Author of the Xpdf program on which pdftohtml is based: 
Derek B. Noonburg <derekn@foolabs.com>

Current maitainer:
Erik Engstrom <erik.engstrom@devaldi.com>

Contributors (in random/chronological order):
Claus Hulstrom and Zoltan Hawryluk - improvements in the generated HTML code
Anjo Anjewierden - XML output
Adjamir Galvão <adjamir@hotmail.com> - improvements of XML output
Søren Boll Overgaard <boll@fork.dk> - FreeBSD port, Debian package
Ivar Abrahamsen <k111@ivar.co.uk> - help with noframes complex output
Edgar Luna Diaz <kheb@gnulinux.org.mx> - page numbering patch
Nicolas Pitre <nico@cam.org> - document outline patch, cleanup
pdf2image 0.64
=======================
- correction for documents containing hidden text
- correction for some words not breaking up properly in very specific scenarios
- added escaping of backspace character

pdf2image 0.40
=======================
- updated Xpdf to 3.01p2
- improved the output of complex mode documents
- fixed links, They should not include text that should not be part of the link anymore.

pdf2image 0.49
=======================
- updated Xpdf to 3.02
- added support for freetype in both linux and windows & added support for splash to allow rasterizing of images
- fixed issue with GString for WIN32
#========================================================================
#
# Main xpdf Makefile.
#
# Copyright 2011 Devaldi Ltd
#
# Copyright 1996-2003 Glyph & Cog, LLC
#
#========================================================================

SHELL = /bin/sh

DESTDIR =

prefix = @prefix@
exec_prefix = @exec_prefix@
srcdir = @srcdir@

INSTALL = /usr/bin/install -c
INSTALL_PROGRAM = ${INSTALL}
INSTALL_DATA = ${INSTALL} -m 644

EXE = 

all:
	cd goo; $(MAKE)
	cd fofi; $(MAKE)
	cd splash; $(MAKE)
	cd xpdf; $(MAKE)
	cd src; $(MAKE) 

dummy:

install: dummy
	-mkdir -p $(DESTDIR)${exec_prefix}/bin
#	$(INSTALL_PROGRAM) xpdf/xpdf$(EXE) $(DESTDIR)${exec_prefix}/bin/xpdf$(EXE)
	$(INSTALL_PROGRAM) xpdf/pdftops$(EXE) $(DESTDIR)${exec_prefix}/bin/pdftops$(EXE)
	$(INSTALL_PROGRAM) xpdf/pdftotext$(EXE) $(DESTDIR)${exec_prefix}/bin/pdftotext$(EXE)
	$(INSTALL_PROGRAM) xpdf/pdfinfo$(EXE) $(DESTDIR)${exec_prefix}/bin/pdfinfo$(EXE)
	$(INSTALL_PROGRAM) xpdf/pdffonts$(EXE) $(DESTDIR)${exec_prefix}/bin/pdffonts$(EXE)
#	$(INSTALL_PROGRAM) xpdf/pdftoppm$(EXE) $(DESTDIR)${exec_prefix}/bin/pdftoppm$(EXE)
	$(INSTALL_PROGRAM) xpdf/pdfimages$(EXE) $(DESTDIR)${exec_prefix}/bin/pdfimages$(EXE)
	$(INSTALL_PROGRAM) src/pdf2json$(EXE) $(DESTDIR)${exec_prefix}/bin/pdf2json$(EXE)
	-mkdir -p $(DESTDIR)${prefix}/man/man1
#	$(INSTALL_DATA) $(srcdir)/doc/xpdf.1 $(DESTDIR)${prefix}/man/man1/xpdf.1
	$(INSTALL_DATA) $(srcdir)/doc/pdftops.1 $(DESTDIR)${prefix}/man/man1/pdftops.1
	$(INSTALL_DATA) $(srcdir)/doc/pdftotext.1 $(DESTDIR)${prefix}/man/man1/pdftotext.1
	$(INSTALL_DATA) $(srcdir)/doc/pdfinfo.1 $(DESTDIR)${prefix}/man/man1/pdfinfo.1
	$(INSTALL_DATA) $(srcdir)/doc/pdffonts.1 $(DESTDIR)${prefix}/man/man1/pdffonts.1
#	$(INSTALL_DATA) $(srcdir)/doc/pdftoppm.1 $(DESTDIR)${prefix}/man/man1/pdftoppm.1
	$(INSTALL_DATA) $(srcdir)/doc/pdfimages.1 $(DESTDIR)${prefix}/man/man1/pdfimages.1
	-mkdir -p $(DESTDIR)${prefix}/man/man5
	$(INSTALL_DATA) $(srcdir)/doc/xpdfrc.5 $(DESTDIR)${prefix}/man/man5/xpdfrc.5
	-mkdir -p $(DESTDIR)${prefix}/etc
	@if test ! -f $(DESTDIR)${prefix}/etc/xpdfrc; then \
		echo "$(INSTALL_DATA) $(srcdir)/doc/sample-xpdfrc $(DESTDIR)${prefix}/etc/xpdfrc"; \
		$(INSTALL_DATA) $(srcdir)/doc/sample-xpdfrc $(DESTDIR)${prefix}/etc/xpdfrc; \
	else \
		echo "# not overwriting the existing $(DESTDIR)${prefix}/etc/xpdfrc"; \
	fi

clean:
	-cd goo; $(MAKE) clean
	-cd fofi; $(MAKE) clean
	-cd splash; $(MAKE) clean
	-cd xpdf; $(MAKE) clean
	-cd src; $(MAKE) clean

distclean: clean
	rm -f config.log config.status config.cache
	rm -f aconf.h
	rm -f Makefile goo/Makefile xpdf/Makefile
	rm -f goo/Makefile.dep fofi/Makefile.dep splash/Makefile.dep xpdf/Makefile.dep
	rm -f goo/Makefile.in.bak fofi/Makefile.in.bak splash/Makefile.in.bak xpdf/Makefile.in.bak
	touch goo/Makefile.dep
	touch fofi/Makefile.dep
	touch splash/Makefile.dep
	touch xpdf/Makefile.dep

depend:
	cd goo; $(MAKE) depend
	cd fofi; $(MAKE) depend
	cd splash; $(MAKE) depend
	cd xpdf; $(MAKE) depend
PDF2JSON
=======

PDF2JSON is a conversion library based on XPDF (3.02) which can be used for high performance PDF page by page conversion to JSON and XML format. It also supports compressing data to minimize size. PDF2JSON is available for Windows and Linux.

Usage
-----
```
Usage: pdf2json [options] <PDF-file> [<output-file>]
  -f <int>          : first page to convert
  -l <int>          : last page to convert
  -compress         : Use compressed mode
  -q                : dont print any messages or errors
  -h                : print usage information
  -help             : print usage information
  -i                : ignore images
  -noframes         : use standard output
  -xml              : output for XML post-processing
  -split <int>      : split the output into separate files on every X page. 
                      use '%' as part of output file name to specify where page number should appear (e.g. Paper_%.js)
  -hidden           : output hidden text
  -enc <string>     : output text encoding name
  -v                : print copyright and version info
  -opw <string>     : owner password (for encrypted files)
  -upw <string>     : user password (for encrypted files)
```

Compiling & Installing on Linux
-------------------
./configure<br/>
make<br/>
sudo make install<br/>


Installing on Windows
-------------------
Download the latest installer (.msi file) and run through the installation steps



Compiling on Windows
-------------------
Make sure you have Visual Studio installed and run ms_make.bat



License
-------------------
GNU GPL v2

<br/>

<h3><a href="http://flowpaper.com/">For the latest about this PDF to JSON converter see the FlowPaper home page</a></h3>

# <<< smr.m4 from smr_macros 0.2.4 >>>

dnl ####################### -*- Mode: M4 -*- ###########################
dnl smr.m4 -- 
dnl 
dnl Copyright (C) 1999 Matthew D. Langston <langston@SLAC.Stanford.EDU>
dnl Copyright (C) 1998 Steve Robbins <stever@cs.mcgill.ca>
dnl
dnl This file is free software; you can redistribute it and/or modify it
dnl under the terms of the GNU General Public License as published by
dnl the Free Software Foundation; either version 2 of the License, or
dnl (at your option) any later version.
dnl
dnl This file is distributed in the hope that it will be useful, but
dnl WITHOUT ANY WARRANTY; without even the implied warranty of
dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
dnl General Public License for more details.
dnl
dnl You should have received a copy of the GNU General Public License
dnl along with this file; if not, write to:
dnl
dnl   Free Software Foundation, Inc.
dnl   Suite 330
dnl   59 Temple Place
dnl   Boston, MA 02111-1307, USA.
dnl ####################################################################


dnl NOTE: The macros in this file are extensively documented in the
dnl       accompanying `smr_macros.texi' Texinfo file.  Please see the
dnl       Texinfo documentation for the definitive specification of how
dnl       these macros are supposed to work.  If the macros work
dnl       differently than the Texinfo documentation says they should,
dnl       then the macros (and not the Texinfo documentation) has the
dnl       bug(s).

dnl This is a convenient macro which translates illegal characters for
dnl bourne shell variables into legal characters.  It has the same
dnl functionality as sed 'y%./+-:%__p__%'.
AC_DEFUN([smr_safe_translation], [patsubst(patsubst([$1], [+], [p]), [./-:], [_])])

AC_DEFUN(smr_SWITCH,
[
  dnl Define convenient aliases for the arguments since there are so
  dnl many of them and I keep confusing myself whenever I have to edit
  dnl this macro.
  pushdef([smr_name],        $1)
  pushdef([smr_help_string], $2)
  pushdef([smr_default],     $3)
  pushdef([smr_yes_define],  $4)
  pushdef([smr_no_define],   $5)

  dnl Do some sanity checking of the arguments.
  ifelse([regexp(smr_default, [^\(yes\|no\)$])], -1, [AC_MSG_ERROR($0: third arg must be either yes or no)])

  dnl Create the help string
  pushdef([smr_lhs], [--ifelse(smr_default, yes, disable, enable)-smr_name])dnl
  pushdef([smr_rhs], [ifelse(smr_default, yes, disable, enable) smr_help_string (default is smr_default)])dnl

  dnl Add the option to `configure --help'.  We don't need to supply the
  dnl 4th argument to AC_ARG_ENABLE (i.e. the code to set the default
  dnl value) because that is done below by AC_CACHE_CHECK.
  AC_ARG_ENABLE([smr_name],
                AC_HELP_STRING([smr_lhs], [smr_rhs]),
                smr_cv_enable_[]smr_name=$enableval)

  dnl We cache the result so that the user doesn't have to remember
  dnl which flags they passed to `configure'.
  AC_CACHE_CHECK([whether to enable smr_help_string],
                 smr_cv_enable_[]smr_name,
                 smr_cv_enable_[]smr_name=smr_default)

    ifelse(smr_yes_define, , , test x"[$]smr_cv_enable_[]smr_name" = xyes && AC_DEFINE(smr_yes_define))
    ifelse(smr_no_define, , ,  test x"[$]smr_cv_enable_[]smr_name" = xno  && AC_DEFINE(smr_no_define))

  dnl Sanity check the value assigned to smr_cv_enable_$1 to force it to
  dnl be either `yes' or `no'.
  if test ! x"[$]smr_cv_enable_[]smr_name" = xyes; then
    if test ! x"[$]smr_cv_enable_[]smr_name" = xno; then
      AC_MSG_ERROR([smr_lhs must be either yes or no])
    fi
  fi

  popdef([smr_name])
  popdef([smr_help_string])
  popdef([smr_default])
  popdef([smr_yes_define])
  popdef([smr_no_define])
  popdef([smr_lhs])
  popdef([smr_rhs])
])


AC_DEFUN(smr_ARG_WITHLIB,
[
  dnl Define convenient aliases for the arguments since there are so
  dnl many of them and I keep confusing myself whenever I have to edit
  dnl this macro.
  pushdef([smr_name],        $1)
  pushdef([smr_libname],     ifelse($2, , smr_name, $2))
  pushdef([smr_help_string], $3)
  pushdef([smr_safe_name],   smr_safe_translation(smr_name))

  dnl Add the option to `configure --help'.  We don't need to supply the
  dnl 4th argument to AC_ARG_WITH (i.e. the code to set the default
  dnl value) because that is done below by AC_CACHE_CHECK.
  AC_ARG_WITH(smr_safe_name-library,
              AC_HELP_STRING([--with-smr_safe_name-library[[=PATH]]],
                             [use smr_name library ifelse(smr_help_string, , , (smr_help_string))]),
              smr_cv_with_[]smr_safe_name[]_library=$withval)

  dnl We cache the result so that the user doesn't have to remember
  dnl which flags they passed to `configure'.
  AC_CACHE_CHECK([whether to use smr_name library],
                 smr_cv_with_[]smr_safe_name[]_library,
                 smr_cv_with_[]smr_safe_name[]_library=maybe)


  case x"[$]smr_cv_with_[]smr_safe_name[]_library" in
      xyes | xmaybe)
          smr_safe_name[]_LIBS="-l[]smr_libname"
          with_[]smr_safe_name=[$]smr_cv_with_[]smr_safe_name[]_library
          ;;
      xno)
          smr_safe_name[]_LIBS=
          with_[]smr_safe_name=no
          ;;
      *)
          if test -f "[$]smr_cv_with_[]smr_safe_name[]_library"; then
            smr_safe_name[]_LIBS=[$]smr_cv_with_[]smr_safe_name[]_library
          elif test -d "[$]smr_cv_with_[]smr_safe_name[]_library"; then
            smr_safe_name[]_LIBS="-L[$]smr_cv_with_[]smr_safe_name[]_library -l[]smr_libname"
          else
            AC_MSG_ERROR([argument must be boolean, file, or directory])
          fi
          with_[]smr_safe_name=yes
          ;;
  esac

  popdef([smr_name])
  popdef([smr_libname])
  popdef([smr_help_string])
  popdef([smr_safe_name])
])


AC_DEFUN(smr_ARG_WITHINCLUDES,
[
  dnl Define convenient aliases for the arguments since there are so
  dnl many of them and I keep confusing myself whenever I have to edit
  dnl this macro.
  pushdef([smr_name],        $1)
  pushdef([smr_header],      $2)
  pushdef([smr_extra_flags], $3)
  pushdef([smr_safe_name],   smr_safe_translation(smr_name))

  dnl Add the option to `configure --help'.  We don't need to supply the
  dnl 4th argument to AC_ARG_WITH (i.e. the code to set the default
  dnl value) because that is done below by AC_CACHE_CHECK.
  AC_ARG_WITH(smr_safe_name-includes,
              AC_HELP_STRING([--with-smr_safe_name-includes[[=DIR]]],
                             [set directory for smr_name headers]),
              smr_cv_with_[]smr_safe_name[]_includes=$withval)

  dnl We cache the result so that the user doesn't have to remember
  dnl which flags they passed to `configure'.
  AC_CACHE_CHECK([where to find the smr_name header files],
                 smr_cv_with_[]smr_safe_name[]_includes,
                 smr_cv_with_[]smr_safe_name[]_includes=)

  if test ! x"[$]smr_cv_with_[]smr_safe_name[]_includes" = x; then
    if test -d "[$]smr_cv_with_[]smr_safe_name[]_includes"; then
      smr_safe_name[]_CFLAGS="-I[$]smr_cv_with_[]smr_safe_name[]_includes"
    else
      AC_MSG_ERROR([argument must be a directory])
    fi
  else
    smr_safe_name[]_CFLAGS=
  fi

  dnl This bit of logic comes from the autoconf AC_PROG_CC macro.  We
  dnl need to put the given include directory into CPPFLAGS temporarily,
  dnl but then restore CPPFLAGS to its old value.
  smr_test_CPPFLAGS="${CPPFLAGS+set}"
  smr_save_CPPFLAGS="$CPPFLAGS"
  CPPFLAGS="$CPPFLAGS [$]smr_safe_name[]_CFLAGS smr_extra_flags"

  dnl If the header file smr_header exists, then define
  dnl HAVE_[]smr_header (in all capitals).
  AC_CHECK_HEADERS([smr_header],
                   smr_have_[]smr_safe_name[]_header=yes,
                   smr_have_[]smr_safe_name[]_header=no)

  if test x"$smr_test_CPPFLAGS" = xset; then
    CPPFLAGS=$smr_save_CPPFLAGS
  else
    unset CPPFLAGS
  fi

  popdef([smr_name])
  popdef([smr_header])
  popdef([smr_extra_flags])
  popdef([smr_safe_name])
])


AC_DEFUN(smr_CHECK_LIB,
[
  dnl Define convenient aliases for the arguments since there are so
  dnl many of them and I keep confusing myself whenever I have to edit
  dnl this macro.
  pushdef([smr_name],        $1)
  pushdef([smr_libname],     ifelse($2, , smr_name, $2))
  pushdef([smr_help_string], $3)
  pushdef([smr_function],    $4)
  pushdef([smr_header],      $5)
  pushdef([smr_extra_libs],  $6)
  pushdef([smr_extra_flags], $7)
  pushdef([smr_prototype],   $8)
  pushdef([smr_safe_name],   smr_safe_translation(smr_name))

  dnl Give the user (via "configure --help") an interface to specify
  dnl whether we should use the library or not, and possibly where we
  dnl should find it.
  smr_ARG_WITHLIB([smr_name], [smr_libname], [smr_help_string])

  if test ! x"$with_[]smr_safe_name" = xno; then

    # If we got this far, then the user didn't explicitly ask not to use
    # the library.

    dnl If the caller of smr_CHECK_LIB specified a header file for this
    dnl library, then give the user (via "configure --help") an
    dnl interface to specify where this header file can be found (if it
    dnl isn't found by the compiler by default).
    ifelse(smr_header, , , [smr_ARG_WITHINCLUDES(smr_name, smr_header, smr_extra_flags)])

    # We need only look for the library if the header has been found
    # (or no header is needed).
    if test [$]smr_have_[]smr_safe_name[]_header != no; then

       AC_CHECK_LIB(smr_libname,
                    smr_function,
                    smr_have_[]smr_safe_name[]_library=yes,
                    smr_have_[]smr_safe_name[]_library=no,
                    [$]smr_safe_name[]_CFLAGS [smr_extra_flags] [$]smr_safe_name[]_LIBS [smr_extra_libs],
                    [ifelse(smr_prototype, , , [[#]include <smr_header>])],
                    smr_prototype)
    fi

    if test x"[$]smr_have_[]smr_safe_name[]_library" = xyes; then
      AC_MSG_RESULT([using smr_name library])
    else
      smr_safe_name[]_LIBS=
      smr_safe_name[]_CFLAGS=

      if test x"$with_[]smr_safe_name" = xmaybe; then
        AC_MSG_RESULT([not using smr_name library])
      else
        AC_MSG_WARN([requested smr_name library not found!])
      fi
    fi
  fi

  popdef([smr_name])
  popdef([smr_libname])
  popdef([smr_help_string])
  popdef([smr_function])
  popdef([smr_header])
  popdef([smr_extra_libs])
  popdef([smr_extra_flags])
  popdef([smr_prototype])
  popdef([smr_safe_name])
])
/*
 * aconf-win32.h
 *
 * Copyright 2002 Derek B. Noonburg
 */

#ifndef ACONF_H
#define ACONF_H

/*
 * Use A4 paper size instead of Letter for PostScript output.
 */
#undef A4_PAPER

/*
 * Do not allow text selection.
 */
#undef NO_TEXT_SELECT

/*
 * Include support for OPI comments.
 */
#undef OPI_SUPPORT

/*
 * Use gzip instead of uncompress.
 */
#define USE_GZIP 1
#define GHOSTSCRIPT "gswin32c"

/*
 * Directory with the Xpdf app-defaults file.
 */
#undef APPDEFDIR

/*
 * Full path for the system-wide xpdfrc file.
 */
#undef SYSTEM_XPDFRC

/*
 * Various include files and functions.
 */
#undef HAVE_DIRENT_H
#undef HAVE_SYS_NDIR_H
#undef HAVE_SYS_DIR_H
#undef HAVE_NDIR_H
#undef HAVE_SYS_SELECT_H
#undef HAVE_SYS_BSDTYPES_H
#undef HAVE_STRINGS_H
#undef HAVE_BSTRING_H
#undef HAVE_POPEN
#undef HAVE_MKSTEMP
#undef HAVE_MKSTEMPS
#undef SELECT_TAKES_INT

/*
 * This is defined if using libXpm.
 */
#undef HAVE_X11_XPM_H

/*
 * This is defined if using t1lib.
 */
#undef HAVE_T1LIB_H

/*
 * One of these is defined if using FreeType (version 1 or 2).
 */
#undef HAVE_FREETYPE_H
#undef HAVE_FREETYPE_FREETYPE_H
#undef HAVE_FREETYPE_FREETYPE_H 1

/*
 * This is defined if using FreeType version 2.
 */
#undef FREETYPE2

/*
 * This is defined if using libpaper.
 */
#undef HAVE_PAPER_H

#define HAVE_SPLASH 1

#endif
/* aconf.h.  Generated automatically by configure.  */
/*
 * aconf.h
 *
 * Copyright 2002 Derek B. Noonburg
 */

#ifndef ACONF_H
#define ACONF_H

/*
 * Full path for the system-wide xpdfrc file.
 */
#undef SYSTEM_XPDFRC

/*
 * Various include files and functions.
 */
#undef HAVE_DIRENT_H
#undef HAVE_STRINGS_H
#undef HAVE_POPEN
#undef HAVE_MKSTEMP
#undef HAVE_MKSTEMPS

/*
 * This is defined if using libXpm.
 */
#undef HAVE_X11_XPM_H

/*
 * One of these is defined if using FreeType 2.
 */
#undef HAVE_FREETYPE_H
#undef HAVE_FREETYPE_FREETYPE_H

/*
 * Defined if the Splash library is avaiable.
 */
#undef HAVE_SPLASH

#endif
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.65 for FULL-PACKAGE-NAME VERSION.
#
# Report bugs to <BUG-REPORT-ADDRESS>.
#
#
# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 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=:
  # Pre-4.2 versions of Zsh do 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_nl='
'
export as_nl
# Printing a long string crashes Solaris 7 /usr/bin/printf.
as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
# Prefer a ksh shell builtin over an external printf program on Solaris,
# but without wasting forks for bash or zsh.
if test -z "$BASH_VERSION$ZSH_VERSION" \
    && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
  as_echo='print -r --'
  as_echo_n='print -rn --'
elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
  as_echo='printf %s\n'
  as_echo_n='printf %s'
else
  if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
    as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
    as_echo_n='/usr/ucb/echo -n'
  else
    as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
    as_echo_n_body='eval
      arg=$1;
      case $arg in #(
      *"$as_nl"*)
	expr "X$arg" : "X\\(.*\\)$as_nl";
	arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
      esac;
      expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
    '
    export as_echo_n_body
    as_echo_n='sh -c $as_echo_n_body as_echo'
  fi
  export as_echo_body
  as_echo='sh -c $as_echo_body as_echo'
fi

# The user is always right.
if test "${PATH_SEPARATOR+set}" != set; then
  PATH_SEPARATOR=:
  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
      PATH_SEPARATOR=';'
  }
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.)
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
  $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
  exit 1
fi

# Unset variables that we do not need and which cause bugs (e.g. in
# pre-3.0 UWIN ksh).  But do not cause bugs in bash 2.01; the "|| exit 1"
# suppresses any "Segmentation fault" message there.  '((' could
# trigger a bug in pdksh 5.2.14.
for as_var in BASH_ENV ENV MAIL MAILPATH
do eval test x\${$as_var+set} = xset \
  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
done
PS1='$ '
PS2='> '
PS4='+ '

# NLS nuisances.
LC_ALL=C
export LC_ALL
LANGUAGE=C
export LANGUAGE

# CDPATH.
(unset CDPATH) >/dev/null 2>&1 && unset CDPATH

if test "x$CONFIG_SHELL" = x; then
  as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
  emulate sh
  NULLCMD=:
  # Pre-4.2 versions of Zsh do 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_required="as_fn_return () { (exit \$1); }
as_fn_success () { as_fn_return 0; }
as_fn_failure () { as_fn_return 1; }
as_fn_ret_success () { return 0; }
as_fn_ret_failure () { return 1; }

exitcode=0
as_fn_success || { exitcode=1; echo as_fn_success failed.; }
as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; }
as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }
as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }
if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then :

else
  exitcode=1; echo positional parameters were not saved.
fi
test x\$exitcode = x0 || exit 1"
  as_suggested="  as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
  as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
  eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
  test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1
test \$(( 1 + 1 )) = 2 || exit 1"
  if (eval "$as_required") 2>/dev/null; then :
  as_have_required=yes
else
  as_have_required=no
fi
  if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then :

else
  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
as_found=false
for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
do
  IFS=$as_save_IFS
  test -z "$as_dir" && as_dir=.
  as_found=:
  case $as_dir in #(
	 /*)
	   for as_base in sh bash ksh sh5; do
	     # Try only shells that exist, to save several forks.
	     as_shell=$as_dir/$as_base
	     if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
		    { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then :
  CONFIG_SHELL=$as_shell as_have_required=yes
		   if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then :
  break 2
fi
fi
	   done;;
       esac
  as_found=false
done
$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&
	      { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then :
  CONFIG_SHELL=$SHELL as_have_required=yes
fi; }
IFS=$as_save_IFS


      if test "x$CONFIG_SHELL" != x; then :
  # We cannot yet assume a decent shell, so we have to provide a
	# neutralization value for shells without unset; and this also
	# works around shells that cannot unset nonexistent variables.
	BASH_ENV=/dev/null
	ENV=/dev/null
	(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
	export CONFIG_SHELL
	exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"}
fi

    if test x$as_have_required = xno; then :
  $as_echo "$0: This script requires a shell more modern than all"
  $as_echo "$0: the shells that I found on your system."
  if test x${ZSH_VERSION+set} = xset ; then
    $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should"
    $as_echo "$0: be upgraded to zsh 4.3.4 or later."
  else
    $as_echo "$0: Please tell bug-autoconf@gnu.org and BUG-REPORT-ADDRESS
$0: about your system, including any error possibly output
$0: before this message. Then install a modern shell, or
$0: manually run the script under such a shell if you do
$0: have one."
  fi
  exit 1
fi
fi
fi
SHELL=${CONFIG_SHELL-/bin/sh}
export SHELL
# Unset more variables known to interfere with behavior of common tools.
CLICOLOR_FORCE= GREP_OPTIONS=
unset CLICOLOR_FORCE GREP_OPTIONS

## --------------------- ##
## M4sh Shell Functions. ##
## --------------------- ##
# as_fn_unset VAR
# ---------------
# Portably unset VAR.
as_fn_unset ()
{
  { eval $1=; unset $1;}
}
as_unset=as_fn_unset

# as_fn_set_status STATUS
# -----------------------
# Set $? to STATUS, without forking.
as_fn_set_status ()
{
  return $1
} # as_fn_set_status

# as_fn_exit STATUS
# -----------------
# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
as_fn_exit ()
{
  set +e
  as_fn_set_status $1
  exit $1
} # as_fn_exit

# as_fn_mkdir_p
# -------------
# Create "$as_dir" as a directory, including parents if necessary.
as_fn_mkdir_p ()
{

  case $as_dir in #(
  -*) as_dir=./$as_dir;;
  esac
  test -d "$as_dir" || eval $as_mkdir_p || {
    as_dirs=
    while :; do
      case $as_dir in #(
      *\'*) as_qdir=`$as_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 ||
$as_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" || as_fn_error "cannot create directory $as_dir"


} # as_fn_mkdir_p
# as_fn_append VAR VALUE
# ----------------------
# Append the text in VALUE to the end of the definition contained in VAR. Take
# advantage of any shell optimizations that allow amortized linear growth over
# repeated appends, instead of the typical quadratic growth present in naive
# implementations.
if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
  eval 'as_fn_append ()
  {
    eval $1+=\$2
  }'
else
  as_fn_append ()
  {
    eval $1=\$$1\$2
  }
fi # as_fn_append

# as_fn_arith ARG...
# ------------------
# Perform arithmetic evaluation on the ARGs, and store the result in the
# global $as_val. Take advantage of shells that can avoid forks. The arguments
# must be portable across $(()) and expr.
if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
  eval 'as_fn_arith ()
  {
    as_val=$(( $* ))
  }'
else
  as_fn_arith ()
  {
    as_val=`expr "$@" || test $? -eq 1`
  }
fi # as_fn_arith


# as_fn_error ERROR [LINENO LOG_FD]
# ---------------------------------
# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
# script with status $?, using 1 if that was 0.
as_fn_error ()
{
  as_status=$?; test $as_status -eq 0 && as_status=1
  if test "$3"; then
    as_lineno=${as_lineno-"$2"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
    $as_echo "$as_me:${as_lineno-$LINENO}: error: $1" >&$3
  fi
  $as_echo "$as_me: error: $1" >&2
  as_fn_exit $as_status
} # as_fn_error

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

if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
  as_dirname=dirname
else
  as_dirname=false
fi

as_me=`$as_basename -- "$0" ||
$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
	 X"$0" : 'X\(//\)$' \| \
	 X"$0" : 'X\(/\)' \| . 2>/dev/null ||
$as_echo X/"$0" |
    sed '/^.*\/\([^/][^/]*\)\/*$/{
	    s//\1/
	    q
	  }
	  /^X\/\(\/\/\)$/{
	    s//\1/
	    q
	  }
	  /^X\/\(\/\).*/{
	    s//\1/
	    q
	  }
	  s/.*/./; q'`

# 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


  as_lineno_1=$LINENO as_lineno_1a=$LINENO
  as_lineno_2=$LINENO as_lineno_2a=$LINENO
  eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" &&
  test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || {
  # 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" ||
    { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_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
}

ECHO_C= ECHO_N= ECHO_T=
case `echo -n x` in #(((((
-n*)
  case `echo 'xy\c'` in
  *c*) ECHO_T='	';;	# ECHO_T is single tab character.
  xy)  ECHO_C='\c';;
  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null
       ECHO_T='	';;
  esac;;
*)
  ECHO_N='-n';;
esac

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 2>/dev/null
fi
if (echo >conf$$.file) 2>/dev/null; then
  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
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='mkdir -p "$as_dir"'
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'"


test -n "$DJDIR" || exec 7<&0 </dev/null
exec 6>&1

# Name of the host.
# hostname on some systems (SVR3.2, Linux) returns a bogus exit status,
# so uname gets run too.
ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`

#
# Initializations.
#
ac_default_prefix=/usr/local
ac_clean_files=
ac_config_libobj_dir=.
LIBOBJS=
cross_compiling=no
subdirs=
MFLAGS=
MAKEFLAGS=

# Identity of this package.
PACKAGE_NAME='FULL-PACKAGE-NAME'
PACKAGE_TARNAME='full-package-name'
PACKAGE_VERSION='VERSION'
PACKAGE_STRING='FULL-PACKAGE-NAME VERSION'
PACKAGE_BUGREPORT='BUG-REPORT-ADDRESS'
PACKAGE_URL=''

# Factoring default headers for most tests.
ac_includes_default="\
#include <stdio.h>
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
#  include <stdlib.h>
# endif
#endif
#ifdef HAVE_STRING_H
# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
#  include <memory.h>
# endif
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#ifdef HAVE_INTTYPES_H
# include <inttypes.h>
#endif
#ifdef HAVE_STDINT_H
# include <stdint.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif"

ac_header_list=
ac_func_list=
ac_subst_vars='LTLIBOBJS
LIBOBJS
EGREP
GREP
X_EXTRA_LIBS
X_LIBS
X_PRE_LIBS
X_CFLAGS
CPP
XMKMF
RANLIB
ac_ct_CC
CFLAGS
CC
OBJEXT
EXEEXT
ac_ct_CXX
CPPFLAGS
LDFLAGS
CXXFLAGS
CXX
target_alias
host_alias
build_alias
LIBS
ECHO_T
ECHO_N
ECHO_C
DEFS
mandir
localedir
libdir
psdir
pdfdir
dvidir
htmldir
infodir
docdir
oldincludedir
includedir
localstatedir
sharedstatedir
sysconfdir
datadir
datarootdir
libexecdir
sbindir
bindir
program_transform_name
prefix
exec_prefix
PACKAGE_URL
PACKAGE_BUGREPORT
PACKAGE_STRING
PACKAGE_VERSION
PACKAGE_TARNAME
PACKAGE_NAME
PATH_SEPARATOR
SHELL'
ac_subst_files=''
ac_user_opts='
enable_option_checking
with_x
'
      ac_precious_vars='build_alias
host_alias
target_alias
CXX
CXXFLAGS
LDFLAGS
LIBS
CPPFLAGS
CCC
CC
CFLAGS
XMKMF
CPP'


# Initialize some variables set by options.
ac_init_help=
ac_init_version=false
ac_unrecognized_opts=
ac_unrecognized_sep=
# 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_TARNAME}'
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_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
    # Reject names that are not valid shell variable names.
    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
      as_fn_error "invalid feature name: $ac_useropt"
    ac_useropt_orig=$ac_useropt
    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
    case $ac_user_opts in
      *"
"enable_$ac_useropt"
"*) ;;
      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig"
	 ac_unrecognized_sep=', ';;
    esac
    eval enable_$ac_useropt=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_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
    # Reject names that are not valid shell variable names.
    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
      as_fn_error "invalid feature name: $ac_useropt"
    ac_useropt_orig=$ac_useropt
    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
    case $ac_user_opts in
      *"
"enable_$ac_useropt"
"*) ;;
      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig"
	 ac_unrecognized_sep=', ';;
    esac
    eval enable_$ac_useropt=\$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_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
    # Reject names that are not valid shell variable names.
    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
      as_fn_error "invalid package name: $ac_useropt"
    ac_useropt_orig=$ac_useropt
    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
    case $ac_user_opts in
      *"
"with_$ac_useropt"
"*) ;;
      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig"
	 ac_unrecognized_sep=', ';;
    esac
    eval with_$ac_useropt=\$ac_optarg ;;

  -without-* | --without-*)
    ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
    # Reject names that are not valid shell variable names.
    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
      as_fn_error "invalid package name: $ac_useropt"
    ac_useropt_orig=$ac_useropt
    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
    case $ac_user_opts in
      *"
"with_$ac_useropt"
"*) ;;
      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig"
	 ac_unrecognized_sep=', ';;
    esac
    eval with_$ac_useropt=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 ;;

  -*) as_fn_error "unrecognized option: \`$ac_option'
Try \`$0 --help' for more information."
    ;;

  *=*)
    ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
    # Reject names that are not valid shell variable names.
    case $ac_envvar in #(
      '' | [0-9]* | *[!_$as_cr_alnum]* )
      as_fn_error "invalid variable name: \`$ac_envvar'" ;;
    esac
    eval $ac_envvar=\$ac_optarg
    export $ac_envvar ;;

  *)
    # FIXME: should be removed in autoconf 3.0.
    $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2
    expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
      $as_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'`
  as_fn_error "missing argument to $ac_option"
fi

if test -n "$ac_unrecognized_opts"; then
  case $enable_option_checking in
    no) ;;
    fatal) as_fn_error "unrecognized options: $ac_unrecognized_opts" ;;
    *)     $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
  esac
fi

# Check all directory arguments for consistency.
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
  # Remove trailing slashes.
  case $ac_val in
    */ )
      ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
      eval $ac_var=\$ac_val;;
  esac
  # Be sure to have absolute directory names.
  case $ac_val in
    [\\/$]* | ?:[\\/]* )  continue;;
    NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
  esac
  as_fn_error "expected an absolute directory name for --$ac_var: $ac_val"
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
    $as_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 .` ||
  as_fn_error "working directory cannot be determined"
test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
  as_fn_error "pwd does not report name of working directory"


# 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 -- "$as_myself" ||
$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
	 X"$as_myself" : 'X\(//\)[^/]' \| \
	 X"$as_myself" : 'X\(//\)$' \| \
	 X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
$as_echo X"$as_myself" |
    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 .."
  as_fn_error "cannot find sources ($ac_unique_file) in $srcdir"
fi
ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
ac_abs_confdir=`(
	cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error "$ac_msg"
	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 FULL-PACKAGE-NAME VERSION 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/full-package-name]
  --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
  case $ac_init_help in
     short | recursive ) echo "Configuration of FULL-PACKAGE-NAME VERSION:";;
   esac
  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:
  CXX         C++ compiler command
  CXXFLAGS    C++ compiler flags
  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
              nonstandard directory <lib dir>
  LIBS        libraries to pass to the linker, e.g. -l<library>
  CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
              you have headers in a nonstandard directory <include dir>
  CC          C compiler command
  CFLAGS      C compiler flags
  XMKMF       Path to xmkmf, Makefile generator for X Window System
  CPP         C preprocessor

Use these variables to override the choices made by `configure' or to help
it to find libraries and programs with nonstandard names/locations.

Report bugs to <BUG-REPORT-ADDRESS>.
_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" ||
      { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && 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=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
  # A ".." for each directory in $ac_dir_suffix.
  ac_top_builddir_sub=`$as_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
      $as_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
FULL-PACKAGE-NAME configure VERSION
generated by GNU Autoconf 2.65

Copyright (C) 2009 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

## ------------------------ ##
## Autoconf initialization. ##
## ------------------------ ##

# ac_fn_cxx_try_compile LINENO
# ----------------------------
# Try to compile conftest.$ac_ext, and return whether this succeeded.
ac_fn_cxx_try_compile ()
{
  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
  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 ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
$as_echo "$ac_try_echo"; } >&5
  (eval "$ac_compile") 2>conftest.err
  ac_status=$?
  if test -s conftest.err; then
    grep -v '^ *+' conftest.err >conftest.er1
    cat conftest.er1 >&5
    mv -f conftest.er1 conftest.err
  fi
  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
  test $ac_status = 0; } && {
	 test -z "$ac_cxx_werror_flag" ||
	 test ! -s conftest.err
       } && test -s conftest.$ac_objext; then :
  ac_retval=0
else
  $as_echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5

	ac_retval=1
fi
  eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
  as_fn_set_status $ac_retval

} # ac_fn_cxx_try_compile

# ac_fn_c_try_compile LINENO
# --------------------------
# Try to compile conftest.$ac_ext, and return whether this succeeded.
ac_fn_c_try_compile ()
{
  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
  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 ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
$as_echo "$ac_try_echo"; } >&5
  (eval "$ac_compile") 2>conftest.err
  ac_status=$?
  if test -s conftest.err; then
    grep -v '^ *+' conftest.err >conftest.er1
    cat conftest.er1 >&5
    mv -f conftest.er1 conftest.err
  fi
  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
  test $ac_status = 0; } && {
	 test -z "$ac_c_werror_flag" ||
	 test ! -s conftest.err
       } && test -s conftest.$ac_objext; then :
  ac_retval=0
else
  $as_echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5

	ac_retval=1
fi
  eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
  as_fn_set_status $ac_retval

} # ac_fn_c_try_compile

# ac_fn_c_try_cpp LINENO
# ----------------------
# Try to preprocess conftest.$ac_ext, and return whether this succeeded.
ac_fn_c_try_cpp ()
{
  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
  if { { ac_try="$ac_cpp conftest.$ac_ext"
case "(($ac_try" in
  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
  *) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
$as_echo "$ac_try_echo"; } >&5
  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err
  ac_status=$?
  if test -s conftest.err; then
    grep -v '^ *+' conftest.err >conftest.er1
    cat conftest.er1 >&5
    mv -f conftest.er1 conftest.err
  fi
  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
  test $ac_status = 0; } >/dev/null && {
	 test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
	 test ! -s conftest.err
       }; then :
  ac_retval=0
else
  $as_echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5

    ac_retval=1
fi
  eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
  as_fn_set_status $ac_retval

} # ac_fn_c_try_cpp

# ac_fn_c_try_link LINENO
# -----------------------
# Try to link conftest.$ac_ext, and return whether this succeeded.
ac_fn_c_try_link ()
{
  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
  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 ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
$as_echo "$ac_try_echo"; } >&5
  (eval "$ac_link") 2>conftest.err
  ac_status=$?
  if test -s conftest.err; then
    grep -v '^ *+' conftest.err >conftest.er1
    cat conftest.er1 >&5
    mv -f conftest.er1 conftest.err
  fi
  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
  test $ac_status = 0; } && {
	 test -z "$ac_c_werror_flag" ||
	 test ! -s conftest.err
       } && test -s conftest$ac_exeext && {
	 test "$cross_compiling" = yes ||
	 $as_test_x conftest$ac_exeext
       }; then :
  ac_retval=0
else
  $as_echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5

	ac_retval=1
fi
  # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
  # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
  # interfere with the next link command; also delete a directory that is
  # left behind by Apple's compiler.  We do this before executing the actions.
  rm -rf conftest.dSYM conftest_ipa8_conftest.oo
  eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
  as_fn_set_status $ac_retval

} # ac_fn_c_try_link

# ac_fn_c_check_func LINENO FUNC VAR
# ----------------------------------
# Tests whether FUNC exists, setting the cache variable VAR accordingly
ac_fn_c_check_func ()
{
  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
$as_echo_n "checking for $2... " >&6; }
if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then :
  $as_echo_n "(cached) " >&6
else
  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */
/* Define $2 to an innocuous variant, in case <limits.h> declares $2.
   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
#define $2 innocuous_$2

/* System header to define __stub macros and hopefully few prototypes,
    which can conflict with char $2 (); below.
    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
    <limits.h> exists even on freestanding compilers.  */

#ifdef __STDC__
# include <limits.h>
#else
# include <assert.h>
#endif

#undef $2

/* 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 $2 ();
/* 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_$2 || defined __stub___$2
choke me
#endif

int
main ()
{
return $2 ();
  ;
  return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
  eval "$3=yes"
else
  eval "$3=no"
fi
rm -f core conftest.err conftest.$ac_objext \
    conftest$ac_exeext conftest.$ac_ext
fi
eval ac_res=\$$3
	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
$as_echo "$ac_res" >&6; }
  eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}

} # ac_fn_c_check_func

# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES
# -------------------------------------------------------
# Tests whether HEADER exists, giving a warning if it cannot be compiled using
# the include files in INCLUDES and setting the cache variable VAR
# accordingly.
ac_fn_c_check_header_mongrel ()
{
  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
  if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then :
  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
$as_echo_n "checking for $2... " >&6; }
if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then :
  $as_echo_n "(cached) " >&6
fi
eval ac_res=\$$3
	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
$as_echo "$ac_res" >&6; }
else
  # Is the header compilable?
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5
$as_echo_n "checking $2 usability... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */
$4
#include <$2>
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
  ac_header_compiler=yes
else
  ac_header_compiler=no
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5
$as_echo "$ac_header_compiler" >&6; }

# Is the header present?
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5
$as_echo_n "checking $2 presence... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */
#include <$2>
_ACEOF
if ac_fn_c_try_cpp "$LINENO"; then :
  ac_header_preproc=yes
else
  ac_header_preproc=no
fi
rm -f conftest.err conftest.$ac_ext
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5
$as_echo "$ac_header_preproc" >&6; }

# So?  What about this header?
case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #((
  yes:no: )
    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5
$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;}
    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
    ;;
  no:yes:* )
    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5
$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;}
    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2:     check for missing prerequisite headers?" >&5
$as_echo "$as_me: WARNING: $2:     check for missing prerequisite headers?" >&2;}
    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5
$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;}
    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2:     section \"Present But Cannot Be Compiled\"" >&5
$as_echo "$as_me: WARNING: $2:     section \"Present But Cannot Be Compiled\"" >&2;}
    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
( cat <<\_ASBOX
## --------------------------------- ##
## Report this to BUG-REPORT-ADDRESS ##
## --------------------------------- ##
_ASBOX
     ) | sed "s/^/$as_me: WARNING:     /" >&2
    ;;
esac
  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
$as_echo_n "checking for $2... " >&6; }
if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then :
  $as_echo_n "(cached) " >&6
else
  eval "$3=\$ac_header_compiler"
fi
eval ac_res=\$$3
	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
$as_echo "$ac_res" >&6; }
fi
  eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}

} # ac_fn_c_check_header_mongrel

# ac_fn_c_try_run LINENO
# ----------------------
# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes
# that executables *can* be run.
ac_fn_c_try_run ()
{
  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
  if { { ac_try="$ac_link"
case "(($ac_try" in
  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
  *) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
$as_echo "$ac_try_echo"; } >&5
  (eval "$ac_link") 2>&5
  ac_status=$?
  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
  test $ac_status = 0; } && { ac_try='./conftest$ac_exeext'
  { { case "(($ac_try" in
  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
  *) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
$as_echo "$ac_try_echo"; } >&5
  (eval "$ac_try") 2>&5
  ac_status=$?
  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
  test $ac_status = 0; }; }; then :
  ac_retval=0
else
  $as_echo "$as_me: program exited with status $ac_status" >&5
       $as_echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5

       ac_retval=$ac_status
fi
  rm -rf conftest.dSYM conftest_ipa8_conftest.oo
  eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
  as_fn_set_status $ac_retval

} # ac_fn_c_try_run

# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES
# -------------------------------------------------------
# Tests whether HEADER exists and can be compiled using the include files in
# INCLUDES, setting the cache variable VAR accordingly.
ac_fn_c_check_header_compile ()
{
  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
$as_echo_n "checking for $2... " >&6; }
if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then :
  $as_echo_n "(cached) " >&6
else
  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */
$4
#include <$2>
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
  eval "$3=yes"
else
  eval "$3=no"
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
eval ac_res=\$$3
	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
$as_echo "$ac_res" >&6; }
  eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}

} # ac_fn_c_check_header_compile

# ac_fn_c_check_type LINENO TYPE VAR INCLUDES
# -------------------------------------------
# Tests whether TYPE exists after having included INCLUDES, setting cache
# variable VAR accordingly.
ac_fn_c_check_type ()
{
  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
$as_echo_n "checking for $2... " >&6; }
if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then :
  $as_echo_n "(cached) " >&6
else
  eval "$3=no"
  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */
$4
int
main ()
{
if (sizeof ($2))
	 return 0;
  ;
  return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */
$4
int
main ()
{
if (sizeof (($2)))
	    return 0;
  ;
  return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :

else
  eval "$3=yes"
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
eval ac_res=\$$3
	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
$as_echo "$ac_res" >&6; }
  eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}

} # ac_fn_c_check_type
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 FULL-PACKAGE-NAME $as_me VERSION, which was
generated by GNU Autoconf 2.65.  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=.
    $as_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=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
    esac
    case $ac_pass in
    1) as_fn_append ac_configure_args0 " '$ac_arg'" ;;
    2)
      as_fn_append 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
      as_fn_append ac_configure_args " '$ac_arg'"
      ;;
    esac
  done
done
{ ac_configure_args0=; unset ac_configure_args0;}
{ ac_configure_args1=; unset 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_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
      esac
      case $ac_var in #(
      _ | IFS | as_nl) ;; #(
      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
      *) { eval $ac_var=; 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=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
      esac
      $as_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=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
	esac
	$as_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 &&
      $as_echo "$as_me: caught signal $ac_signal"
    $as_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'; as_fn_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

$as_echo "/* confdefs.h */" > 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

cat >>confdefs.h <<_ACEOF
#define PACKAGE_URL "$PACKAGE_URL"
_ACEOF


# Let the site file select an alternate cache file if it wants to.
# Prefer an explicitly selected file to automatically selected ones.
ac_site_file1=NONE
ac_site_file2=NONE
if test -n "$CONFIG_SITE"; then
  ac_site_file1=$CONFIG_SITE
elif test "x$prefix" != xNONE; then
  ac_site_file1=$prefix/share/config.site
  ac_site_file2=$prefix/etc/config.site
else
  ac_site_file1=$ac_default_prefix/share/config.site
  ac_site_file2=$ac_default_prefix/etc/config.site
fi
for ac_site_file in "$ac_site_file1" "$ac_site_file2"
do
  test "x$ac_site_file" = xNONE && continue
  if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then
    { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
$as_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.  DJGPP emulates it as a regular file.
  if test /dev/null != "$cache_file" && test -f "$cache_file"; then
    { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5
$as_echo "$as_me: loading cache $cache_file" >&6;}
    case $cache_file in
      [\\/]* | ?:[\\/]* ) . "$cache_file";;
      *)                      . "./$cache_file";;
    esac
  fi
else
  { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5
$as_echo "$as_me: creating cache $cache_file" >&6;}
  >$cache_file
fi

as_fn_append ac_header_list " sys/time.h"
as_fn_append ac_header_list " unistd.h"
as_fn_append ac_func_list " alarm"
# 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,)
      { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
      ac_cache_corrupted=: ;;
    ,set)
      { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5
$as_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
	# differences in whitespace do not lead to failure.
	ac_old_val_w=`echo x $ac_old_val`
	ac_new_val_w=`echo x $ac_new_val`
	if test "$ac_old_val_w" != "$ac_new_val_w"; then
	  { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5
$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
	  ac_cache_corrupted=:
	else
	  { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
	  eval $ac_var=\$ac_old_val
	fi
	{ $as_echo "$as_me:${as_lineno-$LINENO}:   former value:  \`$ac_old_val'" >&5
$as_echo "$as_me:   former value:  \`$ac_old_val'" >&2;}
	{ $as_echo "$as_me:${as_lineno-$LINENO}:   current value: \`$ac_new_val'" >&5
$as_echo "$as_me:   current value: \`$ac_new_val'" >&2;}
      fi;;
  esac
  # Pass precious variables to config.status.
  if test "$ac_new_set" = set; then
    case $ac_new_val in
    *\'*) ac_arg=$ac_var=`$as_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.
      *) as_fn_append ac_configure_args " '$ac_arg'" ;;
    esac
  fi
done
if $ac_cache_corrupted; then
  { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
  { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;}
  as_fn_error "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5
fi
## -------------------- ##
## Main body of script. ##
## -------------------- ##

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_config_headers="$ac_config_headers aconf.h"


# Checks for programs.
ac_ext=cpp
ac_cpp='$CXXCPP $CPPFLAGS'
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
if test -z "$CXX"; then
  if test -n "$CCC"; then
    CXX=$CCC
  else
    if test -n "$ac_tool_prefix"; then
  for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC
  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
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if test "${ac_cv_prog_CXX+set}" = set; then :
  $as_echo_n "(cached) " >&6
else
  if test -n "$CXX"; then
  ac_cv_prog_CXX="$CXX" # 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_CXX="$ac_tool_prefix$ac_prog"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS

fi
fi
CXX=$ac_cv_prog_CXX
if test -n "$CXX"; then
  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5
$as_echo "$CXX" >&6; }
else
  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi


    test -n "$CXX" && break
  done
fi
if test -z "$CXX"; then
  ac_ct_CXX=$CXX
  for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC
do
  # Extract the first word of "$ac_prog", so it can be a program name with args.
set dummy $ac_prog; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if test "${ac_cv_prog_ac_ct_CXX+set}" = set; then :
  $as_echo_n "(cached) " >&6
else
  if test -n "$ac_ct_CXX"; then
  ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # 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_CXX="$ac_prog"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS

fi
fi
ac_ct_CXX=$ac_cv_prog_ac_ct_CXX
if test -n "$ac_ct_CXX"; then
  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5
$as_echo "$ac_ct_CXX" >&6; }
else
  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi


  test -n "$ac_ct_CXX" && break
done

  if test "x$ac_ct_CXX" = x; then
    CXX="g++"
  else
    case $cross_compiling:$ac_tool_warned in
yes:)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
    CXX=$ac_ct_CXX
  fi
fi

  fi
fi
# Provide some information about the compiler.
$as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5
set X $ac_compile
ac_compiler=$2
for ac_option in --version -v -V -qversion; do
  { { ac_try="$ac_compiler $ac_option >&5"
case "(($ac_try" in
  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
  *) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
$as_echo "$ac_try_echo"; } >&5
  (eval "$ac_compiler $ac_option >&5") 2>conftest.err
  ac_status=$?
  if test -s conftest.err; then
    sed '10a\
... rest of stderr output deleted ...
         10q' conftest.err >conftest.er1
    cat conftest.er1 >&5
  fi
  rm -f conftest.er1 conftest.err
  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
  test $ac_status = 0; }
done

cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */

int
main ()
{

  ;
  return 0;
}
_ACEOF
ac_clean_files_save=$ac_clean_files
ac_clean_files="$ac_clean_files a.out a.out.dSYM 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.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C++ compiler works" >&5
$as_echo_n "checking whether the C++ compiler works... " >&6; }
ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`

# The possible output files:
ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"

ac_rmfiles=
for ac_file in $ac_files
do
  case $ac_file in
    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.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 ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
$as_echo "$ac_try_echo"; } >&5
  (eval "$ac_link_default") 2>&5
  ac_status=$?
  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
  test $ac_status = 0; }; 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 | *.dSYM | *.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
if test -z "$ac_file"; then :
  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
$as_echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5

{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
{ as_fn_set_status 77
as_fn_error "C++ compiler cannot create executables
See \`config.log' for more details." "$LINENO" 5; }; }
else
  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler default output file name" >&5
$as_echo_n "checking for C++ compiler default output file name... " >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
$as_echo "$ac_file" >&6; }
ac_exeext=$ac_cv_exeext

rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
ac_clean_files=$ac_clean_files_save
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
$as_echo_n "checking for suffix of executables... " >&6; }
if { { ac_try="$ac_link"
case "(($ac_try" in
  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
  *) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
$as_echo "$ac_try_echo"; } >&5
  (eval "$ac_link") 2>&5
  ac_status=$?
  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
  test $ac_status = 0; }; 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 | *.dSYM | *.o | *.obj ) ;;
    *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
	  break;;
    * ) break;;
  esac
done
else
  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error "cannot compute suffix of executables: cannot compile and link
See \`config.log' for more details." "$LINENO" 5; }
fi
rm -f conftest conftest$ac_cv_exeext
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
$as_echo "$ac_cv_exeext" >&6; }

rm -f conftest.$ac_ext
EXEEXT=$ac_cv_exeext
ac_exeext=$EXEEXT
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */
#include <stdio.h>
int
main ()
{
FILE *f = fopen ("conftest.out", "w");
 return ferror (f) || fclose (f) != 0;

  ;
  return 0;
}
_ACEOF
ac_clean_files="$ac_clean_files conftest.out"
# Check that the compiler produces executables we can run.  If not, either
# the compiler is broken, or we cross compile.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
$as_echo_n "checking whether we are cross compiling... " >&6; }
if test "$cross_compiling" != yes; then
  { { ac_try="$ac_link"
case "(($ac_try" in
  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
  *) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
$as_echo "$ac_try_echo"; } >&5
  (eval "$ac_link") 2>&5
  ac_status=$?
  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
  test $ac_status = 0; }
  if { ac_try='./conftest$ac_cv_exeext'
  { { case "(($ac_try" in
  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
  *) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
$as_echo "$ac_try_echo"; } >&5
  (eval "$ac_try") 2>&5
  ac_status=$?
  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
  test $ac_status = 0; }; }; then
    cross_compiling=no
  else
    if test "$cross_compiling" = maybe; then
	cross_compiling=yes
    else
	{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error "cannot run C++ compiled programs.
If you meant to cross compile, use \`--host'.
See \`config.log' for more details." "$LINENO" 5; }
    fi
  fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
$as_echo "$cross_compiling" >&6; }

rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
ac_clean_files=$ac_clean_files_save
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
$as_echo_n "checking for suffix of object files... " >&6; }
if test "${ac_cv_objext+set}" = set; then :
  $as_echo_n "(cached) " >&6
else
  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* 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 ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
$as_echo "$ac_try_echo"; } >&5
  (eval "$ac_compile") 2>&5
  ac_status=$?
  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
  test $ac_status = 0; }; 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 | *.dSYM ) ;;
    *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
       break;;
  esac
done
else
  $as_echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5

{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error "cannot compute suffix of object files: cannot compile
See \`config.log' for more details." "$LINENO" 5; }
fi
rm -f conftest.$ac_cv_objext conftest.$ac_ext
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
$as_echo "$ac_cv_objext" >&6; }
OBJEXT=$ac_cv_objext
ac_objext=$OBJEXT
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5
$as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; }
if test "${ac_cv_cxx_compiler_gnu+set}" = set; then :
  $as_echo_n "(cached) " >&6
else
  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */

int
main ()
{
#ifndef __GNUC__
       choke me
#endif

  ;
  return 0;
}
_ACEOF
if ac_fn_cxx_try_compile "$LINENO"; then :
  ac_compiler_gnu=yes
else
  ac_compiler_gnu=no
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
ac_cv_cxx_compiler_gnu=$ac_compiler_gnu

fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5
$as_echo "$ac_cv_cxx_compiler_gnu" >&6; }
if test $ac_compiler_gnu = yes; then
  GXX=yes
else
  GXX=
fi
ac_test_CXXFLAGS=${CXXFLAGS+set}
ac_save_CXXFLAGS=$CXXFLAGS
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5
$as_echo_n "checking whether $CXX accepts -g... " >&6; }
if test "${ac_cv_prog_cxx_g+set}" = set; then :
  $as_echo_n "(cached) " >&6
else
  ac_save_cxx_werror_flag=$ac_cxx_werror_flag
   ac_cxx_werror_flag=yes
   ac_cv_prog_cxx_g=no
   CXXFLAGS="-g"
   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */

int
main ()
{

  ;
  return 0;
}
_ACEOF
if ac_fn_cxx_try_compile "$LINENO"; then :
  ac_cv_prog_cxx_g=yes
else
  CXXFLAGS=""
      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */

int
main ()
{

  ;
  return 0;
}
_ACEOF
if ac_fn_cxx_try_compile "$LINENO"; then :

else
  ac_cxx_werror_flag=$ac_save_cxx_werror_flag
	 CXXFLAGS="-g"
	 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */

int
main ()
{

  ;
  return 0;
}
_ACEOF
if ac_fn_cxx_try_compile "$LINENO"; then :
  ac_cv_prog_cxx_g=yes
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_cxx_werror_flag=$ac_save_cxx_werror_flag
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5
$as_echo "$ac_cv_prog_cxx_g" >&6; }
if test "$ac_test_CXXFLAGS" = set; then
  CXXFLAGS=$ac_save_CXXFLAGS
elif test $ac_cv_prog_cxx_g = yes; then
  if test "$GXX" = yes; then
    CXXFLAGS="-g -O2"
  else
    CXXFLAGS="-g"
  fi
else
  if test "$GXX" = yes; then
    CXXFLAGS="-O2"
  else
    CXXFLAGS=
  fi
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
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if test "${ac_cv_prog_CC+set}" = set; then :
  $as_echo_n "(cached) " >&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"
    $as_echo "$as_me:${as_lineno-$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
  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
$as_echo "$CC" >&6; }
else
  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "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
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if test "${ac_cv_prog_ac_ct_CC+set}" = set; then :
  $as_echo_n "(cached) " >&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"
    $as_echo "$as_me:${as_lineno-$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
  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
$as_echo "$ac_ct_CC" >&6; }
else
  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi

  if test "x$ac_ct_CC" = x; then
    CC=""
  else
    case $cross_compiling:$ac_tool_warned in
yes:)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&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
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if test "${ac_cv_prog_CC+set}" = set; then :
  $as_echo_n "(cached) " >&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"
    $as_echo "$as_me:${as_lineno-$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
  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
$as_echo "$CC" >&6; }
else
  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "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
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if test "${ac_cv_prog_CC+set}" = set; then :
  $as_echo_n "(cached) " >&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"
    $as_echo "$as_me:${as_lineno-$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
  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
$as_echo "$CC" >&6; }
else
  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "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
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if test "${ac_cv_prog_CC+set}" = set; then :
  $as_echo_n "(cached) " >&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"
    $as_echo "$as_me:${as_lineno-$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
  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
$as_echo "$CC" >&6; }
else
  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "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
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if test "${ac_cv_prog_ac_ct_CC+set}" = set; then :
  $as_echo_n "(cached) " >&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"
    $as_echo "$as_me:${as_lineno-$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
  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
$as_echo "$ac_ct_CC" >&6; }
else
  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "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:)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
    CC=$ac_ct_CC
  fi
fi

fi


test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error "no acceptable C compiler found in \$PATH
See \`config.log' for more details." "$LINENO" 5; }

# Provide some information about the compiler.
$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
set X $ac_compile
ac_compiler=$2
for ac_option in --version -v -V -qversion; do
  { { ac_try="$ac_compiler $ac_option >&5"
case "(($ac_try" in
  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
  *) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
$as_echo "$ac_try_echo"; } >&5
  (eval "$ac_compiler $ac_option >&5") 2>conftest.err
  ac_status=$?
  if test -s conftest.err; then
    sed '10a\
... rest of stderr output deleted ...
         10q' conftest.err >conftest.er1
    cat conftest.er1 >&5
  fi
  rm -f conftest.er1 conftest.err
  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
  test $ac_status = 0; }
done

{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5
$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
if test "${ac_cv_c_compiler_gnu+set}" = set; then :
  $as_echo_n "(cached) " >&6
else
  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */

int
main ()
{
#ifndef __GNUC__
       choke me
#endif

  ;
  return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
  ac_compiler_gnu=yes
else
  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
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
$as_echo "$ac_cv_c_compiler_gnu" >&6; }
if test $ac_compiler_gnu = yes; then
  GCC=yes
else
  GCC=
fi
ac_test_CFLAGS=${CFLAGS+set}
ac_save_CFLAGS=$CFLAGS
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
$as_echo_n "checking whether $CC accepts -g... " >&6; }
if test "${ac_cv_prog_cc_g+set}" = set; then :
  $as_echo_n "(cached) " >&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 confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */

int
main ()
{

  ;
  return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
  ac_cv_prog_cc_g=yes
else
  CFLAGS=""
      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */

int
main ()
{

  ;
  return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :

else
  ac_c_werror_flag=$ac_save_c_werror_flag
	 CFLAGS="-g"
	 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */

int
main ()
{

  ;
  return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
  ac_cv_prog_cc_g=yes
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
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
$as_echo "$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
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
if test "${ac_cv_prog_cc_c89+set}" = set; then :
  $as_echo_n "(cached) " >&6
else
  ac_cv_prog_cc_c89=no
ac_save_CC=$CC
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */
#include <stdarg.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
/* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */
struct buf { int x; };
FILE * (*rcsopen) (struct buf *, struct stat *, int);
static char *e (p, i)
     char **p;
     int i;
{
  return p[i];
}
static char *f (char * (*g) (char **, int), char **p, ...)
{
  char *s;
  va_list v;
  va_start (v,p);
  s = g (p, va_arg (v,int));
  va_end (v);
  return s;
}

/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default.  It has
   function prototypes and stuff, but not '\xHH' hex character constants.
   These don't provoke an error unfortunately, instead are silently treated
   as 'x'.  The following induces an error, until -std is added to get
   proper ANSI mode.  Curiously '\x00'!='x' always comes out true, for an
   array size at least.  It's necessary to write '\x00'==0 to get something
   that's true only with -std.  */
int osf4_cc_array ['\x00' == 0 ? 1 : -1];

/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
   inside strings and character constants.  */
#define FOO(x) 'x'
int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];

int test (int i, double x);
struct s1 {int (*f) (int a);};
struct s2 {int (*f) (double a);};
int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
int argc;
char **argv;
int
main ()
{
return f (e, argv, 0) != argv[0]  ||  f (e, argv, 1) != argv[1];
  ;
  return 0;
}
_ACEOF
for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
	-Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
do
  CC="$ac_save_CC $ac_arg"
  if ac_fn_c_try_compile "$LINENO"; then :
  ac_cv_prog_cc_c89=$ac_arg
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)
    { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
$as_echo "none needed" >&6; } ;;
  xno)
    { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
$as_echo "unsupported" >&6; } ;;
  *)
    CC="$CC $ac_cv_prog_cc_c89"
    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
esac
if test "x$ac_cv_prog_cc_c89" != xno; then :

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

if test -n "$ac_tool_prefix"; then
  # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
set dummy ${ac_tool_prefix}ranlib; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if test "${ac_cv_prog_RANLIB+set}" = set; then :
  $as_echo_n "(cached) " >&6
else
  if test -n "$RANLIB"; then
  ac_cv_prog_RANLIB="$RANLIB" # 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_RANLIB="${ac_tool_prefix}ranlib"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS

fi
fi
RANLIB=$ac_cv_prog_RANLIB
if test -n "$RANLIB"; then
  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5
$as_echo "$RANLIB" >&6; }
else
  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi


fi
if test -z "$ac_cv_prog_RANLIB"; then
  ac_ct_RANLIB=$RANLIB
  # Extract the first word of "ranlib", so it can be a program name with args.
set dummy ranlib; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then :
  $as_echo_n "(cached) " >&6
else
  if test -n "$ac_ct_RANLIB"; then
  ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # 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_RANLIB="ranlib"
    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    break 2
  fi
done
  done
IFS=$as_save_IFS

fi
fi
ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
if test -n "$ac_ct_RANLIB"; then
  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5
$as_echo "$ac_ct_RANLIB" >&6; }
else
  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi

  if test "x$ac_ct_RANLIB" = x; then
    RANLIB=":"
  else
    case $cross_compiling:$ac_tool_warned in
yes:)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
    RANLIB=$ac_ct_RANLIB
  fi
else
  RANLIB="$ac_cv_prog_RANLIB"
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
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5
$as_echo_n "checking how to run the C preprocessor... " >&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 :
  $as_echo_n "(cached) " >&6
else
      # Double quotes because CPP needs to be expanded
    for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
    do
      ac_preproc_ok=false
for ac_c_preproc_warn_flag in '' yes
do
  # Use a header file that comes with gcc, so configuring glibc
  # with a fresh cross-compiler works.
  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
  # <limits.h> exists even on freestanding compilers.
  # On the NeXT, cc -E runs the code through the compiler's parser,
  # not just through cpp. "Syntax error" is here to catch this case.
  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */
#ifdef __STDC__
# include <limits.h>
#else
# include <assert.h>
#endif
		     Syntax error
_ACEOF
if ac_fn_c_try_cpp "$LINENO"; then :

else
  # 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 confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */
#include <ac_nonexistent.h>
_ACEOF
if ac_fn_c_try_cpp "$LINENO"; then :
  # Broken: success on invalid input.
continue
else
  # 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
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5
$as_echo "$CPP" >&6; }
ac_preproc_ok=false
for ac_c_preproc_warn_flag in '' yes
do
  # Use a header file that comes with gcc, so configuring glibc
  # with a fresh cross-compiler works.
  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
  # <limits.h> exists even on freestanding compilers.
  # On the NeXT, cc -E runs the code through the compiler's parser,
  # not just through cpp. "Syntax error" is here to catch this case.
  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */
#ifdef __STDC__
# include <limits.h>
#else
# include <assert.h>
#endif
		     Syntax error
_ACEOF
if ac_fn_c_try_cpp "$LINENO"; then :

else
  # 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 confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */
#include <ac_nonexistent.h>
_ACEOF
if ac_fn_c_try_cpp "$LINENO"; then :
  # Broken: success on invalid input.
continue
else
  # 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
  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error "C preprocessor \"$CPP\" fails sanity check
See \`config.log' for more details." "$LINENO" 5; }
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


{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for X" >&5
$as_echo_n "checking for X... " >&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 #(
    *\'*) as_fn_error "cannot use X directory names containing '" "$LINENO" 5;; #(
    *,NONE | NONE,*) if test "${ac_cv_have_x+set}" = set; then :
  $as_echo_n "(cached) " >&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 dylib la dll; 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 | /usr/lib64 | /lib | /lib64) ;;
	*) 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/X11R7/include
/usr/X11R6/include
/usr/X11R5/include
/usr/X11R4/include

/usr/include/X11
/usr/include/X11R7
/usr/include/X11R6
/usr/include/X11R5
/usr/include/X11R4

/usr/local/X11/include
/usr/local/X11R7/include
/usr/local/X11R6/include
/usr/local/X11R5/include
/usr/local/X11R4/include

/usr/local/include/X11
/usr/local/include/X11R7
/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 confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */
#include <X11/Xlib.h>
_ACEOF
if ac_fn_c_try_cpp "$LINENO"; then :
  # We can compile using X headers with no special include directory.
ac_x_includes=
else
  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 confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */
#include <X11/Xlib.h>
int
main ()
{
XrmInitialize ()
  ;
  return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
  LIBS=$ac_save_LIBS
# We can link X programs with no special library path.
ac_x_libraries=
else
  LIBS=$ac_save_LIBS
for ac_dir in `$as_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 dylib la dll; 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$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
  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_x" >&5
$as_echo "$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'"
  { $as_echo "$as_me:${as_lineno-$LINENO}: result: libraries $x_libraries, headers $x_includes" >&5
$as_echo "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.

$as_echo "#define X_DISPLAY_MISSING 1" >>confdefs.h

  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 . . . .
    { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -R must be followed by a space" >&5
$as_echo_n "checking whether -R must be followed by a space... " >&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 confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */

int
main ()
{

  ;
  return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
       X_LIBS="$X_LIBS -R$x_libraries"
else
  LIBS="$ac_xsave_LIBS -R $x_libraries"
       cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */

int
main ()
{

  ;
  return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
	  X_LIBS="$X_LIBS -R $x_libraries"
else
  { $as_echo "$as_me:${as_lineno-$LINENO}: result: neither works" >&5
$as_echo "neither works" >&6; }
fi
rm -f core conftest.err conftest.$ac_objext \
    conftest$ac_exeext conftest.$ac_ext
fi
rm -f core conftest.err conftest.$ac_objext \
    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 confdefs.h - <<_ACEOF >conftest.$ac_ext
/* 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
if ac_fn_c_try_link "$LINENO"; then :

else
  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dnet_ntoa in -ldnet" >&5
$as_echo_n "checking for dnet_ntoa in -ldnet... " >&6; }
if test "${ac_cv_lib_dnet_dnet_ntoa+set}" = set; then :
  $as_echo_n "(cached) " >&6
else
  ac_check_lib_save_LIBS=$LIBS
LIBS="-ldnet  $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* 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
if ac_fn_c_try_link "$LINENO"; then :
  ac_cv_lib_dnet_dnet_ntoa=yes
else
  ac_cv_lib_dnet_dnet_ntoa=no
fi
rm -f core conftest.err conftest.$ac_objext \
    conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dnet_dnet_ntoa" >&5
$as_echo "$ac_cv_lib_dnet_dnet_ntoa" >&6; }
if test "x$ac_cv_lib_dnet_dnet_ntoa" = x""yes; then :
  X_EXTRA_LIBS="$X_EXTRA_LIBS -ldnet"
fi

    if test $ac_cv_lib_dnet_dnet_ntoa = no; then
      { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dnet_ntoa in -ldnet_stub" >&5
$as_echo_n "checking for dnet_ntoa in -ldnet_stub... " >&6; }
if test "${ac_cv_lib_dnet_stub_dnet_ntoa+set}" = set; then :
  $as_echo_n "(cached) " >&6
else
  ac_check_lib_save_LIBS=$LIBS
LIBS="-ldnet_stub  $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* 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
if ac_fn_c_try_link "$LINENO"; then :
  ac_cv_lib_dnet_stub_dnet_ntoa=yes
else
  ac_cv_lib_dnet_stub_dnet_ntoa=no
fi
rm -f core conftest.err conftest.$ac_objext \
    conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dnet_stub_dnet_ntoa" >&5
$as_echo "$ac_cv_lib_dnet_stub_dnet_ntoa" >&6; }
if test "x$ac_cv_lib_dnet_stub_dnet_ntoa" = x""yes; then :
  X_EXTRA_LIBS="$X_EXTRA_LIBS -ldnet_stub"
fi

    fi
fi
rm -f core conftest.err conftest.$ac_objext \
    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.
    ac_fn_c_check_func "$LINENO" "gethostbyname" "ac_cv_func_gethostbyname"
if test "x$ac_cv_func_gethostbyname" = x""yes; then :

fi

    if test $ac_cv_func_gethostbyname = no; then
      { $as_echo "$as_me:${as_lineno-$LINENO}: checking for gethostbyname in -lnsl" >&5
$as_echo_n "checking for gethostbyname in -lnsl... " >&6; }
if test "${ac_cv_lib_nsl_gethostbyname+set}" = set; then :
  $as_echo_n "(cached) " >&6
else
  ac_check_lib_save_LIBS=$LIBS
LIBS="-lnsl  $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* 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
if ac_fn_c_try_link "$LINENO"; then :
  ac_cv_lib_nsl_gethostbyname=yes
else
  ac_cv_lib_nsl_gethostbyname=no
fi
rm -f core conftest.err conftest.$ac_objext \
    conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_nsl_gethostbyname" >&5
$as_echo "$ac_cv_lib_nsl_gethostbyname" >&6; }
if test "x$ac_cv_lib_nsl_gethostbyname" = x""yes; then :
  X_EXTRA_LIBS="$X_EXTRA_LIBS -lnsl"
fi

      if test $ac_cv_lib_nsl_gethostbyname = no; then
	{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for gethostbyname in -lbsd" >&5
$as_echo_n "checking for gethostbyname in -lbsd... " >&6; }
if test "${ac_cv_lib_bsd_gethostbyname+set}" = set; then :
  $as_echo_n "(cached) " >&6
else
  ac_check_lib_save_LIBS=$LIBS
LIBS="-lbsd  $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* 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
if ac_fn_c_try_link "$LINENO"; then :
  ac_cv_lib_bsd_gethostbyname=yes
else
  ac_cv_lib_bsd_gethostbyname=no
fi
rm -f core conftest.err conftest.$ac_objext \
    conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_bsd_gethostbyname" >&5
$as_echo "$ac_cv_lib_bsd_gethostbyname" >&6; }
if test "x$ac_cv_lib_bsd_gethostbyname" = x""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.
    ac_fn_c_check_func "$LINENO" "connect" "ac_cv_func_connect"
if test "x$ac_cv_func_connect" = x""yes; then :

fi

    if test $ac_cv_func_connect = no; then
      { $as_echo "$as_me:${as_lineno-$LINENO}: checking for connect in -lsocket" >&5
$as_echo_n "checking for connect in -lsocket... " >&6; }
if test "${ac_cv_lib_socket_connect+set}" = set; then :
  $as_echo_n "(cached) " >&6
else
  ac_check_lib_save_LIBS=$LIBS
LIBS="-lsocket $X_EXTRA_LIBS $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* 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
if ac_fn_c_try_link "$LINENO"; then :
  ac_cv_lib_socket_connect=yes
else
  ac_cv_lib_socket_connect=no
fi
rm -f core conftest.err conftest.$ac_objext \
    conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_socket_connect" >&5
$as_echo "$ac_cv_lib_socket_connect" >&6; }
if test "x$ac_cv_lib_socket_connect" = x""yes; then :
  X_EXTRA_LIBS="-lsocket $X_EXTRA_LIBS"
fi

    fi

    # Guillermo Gomez says -lposix is necessary on A/UX.
    ac_fn_c_check_func "$LINENO" "remove" "ac_cv_func_remove"
if test "x$ac_cv_func_remove" = x""yes; then :

fi

    if test $ac_cv_func_remove = no; then
      { $as_echo "$as_me:${as_lineno-$LINENO}: checking for remove in -lposix" >&5
$as_echo_n "checking for remove in -lposix... " >&6; }
if test "${ac_cv_lib_posix_remove+set}" = set; then :
  $as_echo_n "(cached) " >&6
else
  ac_check_lib_save_LIBS=$LIBS
LIBS="-lposix  $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* 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
if ac_fn_c_try_link "$LINENO"; then :
  ac_cv_lib_posix_remove=yes
else
  ac_cv_lib_posix_remove=no
fi
rm -f core conftest.err conftest.$ac_objext \
    conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_posix_remove" >&5
$as_echo "$ac_cv_lib_posix_remove" >&6; }
if test "x$ac_cv_lib_posix_remove" = x""yes; then :
  X_EXTRA_LIBS="$X_EXTRA_LIBS -lposix"
fi

    fi

    # BSDI BSD/OS 2.1 needs -lipc for XOpenDisplay.
    ac_fn_c_check_func "$LINENO" "shmat" "ac_cv_func_shmat"
if test "x$ac_cv_func_shmat" = x""yes; then :

fi

    if test $ac_cv_func_shmat = no; then
      { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shmat in -lipc" >&5
$as_echo_n "checking for shmat in -lipc... " >&6; }
if test "${ac_cv_lib_ipc_shmat+set}" = set; then :
  $as_echo_n "(cached) " >&6
else
  ac_check_lib_save_LIBS=$LIBS
LIBS="-lipc  $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* 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
if ac_fn_c_try_link "$LINENO"; then :
  ac_cv_lib_ipc_shmat=yes
else
  ac_cv_lib_ipc_shmat=no
fi
rm -f core conftest.err conftest.$ac_objext \
    conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ipc_shmat" >&5
$as_echo "$ac_cv_lib_ipc_shmat" >&6; }
if test "x$ac_cv_lib_ipc_shmat" = x""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
  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for IceConnectionNumber in -lICE" >&5
$as_echo_n "checking for IceConnectionNumber in -lICE... " >&6; }
if test "${ac_cv_lib_ICE_IceConnectionNumber+set}" = set; then :
  $as_echo_n "(cached) " >&6
else
  ac_check_lib_save_LIBS=$LIBS
LIBS="-lICE $X_EXTRA_LIBS $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* 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
if ac_fn_c_try_link "$LINENO"; then :
  ac_cv_lib_ICE_IceConnectionNumber=yes
else
  ac_cv_lib_ICE_IceConnectionNumber=no
fi
rm -f core conftest.err conftest.$ac_objext \
    conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ICE_IceConnectionNumber" >&5
$as_echo "$ac_cv_lib_ICE_IceConnectionNumber" >&6; }
if test "x$ac_cv_lib_ICE_IceConnectionNumber" = x""yes; then :
  X_PRE_LIBS="$X_PRE_LIBS -lSM -lICE"
fi

  LDFLAGS=$ac_save_LDFLAGS

fi

ac_header_dirent=no
for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h; do
  as_ac_Header=`$as_echo "ac_cv_header_dirent_$ac_hdr" | $as_tr_sh`
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_hdr that defines DIR" >&5
$as_echo_n "checking for $ac_hdr that defines DIR... " >&6; }
if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then :
  $as_echo_n "(cached) " >&6
else
  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */
#include <sys/types.h>
#include <$ac_hdr>

int
main ()
{
if ((DIR *) 0)
return 0;
  ;
  return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
  eval "$as_ac_Header=yes"
else
  eval "$as_ac_Header=no"
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
eval ac_res=\$$as_ac_Header
	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
$as_echo "$ac_res" >&6; }
eval as_val=\$$as_ac_Header
   if test "x$as_val" = x""yes; then :
  cat >>confdefs.h <<_ACEOF
#define `$as_echo "HAVE_$ac_hdr" | $as_tr_cpp` 1
_ACEOF

ac_header_dirent=$ac_hdr; break
fi

done
# Two versions of opendir et al. are in -ldir and -lx on SCO Xenix.
if test $ac_header_dirent = dirent.h; then
  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing opendir" >&5
$as_echo_n "checking for library containing opendir... " >&6; }
if test "${ac_cv_search_opendir+set}" = set; then :
  $as_echo_n "(cached) " >&6
else
  ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* 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 opendir ();
int
main ()
{
return opendir ();
  ;
  return 0;
}
_ACEOF
for ac_lib in '' dir; do
  if test -z "$ac_lib"; then
    ac_res="none required"
  else
    ac_res=-l$ac_lib
    LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
  fi
  if ac_fn_c_try_link "$LINENO"; then :
  ac_cv_search_opendir=$ac_res
fi
rm -f core conftest.err conftest.$ac_objext \
    conftest$ac_exeext
  if test "${ac_cv_search_opendir+set}" = set; then :
  break
fi
done
if test "${ac_cv_search_opendir+set}" = set; then :

else
  ac_cv_search_opendir=no
fi
rm conftest.$ac_ext
LIBS=$ac_func_search_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_opendir" >&5
$as_echo "$ac_cv_search_opendir" >&6; }
ac_res=$ac_cv_search_opendir
if test "$ac_res" != no; then :
  test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"

fi

else
  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing opendir" >&5
$as_echo_n "checking for library containing opendir... " >&6; }
if test "${ac_cv_search_opendir+set}" = set; then :
  $as_echo_n "(cached) " >&6
else
  ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* 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 opendir ();
int
main ()
{
return opendir ();
  ;
  return 0;
}
_ACEOF
for ac_lib in '' x; do
  if test -z "$ac_lib"; then
    ac_res="none required"
  else
    ac_res=-l$ac_lib
    LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
  fi
  if ac_fn_c_try_link "$LINENO"; then :
  ac_cv_search_opendir=$ac_res
fi
rm -f core conftest.err conftest.$ac_objext \
    conftest$ac_exeext
  if test "${ac_cv_search_opendir+set}" = set; then :
  break
fi
done
if test "${ac_cv_search_opendir+set}" = set; then :

else
  ac_cv_search_opendir=no
fi
rm conftest.$ac_ext
LIBS=$ac_func_search_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_opendir" >&5
$as_echo "$ac_cv_search_opendir" >&6; }
ac_res=$ac_cv_search_opendir
if test "$ac_res" != no; then :
  test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"

fi

fi


# Checks for libraries.
# FIXME: Replace `main' with a function in `-lGoo':
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lGoo" >&5
$as_echo_n "checking for main in -lGoo... " >&6; }
if test "${ac_cv_lib_Goo_main+set}" = set; then :
  $as_echo_n "(cached) " >&6
else
  ac_check_lib_save_LIBS=$LIBS
LIBS="-lGoo  $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */


int
main ()
{
return main ();
  ;
  return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
  ac_cv_lib_Goo_main=yes
else
  ac_cv_lib_Goo_main=no
fi
rm -f core conftest.err conftest.$ac_objext \
    conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_Goo_main" >&5
$as_echo "$ac_cv_lib_Goo_main" >&6; }
if test "x$ac_cv_lib_Goo_main" = x""yes; then :
  cat >>confdefs.h <<_ACEOF
#define HAVE_LIBGOO 1
_ACEOF

  LIBS="-lGoo $LIBS"

fi

# FIXME: Replace `main' with a function in `-lX11':
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lX11" >&5
$as_echo_n "checking for main in -lX11... " >&6; }
if test "${ac_cv_lib_X11_main+set}" = set; then :
  $as_echo_n "(cached) " >&6
else
  ac_check_lib_save_LIBS=$LIBS
LIBS="-lX11  $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */


int
main ()
{
return main ();
  ;
  return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
  ac_cv_lib_X11_main=yes
else
  ac_cv_lib_X11_main=no
fi
rm -f core conftest.err conftest.$ac_objext \
    conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_X11_main" >&5
$as_echo "$ac_cv_lib_X11_main" >&6; }
if test "x$ac_cv_lib_X11_main" = x""yes; then :
  cat >>confdefs.h <<_ACEOF
#define HAVE_LIBX11 1
_ACEOF

  LIBS="-lX11 $LIBS"

fi

# FIXME: Replace `main' with a function in `-lXpdf':
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lXpdf" >&5
$as_echo_n "checking for main in -lXpdf... " >&6; }
if test "${ac_cv_lib_Xpdf_main+set}" = set; then :
  $as_echo_n "(cached) " >&6
else
  ac_check_lib_save_LIBS=$LIBS
LIBS="-lXpdf  $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */


int
main ()
{
return main ();
  ;
  return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
  ac_cv_lib_Xpdf_main=yes
else
  ac_cv_lib_Xpdf_main=no
fi
rm -f core conftest.err conftest.$ac_objext \
    conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_Xpdf_main" >&5
$as_echo "$ac_cv_lib_Xpdf_main" >&6; }
if test "x$ac_cv_lib_Xpdf_main" = x""yes; then :
  cat >>confdefs.h <<_ACEOF
#define HAVE_LIBXPDF 1
_ACEOF

  LIBS="-lXpdf $LIBS"

fi

# FIXME: Replace `main' with a function in `-lfofi':
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lfofi" >&5
$as_echo_n "checking for main in -lfofi... " >&6; }
if test "${ac_cv_lib_fofi_main+set}" = set; then :
  $as_echo_n "(cached) " >&6
else
  ac_check_lib_save_LIBS=$LIBS
LIBS="-lfofi  $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */


int
main ()
{
return main ();
  ;
  return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
  ac_cv_lib_fofi_main=yes
else
  ac_cv_lib_fofi_main=no
fi
rm -f core conftest.err conftest.$ac_objext \
    conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_fofi_main" >&5
$as_echo "$ac_cv_lib_fofi_main" >&6; }
if test "x$ac_cv_lib_fofi_main" = x""yes; then :
  cat >>confdefs.h <<_ACEOF
#define HAVE_LIBFOFI 1
_ACEOF

  LIBS="-lfofi $LIBS"

fi

# FIXME: Replace `main' with a function in `-lm':
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lm" >&5
$as_echo_n "checking for main in -lm... " >&6; }
if test "${ac_cv_lib_m_main+set}" = set; then :
  $as_echo_n "(cached) " >&6
else
  ac_check_lib_save_LIBS=$LIBS
LIBS="-lm  $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */


int
main ()
{
return main ();
  ;
  return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
  ac_cv_lib_m_main=yes
else
  ac_cv_lib_m_main=no
fi
rm -f core conftest.err conftest.$ac_objext \
    conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_m_main" >&5
$as_echo "$ac_cv_lib_m_main" >&6; }
if test "x$ac_cv_lib_m_main" = x""yes; then :
  cat >>confdefs.h <<_ACEOF
#define HAVE_LIBM 1
_ACEOF

  LIBS="-lm $LIBS"

fi

# FIXME: Replace `main' with a function in `-lsplash':
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lsplash" >&5
$as_echo_n "checking for main in -lsplash... " >&6; }
if test "${ac_cv_lib_splash_main+set}" = set; then :
  $as_echo_n "(cached) " >&6
else
  ac_check_lib_save_LIBS=$LIBS
LIBS="-lsplash  $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */


int
main ()
{
return main ();
  ;
  return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
  ac_cv_lib_splash_main=yes
else
  ac_cv_lib_splash_main=no
fi
rm -f core conftest.err conftest.$ac_objext \
    conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_splash_main" >&5
$as_echo "$ac_cv_lib_splash_main" >&6; }
if test "x$ac_cv_lib_splash_main" = x""yes; then :
  cat >>confdefs.h <<_ACEOF
#define HAVE_LIBSPLASH 1
_ACEOF

  LIBS="-lsplash $LIBS"

fi


# Checks for header files.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5
$as_echo_n "checking for grep that handles long lines and -e... " >&6; }
if test "${ac_cv_path_GREP+set}" = set; then :
  $as_echo_n "(cached) " >&6
else
  if test -z "$GREP"; then
  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
  $as_echo_n 0123456789 >"conftest.in"
  while :
  do
    cat "conftest.in" "conftest.in" >"conftest.tmp"
    mv "conftest.tmp" "conftest.in"
    cp "conftest.in" "conftest.nl"
    $as_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
    as_fn_arith $ac_count + 1 && ac_count=$as_val
    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
  if test -z "$ac_cv_path_GREP"; then
    as_fn_error "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
  fi
else
  ac_cv_path_GREP=$GREP
fi

fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5
$as_echo "$ac_cv_path_GREP" >&6; }
 GREP="$ac_cv_path_GREP"


{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5
$as_echo_n "checking for egrep... " >&6; }
if test "${ac_cv_path_EGREP+set}" = set; then :
  $as_echo_n "(cached) " >&6
else
  if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
   then ac_cv_path_EGREP="$GREP -E"
   else
     if test -z "$EGREP"; then
  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
  $as_echo_n 0123456789 >"conftest.in"
  while :
  do
    cat "conftest.in" "conftest.in" >"conftest.tmp"
    mv "conftest.tmp" "conftest.in"
    cp "conftest.in" "conftest.nl"
    $as_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
    as_fn_arith $ac_count + 1 && ac_count=$as_val
    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
  if test -z "$ac_cv_path_EGREP"; then
    as_fn_error "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
  fi
else
  ac_cv_path_EGREP=$EGREP
fi

   fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5
$as_echo "$ac_cv_path_EGREP" >&6; }
 EGREP="$ac_cv_path_EGREP"


{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
$as_echo_n "checking for ANSI C header files... " >&6; }
if test "${ac_cv_header_stdc+set}" = set; then :
  $as_echo_n "(cached) " >&6
else
  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <float.h>

int
main ()
{

  ;
  return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
  ac_cv_header_stdc=yes
else
  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 confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */
#include <string.h>

_ACEOF
if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
  $EGREP "memchr" >/dev/null 2>&1; then :

else
  ac_cv_header_stdc=no
fi
rm -f conftest*

fi

if test $ac_cv_header_stdc = yes; then
  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */
#include <stdlib.h>

_ACEOF
if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
  $EGREP "free" >/dev/null 2>&1; then :

else
  ac_cv_header_stdc=no
fi
rm -f conftest*

fi

if test $ac_cv_header_stdc = yes; then
  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
  if test "$cross_compiling" = yes; then :
  :
else
  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */
#include <ctype.h>
#include <stdlib.h>
#if ((' ' & 0x0FF) == 0x020)
# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
#else
# define ISLOWER(c) \
		   (('a' <= (c) && (c) <= 'i') \
		     || ('j' <= (c) && (c) <= 'r') \
		     || ('s' <= (c) && (c) <= 'z'))
# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
#endif

#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
int
main ()
{
  int i;
  for (i = 0; i < 256; i++)
    if (XOR (islower (i), ISLOWER (i))
	|| toupper (i) != TOUPPER (i))
      return 2;
  return 0;
}
_ACEOF
if ac_fn_c_try_run "$LINENO"; then :

else
  ac_cv_header_stdc=no
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
  conftest.$ac_objext conftest.beam conftest.$ac_ext
fi

fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5
$as_echo "$ac_cv_header_stdc" >&6; }
if test $ac_cv_header_stdc = yes; then

$as_echo "#define STDC_HEADERS 1" >>confdefs.h

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=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default
"
eval as_val=\$$as_ac_Header
   if test "x$as_val" = x""yes; then :
  cat >>confdefs.h <<_ACEOF
#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
_ACEOF

fi

done


for ac_header in fcntl.h limits.h stddef.h stdlib.h string.h unistd.h
do :
  as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
eval as_val=\$$as_ac_Header
   if test "x$as_val" = x""yes; then :
  cat >>confdefs.h <<_ACEOF
#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
_ACEOF

fi

done


# Checks for typedefs, structures, and compiler characteristics.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for stdbool.h that conforms to C99" >&5
$as_echo_n "checking for stdbool.h that conforms to C99... " >&6; }
if test "${ac_cv_header_stdbool_h+set}" = set; then :
  $as_echo_n "(cached) " >&6
else
  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */

#include <stdbool.h>
#ifndef bool
 "error: bool is not defined"
#endif
#ifndef false
 "error: false is not defined"
#endif
#if false
 "error: false is not 0"
#endif
#ifndef true
 "error: true is not defined"
#endif
#if true != 1
 "error: true is not 1"
#endif
#ifndef __bool_true_false_are_defined
 "error: __bool_true_false_are_defined is not defined"
#endif

	struct s { _Bool s: 1; _Bool t; } s;

	char a[true == 1 ? 1 : -1];
	char b[false == 0 ? 1 : -1];
	char c[__bool_true_false_are_defined == 1 ? 1 : -1];
	char d[(bool) 0.5 == true ? 1 : -1];
	bool e = &s;
	char f[(_Bool) 0.0 == false ? 1 : -1];
	char g[true];
	char h[sizeof (_Bool)];
	char i[sizeof s.t];
	enum { j = false, k = true, l = false * true, m = true * 256 };
	/* The following fails for
	   HP aC++/ANSI C B3910B A.05.55 [Dec 04 2003]. */
	_Bool n[m];
	char o[sizeof n == m * sizeof n[0] ? 1 : -1];
	char p[-1 - (_Bool) 0 < 0 && -1 - (bool) 0 < 0 ? 1 : -1];
#	if defined __xlc__ || defined __GNUC__
	 /* Catch a bug in IBM AIX xlc compiler version 6.0.0.0
	    reported by James Lemley on 2005-10-05; see
	    http://lists.gnu.org/archive/html/bug-coreutils/2005-10/msg00086.html
	    This test is not quite right, since xlc is allowed to
	    reject this program, as the initializer for xlcbug is
	    not one of the forms that C requires support for.
	    However, doing the test right would require a runtime
	    test, and that would make cross-compilation harder.
	    Let us hope that IBM fixes the xlc bug, and also adds
	    support for this kind of constant expression.  In the
	    meantime, this test will reject xlc, which is OK, since
	    our stdbool.h substitute should suffice.  We also test
	    this with GCC, where it should work, to detect more
	    quickly whether someone messes up the test in the
	    future.  */
	 char digs[] = "0123456789";
	 int xlcbug = 1 / (&(digs + 5)[-2 + (bool) 1] == &digs[4] ? 1 : -1);
#	endif
	/* Catch a bug in an HP-UX C compiler.  See
	   http://gcc.gnu.org/ml/gcc-patches/2003-12/msg02303.html
	   http://lists.gnu.org/archive/html/bug-coreutils/2005-11/msg00161.html
	 */
	_Bool q = true;
	_Bool *pq = &q;

int
main ()
{

	*pq |= q;
	*pq |= ! q;
	/* Refer to every declared value, to avoid compiler optimizations.  */
	return (!a + !b + !c + !d + !e + !f + !g + !h + !i + !!j + !k + !!l
		+ !m + !n + !o + !p + !q + !pq);

  ;
  return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
  ac_cv_header_stdbool_h=yes
else
  ac_cv_header_stdbool_h=no
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdbool_h" >&5
$as_echo "$ac_cv_header_stdbool_h" >&6; }
ac_fn_c_check_type "$LINENO" "_Bool" "ac_cv_type__Bool" "$ac_includes_default"
if test "x$ac_cv_type__Bool" = x""yes; then :

cat >>confdefs.h <<_ACEOF
#define HAVE__BOOL 1
_ACEOF


fi

if test $ac_cv_header_stdbool_h = yes; then

$as_echo "#define HAVE_STDBOOL_H 1" >>confdefs.h

fi

{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for inline" >&5
$as_echo_n "checking for inline... " >&6; }
if test "${ac_cv_c_inline+set}" = set; then :
  $as_echo_n "(cached) " >&6
else
  ac_cv_c_inline=no
for ac_kw in inline __inline__ __inline; do
  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */
#ifndef __cplusplus
typedef int foo_t;
static $ac_kw foo_t static_foo () {return 0; }
$ac_kw foo_t foo () {return 0; }
#endif

_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
  ac_cv_c_inline=$ac_kw
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
  test "$ac_cv_c_inline" != no && break
done

fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_inline" >&5
$as_echo "$ac_cv_c_inline" >&6; }

case $ac_cv_c_inline in
  inline | yes) ;;
  *)
    case $ac_cv_c_inline in
      no) ac_val=;;
      *) ac_val=$ac_cv_c_inline;;
    esac
    cat >>confdefs.h <<_ACEOF
#ifndef __cplusplus
#define inline $ac_val
#endif
_ACEOF
    ;;
esac

ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default"
if test "x$ac_cv_type_size_t" = x""yes; then :

else

cat >>confdefs.h <<_ACEOF
#define size_t unsigned int
_ACEOF

fi


if test "$sysconfdir" = '${prefix}/etc'; then
  if test "x$prefix" = xNONE; then
    system_xpdfrc="$ac_default_prefix/etc/xpdfrc"
  else
    system_xpdfrc="$prefix/etc/xpdfrc"
  fi
else
  system_xpdfrc="$sysconfdir/xpdfrc"
fi
cat >>confdefs.h <<_ACEOF
#define SYSTEM_XPDFRC "$system_xpdfrc"
_ACEOF


for ac_func in popen
do :
  ac_fn_c_check_func "$LINENO" "popen" "ac_cv_func_popen"
if test "x$ac_cv_func_popen" = x""yes; then :
  cat >>confdefs.h <<_ACEOF
#define HAVE_POPEN 1
_ACEOF

fi
done

{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for mkstemp" >&5
$as_echo_n "checking for mkstemp... " >&6; }
if test "${xpdf_cv_func_mkstemp+set}" = set; then :
  $as_echo_n "(cached) " >&6
else
  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */
#include <stdlib.h>
#include <unistd.h>
int
main ()
{
mkstemp("foo");
  ;
  return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
  xpdf_cv_func_mkstemp=yes
else
  xpdf_cv_func_mkstemp=no
fi
rm -f core conftest.err conftest.$ac_objext \
    conftest$ac_exeext conftest.$ac_ext
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $xpdf_cv_func_mkstemp" >&5
$as_echo "$xpdf_cv_func_mkstemp" >&6; }
if test "$xpdf_cv_func_mkstemp" = yes; then
  $as_echo "#define HAVE_MKSTEMP 1" >>confdefs.h

fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for mkstemps" >&5
$as_echo_n "checking for mkstemps... " >&6; }
if test "${xpdf_cv_func_mkstemps+set}" = set; then :
  $as_echo_n "(cached) " >&6
else
  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */
#include <stdlib.h>
#include <unistd.h>
int
main ()
{
mkstemps("foo", 0);
  ;
  return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
  xpdf_cv_func_mkstemps=yes
else
  xpdf_cv_func_mkstemps=no
fi
rm -f core conftest.err conftest.$ac_objext \
    conftest$ac_exeext conftest.$ac_ext
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $xpdf_cv_func_mkstemps" >&5
$as_echo "$xpdf_cv_func_mkstemps" >&6; }
if test "$xpdf_cv_func_mkstemps" = yes; then
  $as_echo "#define HAVE_MKSTEMPS 1" >>confdefs.h

fi

# Checks for library functions.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for error_at_line" >&5
$as_echo_n "checking for error_at_line... " >&6; }
if test "${ac_cv_lib_error_at_line+set}" = set; then :
  $as_echo_n "(cached) " >&6
else
  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */
#include <error.h>
int
main ()
{
error_at_line (0, 0, "", 0, "an error occurred");
  ;
  return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
  ac_cv_lib_error_at_line=yes
else
  ac_cv_lib_error_at_line=no
fi
rm -f core conftest.err conftest.$ac_objext \
    conftest$ac_exeext conftest.$ac_ext
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_error_at_line" >&5
$as_echo "$ac_cv_lib_error_at_line" >&6; }
if test $ac_cv_lib_error_at_line = no; then
  case " $LIBOBJS " in
  *" error.$ac_objext "* ) ;;
  *) LIBOBJS="$LIBOBJS error.$ac_objext"
 ;;
esac

fi

{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for _LARGEFILE_SOURCE value needed for large files" >&5
$as_echo_n "checking for _LARGEFILE_SOURCE value needed for large files... " >&6; }
if test "${ac_cv_sys_largefile_source+set}" = set; then :
  $as_echo_n "(cached) " >&6
else
  while :; do
  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */
#include <sys/types.h> /* for off_t */
     #include <stdio.h>
int
main ()
{
int (*fp) (FILE *, off_t, int) = fseeko;
     return fseeko (stdin, 0, 0) && fp (stdin, 0, 0);
  ;
  return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
  ac_cv_sys_largefile_source=no; break
fi
rm -f core conftest.err conftest.$ac_objext \
    conftest$ac_exeext conftest.$ac_ext
  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */
#define _LARGEFILE_SOURCE 1
#include <sys/types.h> /* for off_t */
     #include <stdio.h>
int
main ()
{
int (*fp) (FILE *, off_t, int) = fseeko;
     return fseeko (stdin, 0, 0) && fp (stdin, 0, 0);
  ;
  return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
  ac_cv_sys_largefile_source=1; break
fi
rm -f core conftest.err conftest.$ac_objext \
    conftest$ac_exeext conftest.$ac_ext
  ac_cv_sys_largefile_source=unknown
  break
done
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_largefile_source" >&5
$as_echo "$ac_cv_sys_largefile_source" >&6; }
case $ac_cv_sys_largefile_source in #(
  no | unknown) ;;
  *)
cat >>confdefs.h <<_ACEOF
#define _LARGEFILE_SOURCE $ac_cv_sys_largefile_source
_ACEOF
;;
esac
rm -rf conftest*

# We used to try defining _XOPEN_SOURCE=500 too, to work around a bug
# in glibc 2.1.3, but that breaks too many other things.
# If you want fseeko and ftello with glibc, upgrade to a fixed glibc.
if test $ac_cv_sys_largefile_source != unknown; then

$as_echo "#define HAVE_FSEEKO 1" >>confdefs.h

fi

for ac_header in stdlib.h
do :
  ac_fn_c_check_header_mongrel "$LINENO" "stdlib.h" "ac_cv_header_stdlib_h" "$ac_includes_default"
if test "x$ac_cv_header_stdlib_h" = x""yes; then :
  cat >>confdefs.h <<_ACEOF
#define HAVE_STDLIB_H 1
_ACEOF

fi

done

{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU libc compatible malloc" >&5
$as_echo_n "checking for GNU libc compatible malloc... " >&6; }
if test "${ac_cv_func_malloc_0_nonnull+set}" = set; then :
  $as_echo_n "(cached) " >&6
else
  if test "$cross_compiling" = yes; then :
  ac_cv_func_malloc_0_nonnull=no
else
  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */
#if defined STDC_HEADERS || defined HAVE_STDLIB_H
# include <stdlib.h>
#else
char *malloc ();
#endif

int
main ()
{
return ! malloc (0);
  ;
  return 0;
}
_ACEOF
if ac_fn_c_try_run "$LINENO"; then :
  ac_cv_func_malloc_0_nonnull=yes
else
  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.beam conftest.$ac_ext
fi

fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_malloc_0_nonnull" >&5
$as_echo "$ac_cv_func_malloc_0_nonnull" >&6; }
if test $ac_cv_func_malloc_0_nonnull = yes; then :

$as_echo "#define HAVE_MALLOC 1" >>confdefs.h

else
  $as_echo "#define HAVE_MALLOC 0" >>confdefs.h

   case " $LIBOBJS " in
  *" malloc.$ac_objext "* ) ;;
  *) LIBOBJS="$LIBOBJS malloc.$ac_objext"
 ;;
esac


$as_echo "#define malloc rpl_malloc" >>confdefs.h

fi


{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether time.h and sys/time.h may both be included" >&5
$as_echo_n "checking whether time.h and sys/time.h may both be included... " >&6; }
if test "${ac_cv_header_time+set}" = set; then :
  $as_echo_n "(cached) " >&6
else
  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */
#include <sys/types.h>
#include <sys/time.h>
#include <time.h>

int
main ()
{
if ((struct tm *) 0)
return 0;
  ;
  return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
  ac_cv_header_time=yes
else
  ac_cv_header_time=no
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_time" >&5
$as_echo "$ac_cv_header_time" >&6; }
if test $ac_cv_header_time = yes; then

$as_echo "#define TIME_WITH_SYS_TIME 1" >>confdefs.h

fi




  for ac_header in $ac_header_list
do :
  as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default
"
eval as_val=\$$as_ac_Header
   if test "x$as_val" = x""yes; then :
  cat >>confdefs.h <<_ACEOF
#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
_ACEOF

fi

done








  for ac_func in $ac_func_list
do :
  as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
eval as_val=\$$as_ac_var
   if test "x$as_val" = x""yes; then :
  cat >>confdefs.h <<_ACEOF
#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
_ACEOF

fi
done





{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for working mktime" >&5
$as_echo_n "checking for working mktime... " >&6; }
if test "${ac_cv_func_working_mktime+set}" = set; then :
  $as_echo_n "(cached) " >&6
else
  if test "$cross_compiling" = yes; then :
  ac_cv_func_working_mktime=no
else
  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */
/* Test program from Paul Eggert and Tony Leneis.  */
#ifdef TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# ifdef HAVE_SYS_TIME_H
#  include <sys/time.h>
# else
#  include <time.h>
# endif
#endif

#include <limits.h>
#include <stdlib.h>

#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif

#ifndef HAVE_ALARM
# define alarm(X) /* empty */
#endif

/* Work around redefinition to rpl_putenv by other config tests.  */
#undef putenv

static time_t time_t_max;
static time_t time_t_min;

/* Values we'll use to set the TZ environment variable.  */
static char *tz_strings[] = {
  (char *) 0, "TZ=GMT0", "TZ=JST-9",
  "TZ=EST+3EDT+2,M10.1.0/00:00:00,M2.3.0/00:00:00"
};
#define N_STRINGS (sizeof (tz_strings) / sizeof (tz_strings[0]))

/* Return 0 if mktime fails to convert a date in the spring-forward gap.
   Based on a problem report from Andreas Jaeger.  */
static int
spring_forward_gap ()
{
  /* glibc (up to about 1998-10-07) failed this test. */
  struct tm tm;

  /* Use the portable POSIX.1 specification "TZ=PST8PDT,M4.1.0,M10.5.0"
     instead of "TZ=America/Vancouver" in order to detect the bug even
     on systems that don't support the Olson extension, or don't have the
     full zoneinfo tables installed.  */
  putenv ("TZ=PST8PDT,M4.1.0,M10.5.0");

  tm.tm_year = 98;
  tm.tm_mon = 3;
  tm.tm_mday = 5;
  tm.tm_hour = 2;
  tm.tm_min = 0;
  tm.tm_sec = 0;
  tm.tm_isdst = -1;
  return mktime (&tm) != (time_t) -1;
}

static int
mktime_test1 (now)
     time_t now;
{
  struct tm *lt;
  return ! (lt = localtime (&now)) || mktime (lt) == now;
}

static int
mktime_test (now)
     time_t now;
{
  return (mktime_test1 (now)
	  && mktime_test1 ((time_t) (time_t_max - now))
	  && mktime_test1 ((time_t) (time_t_min + now)));
}

static int
irix_6_4_bug ()
{
  /* Based on code from Ariel Faigon.  */
  struct tm tm;
  tm.tm_year = 96;
  tm.tm_mon = 3;
  tm.tm_mday = 0;
  tm.tm_hour = 0;
  tm.tm_min = 0;
  tm.tm_sec = 0;
  tm.tm_isdst = -1;
  mktime (&tm);
  return tm.tm_mon == 2 && tm.tm_mday == 31;
}

static int
bigtime_test (j)
     int j;
{
  struct tm tm;
  time_t now;
  tm.tm_year = tm.tm_mon = tm.tm_mday = tm.tm_hour = tm.tm_min = tm.tm_sec = j;
  now = mktime (&tm);
  if (now != (time_t) -1)
    {
      struct tm *lt = localtime (&now);
      if (! (lt
	     && lt->tm_year == tm.tm_year
	     && lt->tm_mon == tm.tm_mon
	     && lt->tm_mday == tm.tm_mday
	     && lt->tm_hour == tm.tm_hour
	     && lt->tm_min == tm.tm_min
	     && lt->tm_sec == tm.tm_sec
	     && lt->tm_yday == tm.tm_yday
	     && lt->tm_wday == tm.tm_wday
	     && ((lt->tm_isdst < 0 ? -1 : 0 < lt->tm_isdst)
		  == (tm.tm_isdst < 0 ? -1 : 0 < tm.tm_isdst))))
	return 0;
    }
  return 1;
}

static int
year_2050_test ()
{
  /* The correct answer for 2050-02-01 00:00:00 in Pacific time,
     ignoring leap seconds.  */
  unsigned long int answer = 2527315200UL;

  struct tm tm;
  time_t t;
  tm.tm_year = 2050 - 1900;
  tm.tm_mon = 2 - 1;
  tm.tm_mday = 1;
  tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
  tm.tm_isdst = -1;

  /* Use the portable POSIX.1 specification "TZ=PST8PDT,M4.1.0,M10.5.0"
     instead of "TZ=America/Vancouver" in order to detect the bug even
     on systems that don't support the Olson extension, or don't have the
     full zoneinfo tables installed.  */
  putenv ("TZ=PST8PDT,M4.1.0,M10.5.0");

  t = mktime (&tm);

  /* Check that the result is either a failure, or close enough
     to the correct answer that we can assume the discrepancy is
     due to leap seconds.  */
  return (t == (time_t) -1
	  || (0 < t && answer - 120 <= t && t <= answer + 120));
}

int
main ()
{
  time_t t, delta;
  int i, j;

  /* This test makes some buggy mktime implementations loop.
     Give up after 60 seconds; a mktime slower than that
     isn't worth using anyway.  */
  alarm (60);

  for (;;)
    {
      t = (time_t_max << 1) + 1;
      if (t <= time_t_max)
	break;
      time_t_max = t;
    }
  time_t_min = - ((time_t) ~ (time_t) 0 == (time_t) -1) - time_t_max;

  delta = time_t_max / 997; /* a suitable prime number */
  for (i = 0; i < N_STRINGS; i++)
    {
      if (tz_strings[i])
	putenv (tz_strings[i]);

      for (t = 0; t <= time_t_max - delta; t += delta)
	if (! mktime_test (t))
	  return 1;
      if (! (mktime_test ((time_t) 1)
	     && mktime_test ((time_t) (60 * 60))
	     && mktime_test ((time_t) (60 * 60 * 24))))
	return 1;

      for (j = 1; ; j <<= 1)
	if (! bigtime_test (j))
	  return 1;
	else if (INT_MAX / 2 < j)
	  break;
      if (! bigtime_test (INT_MAX))
	return 1;
    }
  return ! (irix_6_4_bug () && spring_forward_gap () && year_2050_test ());
}
_ACEOF
if ac_fn_c_try_run "$LINENO"; then :
  ac_cv_func_working_mktime=yes
else
  ac_cv_func_working_mktime=no
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
  conftest.$ac_objext conftest.beam conftest.$ac_ext
fi

fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_working_mktime" >&5
$as_echo "$ac_cv_func_working_mktime" >&6; }
if test $ac_cv_func_working_mktime = no; then
  case " $LIBOBJS " in
  *" mktime.$ac_objext "* ) ;;
  *) LIBOBJS="$LIBOBJS mktime.$ac_objext"
 ;;
esac

fi

for ac_header in stdlib.h
do :
  ac_fn_c_check_header_mongrel "$LINENO" "stdlib.h" "ac_cv_header_stdlib_h" "$ac_includes_default"
if test "x$ac_cv_header_stdlib_h" = x""yes; then :
  cat >>confdefs.h <<_ACEOF
#define HAVE_STDLIB_H 1
_ACEOF

fi

done

{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU libc compatible realloc" >&5
$as_echo_n "checking for GNU libc compatible realloc... " >&6; }
if test "${ac_cv_func_realloc_0_nonnull+set}" = set; then :
  $as_echo_n "(cached) " >&6
else
  if test "$cross_compiling" = yes; then :
  ac_cv_func_realloc_0_nonnull=no
else
  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h.  */
#if defined STDC_HEADERS || defined HAVE_STDLIB_H
# include <stdlib.h>
#else
char *realloc ();
#endif

int
main ()
{
return ! realloc (0, 0);
  ;
  return 0;
}
_ACEOF
if ac_fn_c_try_run "$LINENO"; then :
  ac_cv_func_realloc_0_nonnull=yes
else
  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.beam conftest.$ac_ext
fi

fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_realloc_0_nonnull" >&5
$as_echo "$ac_cv_func_realloc_0_nonnull" >&6; }
if test $ac_cv_func_realloc_0_nonnull = yes; then :

$as_echo "#define HAVE_REALLOC 1" >>confdefs.h

else
  $as_echo "#define HAVE_REALLOC 0" >>confdefs.h

   case " $LIBOBJS " in
  *" realloc.$ac_objext "* ) ;;
  *) LIBOBJS="$LIBOBJS realloc.$ac_objext"
 ;;
esac


$as_echo "#define realloc rpl_realloc" >>confdefs.h

fi


$as_echo "#define HAVE_SPLASH 1" >>confdefs.h

for ac_func in floor ftime getcwd gettimeofday memmove memset pow sqrt strcasecmp strchr strcspn strrchr strstr
do :
  as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
eval as_val=\$$as_ac_var
   if test "x$as_val" = x""yes; then :
  cat >>confdefs.h <<_ACEOF
#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
_ACEOF

fi
done


ac_config_files="$ac_config_files Makefile fofi/Makefile goo/Makefile splash/Makefile src/Makefile xpdf/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_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
      esac
      case $ac_var in #(
      _ | IFS | as_nl) ;; #(
      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
      *) { eval $ac_var=; 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" &&
      { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
$as_echo "$as_me: updating cache $cache_file" >&6;}
    cat confcache >$cache_file
  else
    { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
$as_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}'

DEFS=-DHAVE_CONFIG_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=`$as_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.
  as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext"
  as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo'
done
LIBOBJS=$ac_libobjs

LTLIBOBJS=$ac_ltlibobjs



: ${CONFIG_STATUS=./config.status}
ac_write_fail=0
ac_clean_files_save=$ac_clean_files
ac_clean_files="$ac_clean_files $CONFIG_STATUS"
{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5
$as_echo "$as_me: creating $CONFIG_STATUS" >&6;}
as_write_fail=0
cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1
#! $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}
export SHELL
_ASEOF
cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1
## -------------------- ##
## 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=:
  # Pre-4.2 versions of Zsh do 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_nl='
'
export as_nl
# Printing a long string crashes Solaris 7 /usr/bin/printf.
as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
# Prefer a ksh shell builtin over an external printf program on Solaris,
# but without wasting forks for bash or zsh.
if test -z "$BASH_VERSION$ZSH_VERSION" \
    && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
  as_echo='print -r --'
  as_echo_n='print -rn --'
elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
  as_echo='printf %s\n'
  as_echo_n='printf %s'
else
  if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
    as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
    as_echo_n='/usr/ucb/echo -n'
  else
    as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
    as_echo_n_body='eval
      arg=$1;
      case $arg in #(
      *"$as_nl"*)
	expr "X$arg" : "X\\(.*\\)$as_nl";
	arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
      esac;
      expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
    '
    export as_echo_n_body
    as_echo_n='sh -c $as_echo_n_body as_echo'
  fi
  export as_echo_body
  as_echo='sh -c $as_echo_body as_echo'
fi

# The user is always right.
if test "${PATH_SEPARATOR+set}" != set; then
  PATH_SEPARATOR=:
  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
      PATH_SEPARATOR=';'
  }
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.)
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
  $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
  exit 1
fi

# Unset variables that we do not need and which cause bugs (e.g. in
# pre-3.0 UWIN ksh).  But do not cause bugs in bash 2.01; the "|| exit 1"
# suppresses any "Segmentation fault" message there.  '((' could
# trigger a bug in pdksh 5.2.14.
for as_var in BASH_ENV ENV MAIL MAILPATH
do eval test x\${$as_var+set} = xset \
  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
done
PS1='$ '
PS2='> '
PS4='+ '

# NLS nuisances.
LC_ALL=C
export LC_ALL
LANGUAGE=C
export LANGUAGE

# CDPATH.
(unset CDPATH) >/dev/null 2>&1 && unset CDPATH


# as_fn_error ERROR [LINENO LOG_FD]
# ---------------------------------
# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
# script with status $?, using 1 if that was 0.
as_fn_error ()
{
  as_status=$?; test $as_status -eq 0 && as_status=1
  if test "$3"; then
    as_lineno=${as_lineno-"$2"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
    $as_echo "$as_me:${as_lineno-$LINENO}: error: $1" >&$3
  fi
  $as_echo "$as_me: error: $1" >&2
  as_fn_exit $as_status
} # as_fn_error


# as_fn_set_status STATUS
# -----------------------
# Set $? to STATUS, without forking.
as_fn_set_status ()
{
  return $1
} # as_fn_set_status

# as_fn_exit STATUS
# -----------------
# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
as_fn_exit ()
{
  set +e
  as_fn_set_status $1
  exit $1
} # as_fn_exit

# as_fn_unset VAR
# ---------------
# Portably unset VAR.
as_fn_unset ()
{
  { eval $1=; unset $1;}
}
as_unset=as_fn_unset
# as_fn_append VAR VALUE
# ----------------------
# Append the text in VALUE to the end of the definition contained in VAR. Take
# advantage of any shell optimizations that allow amortized linear growth over
# repeated appends, instead of the typical quadratic growth present in naive
# implementations.
if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
  eval 'as_fn_append ()
  {
    eval $1+=\$2
  }'
else
  as_fn_append ()
  {
    eval $1=\$$1\$2
  }
fi # as_fn_append

# as_fn_arith ARG...
# ------------------
# Perform arithmetic evaluation on the ARGs, and store the result in the
# global $as_val. Take advantage of shells that can avoid forks. The arguments
# must be portable across $(()) and expr.
if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
  eval 'as_fn_arith ()
  {
    as_val=$(( $* ))
  }'
else
  as_fn_arith ()
  {
    as_val=`expr "$@" || test $? -eq 1`
  }
fi # as_fn_arith


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

if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
  as_dirname=dirname
else
  as_dirname=false
fi

as_me=`$as_basename -- "$0" ||
$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
	 X"$0" : 'X\(//\)$' \| \
	 X"$0" : 'X\(/\)' \| . 2>/dev/null ||
$as_echo X/"$0" |
    sed '/^.*\/\([^/][^/]*\)\/*$/{
	    s//\1/
	    q
	  }
	  /^X\/\(\/\/\)$/{
	    s//\1/
	    q
	  }
	  /^X\/\(\/\).*/{
	    s//\1/
	    q
	  }
	  s/.*/./; q'`

# 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

ECHO_C= ECHO_N= ECHO_T=
case `echo -n x` in #(((((
-n*)
  case `echo 'xy\c'` in
  *c*) ECHO_T='	';;	# ECHO_T is single tab character.
  xy)  ECHO_C='\c';;
  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null
       ECHO_T='	';;
  esac;;
*)
  ECHO_N='-n';;
esac

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 2>/dev/null
fi
if (echo >conf$$.file) 2>/dev/null; then
  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
else
  as_ln_s='cp -p'
fi
rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
rmdir conf$$.dir 2>/dev/null


# as_fn_mkdir_p
# -------------
# Create "$as_dir" as a directory, including parents if necessary.
as_fn_mkdir_p ()
{

  case $as_dir in #(
  -*) as_dir=./$as_dir;;
  esac
  test -d "$as_dir" || eval $as_mkdir_p || {
    as_dirs=
    while :; do
      case $as_dir in #(
      *\'*) as_qdir=`$as_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 ||
$as_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" || as_fn_error "cannot create directory $as_dir"


} # as_fn_mkdir_p
if mkdir -p . 2>/dev/null; then
  as_mkdir_p='mkdir -p "$as_dir"'
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
## ----------------------------------- ##
## Main body of $CONFIG_STATUS script. ##
## ----------------------------------- ##
_ASEOF
test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1

cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=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 FULL-PACKAGE-NAME $as_me VERSION, which was
generated by GNU Autoconf 2.65.  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

case $ac_config_files in *"
"*) set x $ac_config_files; shift; ac_config_files=$*;;
esac

case $ac_config_headers in *"
"*) set x $ac_config_headers; shift; ac_config_headers=$*;;
esac


cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
# Files that config.status was made for.
config_files="$ac_config_files"
config_headers="$ac_config_headers"

_ACEOF

cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
ac_cs_usage="\
\`$as_me' instantiates files and other configuration actions
from templates according to the current configuration.  Unless the files
and actions are specified as TAGs, all are instantiated by default.

Usage: $0 [OPTION]... [TAG]...

  -h, --help       print this help, then exit
  -V, --version    print version number and configuration settings, then exit
      --config     print configuration, then exit
  -q, --quiet, --silent
                   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
      --header=FILE[:TEMPLATE]
                   instantiate the configuration header FILE

Configuration files:
$config_files

Configuration headers:
$config_headers

Report bugs to <BUG-REPORT-ADDRESS>."

_ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
FULL-PACKAGE-NAME config.status VERSION
configured by $0, generated by GNU Autoconf 2.65,
  with options \\"\$ac_cs_config\\"

Copyright (C) 2009 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'
test -n "\$AWK" || AWK=awk
_ACEOF

cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# The default lists apply if the user does not specify any file.
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 )
    $as_echo "$ac_cs_version"; exit ;;
  --config | --confi | --conf | --con | --co | --c )
    $as_echo "$ac_cs_config"; exit ;;
  --debug | --debu | --deb | --de | --d | -d )
    debug=: ;;
  --file | --fil | --fi | --f )
    $ac_shift
    case $ac_optarg in
    *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
    esac
    as_fn_append CONFIG_FILES " '$ac_optarg'"
    ac_need_defaults=false;;
  --header | --heade | --head | --hea )
    $ac_shift
    case $ac_optarg in
    *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
    esac
    as_fn_append CONFIG_HEADERS " '$ac_optarg'"
    ac_need_defaults=false;;
  --he | --h)
    # Conflict between --help and --header
    as_fn_error "ambiguous option: \`$1'
Try \`$0 --help' for more information.";;
  --help | --hel | -h )
    $as_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.
  -*) as_fn_error "unrecognized option: \`$1'
Try \`$0 --help' for more information." ;;

  *) as_fn_append 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 || ac_write_fail=1
if \$ac_cs_recheck; then
  set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
  shift
  \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
  CONFIG_SHELL='$SHELL'
  export CONFIG_SHELL
  exec "\$@"
fi

_ACEOF
cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
exec 5>>config.log
{
  echo
  sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
## Running $as_me. ##
_ASBOX
  $as_echo "$ac_log"
} >&5

_ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
_ACEOF

cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1

# Handling of arguments.
for ac_config_target in $ac_config_targets
do
  case $ac_config_target in
    "aconf.h") CONFIG_HEADERS="$CONFIG_HEADERS aconf.h" ;;
    "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
    "fofi/Makefile") CONFIG_FILES="$CONFIG_FILES fofi/Makefile" ;;
    "goo/Makefile") CONFIG_FILES="$CONFIG_FILES goo/Makefile" ;;
    "splash/Makefile") CONFIG_FILES="$CONFIG_FILES splash/Makefile" ;;
    "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;;
    "xpdf/Makefile") CONFIG_FILES="$CONFIG_FILES xpdf/Makefile" ;;

  *) as_fn_error "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
  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
  test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
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 'as_fn_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")
} || as_fn_error "cannot create a temporary directory in ." "$LINENO" 5

# Set up the scripts for CONFIG_FILES section.
# No need to generate them if there are no CONFIG_FILES.
# This happens for instance with `./config.status config.h'.
if test -n "$CONFIG_FILES"; then


ac_cr=`echo X | tr X '\015'`
# On cygwin, bash can eat \r inside `` if the user requested igncr.
# But we know of no other shell where ac_cr would be empty at this
# point, so we can use a bashism as a fallback.
if test "x$ac_cr" = x; then
  eval ac_cr=\$\'\\r\'
fi
ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
  ac_cs_awk_cr='\r'
else
  ac_cs_awk_cr=$ac_cr
fi

echo 'BEGIN {' >"$tmp/subs1.awk" &&
_ACEOF


{
  echo "cat >conf$$subs.awk <<_ACEOF" &&
  echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
  echo "_ACEOF"
} >conf$$subs.sh ||
  as_fn_error "could not make $CONFIG_STATUS" "$LINENO" 5
ac_delim_num=`echo "$ac_subst_vars" | grep -c '$'`
ac_delim='%!_!# '
for ac_last_try in false false false false false :; do
  . ./conf$$subs.sh ||
    as_fn_error "could not make $CONFIG_STATUS" "$LINENO" 5

  ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
  if test $ac_delim_n = $ac_delim_num; then
    break
  elif $ac_last_try; then
    as_fn_error "could not make $CONFIG_STATUS" "$LINENO" 5
  else
    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
  fi
done
rm -f conf$$subs.sh

cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
cat >>"\$tmp/subs1.awk" <<\\_ACAWK &&
_ACEOF
sed -n '
h
s/^/S["/; s/!.*/"]=/
p
g
s/^[^!]*!//
:repl
t repl
s/'"$ac_delim"'$//
t delim
:nl
h
s/\(.\{148\}\)..*/\1/
t more1
s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
p
n
b repl
:more1
s/["\\]/\\&/g; s/^/"/; s/$/"\\/
p
g
s/.\{148\}//
t nl
:delim
h
s/\(.\{148\}\)..*/\1/
t more2
s/["\\]/\\&/g; s/^/"/; s/$/"/
p
b
:more2
s/["\\]/\\&/g; s/^/"/; s/$/"\\/
p
g
s/.\{148\}//
t delim
' <conf$$subs.awk | sed '
/^[^""]/{
  N
  s/\n//
}
' >>$CONFIG_STATUS || ac_write_fail=1
rm -f conf$$subs.awk
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
_ACAWK
cat >>"\$tmp/subs1.awk" <<_ACAWK &&
  for (key in S) S_is_set[key] = 1
  FS = ""

}
{
  line = $ 0
  nfields = split(line, field, "@")
  substed = 0
  len = length(field[1])
  for (i = 2; i < nfields; i++) {
    key = field[i]
    keylen = length(key)
    if (S_is_set[key]) {
      value = S[key]
      line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
      len += length(value) + length(field[++i])
      substed = 1
    } else
      len += 1 + keylen
  }

  print line
}

_ACAWK
_ACEOF
cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
  sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
else
  cat
fi < "$tmp/subs1.awk" > "$tmp/subs.awk" \
  || as_fn_error "could not setup config files machinery" "$LINENO" 5
_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 || ac_write_fail=1
fi # test -n "$CONFIG_FILES"

# Set up the scripts for CONFIG_HEADERS section.
# No need to generate them if there are no CONFIG_HEADERS.
# This happens for instance with `./config.status Makefile'.
if test -n "$CONFIG_HEADERS"; then
cat >"$tmp/defines.awk" <<\_ACAWK ||
BEGIN {
_ACEOF

# Transform confdefs.h into an awk script `defines.awk', embedded as
# here-document in config.status, that substitutes the proper values into
# config.h.in to produce config.h.

# Create a delimiter string that does not exist in confdefs.h, to ease
# handling of long lines.
ac_delim='%!_!# '
for ac_last_try in false false :; do
  ac_t=`sed -n "/$ac_delim/p" confdefs.h`
  if test -z "$ac_t"; then
    break
  elif $ac_last_try; then
    as_fn_error "could not make $CONFIG_HEADERS" "$LINENO" 5
  else
    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
  fi
done

# For the awk script, D is an array of macro values keyed by name,
# likewise P contains macro parameters if any.  Preserve backslash
# newline sequences.

ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*
sed -n '
s/.\{148\}/&'"$ac_delim"'/g
t rset
:rset
s/^[	 ]*#[	 ]*define[	 ][	 ]*/ /
t def
d
:def
s/\\$//
t bsnl
s/["\\]/\\&/g
s/^ \('"$ac_word_re"'\)\(([^()]*)\)[	 ]*\(.*\)/P["\1"]="\2"\
D["\1"]=" \3"/p
s/^ \('"$ac_word_re"'\)[	 ]*\(.*\)/D["\1"]=" \2"/p
d
:bsnl
s/["\\]/\\&/g
s/^ \('"$ac_word_re"'\)\(([^()]*)\)[	 ]*\(.*\)/P["\1"]="\2"\
D["\1"]=" \3\\\\\\n"\\/p
t cont
s/^ \('"$ac_word_re"'\)[	 ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p
t cont
d
:cont
n
s/.\{148\}/&'"$ac_delim"'/g
t clear
:clear
s/\\$//
t bsnlc
s/["\\]/\\&/g; s/^/"/; s/$/"/p
d
:bsnlc
s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p
b cont
' <confdefs.h | sed '
s/'"$ac_delim"'/"\\\
"/g' >>$CONFIG_STATUS || ac_write_fail=1

cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
  for (key in D) D_is_set[key] = 1
  FS = ""
}
/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ {
  line = \$ 0
  split(line, arg, " ")
  if (arg[1] == "#") {
    defundef = arg[2]
    mac1 = arg[3]
  } else {
    defundef = substr(arg[1], 2)
    mac1 = arg[2]
  }
  split(mac1, mac2, "(") #)
  macro = mac2[1]
  prefix = substr(line, 1, index(line, defundef) - 1)
  if (D_is_set[macro]) {
    # Preserve the white space surrounding the "#".
    print prefix "define", macro P[macro] D[macro]
    next
  } else {
    # Replace #undef with comments.  This is necessary, for example,
    # in the case of _POSIX_SOURCE, which is predefined and required
    # on some systems where configure will not decide to define it.
    if (defundef == "undef") {
      print "/*", prefix defundef, macro, "*/"
      next
    }
  }
}
{ print }
_ACAWK
_ACEOF
cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
  as_fn_error "could not setup config headers machinery" "$LINENO" 5
fi # test -n "$CONFIG_HEADERS"


eval set X "  :F $CONFIG_FILES  :H $CONFIG_HEADERS    "
shift
for ac_tag
do
  case $ac_tag in
  :[FHLC]) ac_mode=$ac_tag; continue;;
  esac
  case $ac_mode$ac_tag in
  :[FHL]*:*);;
  :L* | :C*:*) as_fn_error "invalid tag \`$ac_tag'" "$LINENO" 5;;
  :[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 ||
	   as_fn_error "cannot find input file: \`$ac_f'" "$LINENO" 5;;
      esac
      case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
      as_fn_append 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 '`
	  $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
	`' by configure.'
    if test x"$ac_file" != x-; then
      configure_input="$ac_file.  $configure_input"
      { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
$as_echo "$as_me: creating $ac_file" >&6;}
    fi
    # Neutralize special characters interpreted by sed in replacement strings.
    case $configure_input in #(
    *\&* | *\|* | *\\* )
       ac_sed_conf_input=`$as_echo "$configure_input" |
       sed 's/[\\\\&|]/\\\\&/g'`;; #(
    *) ac_sed_conf_input=$configure_input;;
    esac

    case $ac_tag in
    *:-:* | *:-) cat >"$tmp/stdin" \
      || as_fn_error "could not create $ac_file" "$LINENO" 5 ;;
    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 ||
$as_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"; as_fn_mkdir_p
  ac_builddir=.

case "$ac_dir" in
.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
*)
  ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
  # A ".." for each directory in $ac_dir_suffix.
  ac_top_builddir_sub=`$as_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 || ac_write_fail=1
# 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=
ac_sed_dataroot='
/datarootdir/ {
  p
  q
}
/@datadir@/p
/@docdir@/p
/@infodir@/p
/@localedir@/p
/@mandir@/p'
case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
*datarootdir*) ac_datarootdir_seen=yes;;
*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
_ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
  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 || ac_write_fail=1
ac_sed_extra="$ac_vpsub
$extrasub
_ACEOF
cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
:t
/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
s|@configure_input@|$ac_sed_conf_input|;t t
s&@top_builddir@&$ac_top_builddir_sub&;t t
s&@top_build_prefix@&$ac_top_build_prefix&;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
"
eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$tmp/subs.awk" >$tmp/out \
  || as_fn_error "could not create $ac_file" "$LINENO" 5

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"; } &&
  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
which seems to be undefined.  Please make sure it is defined." >&5
$as_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 \
  || as_fn_error "could not create $ac_file" "$LINENO" 5
 ;;
  :H)
  #
  # CONFIG_HEADER
  #
  if test x"$ac_file" != x-; then
    {
      $as_echo "/* $configure_input  */" \
      && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs"
    } >"$tmp/config.h" \
      || as_fn_error "could not create $ac_file" "$LINENO" 5
    if diff "$ac_file" "$tmp/config.h" >/dev/null 2>&1; then
      { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5
$as_echo "$as_me: $ac_file is unchanged" >&6;}
    else
      rm -f "$ac_file"
      mv "$tmp/config.h" "$ac_file" \
	|| as_fn_error "could not create $ac_file" "$LINENO" 5
    fi
  else
    $as_echo "/* $configure_input  */" \
      && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs" \
      || as_fn_error "could not create -" "$LINENO" 5
  fi
 ;;


  esac

done # for ac_tag


as_fn_exit 0
_ACEOF
ac_clean_files=$ac_clean_files_save

test $ac_write_fail = 0 ||
  as_fn_error "write failure creating $CONFIG_STATUS" "$LINENO" 5


# 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 || as_fn_exit $?
fi
if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
fi

#                                               -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.

AC_PREREQ([2.65])
AC_INIT([FULL-PACKAGE-NAME], [VERSION], [BUG-REPORT-ADDRESS])
AC_CONFIG_HEADERS([aconf.h])

# Checks for programs.
AC_PROG_CXX
AC_PROG_CC
AC_PROG_RANLIB

dnl ##### Checks for header files.
AC_PATH_XTRA
AC_HEADER_DIRENT

# Checks for libraries.
# FIXME: Replace `main' with a function in `-lGoo':
AC_CHECK_LIB([Goo], [main])
# FIXME: Replace `main' with a function in `-lX11':
AC_CHECK_LIB([X11], [main])
# FIXME: Replace `main' with a function in `-lXpdf':
AC_CHECK_LIB([Xpdf], [main])
# FIXME: Replace `main' with a function in `-lfofi':
AC_CHECK_LIB([fofi], [main])
# FIXME: Replace `main' with a function in `-lm':
AC_CHECK_LIB([m], [main])
# FIXME: Replace `main' with a function in `-lsplash':
AC_CHECK_LIB([splash], [main])

# Checks for header files.
AC_CHECK_HEADERS([fcntl.h limits.h stddef.h stdlib.h string.h unistd.h])

# Checks for typedefs, structures, and compiler characteristics.
AC_HEADER_STDBOOL
AC_C_INLINE
AC_TYPE_SIZE_T

dnl ##### Path to xpdfrc.
dnl This ugly kludge to get the sysconfdir path is needed because
dnl autoconf doesn't actually set the prefix variable until later.
if test "$sysconfdir" = '${prefix}/etc'; then
  if test "x$prefix" = xNONE; then
    system_xpdfrc="$ac_default_prefix/etc/xpdfrc"
  else
    system_xpdfrc="$prefix/etc/xpdfrc"
  fi
else
  system_xpdfrc="$sysconfdir/xpdfrc"
fi
AC_DEFINE_UNQUOTED(SYSTEM_XPDFRC, "$system_xpdfrc")

dnl ##### Checks for library functions.
AC_CHECK_FUNCS(popen)
dnl # This should use 'AC_CHECK_FUNCS(mkstemp)' but that fails if
dnl # the mkstemp exists in the library but isn't declared in the
dnl # include file (e.g., in cygwin 1.1.2).
AC_CACHE_CHECK([for mkstemp],
xpdf_cv_func_mkstemp,
[AC_TRY_LINK([#include <stdlib.h>
#include <unistd.h>],
[mkstemp("foo");],
xpdf_cv_func_mkstemp=yes, xpdf_cv_func_mkstemp=no)])
if test "$xpdf_cv_func_mkstemp" = yes; then
  AC_DEFINE(HAVE_MKSTEMP)
fi
dnl # Check for mkstemps, just like mkstemp.
AC_CACHE_CHECK([for mkstemps],
xpdf_cv_func_mkstemps,
[AC_TRY_LINK([#include <stdlib.h>
#include <unistd.h>],
[mkstemps("foo", 0);],
xpdf_cv_func_mkstemps=yes, xpdf_cv_func_mkstemps=no)])
if test "$xpdf_cv_func_mkstemps" = yes; then
  AC_DEFINE(HAVE_MKSTEMPS)
fi

# Checks for library functions.
AC_FUNC_ERROR_AT_LINE
AC_FUNC_FSEEKO
AC_FUNC_MALLOC
AC_FUNC_MKTIME
AC_FUNC_REALLOC
AC_DEFINE(HAVE_SPLASH)
AC_CHECK_FUNCS([floor ftime getcwd gettimeofday memmove memset pow sqrt strcasecmp strchr strcspn strrchr strstr])

AC_CONFIG_FILES([Makefile
                 fofi/Makefile
                 goo/Makefile
                 splash/Makefile
                 src/Makefile
                 xpdf/Makefile])
AC_OUTPUT
.\" Copyright 1999-2005 Glyph & Cog, LLC
.TH pdffonts 1 "17 August 2005"
.SH NAME
pdffonts \- Portable Document Format (PDF) font analyzer (version
3.01)
.SH SYNOPSIS
.B pdffonts
[options]
.RI [ PDF-file ]
.SH DESCRIPTION
.B Pdffonts
lists the fonts used in a Portable Document Format (PDF) file along
with various information for each font.
.PP
The following information is listed for each font:
.TP
.B name
the font name, exactly as given in the PDF file (potentially including
a subset prefix)
.TP
.B type
the font type -- see below for details
.TP
.B emb
"yes" if the font is embedded in the PDF file
.TP
.B sub
"yes" if the font is a subset
.TP
.B uni
"yes" if there is an explicit "ToUnicode" map in the PDF file (the
absence of a ToUnicode map doesn't necessarily mean that the text
can't be converted to Unicode)
.TP
.B object ID
the font dictionary object ID (number and generation)
.PP
PDF files can contain the following types of fonts:
.PP
.RS
Type 1
.RE
.RS
Type 1C -- aka Compact Font Format (CFF)
.RE
.RS
Type 3
.RE
.RS
TrueType
.RE
.RS
CID Type 0 -- 16-bit font with no specified type
.RE
.RS
CID Type 0C -- 16-bit PostScript CFF font
.RE
.RS
CID TrueType -- 16-bit TrueType font
.RE
.SH CONFIGURATION FILE
Pdffonts reads a configuration file at startup.  It first tries to
find the user's private config file, ~/.xpdfrc.  If that doesn't
exist, it looks for a system-wide config file, typically
/usr/local/etc/xpdfrc (but this location can be changed when pdffonts
is built).  See the
.BR xpdfrc (5)
man page for details.
.SH OPTIONS
Many of the following options can be set with configuration file
commands.  These are listed in square brackets with the description of
the corresponding command line option.
.TP
.BI \-f " number"
Specifies the first page to analyze.
.TP
.BI \-l " number"
Specifies the last page to analyze.
.TP
.BI \-opw " password"
Specify the owner password for the PDF file.  Providing this will
bypass all security restrictions.
.TP
.BI \-upw " password"
Specify the user password for the PDF file.
.TP
.BI \-cfg " config-file"
Read
.I config-file
in place of ~/.xpdfrc or the system-wide config file.
.TP
.B \-v
Print copyright and version information.
.TP
.B \-h
Print usage information.
.RB ( \-help
and
.B \-\-help
are equivalent.)
.SH EXIT CODES
The Xpdf tools use the following exit codes:
.TP
0
No error.
.TP
1
Error opening a PDF file.
.TP
2
Error opening an output file.
.TP
3
Error related to PDF permissions.
.TP
99
Other error.
.SH AUTHOR
The pdffonts software and documentation are copyright 1996-2005 Glyph
& Cog, LLC.
.SH "SEE ALSO"
.BR xpdf (1),
.BR pdftops (1),
.BR pdftotext (1),
.BR pdfinfo (1),
.BR pdftoppm (1),
.BR pdfimages (1),
.BR xpdfrc (5)
.br
.B http://www.foolabs.com/xpdf/
pdffonts(1)                                           pdffonts(1)



NAME
       pdffonts  -  Portable  Document Format (PDF) font analyzer
       (version 3.01)

SYNOPSIS
       pdffonts [options] [PDF-file]

DESCRIPTION
       Pdffonts lists the fonts used in a Portable Document  For-
       mat  (PDF)  file  along  with various information for each
       font.

       The following information is listed for each font:

       name   the font name, exactly as given  in  the  PDF  file
              (potentially including a subset prefix)

       type   the font type -- see below for details

       emb    "yes" if the font is embedded in the PDF file

       sub    "yes" if the font is a subset

       uni    "yes"  if  there  is an explicit "ToUnicode" map in
              the PDF  file  (the  absence  of  a  ToUnicode  map
              doesn't  necessarily  mean  that  the text can't be
              converted to Unicode)

       object ID
              the font dictionary object ID (number  and  genera-
              tion)

       PDF files can contain the following types of fonts:

              Type 1
              Type 1C -- aka Compact Font Format (CFF)
              Type 3
              TrueType
              CID Type 0 -- 16-bit font with no specified type
              CID Type 0C -- 16-bit PostScript CFF font
              CID TrueType -- 16-bit TrueType font

CONFIGURATION FILE
       Pdffonts  reads a configuration file at startup.  It first
       tries to find the user's private config  file,  ~/.xpdfrc.
       If  that  doesn't exist, it looks for a system-wide config
       file, typically /usr/local/etc/xpdfrc (but  this  location
       can be changed when pdffonts is built).  See the xpdfrc(5)
       man page for details.

OPTIONS
       Many of the following options can be set  with  configura-
       tion  file  commands.  These are listed in square brackets
       with the description of  the  corresponding  command  line
       option.

       -f number
              Specifies the first page to analyze.

       -l number
              Specifies the last page to analyze.

       -opw password
              Specify  the owner password for the PDF file.  Pro-
              viding this will bypass all security  restrictions.

       -upw password
              Specify the user password for the PDF file.

       -cfg config-file
              Read  config-file in place of ~/.xpdfrc or the sys-
              tem-wide config file.

       -v     Print copyright and version information.

       -h     Print usage information.   (-help  and  --help  are
              equivalent.)

EXIT CODES
       The Xpdf tools use the following exit codes:

       0      No error.

       1      Error opening a PDF file.

       2      Error opening an output file.

       3      Error related to PDF permissions.

       99     Other error.

AUTHOR
       The  pdffonts  software  and  documentation  are copyright
       1996-2005 Glyph & Cog, LLC.

SEE ALSO
       xpdf(1),     pdftops(1),     pdftotext(1),     pdfinfo(1),
       pdftoppm(1), pdfimages(1), xpdfrc(5)
       http://www.foolabs.com/xpdf/



                          17 August 2005              pdffonts(1)
! Generated automatically by mantohlp
1 pdffonts

       pdffonts  -  Portable  Document Format (PDF) font analyzer
       (version

       pdffonts [options] [PDF-file]

       Pdffonts lists the fonts used in a Portable Document  For-
       mat  (PDF)  file  along  with various information for each
       font.

       The following information is listed for each font:

       name   the font name, exactly as given  in  the  PDF  file
              (potentially including a subset prefix)

       type   the font type -- see below for details

       emb    "yes" if the font is embedded in the PDF file

       sub    "yes" if the font is a subset

       uni    "yes"  if  there  is an explicit "ToUnicode" map in
              the PDF  file  (the  absence  of  a  ToUnicode  map
              doesn't  necessarily  mean  that  the text can't be
              converted to Unicode)

       object ID
              the font dictionary object ID (number  and  genera-
              tion)

       PDF files can contain the following types of fonts:

              Type 1
              Type 1C -- aka Compact Font Format (CFF)
              Type 3
              TrueType
              CID Type 0 -- 16-bit font with no specified type
              CID Type 0C -- 16-bit PostScript CFF font
              CID TrueType -- 16-bit TrueType font

                                                               ()

2 ONFIGURATION_FIL

       Pdffonts  reads a configuration file at startup.  It first
       tries to find the user's private config  file,  ~/.xpdfrc.
       If  that  doesn't exist, it looks for a system-wide config
       file, typically /usr/local/etc/xpdfrc (but  this  location
       can be changed when pdffonts is built).  See the xpdfrc(5)
       man page for details.

                                                               ()

2 OPTIONS

       Many  of  the following options can be set with configura-
       tion file commands.  These are listed in  square  brackets
       with  the  description  of  the corresponding command line
       option.

       -f number
              Specifies the first page to analyze.

       -l number
              Specifies the last page to analyze.

       -opw password
              Specify the owner password for the PDF file.   Pro-
              viding  this will bypass all security restrictions.

       -upw password
              Specify the user password for the PDF file.

       -cfg config-file
              Read config-file in place of ~/.xpdfrc or the  sys-
              tem-wide config file.

       -v     Print copyright and version information.

       -h     Print  usage  information.   (-help  and --help are
              equivalent.)

                                                               ()

2 XIT_CODE

       The Xpdf tools use the following exit codes:

       0      No error.

       1      Error opening a PDF file.

       2      Error opening an output file.

       3      Error related to PDF permissions.

       99     Other error.

                                                               ()

2 AUTHOR

       The  pdffonts  software  and  documentation  are copyright
       1996-2005 Glyph & Cog, LLC.

                                                               ()

2 SEE_ALSO

       xpdf(1),     pdftops(1),     pdftotext(1),     pdfinfo(1),
       pdftoppm(1), pdfimages(1), xpdfrc(5)
       http://www.foolabs.com/xpdf/

                                                               ()

.\" Copyright 1998-2005 Glyph & Cog, LLC
.TH pdfimages 1 "17 August 2005"
.SH NAME
pdfimages \- Portable Document Format (PDF) image extractor
(version 3.01)
.SH SYNOPSIS
.B pdfimages
[options]
.I PDF-file image-root
.SH DESCRIPTION
.B Pdfimages
saves images from a Portable Document Format (PDF) file as Portable
Pixmap (PPM), Portable Bitmap (PBM), or JPEG files.
.PP
Pdfimages reads the PDF file, scans one or more pages,
.IR PDF-file ,
and writes one PPM, PBM, or JPEG file for each image,
.IR image-root - nnn . xxx ,
where
.I nnn
is the image number and
.I xxx
is the image type (.ppm, .pbm, .jpg).
.PP
NB: pdfimages extracts the raw image data from the PDF file, without
performing any additional transforms.  Any rotation, clipping,
color inversion, etc. done by the PDF content stream is ignored.
.SH CONFIGURATION FILE
Pdfimages reads a configuration file at startup.  It first tries to
find the user's private config file, ~/.xpdfrc.  If that doesn't
exist, it looks for a system-wide config file, typically
/usr/local/etc/xpdfrc (but this location can be changed when pdfimages
is built).  See the
.BR xpdfrc (5)
man page for details.
.SH OPTIONS
Many of the following options can be set with configuration file
commands.  These are listed in square brackets with the description of
the corresponding command line option.
.TP
.BI \-f " number"
Specifies the first page to scan.
.TP
.BI \-l " number"
Specifies the last page to scan.
.TP
.B \-j
Normally, all images are written as PBM (for monochrome images) or PPM
(for non-monochrome images) files.  With this option, images in DCT
format are saved as JPEG files.  All non-DCT images are saved in
PBM/PPM format as usual.
.TP
.BI \-opw " password"
Specify the owner password for the PDF file.  Providing this will
bypass all security restrictions.
.TP
.BI \-upw " password"
Specify the user password for the PDF file.
.TP
.B \-q
Don't print any messages or errors.
.RB "[config file: " errQuiet ]
.TP
.B \-v
Print copyright and version information.
.TP
.B \-h
Print usage information.
.RB ( \-help
and
.B \-\-help
are equivalent.)
.SH EXIT CODES
The Xpdf tools use the following exit codes:
.TP
0
No error.
.TP
1
Error opening a PDF file.
.TP
2
Error opening an output file.
.TP
3
Error related to PDF permissions.
.TP
99
Other error.
.SH AUTHOR
The pdfimages software and documentation are copyright 1998-2005 Glyph
& Cog, LLC.
.SH "SEE ALSO"
.BR xpdf (1),
.BR pdftops (1),
.BR pdftotext (1),
.BR pdfinfo (1),
.BR pdffonts (1),
.BR pdftoppm (1),
.BR xpdfrc (5)
.br
.B http://www.foolabs.com/xpdf/
pdfimages(1)                                         pdfimages(1)



NAME
       pdfimages - Portable Document Format (PDF) image extractor
       (version 3.01)

SYNOPSIS
       pdfimages [options] PDF-file image-root

DESCRIPTION
       Pdfimages saves images from  a  Portable  Document  Format
       (PDF)  file  as  Portable  Pixmap  (PPM),  Portable Bitmap
       (PBM), or JPEG files.

       Pdfimages reads the PDF file, scans  one  or  more  pages,
       PDF-file,  and  writes one PPM, PBM, or JPEG file for each
       image, image-root-nnn.xxx, where nnn is the  image  number
       and xxx is the image type (.ppm, .pbm, .jpg).

       NB:  pdfimages  extracts  the  raw image data from the PDF
       file, without performing any additional  transforms.   Any
       rotation,  clipping, color inversion, etc. done by the PDF
       content stream is ignored.

CONFIGURATION FILE
       Pdfimages reads a configuration file at startup.  It first
       tries  to  find the user's private config file, ~/.xpdfrc.
       If that doesn't exist, it looks for a  system-wide  config
       file,  typically  /usr/local/etc/xpdfrc (but this location
       can  be  changed  when  pdfimages  is  built).   See   the
       xpdfrc(5) man page for details.

OPTIONS
       Many  of  the following options can be set with configura-
       tion file commands.  These are listed in  square  brackets
       with  the  description  of  the corresponding command line
       option.

       -f number
              Specifies the first page to scan.

       -l number
              Specifies the last page to scan.

       -j     Normally,  all  images  are  written  as  PBM  (for
              monochrome   images)  or  PPM  (for  non-monochrome
              images) files.  With this  option,  images  in  DCT
              format are saved as JPEG files.  All non-DCT images
              are saved in PBM/PPM format as usual.

       -opw password
              Specify the owner password for the PDF file.   Pro-
              viding  this will bypass all security restrictions.

       -upw password
              Specify the user password for the PDF file.

       -q     Don't print any messages or errors.  [config  file:
              errQuiet]

       -v     Print copyright and version information.

       -h     Print  usage  information.   (-help  and --help are
              equivalent.)

EXIT CODES
       The Xpdf tools use the following exit codes:

       0      No error.

       1      Error opening a PDF file.

       2      Error opening an output file.

       3      Error related to PDF permissions.

       99     Other error.

AUTHOR
       The pdfimages software  and  documentation  are  copyright
       1998-2005 Glyph & Cog, LLC.

SEE ALSO
       xpdf(1),   pdftops(1),   pdftotext(1),   pdfinfo(1),  pdf-
       fonts(1), pdftoppm(1), xpdfrc(5)
       http://www.foolabs.com/xpdf/



                          17 August 2005             pdfimages(1)
! Generated automatically by mantohlp
1 pdfimages

       pdfimages - Portable Document Format (PDF) image extractor

       pdfimages [options] PDF-file image-root

       Pdfimages saves images from  a  Portable  Document  Format
       (PDF)  file  as  Portable  Pixmap  (PPM),  Portable Bitmap
       (PBM), or JPEG files.

       Pdfimages reads the PDF file, scans  one  or  more  pages,
       PDF-file,  and  writes one PPM, PBM, or JPEG file for each
       image, image-root-nnn.xxx, where nnn is the  image  number
       and xxx is the image type (.ppm, .pbm, .jpg).

       NB:  pdfimages  extracts  the  raw image data from the PDF
       file, without performing any additional  transforms.   Any
       rotation,  clipping, color inversion, etc. done by the PDF
       content stream is ignored.

                                                               ()

2 ONFIGURATION_FIL

       Pdfimages reads a configuration file at startup.  It first
       tries to find the user's private config  file,  ~/.xpdfrc.
       If  that  doesn't exist, it looks for a system-wide config
       file, typically /usr/local/etc/xpdfrc (but  this  location
       can   be  changed  when  pdfimages  is  built).   See  the
       xpdfrc(5) man page for details.

                                                               ()

2 OPTIONS

       Many  of  the following options can be set with configura-
       tion file commands.  These are listed in  square  brackets
       with  the  description  of  the corresponding command line
       option.

       -f number
              Specifies the first page to scan.

       -l number
              Specifies the last page to scan.

       -j     Normally,  all  images  are  written  as  PBM  (for
              monochrome   images)  or  PPM  (for  non-monochrome
              images) files.  With this  option,  images  in  DCT
              format are saved as JPEG files.  All non-DCT images
              are saved in PBM/PPM format as usual.

       -opw password
              Specify the owner password for the PDF file.   Pro-
              viding  this will bypass all security restrictions.

       -upw password
              Specify the user password for the PDF file.

       -q     Don't print any messages or errors.  [config  file:
              errQuiet]

       -v     Print copyright and version information.

       -h     Print  usage  information.   (-help  and --help are
              equivalent.)

                                                               ()

2 XIT_CODE

       The Xpdf tools use the following exit codes:

       0      No error.

       1      Error opening a PDF file.

       2      Error opening an output file.

       3      Error related to PDF permissions.

       99     Other error.

                                                               ()

2 AUTHOR

       The  pdfimages  software  and  documentation are copyright
       1998-2005 Glyph & Cog, LLC.

                                                               ()

2 SEE_ALSO

       xpdf(1),   pdftops(1),   pdftotext(1),   pdfinfo(1),  pdf-
       fonts(1), pdftoppm(1), xpdfrc(5)
       http://www.foolabs.com/xpdf/

                                                               ()

.\" Copyright 1999-2005 Glyph & Cog, LLC
.TH pdfinfo 1 "17 August 2005"
.SH NAME
pdfinfo \- Portable Document Format (PDF) document information
extractor (version 3.01)
.SH SYNOPSIS
.B pdfinfo
[options]
.RI [ PDF-file ]
.SH DESCRIPTION
.B Pdfinfo
prints the contents of the \'Info' dictionary (plus some other useful
information) from a Portable Document Format (PDF) file.
.PP
The \'Info' dictionary contains the following values:
.PP
.RS
title
.RE
.RS
subject
.RE
.RS
keywords
.RE
.RS
author
.RE
.RS
creator
.RE
.RS
producer
.RE
.RS
creation date
.RE
.RS
modification date
.RE
.PP
In addition, the following information is printed:
.PP
.RS
tagged (yes/no)
.RE
.RS
page count
.RE
.RS
encrypted flag (yes/no)
.RE
.RS
print and copy permissions (if encrypted)
.RE
.RS
page size
.RE
.RS
file size
.RE
.RS
linearized (yes/no)
.RE
.RS
PDF version
.RE
.RS
metadata (only if requested)
.RE
.SH CONFIGURATION FILE
Pdfinfo reads a configuration file at startup.  It first tries to find
the user's private config file, ~/.xpdfrc.  If that doesn't exist, it
looks for a system-wide config file, typically /usr/local/etc/xpdfrc
(but this location can be changed when pdfinfo is built).  See the
.BR xpdfrc (5)
man page for details.
.SH OPTIONS
Many of the following options can be set with configuration file
commands.  These are listed in square brackets with the description of
the corresponding command line option.
.TP
.BI \-f " number"
Specifies the first page to examine.  If multiple pages are requested
using the "-f" and "-l" options, the size of each requested page (and,
optionally, the bounding boxes for each requested page) are printed.
Otherwise, only page one is examined.
.TP
.BI \-l " number"
Specifies the last page to examine.
.TP
.B \-box
Prints the page box bounding boxes: MediaBox, CropBox, BleedBox,
TrimBox, and ArtBox.
.TP
.B \-meta
Prints document-level metadata.  (This is the "Metadata" stream from
the PDF file's Catalog object.)
.TP
.BI \-enc " encoding-name"
Sets the encoding to use for text output.  The
.I encoding\-name
must be defined with the unicodeMap command (see
.BR xpdfrc (5)).
This defaults to "Latin1" (which is a built-in encoding).
.RB "[config file: " textEncoding ]
.TP
.BI \-opw " password"
Specify the owner password for the PDF file.  Providing this will
bypass all security restrictions.
.TP
.BI \-upw " password"
Specify the user password for the PDF file.
.TP
.BI \-cfg " config-file"
Read
.I config-file
in place of ~/.xpdfrc or the system-wide config file.
.TP
.B \-v
Print copyright and version information.
.TP
.B \-h
Print usage information.
.RB ( \-help
and
.B \-\-help
are equivalent.)
.SH EXIT CODES
The Xpdf tools use the following exit codes:
.TP
0
No error.
.TP
1
Error opening a PDF file.
.TP
2
Error opening an output file.
.TP
3
Error related to PDF permissions.
.TP
99
Other error.
.SH AUTHOR
The pdfinfo software and documentation are copyright 1996-2005 Glyph &
Cog, LLC.
.SH "SEE ALSO"
.BR xpdf (1),
.BR pdftops (1),
.BR pdftotext (1),
.BR pdffonts (1),
.BR pdftoppm (1),
.BR pdfimages (1),
.BR xpdfrc (5)
.br
.B http://www.foolabs.com/xpdf/
pdfinfo(1)                                             pdfinfo(1)



NAME
       pdfinfo - Portable Document Format (PDF) document informa-
       tion extractor (version 3.01)

SYNOPSIS
       pdfinfo [options] [PDF-file]

DESCRIPTION
       Pdfinfo prints the contents of the 'Info' dictionary (plus
       some  other  useful  information) from a Portable Document
       Format (PDF) file.

       The 'Info' dictionary contains the following values:

              title
              subject
              keywords
              author
              creator
              producer
              creation date
              modification date

       In addition, the following information is printed:

              tagged (yes/no)
              page count
              encrypted flag (yes/no)
              print and copy permissions (if encrypted)
              page size
              file size
              linearized (yes/no)
              PDF version
              metadata (only if requested)

CONFIGURATION FILE
       Pdfinfo reads a configuration file at startup.   It  first
       tries  to  find the user's private config file, ~/.xpdfrc.
       If that doesn't exist, it looks for a  system-wide  config
       file,  typically  /usr/local/etc/xpdfrc (but this location
       can be changed when pdfinfo is built).  See the  xpdfrc(5)
       man page for details.

OPTIONS
       Many  of  the following options can be set with configura-
       tion file commands.  These are listed in  square  brackets
       with  the  description  of  the corresponding command line
       option.

       -f number
              Specifies the first page to examine.   If  multiple
              pages   are  requested  using  the  "-f"  and  "-l"
              options, the size  of  each  requested  page  (and,
              optionally,  the  bounding boxes for each requested
              page) are printed.  Otherwise,  only  page  one  is
              examined.

       -l number
              Specifies the last page to examine.

       -box   Prints the page box bounding boxes: MediaBox, Crop-
              Box, BleedBox, TrimBox, and ArtBox.

       -meta  Prints  document-level  metadata.   (This  is   the
              "Metadata"  stream  from  the  PDF  file's  Catalog
              object.)

       -enc encoding-name
              Sets the encoding to  use  for  text  output.   The
              encoding-name  must  be defined with the unicodeMap
              command (see xpdfrc(5)).  This defaults to "Latin1"
              (which is a built-in encoding).  [config file: tex-
              tEncoding]

       -opw password
              Specify the owner password for the PDF file.   Pro-
              viding  this will bypass all security restrictions.

       -upw password
              Specify the user password for the PDF file.

       -cfg config-file
              Read config-file in place of ~/.xpdfrc or the  sys-
              tem-wide config file.

       -v     Print copyright and version information.

       -h     Print  usage  information.   (-help  and --help are
              equivalent.)

EXIT CODES
       The Xpdf tools use the following exit codes:

       0      No error.

       1      Error opening a PDF file.

       2      Error opening an output file.

       3      Error related to PDF permissions.

       99     Other error.

AUTHOR
       The  pdfinfo  software  and  documentation  are  copyright
       1996-2005 Glyph & Cog, LLC.

SEE ALSO
       xpdf(1),     pdftops(1),     pdftotext(1),    pdffonts(1),
       pdftoppm(1), pdfimages(1), xpdfrc(5)
       http://www.foolabs.com/xpdf/



                          17 August 2005               pdfinfo(1)
! Generated automatically by mantohlp
1 pdfinfo

       pdfinfo - Portable Document Format (PDF) document informa-
       tion

       pdfinfo [options] [PDF-file]

       Pdfinfo prints the contents of the 'Info' dictionary (plus
       some  other  useful  information) from a Portable Document
       Format (PDF) file.

       The 'Info' dictionary contains the following values:

              title
              subject
              keywords
              author
              creator
              producer
              creation date
              modification date

       In addition, the following information is printed:

              tagged (yes/no)
              page count
              encrypted flag (yes/no)
              print and copy permissions (if encrypted)
              page size
              file size
              linearized (yes/no)
              PDF version
              metadata (only if requested)

                                                               ()

2 ONFIGURATION_FIL

       Pdfinfo  reads  a configuration file at startup.  It first
       tries to find the user's private config  file,  ~/.xpdfrc.
       If  that  doesn't exist, it looks for a system-wide config
       file, typically /usr/local/etc/xpdfrc (but  this  location
       can  be changed when pdfinfo is built).  See the xpdfrc(5)
       man page for details.

                                                               ()

2 OPTIONS

       Many  of  the following options can be set with configura-
       tion file commands.  These are listed in  square  brackets
       with  the  description  of  the corresponding command line
       option.

       -f number
              Specifies the first page to examine.   If  multiple
              pages   are  requested  using  the  "-f"  and  "-l"
              options, the size  of  each  requested  page  (and,
              optionally,  the  bounding boxes for each requested
              page) are printed.  Otherwise,  only  page  one  is
              examined.

       -l number
              Specifies the last page to examine.

       -box   Prints the page box bounding boxes: MediaBox, Crop-
              Box, BleedBox, TrimBox, and ArtBox.

       -meta  Prints  document-level  metadata.   (This  is   the
              "Metadata"  stream  from  the  PDF  file's  Catalog
              object.)

       -enc encoding-name
              Sets the encoding to  use  for  text  output.   The
              encoding-name  must  be defined with the unicodeMap
              command (see xpdfrc(5)).  This defaults to "Latin1"
              (which is a built-in encoding).  [config file: tex-
              tEncoding]

       -opw password
              Specify the owner password for the PDF file.   Pro-
              viding  this will bypass all security restrictions.

       -upw password
              Specify the user password for the PDF file.

       -cfg config-file
              Read config-file in place of ~/.xpdfrc or the  sys-
              tem-wide config file.

       -v     Print copyright and version information.

       -h     Print  usage  information.   (-help  and --help are
              equivalent.)

                                                               ()

2 XIT_CODE

       The Xpdf tools use the following exit codes:

       0      No error.

       1      Error opening a PDF file.

       2      Error opening an output file.

       3      Error related to PDF permissions.

       99     Other error.

                                                               ()

2 AUTHOR

       The  pdfinfo  software  and  documentation  are  copyright
       1996-2005 Glyph & Cog, LLC.

                                                               ()

2 SEE_ALSO

       xpdf(1),     pdftops(1),     pdftotext(1),    pdffonts(1),
       pdftoppm(1), pdfimages(1), xpdfrc(5)
       http://www.foolabs.com/xpdf/

                                                               ()

.\" Copyright 2005 Glyph & Cog, LLC
.TH pdftoppm 1 "17 August 2005"
.SH NAME
pdftoppm \- Portable Document Format (PDF) to Portable Pixmap (PPM)
converter (version 3.01)
.SH SYNOPSIS
.B pdftoppm
[options]
.I PDF-file PPM-root
.SH DESCRIPTION
.B Pdftoppm
converts Portable Document Format (PDF) files to color image files in
Portable Pixmap (PPM) format, grayscale image files in Portable
Graymap (PGM) format, or monochrome image files in Portable Bitmap
(PBM) format.
.PP
Pdftoppm reads the PDF file,
.IR PDF-file ,
and writes one PPM file for each page,
.IR PPM-root - nnnnnn .ppm,
where
.I nnnnnn
is the page number.
.SH CONFIGURATION FILE
Pdftoppm reads a configuration file at startup.  It first tries to
find the user's private config file, ~/.xpdfrc.  If that doesn't
exist, it looks for a system-wide config file, typically
/usr/local/etc/xpdfrc (but this location can be changed when pdftoppm
is built).  See the
.BR xpdfrc (5)
man page for details.
.SH OPTIONS
Many of the following options can be set with configuration file
commands.  These are listed in square brackets with the description of
the corresponding command line option.
.TP
.BI \-f " number"
Specifies the first page to convert.
.TP
.BI \-l " number"
Specifies the last page to convert.
.TP
.BI \-r " number"
Specifies the resolution, in DPI.  The default is 150 DPI.
.TP
.B \-mono
Generate a monochrome PBM file (instead of a color PPM file).
.TP
.B \-gray
Generate a grayscale PGM file (instead of a color PPM file).
.TP
.BI \-t1lib " yes | no"
Enable or disable t1lib (a Type 1 font rasterizer).  This defaults to
"yes".
.RB "[config file: " enableT1lib ]
.TP
.BI \-freetype " yes | no"
Enable or disable FreeType (a TrueType / Type 1 font rasterizer).
This defaults to "yes".
.RB "[config file: " enableFreeType ]
.TP
.BI \-aa " yes | no"
Enable or disable font anti-aliasing.  This defaults to "yes".
.RB "[config file: " antialias ]
.TP
.BI \-opw " password"
Specify the owner password for the PDF file.  Providing this will
bypass all security restrictions.
.TP
.BI \-upw " password"
Specify the user password for the PDF file.
.TP
.B \-q
Don't print any messages or errors.
.RB "[config file: " errQuiet ]
.TP
.B \-v
Print copyright and version information.
.TP
.B \-h
Print usage information.
.RB ( \-help
and
.B \-\-help
are equivalent.)
.SH EXIT CODES
The Xpdf tools use the following exit codes:
.TP
0
No error.
.TP
1
Error opening a PDF file.
.TP
2
Error opening an output file.
.TP
3
Error related to PDF permissions.
.TP
99
Other error.
.SH AUTHOR
The pdftoppm software and documentation are copyright 1996-2005 Glyph
& Cog, LLC.
.SH "SEE ALSO"
.BR xpdf (1),
.BR pdftops (1),
.BR pdftotext (1),
.BR pdfinfo (1),
.BR pdffonts (1),
.BR pdfimages (1),
.BR xpdfrc (5)
.br
.B http://www.foolabs.com/xpdf/
pdftoppm(1)                                           pdftoppm(1)



NAME
       pdftoppm  -  Portable  Document  Format  (PDF) to Portable
       Pixmap (PPM) converter (version 3.01)

SYNOPSIS
       pdftoppm [options] PDF-file PPM-root

DESCRIPTION
       Pdftoppm converts Portable Document Format (PDF) files  to
       color   image  files  in  Portable  Pixmap  (PPM)  format,
       grayscale image files in Portable Graymap (PGM) format, or
       monochrome image files in Portable Bitmap (PBM) format.

       Pdftoppm  reads the PDF file, PDF-file, and writes one PPM
       file for each page, PPM-root-nnnnnn.ppm, where  nnnnnn  is
       the page number.

CONFIGURATION FILE
       Pdftoppm  reads a configuration file at startup.  It first
       tries to find the user's private config  file,  ~/.xpdfrc.
       If  that  doesn't exist, it looks for a system-wide config
       file, typically /usr/local/etc/xpdfrc (but  this  location
       can be changed when pdftoppm is built).  See the xpdfrc(5)
       man page for details.

OPTIONS
       Many of the following options can be set  with  configura-
       tion  file  commands.  These are listed in square brackets
       with the description of  the  corresponding  command  line
       option.

       -f number
              Specifies the first page to convert.

       -l number
              Specifies the last page to convert.

       -r number
              Specifies  the  resolution, in DPI.  The default is
              150 DPI.

       -mono  Generate a monochrome PBM file (instead of a  color
              PPM file).

       -gray  Generate  a  grayscale PGM file (instead of a color
              PPM file).

       -t1lib yes | no
              Enable or disable t1lib (a Type 1 font rasterizer).
              This defaults to "yes".  [config file: enableT1lib]

       -freetype yes | no
              Enable or disable FreeType (a  TrueType  /  Type  1
              font rasterizer).  This defaults to "yes".  [config
              file: enableFreeType]

       -aa yes | no
              Enable  or  disable   font   anti-aliasing.    This
              defaults to "yes".  [config file: antialias]

       -opw password
              Specify  the  owner  password  for  the  PDF  file.
              Providing this will bypass  all  security  restric-
              tions.

       -upw password
              Specify the user password for the PDF file.

       -q     Don't  print any messages or errors.  [config file:
              errQuiet]

       -v     Print copyright and version information.

       -h     Print usage information.   (-help  and  --help  are
              equivalent.)

EXIT CODES
       The Xpdf tools use the following exit codes:

       0      No error.

       1      Error opening a PDF file.

       2      Error opening an output file.

       3      Error related to PDF permissions.

       99     Other error.

AUTHOR
       The  pdftoppm  software  and  documentation  are copyright
       1996-2005 Glyph & Cog, LLC.

SEE ALSO
       xpdf(1),  pdftops(1),   pdftotext(1),   pdfinfo(1),   pdf-
       fonts(1), pdfimages(1), xpdfrc(5)
       http://www.foolabs.com/xpdf/



                          17 August 2005              pdftoppm(1)
! Generated automatically by mantohlp
1 pdftoppm

       pdftoppm  -  Portable  Document  Format  (PDF) to Portable
       Pixmap (PPM)

       pdftoppm [options] PDF-file PPM-root

       Pdftoppm converts Portable Document Format (PDF) files  to
       color   image  files  in  Portable  Pixmap  (PPM)  format,
       grayscale image files in Portable Graymap (PGM) format, or
       monochrome image files in Portable Bitmap (PBM) format.

       Pdftoppm  reads the PDF file, PDF-file, and writes one PPM
       file for each page, PPM-root-nnnnnn.ppm, where  nnnnnn  is
       the page number.

                                                               ()

2 ONFIGURATION_FIL

       Pdftoppm  reads a configuration file at startup.  It first
       tries to find the user's private config  file,  ~/.xpdfrc.
       If  that  doesn't exist, it looks for a system-wide config
       file, typically /usr/local/etc/xpdfrc (but  this  location
       can be changed when pdftoppm is built).  See the xpdfrc(5)
       man page for details.

                                                               ()

2 OPTIONS

       Many  of  the following options can be set with configura-
       tion file commands.  These are listed in  square  brackets
       with  the  description  of  the corresponding command line
       option.

       -f number
              Specifies the first page to convert.

       -l number
              Specifies the last page to convert.

       -r number
              Specifies the resolution, in DPI.  The  default  is
              150 DPI.

       -mono  Generate  a monochrome PBM file (instead of a color
              PPM file).

       -gray  Generate a grayscale PGM file (instead of  a  color
              PPM file).

       -t1lib yes | no
              Enable or disable t1lib (a Type 1 font rasterizer).
              This defaults to "yes".  [config file: enableT1lib]

       -freetype yes | no
              Enable  or  disable  FreeType  (a TrueType / Type 1
              font rasterizer).  This defaults to "yes".  [config
              file: enableFreeType]

       -aa yes | no
              Enable   or   disable   font  anti-aliasing.   This
              defaults to "yes".  [config file: antialias]

       -opw password
              Specify the owner password for the PDF file.   Pro-
              viding  this will bypass all security restrictions.

       -upw password
              Specify the user password for the PDF file.

       -q     Don't print any messages or errors.  [config  file:
              errQuiet]

       -v     Print copyright and version information.

       -h     Print  usage  information.   (-help  and --help are
              equivalent.)

                                                               ()

2 XIT_CODE

       The Xpdf tools use the following exit codes:

       0      No error.

       1      Error opening a PDF file.

       2      Error opening an output file.

       3      Error related to PDF permissions.

       99     Other error.

                                                               ()

2 AUTHOR

       The  pdftoppm  software  and  documentation  are copyright
       1996-2005 Glyph & Cog, LLC.

                                                               ()

2 SEE_ALSO

       xpdf(1),   pdftops(1),   pdftotext(1),   pdfinfo(1),  pdf-
       fonts(1), pdfimages(1), xpdfrc(5)
       http://www.foolabs.com/xpdf/

                                                               ()

.\" Copyright 1996-2005 Glyph & Cog, LLC
.TH pdftops 1 "17 August 2005"
.SH NAME
pdftops \- Portable Document Format (PDF) to PostScript converter
(version 3.01)
.SH SYNOPSIS
.B pdftops
[options]
.RI [ PDF-file
.RI [ PS-file ]]
.SH DESCRIPTION
.B Pdftops
converts Portable Document Format (PDF) files to PostScript so they
can be printed.
.PP
Pdftops reads the PDF file,
.IR PDF-file ,
and writes a PostScript file,
.IR PS-file .
If
.I PS-file
is not specified, pdftops converts
.I file.pdf
to
.I file.ps
(or
.I file.eps
with the -eps option).  If 
.I PS-file
is \'-', the PostScript is sent to stdout.
.SH CONFIGURATION FILE
Pdftops reads a configuration file at startup.  It first tries to find
the user's private config file, ~/.xpdfrc.  If that doesn't exist, it
looks for a system-wide config file, typically /usr/local/etc/xpdfrc
(but this location can be changed when pdftops is built).  See the
.BR xpdfrc (5)
man page for details.
.SH OPTIONS
Many of the following options can be set with configuration file
commands.  These are listed in square brackets with the description of
the corresponding command line option.
.TP
.BI \-f " number"
Specifies the first page to print.
.TP
.BI \-l " number"
Specifies the last page to print.
.TP
.B \-level1
Generate Level 1 PostScript.  The resulting PostScript files will be
significantly larger (if they contain images), but will print on Level
1 printers.  This also converts all images to black and white.  No
more than one of the PostScript level options (-level1, -level1sep,
-level2, -level2sep, -level3, -level3Sep) may be given.
.RB "[config file: " psLevel ]
.TP
.B \-level1sep
Generate Level 1 separable PostScript.  All colors are converted to
CMYK.  Images are written with separate stream data for the four
components.
.RB "[config file: " psLevel ]
.TP
.B \-level2
Generate Level 2 PostScript.  Level 2 supports color images and image
compression.  This is the default setting.
.RB "[config file: " psLevel ]
.TP
.B \-level2sep
Generate Level 2 separable PostScript.  All colors are converted to
CMYK.  The PostScript separation convention operators are used to
handle custom (spot) colors.
.RB "[config file: " psLevel ]
.TP
.B \-level3
Generate Level 3 PostScript.  This enables all Level 2 features plus
CID font embedding.
.RB "[config file: " psLevel ]
.TP
.B \-level3Sep
Generate Level 3 separable PostScript.  The separation handling is the
same as for -level2Sep.
.RB "[config file: " psLevel ]
.TP
.B \-eps
Generate an Encapsulated PostScript (EPS) file.  An EPS file contains
a single image, so if you use this option with a multi-page PDF file,
you must use -f and -l to specify a single page.  No more than one of
the mode options (-eps, -form) may be given.
.TP
.B \-form
Generate a PostScript form which can be imported by software that
understands forms.  A form contains a single page, so if you use this
option with a multi-page PDF file, you must use -f and -l to specify a
single page.  The -level1 option cannot be used with -form.
.TP
.B \-opi
Generate OPI comments for all images and forms which have OPI
information.  (This option is only available if pdftops was compiled
with OPI support.)
.RB "[config file: " psOPI ]
.TP
.B \-noembt1
By default, any Type 1 fonts which are embedded in the PDF file are
copied into the PostScript file.  This option causes pdftops to
substitute base fonts instead.  Embedded fonts make PostScript files
larger, but may be necessary for readable output.
.RB "[config file: " psEmbedType1Fonts ]
.TP
.B \-noembtt
By default, any TrueType fonts which are embedded in the PDF file are
copied into the PostScript file.  This option causes pdftops to
substitute base fonts instead.  Embedded fonts make PostScript files
larger, but may be necessary for readable output.  Also, some
PostScript interpreters do not have TrueType rasterizers.
.RB "[config file: " psEmbedTrueTypeFonts ]
.TP
.B \-noembcidps
By default, any CID PostScript fonts which are embedded in the PDF
file are copied into the PostScript file.  This option disables that
embedding.  No attempt is made to substitute for non-embedded CID
PostScript fonts.
.RB "[config file: " psEmbedCIDPostScriptFonts ]
.TP
.B \-noembcidtt
By default, any CID TrueType fonts which are embedded in the PDF file
are copied into the PostScript file.  This option disables that
embedding.  No attempt is made to substitute for non-embedded CID
TrueType fonts.
.RB "[config file: " psEmbedCIDTrueTypeFonts ]
.TP
.BI \-paper " size"
Set the paper size to one of "letter", "legal", "A4", or "A3".  This
can also be set to "match", which will set the paper size to match the
size specified in the PDF file.
.RB "[config file: " psPaperSize ]
.TP
.BI \-paperw " size"
Set the paper width, in points.
.RB "[config file: " psPaperSize ]
.TP
.BI \-paperh " size"
Set the paper height, in points.
.RB "[config file: " psPaperSize ]
.TP
.B \-nocrop
By default, output is cropped to the CropBox specified in the PDF
file.  This option disables cropping.
.RB "[config file: " psCrop ]
.TP
.B \-expand
Expand PDF pages smaller than the paper to fill the paper.  By
default, these pages are not scaled.
.RB "[config file: " psExpandSmaller ]
.TP
.B \-noshrink
Don't scale PDF pages which are larger than the paper.  By default,
pages larger than the paper are shrunk to fit.
.RB "[config file: " psShrinkLarger ]
.TP
.B \-nocenter
By default, PDF pages smaller than the paper (after any scaling) are
centered on the paper.  This option causes them to be aligned to the
lower-left corner of the paper instead.
.RB "[config file: " psCenter ]
.TP
.B \-pagecrop
Treat the CropBox as the PDF page size.  By default, the MediaBox is
used as the page size.
.TP
.B \-duplex
Set the Duplex pagedevice entry in the PostScript file.  This tells
duplex-capable printers to enable duplexing.
.RB "[config file: " psDuplex ]
.TP
.BI \-opw " password"
Specify the owner password for the PDF file.  Providing this will
bypass all security restrictions.
.TP
.BI \-upw " password"
Specify the user password for the PDF file.
.TP
.B \-q
Don't print any messages or errors.
.RB "[config file: " errQuiet ]
.TP
.BI \-cfg " config-file"
Read
.I config-file
in place of ~/.xpdfrc or the system-wide config file.
.TP
.B \-v
Print copyright and version information.
.TP
.B \-h
Print usage information.
.RB ( \-help
and
.B \-\-help
are equivalent.)
.SH EXIT CODES
The Xpdf tools use the following exit codes:
.TP
0
No error.
.TP
1
Error opening a PDF file.
.TP
2
Error opening an output file.
.TP
3
Error related to PDF permissions.
.TP
99
Other error.
.SH AUTHOR
The pdftops software and documentation are copyright 1996-2005 Glyph &
Cog, LLC.
.SH "SEE ALSO"
.BR xpdf (1),
.BR pdftotext (1),
.BR pdfinfo (1),
.BR pdffonts (1),
.BR pdftoppm (1),
.BR pdfimages (1),
.BR xpdfrc (5)
.br
.B http://www.foolabs.com/xpdf/
pdftops(1)                                             pdftops(1)



NAME
       pdftops  -  Portable  Document  Format (PDF) to PostScript
       converter (version 3.01)

SYNOPSIS
       pdftops [options] [PDF-file [PS-file]]

DESCRIPTION
       Pdftops converts Portable Document Format (PDF)  files  to
       PostScript so they can be printed.

       Pdftops  reads  the  PDF  file,  PDF-file,  and  writes  a
       PostScript file, PS-file.  If PS-file  is  not  specified,
       pdftops converts file.pdf to file.ps (or file.eps with the
       -eps option).  If PS-file is '-', the PostScript  is  sent
       to stdout.

CONFIGURATION FILE
       Pdftops  reads  a configuration file at startup.  It first
       tries to find the user's private config  file,  ~/.xpdfrc.
       If  that  doesn't exist, it looks for a system-wide config
       file, typically /usr/local/etc/xpdfrc (but  this  location
       can  be changed when pdftops is built).  See the xpdfrc(5)
       man page for details.

OPTIONS
       Many of the following options can be set  with  configura-
       tion  file  commands.  These are listed in square brackets
       with the description of  the  corresponding  command  line
       option.

       -f number
              Specifies the first page to print.

       -l number
              Specifies the last page to print.

       -level1
              Generate   Level   1   PostScript.   The  resulting
              PostScript files will be significantly  larger  (if
              they  contain  images),  but  will print on Level 1
              printers.  This also converts all images  to  black
              and  white.   No  more  than  one of the PostScript
              level  options   (-level1,   -level1sep,   -level2,
              -level2sep,  -level3,  -level3Sep)  may  be  given.
              [config file: psLevel]

       -level1sep
              Generate Level 1 separable PostScript.  All  colors
              are  converted  to  CMYK.   Images are written with
              separate  stream  data  for  the  four  components.
              [config file: psLevel]

       -level2
              Generate  Level  2  PostScript.   Level  2 supports
              color images and image compression.   This  is  the
              default setting.  [config file: psLevel]

       -level2sep
              Generate  Level 2 separable PostScript.  All colors
              are converted to CMYK.  The  PostScript  separation
              convention  operators  are  used  to  handle custom
              (spot) colors.  [config file: psLevel]

       -level3
              Generate Level  3  PostScript.   This  enables  all
              Level  2 features plus CID font embedding.  [config
              file: psLevel]

       -level3Sep
              Generate Level 3 separable PostScript.  The separa-
              tion handling is the same as for -level2Sep.  [con-
              fig file: psLevel]

       -eps   Generate an Encapsulated PostScript (EPS) file.  An
              EPS  file  contains  a  single image, so if you use
              this option with a multi-page PDF  file,  you  must
              use  -f  and  -l to specify a single page.  No more
              than one of the mode options (-eps, -form)  may  be
              given.

       -form  Generate a PostScript form which can be imported by
              software that understands forms.  A form contains a
              single  page,  so  if  you  use  this option with a
              multi-page PDF file, you must  use  -f  and  -l  to
              specify  a  single page.  The -level1 option cannot
              be used with -form.

       -opi   Generate OPI comments  for  all  images  and  forms
              which  have  OPI information.  (This option is only
              available if pdftops was  compiled  with  OPI  sup-
              port.)  [config file: psOPI]

       -noembt1
              By  default, any Type 1 fonts which are embedded in
              the PDF file are copied into the  PostScript  file.
              This option causes pdftops to substitute base fonts
              instead.   Embedded  fonts  make  PostScript  files
              larger,  but  may be necessary for readable output.
              [config file: psEmbedType1Fonts]

       -noembtt
              By default, any TrueType fonts which  are  embedded
              in  the  PDF  file  are  copied into the PostScript
              file.  This option  causes  pdftops  to  substitute
              base fonts instead.  Embedded fonts make PostScript
              files larger, but may  be  necessary  for  readable
              output.   Also, some PostScript interpreters do not
              have TrueType  rasterizers.   [config  file:  psEm-
              bedTrueTypeFonts]

       -noembcidps
              By  default,  any  CID  PostScript  fonts which are
              embedded in  the  PDF  file  are  copied  into  the
              PostScript  file.  This option disables that embed-
              ding.  No attempt is made to  substitute  for  non-
              embedded CID PostScript fonts.  [config file: psEm-
              bedCIDPostScriptFonts]

       -noembcidtt
              By default, any CID TrueType fonts which are embed-
              ded  in the PDF file are copied into the PostScript
              file.  This option  disables  that  embedding.   No
              attempt  is made to substitute for non-embedded CID
              TrueType fonts.  [config file:  psEmbedCIDTrueType-
              Fonts]

       -paper size
              Set  the  paper  size  to one of "letter", "legal",
              "A4", or "A3".  This can also be  set  to  "match",
              which  will  set  the  paper size to match the size
              specified in the PDF file.  [config file:  psPaper-
              Size]

       -paperw size
              Set  the  paper  width,  in  points.  [config file:
              psPaperSize]

       -paperh size
              Set the paper height,  in  points.   [config  file:
              psPaperSize]

       -nocrop
              By default, output is cropped to the CropBox speci-
              fied in the PDF file.  This option  disables  crop-
              ping.  [config file: psCrop]

       -expand
              Expand PDF pages smaller than the paper to fill the
              paper.  By default, these  pages  are  not  scaled.
              [config file: psExpandSmaller]

       -noshrink
              Don't  scale  PDF  pages  which are larger than the
              paper.  By default, pages larger than the paper are
              shrunk to fit.  [config file: psShrinkLarger]

       -nocenter
              By default, PDF pages smaller than the paper (after
              any scaling)  are  centered  on  the  paper.   This
              option  causes them to be aligned to the lower-left
              corner of the paper instead.  [config file:  psCen-
              ter]

       -pagecrop
              Treat  the  CropBox  as  the  PDF  page  size.   By
              default, the MediaBox is used as the page size.

       -duplex
              Set the Duplex pagedevice entry in  the  PostScript
              file.  This tells duplex-capable printers to enable
              duplexing.  [config file: psDuplex]

       -opw password
              Specify the owner password for the PDF file.   Pro-
              viding  this will bypass all security restrictions.

       -upw password
              Specify the user password for the PDF file.

       -q     Don't print any messages or errors.  [config  file:
              errQuiet]

       -cfg config-file
              Read  config-file in place of ~/.xpdfrc or the sys-
              tem-wide config file.

       -v     Print copyright and version information.

       -h     Print usage information.   (-help  and  --help  are
              equivalent.)

EXIT CODES
       The Xpdf tools use the following exit codes:

       0      No error.

       1      Error opening a PDF file.

       2      Error opening an output file.

       3      Error related to PDF permissions.

       99     Other error.

AUTHOR
       The  pdftops  software  and  documentation  are  copyright
       1996-2005 Glyph & Cog, LLC.

SEE ALSO
       xpdf(1),    pdftotext(1),     pdfinfo(1),     pdffonts(1),
       pdftoppm(1), pdfimages(1), xpdfrc(5)
       http://www.foolabs.com/xpdf/



                          17 August 2005               pdftops(1)
! Generated automatically by mantohlp
1 pdftops

       pdftops  -  Portable  Document  Format (PDF) to PostScript
       converter

       pdftops [options] [PDF-file [PS-file]]

       Pdftops converts Portable Document Format (PDF)  files  to
       PostScript so they can be printed.

       Pdftops  reads  the  PDF  file,  PDF-file,  and  writes  a
       PostScript file, PS-file.  If PS-file  is  not  specified,
       pdftops converts file.pdf to file.ps (or file.eps with the
       -eps option).  If PS-file is '-', the PostScript  is  sent
       to stdout.

                                                               ()

2 ONFIGURATION_FIL

       Pdftops  reads  a configuration file at startup.  It first
       tries to find the user's private config  file,  ~/.xpdfrc.
       If  that  doesn't exist, it looks for a system-wide config
       file, typically /usr/local/etc/xpdfrc (but  this  location
       can  be changed when pdftops is built).  See the xpdfrc(5)
       man page for details.

                                                               ()

2 OPTIONS

       Many  of  the following options can be set with configura-
       tion file commands.  These are listed in  square  brackets
       with  the  description  of  the corresponding command line
       option.

       -f number
              Specifies the first page to print.

       -l number
              Specifies the last page to print.

       -level1
              Generate  Level  1   PostScript.    The   resulting
              PostScript  files  will be significantly larger (if
              they contain images), but will  print  on  Level  1
              printers.   This  also converts all images to black
              and white.  No more  than  one  of  the  PostScript
              level   options   (-level1,   -level1sep,  -level2,
              -level2sep,  -level3,  -level3Sep)  may  be  given.
              [config file: psLevel]

       -level1sep
              Generate  Level 1 separable PostScript.  All colors
              are converted to CMYK.   Images  are  written  with
              separate  stream  data  for  the  four  components.
              [config file: psLevel]

       -level2
              Generate Level  2  PostScript.   Level  2  supports
              color  images  and  image compression.  This is the
              default setting.  [config file: psLevel]

       -level2sep
              Generate Level 2 separable PostScript.  All  colors
              are  converted  to CMYK.  The PostScript separation
              convention operators  are  used  to  handle  custom
              (spot) colors.  [config file: psLevel]

       -level3
              Generate  Level  3  PostScript.   This  enables all
              Level 2 features plus CID font embedding.   [config
              file: psLevel]

       -level3Sep
              Generate Level 3 separable PostScript.  The separa-
              tion handling is the same as for -level2Sep.  [con-
              fig file: psLevel]

       -eps   Generate an Encapsulated PostScript (EPS) file.  An
              EPS file contains a single image,  so  if  you  use
              this  option  with  a multi-page PDF file, you must
              use -f and -l to specify a single  page.   No  more
              than  one  of the mode options (-eps, -form) may be
              given.

       -form  Generate a PostScript form which can be imported by
              software that understands forms.  A form contains a
              single page, so if  you  use  this  option  with  a
              multi-page  PDF  file,  you  must  use -f and -l to
              specify a single page.  The -level1  option  cannot
              be used with -form.

       -opi   Generate  OPI  comments  for  all  images and forms
              which have OPI information.  (This option  is  only
              available  if  pdftops  was  compiled with OPI sup-
              port.)  [config file: psOPI]

       -noembt1
              By default, any Type 1 fonts which are embedded  in
              the  PDF  file are copied into the PostScript file.
              This option causes pdftops to substitute base fonts
              instead.   Embedded  fonts  make  PostScript  files
              larger, but may be necessary for  readable  output.
              [config file: psEmbedType1Fonts]

       -noembtt
              By  default,  any TrueType fonts which are embedded
              in the PDF file  are  copied  into  the  PostScript
              file.   This  option  causes  pdftops to substitute
              base fonts instead.  Embedded fonts make PostScript
              files  larger,  but  may  be necessary for readable
              output.  Also, some PostScript interpreters do  not
              have  TrueType  rasterizers.   [config  file: psEm-
              bedTrueTypeFonts]

       -noembcidps
              By default, any  CID  PostScript  fonts  which  are
              embedded  in  the  PDF  file  are  copied  into the
              PostScript file.  This option disables that  embed-
              ding.   No  attempt  is made to substitute for non-
              embedded CID PostScript fonts.  [config file: psEm-
              bedCIDPostScriptFonts]

       -noembcidtt
              By default, any CID TrueType fonts which are embed-
              ded in the PDF file are copied into the  PostScript
              file.   This  option  disables  that embedding.  No
              attempt is made to substitute for non-embedded  CID
              TrueType  fonts.  [config file: psEmbedCIDTrueType-
              Fonts]

       -paper size
              Set the paper size to  one  of  "letter",  "legal",
              "A4",  or  "A3".   This can also be set to "match",
              which will set the paper size  to  match  the  size
              specified  in the PDF file.  [config file: psPaper-
              Size]

       -paperw size
              Set the paper  width,  in  points.   [config  file:
              psPaperSize]

       -paperh size
              Set  the  paper  height,  in points.  [config file:
              psPaperSize]

       -nocrop
              By default, output is cropped to the CropBox speci-
              fied  in  the PDF file.  This option disables crop-
              ping.  [config file: psCrop]

       -expand
              Expand PDF pages smaller than the paper to fill the
              paper.   By  default,  these  pages are not scaled.
              [config file: psExpandSmaller]

       -noshrink
              Don't scale PDF pages which  are  larger  than  the
              paper.  By default, pages larger than the paper are
              shrunk to fit.  [config file: psShrinkLarger]

       -nocenter
              By default, PDF pages smaller than the paper (after
              any  scaling)  are  centered  on  the  paper.  This
              option causes them to be aligned to the  lower-left
              corner  of the paper instead.  [config file: psCen-
              ter]

       -pagecrop
              Treat  the  CropBox  as  the  PDF  page  size.   By
              default, the MediaBox is used as the page size.

       -duplex
              Set  the  Duplex pagedevice entry in the PostScript
              file.  This tells duplex-capable printers to enable
              duplexing.  [config file: psDuplex]

       -opw password
              Specify  the owner password for the PDF file.  Pro-
              viding this will bypass all security  restrictions.

       -upw password
              Specify the user password for the PDF file.

       -q     Don't  print any messages or errors.  [config file:
              errQuiet]

       -cfg config-file
              Read config-file in place of ~/.xpdfrc or the  sys-
              tem-wide config file.

       -v     Print copyright and version information.

       -h     Print  usage  information.   (-help  and --help are
              equivalent.)

                                                               ()

2 XIT_CODE

       The Xpdf tools use the following exit codes:

       0      No error.

       1      Error opening a PDF file.

       2      Error opening an output file.

       3      Error related to PDF permissions.

       99     Other error.

                                                               ()

2 AUTHOR

       The  pdftops  software  and  documentation  are  copyright
       1996-2005 Glyph & Cog, LLC.

                                                               ()

2 SEE_ALSO

       xpdf(1),     pdftotext(1),     pdfinfo(1),    pdffonts(1),
       pdftoppm(1), pdfimages(1), xpdfrc(5)
       http://www.foolabs.com/xpdf/

                                                               ()

.\" Copyright 1997-2005 Glyph & Cog, LLC
.TH pdftotext 1 "17 August 2005"
.SH NAME
pdftotext \- Portable Document Format (PDF) to text converter
(version 3.01)
.SH SYNOPSIS
.B pdftotext
[options]
.RI [ PDF-file
.RI [ text-file ]]
.SH DESCRIPTION
.B Pdftotext
converts Portable Document Format (PDF) files to plain text.
.PP
Pdftotext reads the PDF file,
.IR PDF-file ,
and writes a text file,
.IR text-file .
If
.I text-file
is not specified, pdftotext converts
.I file.pdf
to
.IR file.txt .
If 
.I text-file
is \'-', the text is sent to stdout.
.SH CONFIGURATION FILE
Pdftotext reads a configuration file at startup.  It first tries to
find the user's private config file, ~/.xpdfrc.  If that doesn't
exist, it looks for a system-wide config file, typically
/usr/local/etc/xpdfrc (but this location can be changed when pdftotext
is built).  See the
.BR xpdfrc (5)
man page for details.
.SH OPTIONS
Many of the following options can be set with configuration file
commands.  These are listed in square brackets with the description of
the corresponding command line option.
.TP
.BI \-f " number"
Specifies the first page to convert.
.TP
.BI \-l " number"
Specifies the last page to convert.
.TP
.B \-layout
Maintain (as best as possible) the original physical layout of the
text.  The default is to \'undo' physical layout (columns,
hyphenation, etc.) and output the text in reading order.
.TP
.B \-raw
Keep the text in content stream order.  This is a hack which often
"undoes" column formatting, etc.  Use of raw mode is no longer
recommended.
.TP
.B \-htmlmeta
Generate a simple HTML file, including the meta information.  This
simply wraps the text in <pre> and </pre> and prepends the meta
headers.
.TP
.BI \-enc " encoding-name"
Sets the encoding to use for text output.  The
.I encoding\-name
must be defined with the unicodeMap command (see
.BR xpdfrc (5)).
The encoding name is case-sensitive.  This defaults to "Latin1" (which
is a built-in encoding).
.RB "[config file: " textEncoding ]
.TP
.BI \-eol " unix | dos | mac"
Sets the end-of-line convention to use for text output.
.RB "[config file: " textEOL ]
.TP
.B \-nopgbrk
Don't insert page breaks (form feed characters) between pages.
.RB "[config file: " textPageBreaks ]
.TP
.BI \-opw " password"
Specify the owner password for the PDF file.  Providing this will
bypass all security restrictions.
.TP
.BI \-upw " password"
Specify the user password for the PDF file.
.TP
.B \-q
Don't print any messages or errors.
.RB "[config file: " errQuiet ]
.TP
.BI \-cfg " config-file"
Read
.I config-file
in place of ~/.xpdfrc or the system-wide config file.
.TP
.B \-v
Print copyright and version information.
.TP
.B \-h
Print usage information.
.RB ( \-help
and
.B \-\-help
are equivalent.)
.SH BUGS
Some PDF files contain fonts whose encodings have been mangled beyond
recognition.  There is no way (short of OCR) to extract text from
these files.
.SH EXIT CODES
The Xpdf tools use the following exit codes:
.TP
0
No error.
.TP
1
Error opening a PDF file.
.TP
2
Error opening an output file.
.TP
3
Error related to PDF permissions.
.TP
99
Other error.
.SH AUTHOR
The pdftotext software and documentation are copyright 1996-2005 Glyph
& Cog, LLC.
.SH "SEE ALSO"
.BR xpdf (1),
.BR pdftops (1),
.BR pdfinfo (1),
.BR pdffonts (1),
.BR pdftoppm (1),
.BR pdfimages (1),
.BR xpdfrc (5)
.br
.B http://www.foolabs.com/xpdf/
pdftotext(1)                                         pdftotext(1)



NAME
       pdftotext  -  Portable  Document Format (PDF) to text con-
       verter (version 3.01)

SYNOPSIS
       pdftotext [options] [PDF-file [text-file]]

DESCRIPTION
       Pdftotext converts Portable Document Format (PDF) files to
       plain text.

       Pdftotext  reads the PDF file, PDF-file, and writes a text
       file, text-file.  If text-file is not specified, pdftotext
       converts  file.pdf  to file.txt.  If text-file is '-', the
       text is sent to stdout.

CONFIGURATION FILE
       Pdftotext reads a configuration file at startup.  It first
       tries  to  find the user's private config file, ~/.xpdfrc.
       If that doesn't exist, it looks for a  system-wide  config
       file,  typically  /usr/local/etc/xpdfrc (but this location
       can  be  changed  when  pdftotext  is  built).   See   the
       xpdfrc(5) man page for details.

OPTIONS
       Many  of  the following options can be set with configura-
       tion file commands.  These are listed in  square  brackets
       with  the  description  of  the corresponding command line
       option.

       -f number
              Specifies the first page to convert.

       -l number
              Specifies the last page to convert.

       -layout
              Maintain (as best as possible) the original  physi-
              cal  layout  of the text.  The default is to 'undo'
              physical layout (columns,  hyphenation,  etc.)  and
              output the text in reading order.

       -raw   Keep  the  text in content stream order.  This is a
              hack which often "undoes" column  formatting,  etc.
              Use of raw mode is no longer recommended.

       -htmlmeta
              Generate  a  simple  HTML  file, including the meta
              information.  This simply wraps the text  in  <pre>
              and </pre> and prepends the meta headers.

       -enc encoding-name
              Sets  the  encoding  to  use  for text output.  The
              encoding-name must be defined with  the  unicodeMap
              command  (see  xpdfrc(5)).   The  encoding  name is
              case-sensitive.  This defaults to  "Latin1"  (which
              is  a built-in encoding).  [config file: textEncod-
              ing]

       -eol unix | dos | mac
              Sets the end-of-line convention  to  use  for  text
              output.  [config file: textEOL]

       -nopgbrk
              Don't  insert  page  breaks  (form feed characters)
              between pages.  [config file: textPageBreaks]

       -opw password
              Specify the owner password for the PDF file.   Pro-
              viding  this will bypass all security restrictions.

       -upw password
              Specify the user password for the PDF file.

       -q     Don't print any messages or errors.  [config  file:
              errQuiet]

       -cfg config-file
              Read  config-file in place of ~/.xpdfrc or the sys-
              tem-wide config file.

       -v     Print copyright and version information.

       -h     Print usage information.   (-help  and  --help  are
              equivalent.)

BUGS
       Some  PDF  files  contain  fonts whose encodings have been
       mangled beyond recognition.  There is  no  way  (short  of
       OCR) to extract text from these files.

EXIT CODES
       The Xpdf tools use the following exit codes:

       0      No error.

       1      Error opening a PDF file.

       2      Error opening an output file.

       3      Error related to PDF permissions.

       99     Other error.

AUTHOR
       The  pdftotext  software  and  documentation are copyright
       1996-2005 Glyph & Cog, LLC.

SEE ALSO
       xpdf(1), pdftops(1), pdfinfo(1), pdffonts(1), pdftoppm(1),
       pdfimages(1), xpdfrc(5)
       http://www.foolabs.com/xpdf/



                          17 August 2005             pdftotext(1)
! Generated automatically by mantohlp
1 pdftotext

       pdftotext  -  Portable  Document Format (PDF) to text con-
       verter

       pdftotext [options] [PDF-file [text-file]]

       Pdftotext converts Portable Document Format (PDF) files to
       plain text.

       Pdftotext  reads the PDF file, PDF-file, and writes a text
       file, text-file.  If text-file is not specified, pdftotext
       converts  file.pdf  to file.txt.  If text-file is '-', the
       text is sent to stdout.

                                                               ()

2 ONFIGURATION_FIL

       Pdftotext reads a configuration file at startup.  It first
       tries to find the user's private config  file,  ~/.xpdfrc.
       If  that  doesn't exist, it looks for a system-wide config
       file, typically /usr/local/etc/xpdfrc (but  this  location
       can   be  changed  when  pdftotext  is  built).   See  the
       xpdfrc(5) man page for details.

                                                               ()

2 OPTIONS

       Many  of  the following options can be set with configura-
       tion file commands.  These are listed in  square  brackets
       with  the  description  of  the corresponding command line
       option.

       -f number
              Specifies the first page to convert.

       -l number
              Specifies the last page to convert.

       -layout
              Maintain (as best as possible) the original  physi-
              cal  layout  of the text.  The default is to 'undo'
              physical layout (columns,  hyphenation,  etc.)  and
              output the text in reading order.

       -raw   Keep  the  text in content stream order.  This is a
              hack which often "undoes" column  formatting,  etc.
              Use of raw mode is no longer recommended.

       -htmlmeta
              Generate  a  simple  HTML  file, including the meta
              information.  This simply wraps the text  in  <pre>
              and </pre> and prepends the meta headers.

       -enc encoding-name
              Sets  the  encoding  to  use  for text output.  The
              encoding-name must be defined with  the  unicodeMap
              command  (see  xpdfrc(5)).   The  encoding  name is
              case-sensitive.  This defaults to  "Latin1"  (which
              is  a built-in encoding).  [config file: textEncod-
              ing]

       -eol unix | dos | mac
              Sets the end-of-line convention  to  use  for  text
              output.  [config file: textEOL]

       -nopgbrk
              Don't  insert  page  breaks  (form feed characters)
              between pages.  [config file: textPageBreaks]

       -opw password
              Specify the owner password for the PDF file.   Pro-
              viding  this will bypass all security restrictions.

       -upw password
              Specify the user password for the PDF file.

       -q     Don't print any messages or errors.  [config  file:
              errQuiet]

       -cfg config-file
              Read  config-file in place of ~/.xpdfrc or the sys-
              tem-wide config file.

       -v     Print copyright and version information.

       -h     Print usage information.   (-help  and  --help  are
              equivalent.)

                                                               ()

2 BUGS

       Some  PDF  files  contain  fonts whose encodings have been
       mangled beyond recognition.  There is  no  way  (short  of
       OCR) to extract text from these files.

                                                               ()

2 XIT_CODE

       The Xpdf tools use the following exit codes:

       0      No error.

       1      Error opening a PDF file.

       2      Error opening an output file.

       3      Error related to PDF permissions.

       99     Other error.

                                                               ()

2 AUTHOR

       The  pdftotext  software  and  documentation are copyright
       1996-2005 Glyph & Cog, LLC.

                                                               ()

2 SEE_ALSO

       xpdf(1), pdftops(1), pdfinfo(1), pdffonts(1), pdftoppm(1),
       pdfimages(1), xpdfrc(5)
       http://www.foolabs.com/xpdf/

                                                               ()

#========================================================================
#
# Sample xpdfrc file
#
# The Xpdf tools look for a config file in two places:
# 1. ~/.xpdfrc
# 2. in a system-wide directory, typically /usr/local/etc/xpdfrc
#
# This sample config file demonstrates some of the more common
# configuration options.  Everything here is commented out.  You
# should edit things (especially the file/directory paths, since
# they'll likely be different on your system), and uncomment whichever
# options you want to use.  For complete details on config file syntax
# and available options, please see the xpdfrc(5) man page.
#
# Also, the Xpdf language support packages each include a set of
# options to be added to the xpdfrc file.
#
# http://www.foolabs.com/xpdf/
#
#========================================================================

#----- display fonts

# These map the Base-14 fonts to the Type 1 fonts that ship with
# ghostscript.  You'll almost certainly want to use something like
# this, but you'll need to adjust this to point to wherever
# ghostscript is installed on your system.  (But if the fonts are
# installed in a "standard" location, xpdf will find them
# automatically.)

#displayFontT1 Times-Roman		/usr/local/share/ghostscript/fonts/n021003l.pfb
#displayFontT1 Times-Italic		/usr/local/share/ghostscript/fonts/n021023l.pfb
#displayFontT1 Times-Bold		/usr/local/share/ghostscript/fonts/n021004l.pfb
#displayFontT1 Times-BoldItalic		/usr/local/share/ghostscript/fonts/n021024l.pfb
#displayFontT1 Helvetica		/usr/local/share/ghostscript/fonts/n019003l.pfb
#displayFontT1 Helvetica-Oblique	/usr/local/share/ghostscript/fonts/n019023l.pfb
#displayFontT1 Helvetica-Bold		/usr/local/share/ghostscript/fonts/n019004l.pfb
#displayFontT1 Helvetica-BoldOblique	/usr/local/share/ghostscript/fonts/n019024l.pfb
#displayFontT1 Courier			/usr/local/share/ghostscript/fonts/n022003l.pfb
#displayFontT1 Courier-Oblique		/usr/local/share/ghostscript/fonts/n022023l.pfb
#displayFontT1 Courier-Bold		/usr/local/share/ghostscript/fonts/n022004l.pfb
#displayFontT1 Courier-BoldOblique	/usr/local/share/ghostscript/fonts/n022024l.pfb
#displayFontT1 Symbol			/usr/local/share/ghostscript/fonts/s050000l.pfb
#displayFontT1 ZapfDingbats		/usr/local/share/ghostscript/fonts/d050000l.pfb

# If you need to display PDF files that refer to non-embedded fonts,
# you should add one or more fontDir options to point to the
# directories containing the font files.  Xpdf will only look at .pfa,
# .pfb, and .ttf files in those directories (other files will simply
# be ignored).

#fontDir		/usr/local/fonts/bakoma

#----- PostScript output control

# Set the default PostScript file or command.

#psFile			"|lpr -Pmyprinter"

# Set the default PostScript paper size -- this can be letter, legal,
# A4, or A3.  You can also specify a paper size as width and height
# (in points).

#psPaperSize		letter

#----- text output control

# Choose a text encoding for copy-and-paste and for pdftotext output.
# The Latin1, ASCII7, and UTF-8 encodings are built into Xpdf.  Other
# encodings are available in the language support packages.

#textEncoding		UTF-8

# Choose the end-of-line convention for multi-line copy-and-past and
# for pdftotext output.  The available options are unix, mac, and dos.

#textEOL		unix

#----- misc settings

# Enable t1lib, FreeType, and anti-aliased text.

#enableT1lib		yes
#enableFreeType		yes
#antialias		yes

# Set the command used to run a web browser when a URL hyperlink is
# clicked.

#urlCommand	"netscape -remote 'openURL(%s)'"
.\" Copyright 1996-2005 Glyph & Cog, LLC
.TH xpdf 1 "17 August 2005"
.SH NAME
xpdf \- Portable Document Format (PDF) file viewer for X (version 3.01)
.SH SYNOPSIS
.B xpdf
[options]
.RI [ PDF-file
.RI [ page " | +" dest ]]
.SH DESCRIPTION
.B Xpdf
is a viewer for Portable Document Format (PDF) files.  (These are also
sometimes also called \'Acrobat' files, from the name of Adobe's PDF
software.)  Xpdf runs under the X Window System on UNIX, VMS, and
OS/2.
.PP
To run xpdf, simply type:
.PP
.RS
xpdf file.pdf
.RE
.PP
where
.I file.pdf
is your PDF file.  The file name can be followed by a number
specifying the page which should be displayed first, e.g.:
.PP
.RS
xpdf file.pdf 18
.RE
.PP
You can also give a named destination, prefixed with \'+' in place of
the page number.  (This is only useful with PDF files that provide
named destination targets.)
.PP
You can also start xpdf without opening any files:
.PP
.RS
xpdf
.RE
.SH CONFIGURATION FILE
Xpdf reads a configuration file at startup.  It first tries to find
the user's private config file, ~/.xpdfrc.  If that doesn't exist, it
looks for a system-wide config file, typically /usr/local/etc/xpdfrc
(but this location can be changed when xpdf is built).  See the
.BR xpdfrc (5)
man page for details.
.SH OPTIONS
Many of the following options can be set with configuration file
commands or X resources.  These are listed in square brackets with the
description of the corresponding command line option.
.TP
.BI \-g " geometry"
Set the initial window geometry.
.RB ( \-geometry
is equivalent.)
.RB "[X resource: " xpdf.geometry ]
.TP
.BI \-title " title"
Set the window title.  By default, the title will be "xpdf: foo.pdf".
.RB "[X resource: " xpdf.title ]
.TP
.B \-cmap
Install a private colormap.  This is ignored on TrueColor visuals.
.RB "[X resource: " xpdf.installCmap ]
.TP
.BI \-rgb " number"
Set the size of largest RGB cube xpdf will try to allocate.  The
default is 5 (for a 5x5x5 cube); set to a smaller number to conserve
color table entries.  This is ignored with private colormaps and on
TrueColor visuals.
.RB "[X resource: " xpdf.rgbCubeSize ]
.TP
.B \-rv
Set reverse video mode.  This reverses the colors of everything except
images.  It may not always produce great results for PDF files which
do weird things with color.  This also causes the paper color to
default to black.
.RB "[X resource: " xpdf.reverseVideo ]
.TP
.BI \-papercolor " color"
Set the "paper color", i.e., the background of the page display.  This
will not work too well with PDF files that do things like filling in
white behind the text.
.RB "[X resource: " xpdf.paperColor ]
.TP
.BI \-mattecolor " color"
Set the matte color, i.e., the color used for background outside the
actual page area.
.RB "[X resource: " xpdf.matteColor ]
.TP
.BI \-z " zoom"
Set the initial zoom factor.  A number specifies a zoom percentage,
where 100 means 72 dpi.You may also specify \'page', to fit the page
to the window size, or \'width', to fit the page width to the window
width.
.RB "[config file: " initialZoom "; or X resource: " xpdf.initialZoom ]
.TP
.B \-cont
Start in continuous view mode, i.e., with one vertical scroll bar for
the whole document.
.RB "[config file: " continuousView ]
.TP
.BI \-t1lib " yes | no"
Enable or disable t1lib (a Type 1 font rasterizer).  This defaults to
"yes".
.RB "[config file: " enableT1lib ]
.TP
.BI \-freetype " yes | no"
Enable or disable FreeType (a TrueType / Type 1 font rasterizer).
This defaults to "yes".
.RB "[config file: " enableFreeType ]
.TP
.BI \-aa " yes | no"
Enable or disable font anti-aliasing.  This defaults to "yes".
.RB "[config file: " antialias ]
.TP
.BI \-ps " PS-file"
Set the default file name for PostScript output (i.e., the name which
will appear in the print dialog).  This can also be of the form
\'|command' to pipe the PostScript through a command.
.RB "[config file: " psFile ]
.TP
.BI \-paper " size"
Set the paper size to one of "letter", "legal", "A4", or "A3".  This
can also be set to "match", which will set the paper size to match the
size specified in the PDF file.
.RB "[config file: " psPaperSize ]
.TP
.BI \-paperw " size"
Set the paper width, in points.
.RB "[config file: " psPaperSize ]
.TP
.BI \-paperh " size"
Set the paper height, in points.
.RB "[config file: " psPaperSize ]
.TP
.B \-level1
Generate Level 1 PostScript.  The resulting PostScript files will be
significantly larger (if they contain images), but will print on Level
1 printers.  This also converts all images to black and white.
.RB "[config file: " psLevel ]
.TP
.BI \-enc " encoding-name"
Sets the encoding to use for text output.  The
.I encoding\-name
must be defined with the unicodeMap command (see
.BR xpdfrc (5)).
This defaults to "Latin1" (which is a built-in encoding).
.RB "[config file: " textEncoding ]
.TP
.BI \-eol " unix | dos | mac"
Sets the end-of-line convention to use for text output.
.RB "[config file: " textEOL ]
.TP
.BI \-opw " password"
Specify the owner password for the PDF file.  Providing this will
bypass all security restrictions.
.TP
.BI \-upw " password"
Specify the user password for the PDF file.
.TP
.B \-fullscreen
Open xpdf in a full-screen mode, useful for presentations.  You may
also want to specify '-bg black' (or similar) with this.  (There is
currently no way to switch between window and full-screen modes on the
fly.)
.TP
.BI \-remote " name"
Start/contact xpdf remote server with specified name (see the
.B "REMOTE SERVER MODE"
section below).
.TP
.B \-reload
Reload xpdf remote server window (with -remote only).
.TP
.B \-raise
Raise xpdf remote server window (with -remote only).
.TP
.B \-quit
Kill xpdf remote server (with -remote only).
.TP
.B \-cmd
Print commands as they're executed (useful for debugging).
.RB "[config file: " printCommands ]
.TP
.B \-q
Don't print any messages or errors.
.RB "[config file: " errQuiet ]
.TP
.BI \-cfg " config-file"
Read
.I config-file
in place of ~/.xpdfrc or the system-wide config file.
.TP
.B \-v
Print copyright and version information.
.TP
.B \-h
Print usage information.
.RB ( \-help
and
.B \-\-help
are equivalent.)
.PP
Several other standard X options and resources will work as expected:
.TP
.BI \-display " display"
.RB "[X resource: " xpdf.display ]
.TP
.BI \-fg " color"
.RB ( \-foreground
is equivalent.)
.RB "[X resource: " xpdf*Foreground ]
.TP
.BI \-bg " color"
.RB ( \-background
is equivalent.)
.RB "[X resource: " xpdf*Background ]
.TP
.BI \-font " font"
.RB ( \-fn
is equivalent.)
.RB "[X resource: " xpdf*fontList ]
.PP
The color and font options only affect the user interface elements,
not the PDF display (the \'paper').
.PP
The following X resources do not have command line option equivalents:
.TP
.B xpdf.viKeys
Enables the \'h', \'l', \'k' and \'j' keys for left, right, up, and
down scrolling.
.TP
.B xpdf.toolTipEnable
Enables (if set to true) or disables (if set to false) the tool-tips
on the toolbar buttons.
.SH CONTROLS
.SS On-screen controls, at the bottom of the xpdf window
.TP
.B "left/right arrow buttons"
Move to the previous/next page.
.TP
.B "double left/right arrow buttons"
Move backward or forward by ten pages.
.TP
.B "dashed left/right arrow buttons"
Move backward or forward along the history path.
.TP
.B "\'Page' entry box"
Move to a specific page number.  Click in the box to activate it, type
the page number, then hit return.
.TP
.B "zoom popup menu"
Change the zoom factor (see the description of the -z option above).
.TP
.B "binoculars button"
Find a text string.
.TP
.B "print button"
Bring up a dialog for generating a PostScript file.  The dialog has
options to set the pages to be printed and the PostScript file name.
The file name can be \'-' for stdout or \'|command' to pipe the
PostScript through a command, e.g., \'|lpr'.
.TP
.B "\'?' button"
Bring up the \'about xpdf' window.
.TP
.B "link info"
The space between the \'?' and \'Quit' buttons is used to show the URL
or external file name when the mouse is over a link.
.TP
.B "\'Quit' button"
Quit xpdf.
.PP
.SS Menu
Pressing the right mouse button will post a popup menu with the
following commands:
.TP
.B "Open..."
Open a new PDF file via a file requester.
.TP
.B "Open in new window..."
Create a new window and open a new PDF file via a file requester.
.TP
.B "Reload"
Reload the current PDF file.  Note that Xpdf will reload the file
automatically (on a page change or redraw) if it has changed since it
was last loaded.
.TP
.B "Save as..."
Save the current file via a file requester.
.TP
.B "Continuous view"
Toggles between single page and continuous view modes.
.TP
.B "Rotate counterclockwise"
Rotate the page 90 degrees counterclockwise.
.TP
.B "Rotate clockwise"
Rotate the page 90 degrees clockwise.  The two rotate commands are
intended primarily for PDF files where the rotation isn't correctly
specified in the file.
.TP
.B "Close"
Close the current window.  If this is the only open window, the
document is closed, but the window is left open (i.e., this menu
command won't quit xpdf).
.TP
.B "Quit"
Quit xpdf.
.PP
.SS Outline
If the PDF contains an outline (a.k.a., bookmarks), there will be an
outline pane on the left side of the window.  The width of the outline
pane is adjustable with a vertical split bar via the knob near its
bottom end.
.PP
.SS Text selection
Dragging the mouse with the left button held down will highlight an
arbitrary rectangle.  Any text inside this rectangle will be copied to
the X selection buffer.
.PP
.SS Links
Clicking on a hyperlink will jump to the link's destination.  A link
to another PDF document will make xpdf load that document.  A
\'launch' link to an executable program will display a dialog, and if
you click \'ok', execute the program.  URL links call an external
command (see the
.B WEB BROWSERS
section below).
.PP
.SS Panning
Dragging the mouse with the middle button held down pans the window.
.PP
.SS Key bindings
.TP
.B o
Open a new PDF file via a file requester.
.TP
.B r
Reload the current PDF file.  Note that Xpdf will reload the file
automatically (on a page change or redraw) if it has changed since it
was last loaded.
.TP
.B control-L
Redraw the current page.
.TP
.B control-W
Close the current window.
.TP
.B f or control-F
Find a text string.
.TP
.B control-G
Find next occurrence.
.TP
.B control-P
Print.
.TP
.B n
Move to the next page.  Scrolls to the top of the page, unless scroll
lock is turned on.
.TP
.B p
Move to the previous page.  Scrolls to the top of the page, unless
scroll lock is turned on.
.TP
.BR <Space> " or " <PageDown> " or " <Next>
Scroll down on the current page; if already at bottom, move to next
page.
.TP
.BR <Backspace> " or " <Delete> " or " <PageUp> " or " <Previous>
Scroll up on the current page; if already at top, move to previous
page.
.TP
.B v
Move forward along the history path.
.TP
.B b
Move backward along the history path.
.TP
.B <Home>
Scroll to top of current page.
.TP
.B <End>
Scroll to bottom of current page.
.TP
.B control-<Home>
Scroll to first page of document.
.TP
.B control-<End>
Scroll to last page of document.
.TP
.B arrows
Scroll the current page.
.TP
.B g
Activate the page number text field ("goto page").
.TP
.B 0
Set the zoom factor to 125%.
.TP
.B +
Zoom in (increment the zoom factor by 1).
.TP
.B -
Zoom out (decrement the zoom factor by 1).
.TP
.B z
Set the zoom factor to 'page' (fit page to window).
.TP
.B w
Set the zoom factor to 'width' (fit page width to window).
.TP
.B q
Quit xpdf.
.SH "WEB BROWSERS"
If you want to run xpdf automatically from netscape or mosaic (and
probably other browsers) when you click on a link to a PDF file, you
need to edit (or create) the files
.I .mime.types
and
.I .mailcap
in your home directory.  In
.I .mime.types
add the line:
.PP
.RS
application/pdf pdf
.RE
.PP
In
.I .mailcap
add the lines:
.PP
.RS
# Use xpdf to view PDF files.
.RE
.RS
application/pdf; xpdf -q %s
.RE
.PP
Make sure that xpdf is on your executable search path.
.PP
When you click on a URL link in a PDF file, xpdf will execute the
command specified by the urlCommand config file option, replacing an
occurrence of \'%s' with the URL.  For example, to call netscape with
the URL, add this line to your config file:
.PP
.RS
urlCommand "netscape -remote 'openURL(%s)'"
.RE
.SH "REMOTE SERVER MODE"
Xpdf can be started in remote server mode by specifying a server name
(in addition to the file name and page number).  For example:
.PP
.RS
xpdf -remote myServer file.pdf
.RE
.PP
If there is currently no xpdf running in server mode with the name
\'myServer', a new xpdf window will be opened.  If another command:
.PP
.RS
xpdf -remote myServer another.pdf 9
.RE
.PP
is issued, a new copy of xpdf will not be started.  Instead, the first
xpdf (the server) will load
.I another.pdf
and display page nine.  If the file name is the same:
.PP
.RS
xpdf -remote myServer another.pdf 4
.RE
.PP
the xpdf server will simply display the specified page.
.PP
The -raise option tells the server to raise its window; it can be
specified with or without a file name and page number.
.PP
The -quit option tells the server to close its window and exit.
.SH EXIT CODES
The Xpdf tools use the following exit codes:
.TP
0
No error.
.TP
1
Error opening a PDF file.
.TP
2
Error opening an output file.
.TP
3
Error related to PDF permissions.
.TP
99
Other error.
.SH AUTHOR
The xpdf software and documentation are copyright 1996-2005 Glyph &
Cog, LLC.
.SH "SEE ALSO"
.BR pdftops (1),
.BR pdftotext (1),
.BR pdfinfo (1),
.BR pdffonts (1),
.BR pdftoppm (1),
.BR pdfimages (1),
.BR xpdfrc (5)
.br
.B http://www.foolabs.com/xpdf/
xpdf(1)                                                   xpdf(1)



NAME
       xpdf  -  Portable  Document Format (PDF) file viewer for X
       (version 3.01)

SYNOPSIS
       xpdf [options] [PDF-file [page | +dest]]

DESCRIPTION
       Xpdf is a viewer for Portable Document Format (PDF) files.
       (These  are  also  sometimes  also called 'Acrobat' files,
       from the name of Adobe's PDF software.)  Xpdf  runs  under
       the X Window System on UNIX, VMS, and OS/2.

       To run xpdf, simply type:

              xpdf file.pdf

       where  file.pdf  is  your  PDF file.  The file name can be
       followed by a number specifying the page which  should  be
       displayed first, e.g.:

              xpdf file.pdf 18

       You  can  also give a named destination, prefixed with '+'
       in place of the page number.  (This is  only  useful  with
       PDF files that provide named destination targets.)

       You can also start xpdf without opening any files:

              xpdf

CONFIGURATION FILE
       Xpdf  reads  a  configuration  file  at startup.  It first
       tries to find the user's private config  file,  ~/.xpdfrc.
       If  that  doesn't exist, it looks for a system-wide config
       file, typically /usr/local/etc/xpdfrc (but  this  location
       can be changed when xpdf is built).  See the xpdfrc(5) man
       page for details.

OPTIONS
       Many of the following options can be set  with  configura-
       tion  file  commands  or X resources.  These are listed in
       square brackets with the description of the  corresponding
       command line option.

       -g geometry
              Set  the  initial  window  geometry.  (-geometry is
              equivalent.)  [X resource: xpdf.geometry]

       -title title
              Set the window title.  By default, the  title  will
              be "xpdf: foo.pdf".  [X resource: xpdf.title]

       -cmap  Install  a  private  colormap.   This is ignored on
              TrueColor visuals.  [X resource: xpdf.installCmap]

       -rgb number
              Set the size of largest RGB cube xpdf will  try  to
              allocate.  The default is 5 (for a 5x5x5 cube); set
              to  a  smaller  number  to  conserve  color   table
              entries.   This  is  ignored with private colormaps
              and   on   TrueColor   visuals.     [X    resource:
              xpdf.rgbCubeSize]

       -rv    Set  reverse  video mode.  This reverses the colors
              of everything except images.   It  may  not  always
              produce  great results for PDF files which do weird
              things with color.   This  also  causes  the  paper
              color   to   default   to   black.    [X  resource:
              xpdf.reverseVideo]

       -papercolor color
              Set the "paper color", i.e., the background of  the
              page display.  This will not work too well with PDF
              files that do things like filling in  white  behind
              the text.  [X resource: xpdf.paperColor]

       -mattecolor color
              Set the matte color, i.e., the color used for back-
              ground outside the actual page area.  [X  resource:
              xpdf.matteColor]

       -z zoom
              Set  the initial zoom factor.  A number specifies a
              zoom percentage, where 100  means  72  dpi.You  may
              also  specify 'page', to fit the page to the window
              size, or 'width', to fit the page width to the win-
              dow   width.    [config  file:  initialZoom;  or  X
              resource: xpdf.initialZoom]

       -cont  Start in continuous view mode, i.e., with one  ver-
              tical  scroll  bar for the whole document.  [config
              file: continuousView]

       -t1lib yes | no
              Enable or disable t1lib (a Type 1 font rasterizer).
              This defaults to "yes".  [config file: enableT1lib]

       -freetype yes | no
              Enable or disable FreeType (a  TrueType  /  Type  1
              font rasterizer).  This defaults to "yes".  [config
              file: enableFreeType]

       -aa yes | no
              Enable  or  disable   font   anti-aliasing.    This
              defaults to "yes".  [config file: antialias]

       -ps PS-file
              Set  the  default  file  name for PostScript output
              (i.e., the name which will appear in the print dia-
              log).   This  can also be of the form '|command' to
              pipe the PostScript  through  a  command.   [config
              file: psFile]

       -paper size
              Set  the  paper  size  to one of "letter", "legal",
              "A4", or "A3".  This can also be  set  to  "match",
              which  will  set  the  paper size to match the size
              specified in the PDF file.  [config file:  psPaper-
              Size]

       -paperw size
              Set  the  paper  width,  in  points.  [config file:
              psPaperSize]

       -paperh size
              Set the paper height,  in  points.   [config  file:
              psPaperSize]

       -level1
              Generate   Level   1   PostScript.   The  resulting
              PostScript files will be significantly  larger  (if
              they  contain  images),  but  will print on Level 1
              printers.  This also converts all images  to  black
              and white.  [config file: psLevel]

       -enc encoding-name
              Sets  the  encoding  to  use  for text output.  The
              encoding-name must be defined with  the  unicodeMap
              command (see xpdfrc(5)).  This defaults to "Latin1"
              (which is a built-in encoding).  [config file: tex-
              tEncoding]

       -eol unix | dos | mac
              Sets  the  end-of-line  convention  to use for text
              output.  [config file: textEOL]

       -opw password
              Specify the owner password for the PDF file.   Pro-
              viding  this will bypass all security restrictions.

       -upw password
              Specify the user password for the PDF file.

       -fullscreen
              Open xpdf in a full-screen mode, useful for presen-
              tations.   You may also want to specify '-bg black'
              (or similar) with this.  (There is currently no way
              to  switch  between window and full-screen modes on
              the fly.)

       -remote name
              Start/contact xpdf  remote  server  with  specified
              name (see the REMOTE SERVER MODE section below).

       -reload
              Reload  xpdf  remote  server  window  (with -remote
              only).

       -raise Raise  xpdf  remote  server  window  (with  -remote
              only).

       -quit  Kill xpdf remote server (with -remote only).

       -cmd   Print  commands  as  they're  executed  (useful for
              debugging).  [config file: printCommands]

       -q     Don't print any messages or errors.  [config  file:
              errQuiet]

       -cfg config-file
              Read  config-file in place of ~/.xpdfrc or the sys-
              tem-wide config file.

       -v     Print copyright and version information.

       -h     Print usage information.   (-help  and  --help  are
              equivalent.)

       Several  other  standard X options and resources will work
       as expected:

       -display display
              [X resource: xpdf.display]

       -fg color
              (-foreground   is   equivalent.)    [X    resource:
              xpdf*Foreground]

       -bg color
              (-background    is   equivalent.)    [X   resource:
              xpdf*Background]

       -font font
              (-fn is equivalent.)  [X resource: xpdf*fontList]

       The color and font options only affect the user  interface
       elements, not the PDF display (the 'paper').

       The  following X resources do not have command line option
       equivalents:

       xpdf.viKeys
              Enables the 'h', 'l', 'k' and 'j'  keys  for  left,
              right, up, and down scrolling.

       xpdf.toolTipEnable
              Enables  (if  set  to  true) or disables (if set to
              false) the tool-tips on the toolbar buttons.

CONTROLS
   On-screen controls, at the bottom of the xpdf window
       left/right arrow buttons
              Move to the previous/next page.

       double left/right arrow buttons
              Move backward or forward by ten pages.

       dashed left/right arrow buttons
              Move backward or forward along the history path.

       'Page' entry box
              Move to a specific page number.  Click in  the  box
              to  activate  it,  type  the  page number, then hit
              return.

       zoom popup menu
              Change the zoom factor (see the description of  the
              -z option above).

       binoculars button
              Find a text string.

       print button
              Bring up a dialog for generating a PostScript file.
              The dialog has options  to  set  the  pages  to  be
              printed  and  the  PostScript  file name.  The file
              name can be '-' for stdout or  '|command'  to  pipe
              the PostScript through a command, e.g., '|lpr'.

       '?' button
              Bring up the 'about xpdf' window.

       link info
              The  space  between  the  '?' and 'Quit' buttons is
              used to show the URL or external file name when the
              mouse is over a link.

       'Quit' button
              Quit xpdf.


   Menu
       Pressing  the  right  mouse  button will post a popup menu
       with the following commands:

       Open...
              Open a new PDF file via a file requester.

       Open in new window...
              Create a new window and open a new PDF file  via  a
              file requester.

       Reload Reload  the  current PDF file.  Note that Xpdf will
              reload the file automatically (on a page change  or
              redraw) if it has changed since it was last loaded.

       Save as...
              Save the current file via a file requester.

       Continuous view
              Toggles between single  page  and  continuous  view
              modes.

       Rotate counterclockwise
              Rotate the page 90 degrees counterclockwise.

       Rotate clockwise
              Rotate  the  page  90  degrees  clockwise.  The two
              rotate commands  are  intended  primarily  for  PDF
              files  where the rotation isn't correctly specified
              in the file.

       Close  Close the current window.  If this is the only open
              window,  the  document is closed, but the window is
              left open  (i.e.,  this  menu  command  won't  quit
              xpdf).

       Quit   Quit xpdf.


   Outline
       If  the PDF contains an outline (a.k.a., bookmarks), there
       will be an outline pane on the left side  of  the  window.
       The  width of the outline pane is adjustable with a verti-
       cal split bar via the knob near its bottom end.


   Text selection
       Dragging the mouse with the left  button  held  down  will
       highlight  an  arbitrary  rectangle.  Any text inside this
       rectangle will be copied to the X selection buffer.


   Links
       Clicking on a hyperlink will jump to the  link's  destina-
       tion.   A link to another PDF document will make xpdf load
       that document.  A 'launch' link to an  executable  program
       will  display a dialog, and if you click 'ok', execute the
       program.  URL links call an external command (see the  WEB
       BROWSERS section below).


   Panning
       Dragging  the  mouse with the middle button held down pans
       the window.


   Key bindings
       o      Open a new PDF file via a file requester.

       r      Reload the current PDF file.  Note that  Xpdf  will
              reload  the file automatically (on a page change or
              redraw) if it has changed since it was last loaded.

       control-L
              Redraw the current page.

       control-W
              Close the current window.

       f or control-F
              Find a text string.

       control-G
              Find next occurrence.

       control-P
              Print.

       n      Move  to  the next page.  Scrolls to the top of the
              page, unless scroll lock is turned on.

       p      Move to the previous page.  Scrolls to the  top  of
              the page, unless scroll lock is turned on.

       <Space> or <PageDown> or <Next>
              Scroll down on the current page; if already at bot-
              tom, move to next page.

       <Backspace> or <Delete> or <PageUp> or <Previous>
              Scroll up on the current page; if already  at  top,
              move to previous page.

       v      Move forward along the history path.

       b      Move backward along the history path.

       <Home> Scroll to top of current page.

       <End>  Scroll to bottom of current page.

       control-<Home>
              Scroll to first page of document.

       control-<End>
              Scroll to last page of document.

       arrows Scroll the current page.

       g      Activate  the page number text field ("goto page").

       0      Set the zoom factor to 125%.

       +      Zoom in (increment the zoom factor by 1).

       -      Zoom out (decrement the zoom factor by 1).

       z      Set the zoom factor to 'page' (fit page to window).

       w      Set  the  zoom factor to 'width' (fit page width to
              window).

       q      Quit xpdf.

WEB BROWSERS
       If you want to run xpdf  automatically  from  netscape  or
       mosaic  (and  probably other browsers) when you click on a
       link to a PDF file, you need to edit (or create) the files
       .mime.types  and  .mailcap  in  your  home  directory.  In
       .mime.types add the line:

              application/pdf pdf

       In .mailcap add the lines:

              # Use xpdf to view PDF files.
              application/pdf; xpdf -q %s

       Make sure that xpdf is on your executable search path.

       When you click on a URL link in a PDF file, xpdf will exe-
       cute  the  command specified by the urlCommand config file
       option, replacing an occurrence of '%s' with the URL.  For
       example,  to  call netscape with the URL, add this line to
       your config file:

              urlCommand "netscape -remote 'openURL(%s)'"

REMOTE SERVER MODE
       Xpdf can be started in remote server mode by specifying  a
       server  name  (in  addition to the file name and page num-
       ber).  For example:

              xpdf -remote myServer file.pdf

       If there is currently no xpdf running in server mode  with
       the name 'myServer', a new xpdf window will be opened.  If
       another command:

              xpdf -remote myServer another.pdf 9

       is issued, a  new  copy  of  xpdf  will  not  be  started.
       Instead, the first xpdf (the server) will load another.pdf
       and display page nine.  If the file name is the same:

              xpdf -remote myServer another.pdf 4

       the xpdf server will simply display the specified page.

       The -raise option tells the server to raise its window; it
       can be specified with or without a file name and page num-
       ber.

       The -quit option tells the server to close its window  and
       exit.

EXIT CODES
       The Xpdf tools use the following exit codes:

       0      No error.

       1      Error opening a PDF file.

       2      Error opening an output file.

       3      Error related to PDF permissions.

       99     Other error.

AUTHOR
       The   xpdf   software   and  documentation  are  copyright
       1996-2005 Glyph & Cog, LLC.

SEE ALSO
       pdftops(1),   pdftotext(1),    pdfinfo(1),    pdffonts(1),
       pdftoppm(1), pdfimages(1), xpdfrc(5)
       http://www.foolabs.com/xpdf/



                          17 August 2005                  xpdf(1)
! Generated automatically by mantohlp
1 xpdf

       xpdf  -  Portable  Document Format (PDF) file viewer for X
       (version 3.01)

       xpdf [options] [PDF-file [page | +dest]]

       Xpdf is a viewer for Portable Document Format (PDF) files.
       (These  are  also  sometimes  also called 'Acrobat' files,
       from the name of Adobe's PDF software.)  Xpdf  runs  under
       the X Window System on UNIX, VMS, and OS/2.

       To run xpdf, simply type:

              xpdf file.pdf

       where  file.pdf  is  your  PDF file.  The file name can be
       followed by a number specifying the page which  should  be
       displayed first, e.g.:

              xpdf file.pdf 18

       You  can  also give a named destination, prefixed with '+'
       in place of the page number.  (This is  only  useful  with
       PDF files that provide named destination targets.)

       You can also start xpdf without opening any files:

              xpdf

                                                               ()

2 ONFIGURATION_FIL

       Xpdf  reads  a  configuration  file  at startup.  It first
       tries to find the user's private config  file,  ~/.xpdfrc.
       If  that  doesn't exist, it looks for a system-wide config
       file, typically /usr/local/etc/xpdfrc (but  this  location
       can be changed when xpdf is built).  See the xpdfrc(5) man
       page for details.

                                                               ()

2 OPTIONS

       Many  of  the following options can be set with configura-
       tion file commands or X resources.  These  are  listed  in
       square  brackets with the description of the corresponding
       command line option.

       -g geometry
              Set the initial  window  geometry.   (-geometry  is
              equivalent.)  [X resource: xpdf.geometry]

       -title title
              Set  the  window title.  By default, the title will
              be "xpdf: foo.pdf".  [X resource: xpdf.title]

       -cmap  Install a private colormap.   This  is  ignored  on
              TrueColor visuals.  [X resource: xpdf.installCmap]

       -rgb number
              Set  the  size of largest RGB cube xpdf will try to
              allocate.  The default is 5 (for a 5x5x5 cube); set
              to   a  smaller  number  to  conserve  color  table
              entries.  This is ignored  with  private  colormaps
              and    on    TrueColor   visuals.    [X   resource:
              xpdf.rgbCubeSize]

       -rv    Set reverse video mode.  This reverses  the  colors
              of  everything  except  images.   It may not always
              produce great results for PDF files which do  weird
              things  with  color.   This  also  causes the paper
              color  to   default   to   black.    [X   resource:
              xpdf.reverseVideo]

       -papercolor color
              Set  the "paper color", i.e., the background of the
              page display.  This will not work too well with PDF
              files  that  do things like filling in white behind
              the text.  [X resource: xpdf.paperColor]

       -mattecolor color
              Set the matte color, i.e., the color used for back-
              ground  outside the actual page area.  [X resource:
              xpdf.matteColor]

       -z zoom
              Set the initial zoom factor.  A number specifies  a
              zoom  percentage,  where  100  means 72 dpi.You may
              also specify 'page', to fit the page to the  window
              size, or 'width', to fit the page width to the win-
              dow  width.   [config  file:  initialZoom;   or   X
              resource: xpdf.initialZoom]

       -cont  Start  in continuous view mode, i.e., with one ver-
              tical scroll bar for the whole  document.   [config
              file: continuousView]

       -t1lib yes | no
              Enable or disable t1lib (a Type 1 font rasterizer).
              This defaults to "yes".  [config file: enableT1lib]

       -freetype yes | no
              Enable  or  disable  FreeType  (a TrueType / Type 1
              font rasterizer).  This defaults to "yes".  [config
              file: enableFreeType]

       -aa yes | no
              Enable   or   disable   font  anti-aliasing.   This
              defaults to "yes".  [config file: antialias]

       -ps PS-file
              Set the default file  name  for  PostScript  output
              (i.e., the name which will appear in the print dia-
              log).  This can also be of the form  '|command'  to
              pipe  the  PostScript  through  a command.  [config
              file: psFile]

       -paper size
              Set the paper size to  one  of  "letter",  "legal",
              "A4",  or  "A3".   This can also be set to "match",
              which will set the paper size  to  match  the  size
              specified  in the PDF file.  [config file: psPaper-
              Size]

       -paperw size
              Set the paper  width,  in  points.   [config  file:
              psPaperSize]

       -paperh size
              Set  the  paper  height,  in points.  [config file:
              psPaperSize]

       -level1
              Generate  Level  1   PostScript.    The   resulting
              PostScript  files  will be significantly larger (if
              they contain images), but will  print  on  Level  1
              printers.   This  also converts all images to black
              and white.  [config file: psLevel]

       -enc encoding-name
              Sets the encoding to  use  for  text  output.   The
              encoding-name  must  be defined with the unicodeMap
              command (see xpdfrc(5)).  This defaults to "Latin1"
              (which is a built-in encoding).  [config file: tex-
              tEncoding]

       -eol unix | dos | mac
              Sets the end-of-line convention  to  use  for  text
              output.  [config file: textEOL]

       -opw password
              Specify  the owner password for the PDF file.  Pro-
              viding this will bypass all security  restrictions.

       -upw password
              Specify the user password for the PDF file.

       -fullscreen
              Open xpdf in a full-screen mode, useful for presen-
              tations.  You may also want to specify '-bg  black'
              (or similar) with this.  (There is currently no way
              to switch between window and full-screen  modes  on
              the fly.)

       -remote name
              Start/contact  xpdf  remote  server  with specified
              name (see the REMOTE SERVER MODE section below).

       -reload
              Reload xpdf  remote  server  window  (with  -remote
              only).

       -raise Raise  xpdf  remote  server  window  (with  -remote
              only).

       -quit  Kill xpdf remote server (with -remote only).

       -cmd   Print commands  as  they're  executed  (useful  for
              debugging).  [config file: printCommands]

       -q     Don't  print any messages or errors.  [config file:
              errQuiet]

       -cfg config-file
              Read config-file in place of ~/.xpdfrc or the  sys-
              tem-wide config file.

       -v     Print copyright and version information.

       -h     Print  usage  information.   (-help  and --help are
              equivalent.)

       Several other standard X options and resources  will  work
       as expected:

       -display display
              [X resource: xpdf.display]

       -fg color
              (-foreground    is   equivalent.)    [X   resource:
              xpdf*Foreground]

       -bg color
              (-background   is   equivalent.)    [X    resource:
              xpdf*Background]

       -font font
              (-fn is equivalent.)  [X resource: xpdf*fontList]

       The  color and font options only affect the user interface
       elements, not the PDF display (the 'paper').

       The following X resources do not have command line  option
       equivalents:

       xpdf.viKeys
              Enables  the  'h',  'l', 'k' and 'j' keys for left,
              right, up, and down scrolling.

       xpdf.toolTipEnable
              Enables (if set to true) or  disables  (if  set  to
              false) the tool-tips on the toolbar buttons.

                                                               ()

2 CONTROLS

   On-screen controls, at the bottom of the xpdf window
       left/right arrow buttons
              Move to the previous/next page.

       double left/right arrow buttons
              Move backward or forward by ten pages.

       dashed left/right arrow buttons
              Move backward or forward along the history path.

       'Page' entry box
              Move  to  a specific page number.  Click in the box
              to activate it, type  the  page  number,  then  hit
              return.

       zoom popup menu
              Change  the zoom factor (see the description of the
              -z option above).

       binoculars button
              Find a text string.

       print button
              Bring up a dialog for generating a PostScript file.
              The  dialog  has  options  to  set  the pages to be
              printed and the PostScript  file  name.   The  file
              name  can  be  '-' for stdout or '|command' to pipe
              the PostScript through a command, e.g., '|lpr'.

       '?' button
              Bring up the 'about xpdf' window.

       link info
              The space between the '?'  and  'Quit'  buttons  is
              used to show the URL or external file name when the
              mouse is over a link.

       'Quit' button
              Quit xpdf.

   Menu
       Pressing the right mouse button will  post  a  popup  menu
       with the following commands:

       Open...
              Open a new PDF file via a file requester.

       Open in new window...
              Create  a  new window and open a new PDF file via a
              file requester.

       Reload Reload the current PDF file.  Note that  Xpdf  will
              reload  the file automatically (on a page change or
              redraw) if it has changed since it was last loaded.

       Save as...
              Save the current file via a file requester.

       Continuous view
              Toggles  between  single  page  and continuous view
              modes.

       Rotate counterclockwise
              Rotate the page 90 degrees counterclockwise.

       Rotate clockwise
              Rotate the page  90  degrees  clockwise.   The  two
              rotate  commands  are  intended  primarily  for PDF
              files where the rotation isn't correctly  specified
              in the file.

       Close  Close the current window.  If this is the only open
              window, the document is closed, but the  window  is
              left  open  (i.e.,  this  menu  command  won't quit
              xpdf).

       Quit   Quit xpdf.

   Outline
       If the PDF contains an outline (a.k.a., bookmarks),  there
       will  be  an  outline pane on the left side of the window.
       The width of the outline pane is adjustable with a  verti-
       cal split bar via the knob near its bottom end.

   Text selection
       Dragging  the  mouse  with  the left button held down will
       highlight an arbitrary rectangle.  Any  text  inside  this
       rectangle will be copied to the X selection buffer.

   Links
       Clicking  on  a hyperlink will jump to the link's destina-
       tion.  A link to another PDF document will make xpdf  load
       that  document.   A 'launch' link to an executable program
       will display a dialog, and if you click 'ok', execute  the
       program.   URL links call an external command (see the WEB
       BROWSERS section below).

   Panning
       Dragging the mouse with the middle button held  down  pans
       the window.

   Key bindings
       o      Open a new PDF file via a file requester.

       r      Reload  the  current PDF file.  Note that Xpdf will
              reload the file automatically (on a page change  or
              redraw) if it has changed since it was last loaded.

       control-L
              Redraw the current page.

       control-W
              Close the current window.

       f or control-F
              Find a text string.

       control-G
              Find next occurrence.

       control-P
              Print.

       n      Move to the next page.  Scrolls to the top  of  the
              page, unless scroll lock is turned on.

       p      Move  to  the previous page.  Scrolls to the top of
              the page, unless scroll lock is turned on.

       <Space> or <PageDown> or <Next>
              Scroll down on the current page; if already at bot-
              tom, move to next page.

       <Backspace> or <Delete> or <PageUp> or <Previous>
              Scroll  up  on the current page; if already at top,
              move to previous page.

       v      Move forward along the history path.

       b      Move backward along the history path.

       <Home> Scroll to top of current page.

       <End>  Scroll to bottom of current page.

       control-<Home>
              Scroll to first page of document.

       control-<End>
              Scroll to last page of document.

       arrows Scroll the current page.

       g      Activate the page number text field ("goto  page").

       0      Set the zoom factor to 125%.

       +      Zoom in (increment the zoom factor by 1).

       -      Zoom out (decrement the zoom factor by 1).

       z      Set the zoom factor to 'page' (fit page to window).

       w      Set the zoom factor to 'width' (fit page  width  to
              window).

       q      Quit xpdf.

                                                               ()

2 WEB_BROWSERS

       If  you  want  to  run xpdf automatically from netscape or
       mosaic (and probably other browsers) when you click  on  a
       link to a PDF file, you need to edit (or create) the files
       .mime.types and  .mailcap  in  your  home  directory.   In
       .mime.types add the line:

              application/pdf pdf

       In .mailcap add the lines:

              # Use xpdf to view PDF files.
              application/pdf; xpdf -q %s

       Make sure that xpdf is on your executable search path.

       When you click on a URL link in a PDF file, xpdf will exe-
       cute the command specified by the urlCommand  config  file
       option, replacing an occurrence of '%s' with the URL.  For
       example, to call netscape with the URL, add this  line  to
       your config file:

              urlCommand "netscape -remote 'openURL(%s)'"

                                                               ()

2 REMOTE_SERVER_MODE

       Xpdf  can be started in remote server mode by specifying a
       server name (in addition to the file name  and  page  num-
       ber).  For example:

              xpdf -remote myServer file.pdf

       If  there is currently no xpdf running in server mode with
       the name 'myServer', a new xpdf window will be opened.  If
       another command:

              xpdf -remote myServer another.pdf 9

       is  issued,  a  new  copy  of  xpdf  will  not be started.
       Instead, the first xpdf (the server) will load another.pdf
       and display page nine.  If the file name is the same:

              xpdf -remote myServer another.pdf 4

       the xpdf server will simply display the specified page.

       The -raise option tells the server to raise its window; it
       can be specified with or without a file name and page num-
       ber.

       The  -quit option tells the server to close its window and
       exit.

                                                               ()

2 XIT_CODE

       The Xpdf tools use the following exit codes:

       0      No error.

       1      Error opening a PDF file.

       2      Error opening an output file.

       3      Error related to PDF permissions.

       99     Other error.

                                                               ()

2 AUTHOR

       The   xpdf   software   and  documentation  are  copyright
       1996-2005 Glyph & Cog, LLC.

                                                               ()

2 SEE_ALSO

       pdftops(1),    pdftotext(1),    pdfinfo(1),   pdffonts(1),
       pdftoppm(1), pdfimages(1), xpdfrc(5)
       http://www.foolabs.com/xpdf/

                                                               ()

.\" Copyright 2002-2005 Glyph & Cog, LLC
.TH xpdfrc 5 "17 August 2005"
.SH NAME
xpdfrc \- configuration file for Xpdf tools (version 3.01)
.SH DESCRIPTION
All of the Xpdf tools read a single configuration file.  If you have a
.I .xpdfrc
file in your home directory, it will be read.  Otherwise, a
system-wide configuration file will be read from
.IR /usr/local/etc/xpdfrc ,
if it exists.  (This is its default location; depending on build
options, it may be placed elsewhere.)  On Win32 systems, the
.I xpdfrc
file should be placed in the same directory as the executables.
.PP
The xpdfrc file consists of a series of configuration options, one
per line.  Blank lines and lines starting with a \'#' (comments) are
ignored.
.PP
The following sections list all of the configuration options, sorted
into functional groups.  There is an examples section at the end.
.SH INCLUDE FILES
.TP
.BI include " config\-file"
Includes the specified config file.  The effect of this is equivalent
to inserting the contents of
.I config\-file
directly into the parent config file in place of the
.I include
command.  Config files can be nested arbitrarily deeply.
.SH CHARACTER MAPPING
.TP
.BI nameToUnicode " map\-file"
Specifies a file with the mapping from character names to Unicode.
This is used to handle PDF fonts that have valid encodings but no
ToUnicode entry.  Each line of a nameToUnicode file looks like this:

.I "    " hex\-string name

The
.I hex\-string
is the Unicode (UCS-2) character index, and
.I name
is the corresponding character name.  Multiple nameToUnicode files can
be used; if a character name is given more than once, the code in the
last specified file is used.  There is a built-in default
nameToUnicode table with all of Adobe's standard character names.
.TP
.BI cidToUnicode " registry\-ordering map\-file"
Specifies the file with the mapping from character collection to
Unicode.  Each line of a cidToUnicode file represents one character:

.I "    " hex\-string

The
.I hex\-string
is the Unicode (UCS-2) index for that character.  The first line maps
CID 0, the second line CID 1, etc.  File size is determined by size of
the character collection.  Only one file is allowed per character
collection; the last specified file is used.  There are no built-in
cidToUnicode mappings.
.TP
.BI unicodeToUnicode " font\-name\-substring map\-file"
This is used to work around PDF fonts which have incorrect Unicode
information.  It specifies a file which maps from the given
(incorrect) Unicode indexes to the correct ones.  The mapping will be
used for any font whose name contains
.IR font\-name\-substring .
Each line of a unicodeToUnicode file represents one Unicode character:

.RI "    " in\-hex " " out\-hex1 " " out\-hex2 " ..."

The
.I in\-hex
field is an input (incorrect) Unicode index, and the rest of the
fields are one or more output (correct) Unicode indexes.  Each
occurrence of
.I in\-hex
will be converted to the specified output sequence.
.TP
.BI unicodeMap " encoding\-name map\-file"
Specifies the file with mapping from Unicode to
.IR encoding\-name .
These encodings are used for X display fonts and text output (see
below).  Each line of a unicodeMap file represents a range of one or
more Unicode characters which maps linearly to a range in the output
encoding:
.nf

.I "    " in\-start\-hex in\-end\-hex out\-start\-hex

.fi
Entries for single characters can be abbreviated to:
.nf

.I "    " in\-hex out\-hex

.fi
The
.I in\-start\-hex
and
.I in\-end\-hex
fields (or the single
.I in\-hex
field) specify the Unicode range.  The
.I out\-start\-hex
field (or the
.I out\-hex
field) specifies the start of the output encoding range.  The length
of the
.I out\-start\-hex
(or
.IR out\-hex )
string determines the length of the output characters (e.g., UTF-8
uses different numbers of bytes to represent characters in different
ranges).  Entries must be given in increasing Unicode order.  Only one
file is allowed per encoding; the last specified file is used.  The
.IR Latin1 ,
.IR ASCII7 ,
.IR Symbol ,
.IR ZapfDingbats ,
.IR UTF-8 ,
and
.I UCS-2
encodings are predefined.
.TP
.BI cMapDir " registry\-ordering dir"
Specifies a search directory,
.IR dir ,
for CMaps for the
.I registry\-ordering
character collection.  There can be multiple directories for a
particular collection.  There are no default CMap directories.
.TP
.BI toUnicodeDir " dir"
Specifies a search directory,
.IR dir ,
for ToUnicode CMaps.  There can be multiple ToUnicode directories.
There are no default ToUnicode directories.
.SH DISPLAY FONTS
.TP
.BI displayFontT1 " PDF\-font\-name T1\-file"
Maps a PDF font,
.IR PDF\-font\-name ,
to a Type 1 font for display.  The Type 1 font file,
.IR T1\-file ,
should be a standard .pfa or .pfb file.
.TP
.BI displayFontTT " PDF\-font\-name TT\-file"
Maps a PDF font,
.IR PDF\-font\-name ,
to a TrueType font for display.  The TrueType font file,
.IR TT\-file ,
should be a standard .ttf file.
.TP
.BI displayNamedCIDFontT1 " PDF\-font\-name T1\-file"
Maps a specific PDF CID (16-bit) font,
.IR PDF\-font\-name ,
to a CID font (16-bit PostScript font), for display.  There are no
default CID font mappings.
.TP
.BI displayCIDFontT1 " registry\-ordering T1\-file"
Maps the
.I registry\-ordering
character collection to a CID font (16-bit PostScript font), for
display.  This mapping is used if the font name doesn't match any of
the fonts declared with displayNamedCIDFont* commands.  There are no
default CID font mappings.
.TP
.BI displayNamedCIDFontTT " PDF\-font\-name TT\-file"
Maps a specific PDF CID (16-bit) font,
.IR PDF\-font\-name ,
to a (16-bit) TrueType font, for display.  There are no default CID
font mappings.
.TP
.BI displayCIDFontTT " registry\-ordering TT\-file"
Maps the
.I registry\-ordering
character collection to a (16-bit) TrueType font, for display.  This
mapping is used if the font name doesn't match any of the fonts
declared with displayNamedCIDFont* commands.  There are no default CID
font mappings.
.TP
.BI fontDir " dir"
Specifies a search directory for external font files.  There can be
multiple fontDir directories.  If a PDF file uses a font but doesn't
embed it, these directories will be searched for a matching font file.
These fonts are used by both xpdf (for display) and pdftops (for
embedding in the generated PostScript).  Type 1 fonts must have a
suffix of ".pfa", ".pfb", ".ps", or no suffix at all.  TrueType fonts
must have a ".ttf" suffix.  Other files in these directories will be
ignored.  There are no default fontDir directories.
.SH POSTSCRIPT CONTROL
.TP
.BI psPaperSize " width(pts) height(pts)"
Sets the paper size for PostScript output.  The
.I width
and
.I height
parameters give the paper size in PostScript points (1 point = 1/72
inch).
.TP
.BR psPaperSize " letter | legal | A4 | A3 | match"
Sets the paper size for PostScript output to a standard size.  The
default paper size is set when xpdf and pdftops are built, typically
to "letter" or "A4".  This can also be set to "match", which will set
the paper size to match the size specified in the PDF file.
.TP
.BR psImageableArea " llx lly urx ury"
Sets the imageable area for PostScript output.  The four integers are
the coordinates of the lower-left and upper-right corners of the
imageable region, specified in points (with the origin being the
lower-left corner of the paper).  This defaults to the full paper
size; the psPaperSize option will reset the imageable area
coordinates.
.TP
.BR psCrop " yes | no"
If set to "yes", PostScript output is cropped to the CropBox specified
in the PDF file; otherwise no cropping is done.  This defaults to
"yes".
.TP
.BR psExpandSmaller " yes | no"
If set to "yes", PDF pages smaller than the PostScript imageable area
are expanded to fill the imageable area.  Otherwise, no scalling is
done on smaller pages.  This defaults to "no".
.TP
.BR psShrinkLarger " yes | no"
If set to yes, PDF pages larger than the PostScript imageable area are
shrunk to fit the imageable area.  Otherwise, no scaling is done on
larger pages.  This defaults to "yes".
.TP
.BR psCenter " yes | no"
If set to yes, PDF pages smaller than the PostScript imageable area
(after any scaling) are centered in the imageable area.  Otherwise,
they are aligned at the lower-left corner of the imageable area.  This
defaults to "yes".
.TP
.BR psDuplex " yes | no"
If set to "yes", the generated PostScript will set the "Duplex"
pagedevice entry.  This tells duplex-capable printers to enable
duplexing.  This defaults to "no".
.TP
.BR psLevel " level1 | level1sep | level2 | level2sep | level3 | level3Sep"
Sets the PostScript level to generate.  This defaults to "level2".
.TP
.BI psFont " PDF\-font\-name PS\-font\-name"
When the
.I PDF\-font\-name
font is used in a PDF file, it will be translated to the PostScript
font
.IR PS\-font\-name ,
which is assumed to be resident in the printer.  Typically,
.I PDF\-font\-name
and
.I PS\-font\-name
are the same.  By default, only the Base-14 fonts are assumed to be
resident.
.TP
.BI psNamedFont16 " PDF\-font\-name wMode PS\-font\-name encoding"
When the 16-bit font
.I PDF\-font\-name
is used in a PDF file with the
.I wMode
writing mode and is not embedded, the
.I PS\-font\-name
font is substituted for it.  The writing mode must be either \'H' for
horizontal or \'V' for vertical.  The
.I PS\-font\-name
font is assumed to be resident in the printer and to use the specified
encoding (which must have been defined with the unicodeMap command).
.TP
.BI psFont16 " registry\-ordering wMode PS\-font\-name encoding"
When a 16-bit font using the
.I registry\-ordering
character collection and 
.I wMode
writing mode is not embedded and does not match any of the fonts
declared in psNamedFont16 commands, the
.I PS\-font\-name
font is substituted for it.  The writing mode must be either \'H' for
horizontal or \'V' for vertical.  The
.I PS\-font\-name
font is assumed to be resident in the printer and to use the specified
writing mode and encoding (which must have been defined with the
unicodeMap command).
.TP
.BR psEmbedType1Fonts " yes | no"
If set to "no", prevents embedding of Type 1 fonts in generated
PostScript.  This defaults to "yes".
.TP
.BR psEmbedTrueTypeFonts " yes | no"
If set to "no", prevents embedding of TrueType fonts in generated
PostScript.  This defaults to "yes".
.TP
.BR psEmbedCIDTrueTypeFonts " yes | no"
If set to "no", prevents embedding of CID TrueType fonts in generated
PostScript.  For Level 3 PostScript, this generates a CID font, for
lower levels it generates a non-CID composite font.
.TP
.BR psEmbedCIDPostScriptFonts " yes | no"
If set to "no", prevents embedding of CID PostScript fonts in
generated PostScript.  For Level 3 PostScript, this generates a CID
font, for lower levels it generates a non-CID composite font.
.TP
.BR psOPI " yes | no"
If set to "yes", generates PostScript OPI comments for all images and
forms which have OPI information.  This option is only available if
the Xpdf tools were compiled with OPI support.  This defaults to "no".
.TP
.BR psASCIIHex " yes | no"
If set to "yes", the ASCIIHexEncode filter will be used instead of
ASCII85Encode for binary data.  This defaults to "no".
.TP
.BI psFile " file\-or\-command"
Sets the default PostScript file or print command for xpdf.  Commands
start with a \'|' character; anything else is a file.  If the file
name or command contains spaces it must be quoted.  This defaults to
unset, which tells xpdf to generate a name of the form <file>.ps for a
PDF file <file>.pdf.
.TP
.BI fontDir " dir"
See the description above, in the DISPLAY FONTS section.
.SH TEXT CONTROL
.TP
.BI textEncoding " encoding\-name"
Sets the encoding to use for text output.  (This can be overridden
with the "-enc" switch on the command line.)  The
.I encoding\-name
must be defined with the unicodeMap command (see above).  This
defaults to "Latin1".
.TP
.BR textEOL " unix | dos | mac"
Sets the end-of-line convention to use for text output.  The options
are:
.nf

    unix = LF
    dos  = CR+LF
    mac  = CR

.fi
(This can be overridden with the "-eol" switch on the command line.)
The default value is based on the OS where xpdf and pdftotext were
built.
.TP
.BR textPageBreaks " yes | no"
If set to "yes", text extraction will insert page breaks (form feed
characters) between pages.  This defaults to "yes".
.TP
.BR textKeepTinyChars " yes | no"
If set to "yes", text extraction will keep all characters.  If set to
"no", text extraction will discard tiny (smaller than 3 point)
characters after the first 50000 per page, avoiding extremely slow run
times for PDF files that use special fonts to do shading or
cross-hatching.  This defaults to "no".
.SH MISCELLANEOUS SETTINGS
.TP
.BR initialZoom " \fIpercentage\fR | page | width"
Sets the initial zoom factor.  A number specifies a zoom percentage,
where 100 means 72 dpi.  You may also specify \'page', to fit the page
to the window size, or \'width', to fit the page width to the window
width.
.TP
.BR enableT1lib " yes | no"
Enables or disables use of t1lib (a Type 1 font rasterizer).  This is
only relevant if the Xpdf tools were built with t1lib support.
("enableT1lib" replaces the old "t1libControl" option.)  This option
defaults to "yes".
.TP
.BR enableFreeType " yes | no"
Enables or disables use of FreeType (a TrueType / Type 1 font
rasterizer).  This is only relevant if the Xpdf tools were built with
FreeType support.  ("enableFreeType" replaces the old
"freetypeControl" option.)  This option defaults to "yes".
.TP
.BR antialias " yes | no"
Enables or disables font anti-aliasing in the PDF rasterizer.  This
option affects all font rasterizers.  ("antialias" replaces the
anti-aliasing control provided by the old "t1libControl" and
"freetypeControl" options.)  This default to "yes".
.TP
.BI urlCommand " command"
Sets the command executed when you click on a URL link.  The string
"%s" will be replaced with the URL.  (See the example below.)  This
has no default value.
.TP
.BI movieCommand " command"
Sets the command executed when you click on a movie annotation.  The
string "%s" will be replaced with the movie file name.  This has no
default value.
.TP
.BI mapNumericCharNames " yes | no"
If set to "yes", the Xpdf tools will attempt to map various numeric
character names sometimes used in font subsets.  In some cases this
leads to usable text, and in other cases it leads to gibberish --
there is no way for Xpdf to tell.  This defaults to "yes".
.TP
.BI printCommands " yes | no"
If set to "yes", drawing commands are printed as they're executed
(useful for debugging).  This defaults to "no".
.TP
.BI errQuiet " yes | no"
If set to "yes", this suppresses all error and warning messages from
all of the Xpdf tools.  This defaults to "no".
.SH EXAMPLES
The following is a sample xpdfrc file.
.nf

# from the Thai support package
nameToUnicode /usr/local/share/xpdf/Thai.nameToUnicode

# from the Japanese support package
cidToUnicode Adobe-Japan1 /usr/local/share/xpdf/Adobe-Japan1.cidToUnicode
unicodeMap   JISX0208     /usr/local/share/xpdf/JISX0208.unicodeMap
cMapDir      Adobe-Japan1 /usr/local/share/xpdf/cmap/Adobe-Japan1

# use the Base-14 Type 1 fonts from ghostscript
displayFontT1 Times-Roman           /usr/local/share/ghostscript/fonts/n021003l.pfb
displayFontT1 Times-Italic          /usr/local/share/ghostscript/fonts/n021023l.pfb
displayFontT1 Times-Bold            /usr/local/share/ghostscript/fonts/n021004l.pfb
displayFontT1 Times-BoldItalic      /usr/local/share/ghostscript/fonts/n021024l.pfb
displayFontT1 Helvetica             /usr/local/share/ghostscript/fonts/n019003l.pfb
displayFontT1 Helvetica-Oblique     /usr/local/share/ghostscript/fonts/n019023l.pfb
displayFontT1 Helvetica-Bold        /usr/local/share/ghostscript/fonts/n019004l.pfb
displayFontT1 Helvetica-BoldOblique /usr/local/share/ghostscript/fonts/n019024l.pfb
displayFontT1 Courier               /usr/local/share/ghostscript/fonts/n022003l.pfb
displayFontT1 Courier-Oblique       /usr/local/share/ghostscript/fonts/n022023l.pfb
displayFontT1 Courier-Bold          /usr/local/share/ghostscript/fonts/n022004l.pfb
displayFontT1 Courier-BoldOblique   /usr/local/share/ghostscript/fonts/n022024l.pfb
displayFontT1 Symbol                /usr/local/share/ghostscript/fonts/s050000l.pfb
displayFontT1 ZapfDingbats          /usr/local/share/ghostscript/fonts/d050000l.pfb

# use the Bakoma Type 1 fonts
# (this assumes they happen to be installed in /usr/local/fonts/bakoma)
fontDir /usr/local/fonts/bakoma

# set some PostScript options
psPaperSize          letter
psDuplex             no
psLevel              level2
psEmbedType1Fonts    yes
psEmbedTrueTypeFonts yes
psFile               "| lpr -Pprinter5"

# assume that the PostScript printer has the Univers and
# Univers-Bold fonts
psFont Univers      Univers
psFont Univers-Bold Univers-Bold

# set the text output options
textEncoding UTF-8
textEOL      unix

# misc options
t1libControl    low
freetypeControl low
urlCommand      "netscape -remote 'openURL(%s)'"

.fi
.SH FILES
.TP
.B /usr/local/etc/xpdfrc
This is the default location for the system-wide configuration file.
Depending on build options, it may be placed elsewhere.
.TP
.B $HOME/.xpdfrc
This is the user's configuration file.  If it exists, it will be read
in place of the system-wide file.
.SH AUTHOR
The Xpdf software and documentation are copyright 1996-2005 Glyph &
Cog, LLC.
.SH "SEE ALSO"
.BR xpdf (1),
.BR pdftops (1),
.BR pdftotext (1),
.BR pdfinfo (1),
.BR pdftoppm (1),
.BR pdfimages (1)
.br
.B http://www.foolabs.com/xpdf/
xpdfrc(5)                                               xpdfrc(5)



NAME
       xpdfrc - configuration file for Xpdf tools (version 3.01)

DESCRIPTION
       All  of  the  Xpdf tools read a single configuration file.
       If you have a .xpdfrc file in your home directory, it will
       be read.  Otherwise, a system-wide configuration file will
       be read from /usr/local/etc/xpdfrc, if it  exists.   (This
       is  its  default  location; depending on build options, it
       may be placed elsewhere.)  On Win32  systems,  the  xpdfrc
       file  should  be  placed in the same directory as the exe-
       cutables.

       The xpdfrc file consists  of  a  series  of  configuration
       options,  one  per  line.   Blank lines and lines starting
       with a '#' (comments) are ignored.

       The following  sections  list  all  of  the  configuration
       options, sorted into functional groups.  There is an exam-
       ples section at the end.

INCLUDE FILES
       include config-file
              Includes the specified config file.  The effect  of
              this  is  equivalent  to  inserting the contents of
              config-file directly into the parent config file in
              place  of the include command.  Config files can be
              nested arbitrarily deeply.

CHARACTER MAPPING
       nameToUnicode map-file
              Specifies a file with the  mapping  from  character
              names to Unicode.  This is used to handle PDF fonts
              that have valid encodings but no  ToUnicode  entry.
              Each line of a nameToUnicode file looks like this:

                   hex-string name

              The  hex-string  is  the  Unicode (UCS-2) character
              index, and  name  is  the  corresponding  character
              name.  Multiple nameToUnicode files can be used; if
              a character name is given more than once, the  code
              in  the  last  specified  file is used.  There is a
              built-in default nameToUnicode table  with  all  of
              Adobe's standard character names.

       cidToUnicode registry-ordering map-file
              Specifies  the file with the mapping from character
              collection to Unicode.  Each line of a cidToUnicode
              file represents one character:

                   hex-string

              The  hex-string  is  the  Unicode (UCS-2) index for
              that character.  The first line  maps  CID  0,  the
              second line CID 1, etc.  File size is determined by
              size of the character collection.  Only one file is
              allowed  per  character collection; the last speci-
              fied file is used.  There are no built-in cidToUni-
              code mappings.

       unicodeToUnicode font-name-substring map-file
              This  is  used  to work around PDF fonts which have
              incorrect Unicode information.  It specifies a file
              which  maps  from  the  given  (incorrect)  Unicode
              indexes to the correct ones.  The mapping  will  be
              used    for    any   font   whose   name   contains
              font-name-substring.  Each line of a  unicodeToUni-
              code file represents one Unicode character:

                  in-hex out-hex1 out-hex2 ...

              The  in-hex  field  is an input (incorrect) Unicode
              index, and the rest of the fields are one  or  more
              output  (correct) Unicode indexes.  Each occurrence
              of in-hex will be converted to the specified output
              sequence.

       unicodeMap encoding-name map-file
              Specifies  the  file  with  mapping from Unicode to
              encoding-name.  These encodings are used for X dis-
              play  fonts and text output (see below).  Each line
              of a unicodeMap file represents a range of  one  or
              more  Unicode  characters  which maps linearly to a
              range in the output encoding:

                   in-start-hex in-end-hex out-start-hex

              Entries for single characters  can  be  abbreviated
              to:

                   in-hex out-hex

              The in-start-hex and in-end-hex fields (or the sin-
              gle in-hex field) specify the Unicode  range.   The
              out-start-hex  field  (or the out-hex field) speci-
              fies the start of the output encoding  range.   The
              length  of  the  out-start-hex  (or out-hex) string
              determines the  length  of  the  output  characters
              (e.g.,  UTF-8  uses  different  numbers of bytes to
              represent characters in different ranges).  Entries
              must  be  given  in increasing Unicode order.  Only
              one file is allowed per encoding; the  last  speci-
              fied  file  is  used.   The Latin1, ASCII7, Symbol,
              ZapfDingbats, UTF-8, and UCS-2 encodings are prede-
              fined.

       cMapDir registry-ordering dir
              Specifies  a  search  directory, dir, for CMaps for
              the registry-ordering character collection.   There
              can  be  multiple directories for a particular col-
              lection.  There are no default CMap directories.

       toUnicodeDir dir
              Specifies a search directory,  dir,  for  ToUnicode
              CMaps.   There  can  be multiple ToUnicode directo-
              ries.  There are no default ToUnicode  directories.

DISPLAY FONTS
       displayFontT1 PDF-font-name T1-file
              Maps  a  PDF  font, PDF-font-name, to a Type 1 font
              for display.  The Type 1 font file, T1-file, should
              be a standard .pfa or .pfb file.

       displayFontTT PDF-font-name TT-file
              Maps  a PDF font, PDF-font-name, to a TrueType font
              for display.   The  TrueType  font  file,  TT-file,
              should be a standard .ttf file.

       displayNamedCIDFontT1 PDF-font-name T1-file
              Maps    a   specific   PDF   CID   (16-bit)   font,
              PDF-font-name, to a  CID  font  (16-bit  PostScript
              font),  for display.  There are no default CID font
              mappings.

       displayCIDFontT1 registry-ordering T1-file
              Maps the registry-ordering character collection  to
              a  CID  font (16-bit PostScript font), for display.
              This mapping is used if the font name doesn't match
              any of the fonts declared with displayNamedCIDFont*
              commands.  There are no default CID font  mappings.

       displayNamedCIDFontTT PDF-font-name TT-file
              Maps    a   specific   PDF   CID   (16-bit)   font,
              PDF-font-name, to a  (16-bit)  TrueType  font,  for
              display.  There are no default CID font mappings.

       displayCIDFontTT registry-ordering TT-file
              Maps  the registry-ordering character collection to
              a (16-bit) TrueType font, for display.   This  map-
              ping  is used if the font name doesn't match any of
              the fonts declared with  displayNamedCIDFont*  com-
              mands.  There are no default CID font mappings.

       fontDir dir
              Specifies  a  search  directory  for  external font
              files.  There can be multiple fontDir  directories.
              If  a  PDF  file  uses a font but doesn't embed it,
              these directories will be searched for  a  matching
              font  file.  These fonts are used by both xpdf (for
              display) and pdftops (for embedding in  the  gener-
              ated  PostScript).  Type 1 fonts must have a suffix
              of ".pfa", ".pfb", ".ps",  or  no  suffix  at  all.
              TrueType  fonts  must  have a ".ttf" suffix.  Other
              files in these directories will be ignored.   There
              are no default fontDir directories.

POSTSCRIPT CONTROL
       psPaperSize width(pts) height(pts)
              Sets  the  paper  size  for PostScript output.  The
              width and height parameters give the paper size  in
              PostScript points (1 point = 1/72 inch).

       psPaperSize letter | legal | A4 | A3 | match
              Sets  the  paper  size  for  PostScript output to a
              standard size.  The default paper size is set  when
              xpdf  and  pdftops are built, typically to "letter"
              or "A4".  This can also be set  to  "match",  which
              will set the paper size to match the size specified
              in the PDF file.

       psImageableArea llx lly urx ury
              Sets the imageable area for PostScript output.  The
              four integers are the coordinates of the lower-left
              and upper-right corners of  the  imageable  region,
              specified  in  points  (with  the  origin being the
              lower-left corner of the paper).  This defaults  to
              the  full  paper  size; the psPaperSize option will
              reset the imageable area coordinates.

       psCrop yes | no
              If set to "yes", PostScript output  is  cropped  to
              the CropBox specified in the PDF file; otherwise no
              cropping is done.  This defaults to "yes".

       psExpandSmaller yes | no
              If  set  to  "yes",  PDF  pages  smaller  than  the
              PostScript  imageable area are expanded to fill the
              imageable area.  Otherwise, no scalling is done  on
              smaller pages.  This defaults to "no".

       psShrinkLarger yes | no
              If set to yes, PDF pages larger than the PostScript
              imageable area are  shrunk  to  fit  the  imageable
              area.   Otherwise,  no  scaling  is  done on larger
              pages.  This defaults to "yes".

       psCenter yes | no
              If  set  to  yes,  PDF  pages  smaller   than   the
              PostScript  imageable  area (after any scaling) are
              centered in the imageable  area.   Otherwise,  they
              are  aligned at the lower-left corner of the image-
              able area.  This defaults to "yes".

       psDuplex yes | no
              If set to "yes", the generated PostScript will  set
              the  "Duplex" pagedevice entry.  This tells duplex-
              capable  printers  to   enable   duplexing.    This
              defaults to "no".

       psLevel level1 | level1sep | level2 | level2sep | level3 |
       level3Sep
              Sets   the  PostScript  level  to  generate.   This
              defaults to "level2".

       psFont PDF-font-name PS-font-name
              When the PDF-font-name font is used in a PDF  file,
              it  will  be  translated  to  the  PostScript  font
              PS-font-name, which is assumed to  be  resident  in
              the    printer.    Typically,   PDF-font-name   and
              PS-font-name are the same.  By  default,  only  the
              Base-14 fonts are assumed to be resident.

       psNamedFont16 PDF-font-name wMode PS-font-name encoding
              When the 16-bit font PDF-font-name is used in a PDF
              file with the wMode writing mode and is not  embed-
              ded,  the  PS-font-name font is substituted for it.
              The writing mode must be either 'H' for  horizontal
              or  'V'  for  vertical.   The  PS-font-name font is
              assumed to be resident in the printer  and  to  use
              the   specified  encoding  (which  must  have  been
              defined with the unicodeMap command).

       psFont16 registry-ordering wMode PS-font-name encoding
              When a  16-bit  font  using  the  registry-ordering
              character  collection and wMode writing mode is not
              embedded and  does  not  match  any  of  the  fonts
              declared    in    psNamedFont16    commands,    the
              PS-font-name font is substituted for it.  The writ-
              ing  mode  must be either 'H' for horizontal or 'V'
              for vertical.  The PS-font-name font is assumed  to
              be resident in the printer and to use the specified
              writing mode and encoding  (which  must  have  been
              defined with the unicodeMap command).

       psEmbedType1Fonts yes | no
              If  set to "no", prevents embedding of Type 1 fonts
              in generated PostScript.  This defaults to "yes".

       psEmbedTrueTypeFonts yes | no
              If set to  "no",  prevents  embedding  of  TrueType
              fonts  in  generated  PostScript.  This defaults to
              "yes".

       psEmbedCIDTrueTypeFonts yes | no
              If set to "no", prevents embedding of CID  TrueType
              fonts   in   generated  PostScript.   For  Level  3
              PostScript, this generates a CID  font,  for  lower
              levels it generates a non-CID composite font.

       psEmbedCIDPostScriptFonts yes | no
              If   set   to   "no",  prevents  embedding  of  CID
              PostScript  fonts  in  generated  PostScript.   For
              Level  3 PostScript, this generates a CID font, for
              lower levels it generates a non-CID composite font.

       psOPI yes | no
              If  set to "yes", generates PostScript OPI comments
              for all images and forms which  have  OPI  informa-
              tion.   This  option  is only available if the Xpdf
              tools  were  compiled  with  OPI   support.    This
              defaults to "no".

       psASCIIHex yes | no
              If  set to "yes", the ASCIIHexEncode filter will be
              used instead  of  ASCII85Encode  for  binary  data.
              This defaults to "no".

       psFile file-or-command
              Sets  the  default PostScript file or print command
              for xpdf.  Commands start  with  a  '|'  character;
              anything  else is a file.  If the file name or com-
              mand contains  spaces  it  must  be  quoted.   This
              defaults  to  unset, which tells xpdf to generate a
              name  of  the  form  <file>.ps  for  a   PDF   file
              <file>.pdf.

       fontDir dir
              See  the  description  above,  in the DISPLAY FONTS
              section.

TEXT CONTROL
       textEncoding encoding-name
              Sets the encoding to use for  text  output.   (This
              can  be  overridden  with  the "-enc" switch on the
              command line.)  The encoding-name must  be  defined
              with  the  unicodeMap  command  (see  above).  This
              defaults to "Latin1".

       textEOL unix | dos | mac
              Sets the end-of-line convention  to  use  for  text
              output.  The options are:

                  unix = LF
                  dos  = CR+LF
                  mac  = CR

              (This  can  be overridden with the "-eol" switch on
              the command line.)  The default value is  based  on
              the OS where xpdf and pdftotext were built.

       textPageBreaks yes | no
              If  set  to "yes", text extraction will insert page
              breaks (form feed characters) between pages.   This
              defaults to "yes".

       textKeepTinyChars yes | no
              If  set  to  "yes",  text  extraction will keep all
              characters.  If set to "no", text  extraction  will
              discard  tiny  (smaller  than  3  point) characters
              after the first 50000 per page, avoiding  extremely
              slow run times for PDF files that use special fonts
              to do shading or cross-hatching.  This defaults  to
              "no".

MISCELLANEOUS SETTINGS
       initialZoom percentage | page | width
              Sets the initial zoom factor.  A number specifies a
              zoom percentage, where 100 means 72 dpi.   You  may
              also  specify 'page', to fit the page to the window
              size, or 'width', to fit the page width to the win-
              dow width.

       enableT1lib yes | no
              Enables  or  disables  use  of t1lib (a Type 1 font
              rasterizer).  This is only  relevant  if  the  Xpdf
              tools    were    built    with    t1lib    support.
              ("enableT1lib"  replaces  the  old   "t1libControl"
              option.)  This option defaults to "yes".

       enableFreeType yes | no
              Enables  or  disables use of FreeType (a TrueType /
              Type 1 font rasterizer).  This is only relevant  if
              the  Xpdf  tools  were built with FreeType support.
              ("enableFreeType" replaces  the  old  "freetypeCon-
              trol" option.)  This option defaults to "yes".

       antialias yes | no
              Enables  or  disables font anti-aliasing in the PDF
              rasterizer.  This option affects all font rasteriz-
              ers.   ("antialias" replaces the anti-aliasing con-
              trol  provided  by  the  old   "t1libControl"   and
              "freetypeControl" options.)  This default to "yes".

       urlCommand command
              Sets the command executed when you click on  a  URL
              link.   The  string  "%s" will be replaced with the
              URL.  (See the example below.)  This has no default
              value.

       movieCommand command
              Sets the command executed when you click on a movie
              annotation.  The string "%s" will be replaced  with
              the movie file name.  This has no default value.

       mapNumericCharNames yes | no
              If set to "yes", the Xpdf tools will attempt to map
              various numeric character names sometimes  used  in
              font  subsets.   In some cases this leads to usable
              text, and in other cases it leads to  gibberish  --
              there is no way for Xpdf to tell.  This defaults to
              "yes".

       printCommands yes | no
              If set to "yes", drawing commands  are  printed  as
              they're  executed  (useful  for  debugging).   This
              defaults to "no".

       errQuiet yes | no
              If set to "yes",  this  suppresses  all  error  and
              warning  messages from all of the Xpdf tools.  This
              defaults to "no".

EXAMPLES
       The following is a sample xpdfrc file.

       # from the Thai support package
       nameToUnicode /usr/local/share/xpdf/Thai.nameToUnicode

       # from the Japanese support package
       cidToUnicode Adobe-Japan1 /usr/local/share/xpdf/Adobe-Japan1.cidToUnicode
       unicodeMap   JISX0208     /usr/local/share/xpdf/JISX0208.unicodeMap
       cMapDir      Adobe-Japan1 /usr/local/share/xpdf/cmap/Adobe-Japan1

       # use the Base-14 Type 1 fonts from ghostscript
       displayFontT1 Times-Roman           /usr/local/share/ghostscript/fonts/n021003l.pfb
       displayFontT1 Times-Italic          /usr/local/share/ghostscript/fonts/n021023l.pfb
       displayFontT1 Times-Bold            /usr/local/share/ghostscript/fonts/n021004l.pfb
       displayFontT1 Times-BoldItalic      /usr/local/share/ghostscript/fonts/n021024l.pfb
       displayFontT1 Helvetica             /usr/local/share/ghostscript/fonts/n019003l.pfb
       displayFontT1 Helvetica-Oblique     /usr/local/share/ghostscript/fonts/n019023l.pfb
       displayFontT1 Helvetica-Bold        /usr/local/share/ghostscript/fonts/n019004l.pfb
       displayFontT1 Helvetica-BoldOblique /usr/local/share/ghostscript/fonts/n019024l.pfb
       displayFontT1 Courier               /usr/local/share/ghostscript/fonts/n022003l.pfb
       displayFontT1 Courier-Oblique       /usr/local/share/ghostscript/fonts/n022023l.pfb
       displayFontT1 Courier-Bold          /usr/local/share/ghostscript/fonts/n022004l.pfb
       displayFontT1 Courier-BoldOblique   /usr/local/share/ghostscript/fonts/n022024l.pfb
       displayFontT1 Symbol                /usr/local/share/ghostscript/fonts/s050000l.pfb
       displayFontT1 ZapfDingbats          /usr/local/share/ghostscript/fonts/d050000l.pfb

       # use the Bakoma Type 1 fonts
       # (this assumes they happen to be installed in /usr/local/fonts/bakoma)
       fontDir /usr/local/fonts/bakoma

       # set some PostScript options
       psPaperSize          letter
       psDuplex             no
       psLevel              level2
       psEmbedType1Fonts    yes
       psEmbedTrueTypeFonts yes
       psFile               "| lpr -Pprinter5"

       # assume that the PostScript printer has the Univers and
       # Univers-Bold fonts
       psFont Univers      Univers
       psFont Univers-Bold Univers-Bold

       # set the text output options
       textEncoding UTF-8
       textEOL      unix

       # misc options
       t1libControl    low
       freetypeControl low
       urlCommand      "netscape -remote 'openURL(%s)'"


FILES
       /usr/local/etc/xpdfrc
              This is the default location  for  the  system-wide
              configuration file.  Depending on build options, it
              may be placed elsewhere.

       $HOME/.xpdfrc
              This is  the  user's  configuration  file.   If  it
              exists, it will be read in place of the system-wide
              file.

AUTHOR
       The  Xpdf  software  and   documentation   are   copyright
       1996-2005 Glyph & Cog, LLC.

SEE ALSO
       xpdf(1),     pdftops(1),     pdftotext(1),     pdfinfo(1),
       pdftoppm(1), pdfimages(1)
       http://www.foolabs.com/xpdf/



                          17 August 2005                xpdfrc(5)
! Generated automatically by mantohlp
1 xpdfrc
2 NCLUDE_FILE

       xpdfrc - configuration file for Xpdf tools (version 3.01)

       include config-file
              Includes  the specified config file.  The effect of
              this is equivalent to  inserting  the  contents  of
              config-file directly into the parent config file in
              place of the include command.  Config files can  be
              nested arbitrarily deeply.

                                                               ()

2 HARACTER_MAPPIN

       nameToUnicode map-file
              Specifies  a  file  with the mapping from character
              names to Unicode.  This is used to handle PDF fonts
              that  have  valid encodings but no ToUnicode entry.
              Each line of a nameToUnicode file looks like this:

                   hex-string name

              The hex-string is  the  Unicode  (UCS-2)  character
              index,  and  name  is  the  corresponding character
              name.  Multiple nameToUnicode files can be used; if
              a  character name is given more than once, the code
              in the last specified file is  used.   There  is  a
              built-in  default  nameToUnicode  table with all of
              Adobe's standard character names.

       cidToUnicode registry-ordering map-file
              Specifies the file with the mapping from  character
              collection to Unicode.  Each line of a cidToUnicode
              file represents one character:

                   hex-string

              The hex-string is the  Unicode  (UCS-2)  index  for
              that  character.   The  first  line maps CID 0, the
              second line CID 1, etc.  File size is determined by
              size of the character collection.  Only one file is
              allowed per character collection; the  last  speci-
              fied file is used.  There are no built-in cidToUni-
              code mappings.

       unicodeToUnicode font-name-substring map-file
              This is used to work around PDF  fonts  which  have
              incorrect Unicode information.  It specifies a file
              which  maps  from  the  given  (incorrect)  Unicode
              indexes  to  the correct ones.  The mapping will be
              used   for   any   font   whose    name    contains
              font-name-substring.   Each line of a unicodeToUni-
              code file represents one Unicode character:

                  in-hex out-hex1 out-hex2 ...

              The in-hex field is an  input  (incorrect)  Unicode
              index,  and  the rest of the fields are one or more
              output (correct) Unicode indexes.  Each  occurrence
              of in-hex will be converted to the specified output
              sequence.

       unicodeMap encoding-name map-file
              Specifies the file with  mapping  from  Unicode  to
              encoding-name.  These encodings are used for X dis-
              play fonts and text output (see below).  Each  line
              of  a  unicodeMap file represents a range of one or
              more Unicode characters which maps  linearly  to  a
              range in the output encoding:

                   in-start-hex in-end-hex out-start-hex

              Entries  for  single  characters can be abbreviated
              to:

                   in-hex out-hex

              The in-start-hex and in-end-hex fields (or the sin-
              gle  in-hex  field) specify the Unicode range.  The
              out-start-hex field (or the out-hex  field)  speci-
              fies  the  start of the output encoding range.  The
              length of the  out-start-hex  (or  out-hex)  string
              determines  the  length  of  the  output characters
              (e.g., UTF-8 uses different  numbers  of  bytes  to
              represent characters in different ranges).  Entries
              must be given in increasing  Unicode  order.   Only
              one  file  is allowed per encoding; the last speci-
              fied file is used.   The  Latin1,  ASCII7,  Symbol,
              ZapfDingbats, UTF-8, and UCS-2 encodings are prede-
              fined.

       cMapDir registry-ordering dir
              Specifies a search directory, dir,  for  CMaps  for
              the  registry-ordering character collection.  There
              can be multiple directories for a  particular  col-
              lection.  There are no default CMap directories.

       toUnicodeDir dir
              Specifies  a  search  directory, dir, for ToUnicode
              CMaps.  There can be  multiple  ToUnicode  directo-
              ries.   There are no default ToUnicode directories.

                                                               ()

2 ISPLAY_FONT

       displayFontT1 PDF-font-name T1-file
              Maps  a  PDF  font, PDF-font-name, to a Type 1 font
              for display.  The Type 1 font file, T1-file, should
              be a standard .pfa or .pfb file.

       displayFontTT PDF-font-name TT-file
              Maps  a PDF font, PDF-font-name, to a TrueType font
              for display.   The  TrueType  font  file,  TT-file,
              should be a standard .ttf file.

       displayNamedCIDFontT1 PDF-font-name T1-file
              Maps    a   specific   PDF   CID   (16-bit)   font,
              PDF-font-name, to a  CID  font  (16-bit  PostScript
              font),  for display.  There are no default CID font
              mappings.

       displayCIDFontT1 registry-ordering T1-file
              Maps the registry-ordering character collection  to
              a  CID  font (16-bit PostScript font), for display.
              This mapping is used if the font name doesn't match
              any of the fonts declared with displayNamedCIDFont*
              commands.  There are no default CID font  mappings.

       displayNamedCIDFontTT PDF-font-name TT-file
              Maps    a   specific   PDF   CID   (16-bit)   font,
              PDF-font-name, to a  (16-bit)  TrueType  font,  for
              display.  There are no default CID font mappings.

       displayCIDFontTT registry-ordering TT-file
              Maps  the registry-ordering character collection to
              a (16-bit) TrueType font, for display.   This  map-
              ping  is used if the font name doesn't match any of
              the fonts declared with  displayNamedCIDFont*  com-
              mands.  There are no default CID font mappings.

       fontDir dir
              Specifies  a  search  directory  for  external font
              files.  There can be multiple fontDir  directories.
              If  a  PDF  file  uses a font but doesn't embed it,
              these directories will be searched for  a  matching
              font  file.  These fonts are used by both xpdf (for
              display) and pdftops (for embedding in  the  gener-
              ated  PostScript).  Type 1 fonts must have a suffix
              of ".pfa", ".pfb", ".ps",  or  no  suffix  at  all.
              TrueType  fonts  must  have a ".ttf" suffix.  Other
              files in these directories will be ignored.   There
              are no default fontDir directories.

                                                               ()

2 OSTSCRIPT_CONTRO

       psPaperSize width(pts) height(pts)
              Sets  the  paper  size  for PostScript output.  The
              width and height parameters give the paper size  in
              PostScript points (1 point = 1/72 inch).

       psPaperSize letter | legal | A4 | A3 | match
              Sets  the  paper  size  for  PostScript output to a
              standard size.  The default paper size is set  when
              xpdf  and  pdftops are built, typically to "letter"
              or "A4".  This can also be set  to  "match",  which
              will set the paper size to match the size specified
              in the PDF file.

       psImageableArea llx lly urx ury
              Sets the imageable area for PostScript output.  The
              four integers are the coordinates of the lower-left
              and upper-right corners of  the  imageable  region,
              specified  in  points  (with  the  origin being the
              lower-left corner of the paper).  This defaults  to
              the  full  paper  size; the psPaperSize option will
              reset the imageable area coordinates.

       psCrop yes | no
              If set to "yes", PostScript output  is  cropped  to
              the CropBox specified in the PDF file; otherwise no
              cropping is done.  This defaults to "yes".

       psExpandSmaller yes | no
              If  set  to  "yes",  PDF  pages  smaller  than  the
              PostScript  imageable area are expanded to fill the
              imageable area.  Otherwise, no scalling is done  on
              smaller pages.  This defaults to "no".

       psShrinkLarger yes | no
              If set to yes, PDF pages larger than the PostScript
              imageable area are  shrunk  to  fit  the  imageable
              area.   Otherwise,  no  scaling  is  done on larger
              pages.  This defaults to "yes".

       psCenter yes | no
              If  set  to  yes,  PDF  pages  smaller   than   the
              PostScript  imageable  area (after any scaling) are
              centered in the imageable  area.   Otherwise,  they
              are  aligned at the lower-left corner of the image-
              able area.  This defaults to "yes".

       psDuplex yes | no
              If set to "yes", the generated PostScript will  set
              the  "Duplex" pagedevice entry.  This tells duplex-
              capable  printers  to   enable   duplexing.    This
              defaults to "no".

       psLevel level1 | level1sep | level2 | level2sep | level3 |
       level3Sep
              Sets   the  PostScript  level  to  generate.   This
              defaults to "level2".

       psFont PDF-font-name PS-font-name
              When the PDF-font-name font is used in a PDF  file,
              it  will  be  translated  to  the  PostScript  font
              PS-font-name, which is assumed to  be  resident  in
              the    printer.    Typically,   PDF-font-name   and
              PS-font-name are the same.  By  default,  only  the
              Base-14 fonts are assumed to be resident.

       psNamedFont16 PDF-font-name wMode PS-font-name encoding
              When the 16-bit font PDF-font-name is used in a PDF
              file with the wMode writing mode and is not  embed-
              ded,  the  PS-font-name font is substituted for it.
              The writing mode must be either 'H' for  horizontal
              or  'V'  for  vertical.   The  PS-font-name font is
              assumed to be resident in the printer  and  to  use
              the   specified  encoding  (which  must  have  been
              defined with the unicodeMap command).

       psFont16 registry-ordering wMode PS-font-name encoding
              When a  16-bit  font  using  the  registry-ordering
              character  collection and wMode writing mode is not
              embedded and  does  not  match  any  of  the  fonts
              declared    in    psNamedFont16    commands,    the
              PS-font-name font is substituted for it.  The writ-
              ing  mode  must be either 'H' for horizontal or 'V'
              for vertical.  The PS-font-name font is assumed  to
              be resident in the printer and to use the specified
              writing mode and encoding  (which  must  have  been
              defined with the unicodeMap command).

       psEmbedType1Fonts yes | no
              If  set to "no", prevents embedding of Type 1 fonts
              in generated PostScript.  This defaults to "yes".

       psEmbedTrueTypeFonts yes | no
              If set to  "no",  prevents  embedding  of  TrueType
              fonts  in  generated  PostScript.  This defaults to
              "yes".

       psEmbedCIDTrueTypeFonts yes | no
              If set to "no", prevents embedding of CID  TrueType
              fonts   in   generated  PostScript.   For  Level  3
              PostScript, this generates a CID  font,  for  lower
              levels it generates a non-CID composite font.

       psEmbedCIDPostScriptFonts yes | no
              If   set   to   "no",  prevents  embedding  of  CID
              PostScript  fonts  in  generated  PostScript.   For
              Level  3 PostScript, this generates a CID font, for
              lower levels it generates a non-CID composite font.

       psOPI yes | no
              If  set to "yes", generates PostScript OPI comments
              for all images and forms which  have  OPI  informa-
              tion.   This  option  is only available if the Xpdf
              tools  were  compiled  with  OPI   support.    This
              defaults to "no".

       psASCIIHex yes | no
              If  set to "yes", the ASCIIHexEncode filter will be
              used instead  of  ASCII85Encode  for  binary  data.
              This defaults to "no".

       psFile file-or-command
              Sets  the  default PostScript file or print command
              for xpdf.  Commands start  with  a  '|'  character;
              anything  else is a file.  If the file name or com-
              mand contains  spaces  it  must  be  quoted.   This
              defaults  to  unset, which tells xpdf to generate a
              name  of  the  form  <file>.ps  for  a   PDF   file
              <file>.pdf.

       fontDir dir
              See  the  description  above,  in the DISPLAY FONTS
              section.

                                                               ()

2 EXT_CONTRO

       textEncoding encoding-name
              Sets  the  encoding  to use for text output.  (This
              can be overridden with the  "-enc"  switch  on  the
              command  line.)   The encoding-name must be defined
              with the  unicodeMap  command  (see  above).   This
              defaults to "Latin1".

       textEOL unix | dos | mac
              Sets  the  end-of-line  convention  to use for text
              output.  The options are:

                  unix = LF
                  dos  = CR+LF
                  mac  = CR

              (This can be overridden with the "-eol"  switch  on
              the  command  line.)  The default value is based on
              the OS where xpdf and pdftotext were built.

       textPageBreaks yes | no
              If set to "yes", text extraction will  insert  page
              breaks  (form feed characters) between pages.  This
              defaults to "yes".

       textKeepTinyChars yes | no
              If set to "yes",  text  extraction  will  keep  all
              characters.   If  set to "no", text extraction will
              discard tiny  (smaller  than  3  point)  characters
              after  the first 50000 per page, avoiding extremely
              slow run times for PDF files that use special fonts
              to  do shading or cross-hatching.  This defaults to
              "no".

                                                               ()

2 ISCELLANEOUS_SETTING

       initialZoom percentage | page | width
              Sets the initial zoom factor.  A number specifies a
              zoom percentage, where 100 means 72 dpi.   You  may
              also  specify 'page', to fit the page to the window
              size, or 'width', to fit the page width to the win-
              dow width.

       enableT1lib yes | no
              Enables  or  disables  use  of t1lib (a Type 1 font
              rasterizer).  This is only  relevant  if  the  Xpdf
              tools    were    built    with    t1lib    support.
              ("enableT1lib"  replaces  the  old   "t1libControl"
              option.)  This option defaults to "yes".

       enableFreeType yes | no
              Enables  or  disables use of FreeType (a TrueType /
              Type 1 font rasterizer).  This is only relevant  if
              the  Xpdf  tools  were built with FreeType support.
              ("enableFreeType" replaces  the  old  "freetypeCon-
              trol" option.)  This option defaults to "yes".

       antialias yes | no
              Enables  or  disables font anti-aliasing in the PDF
              rasterizer.  This option affects all font rasteriz-
              ers.   ("antialias" replaces the anti-aliasing con-
              trol  provided  by  the  old   "t1libControl"   and
              "freetypeControl" options.)  This default to "yes".

       urlCommand command
              Sets the command executed when you click on  a  URL
              link.   The  string  "%s" will be replaced with the
              URL.  (See the example below.)  This has no default
              value.

       movieCommand command
              Sets the command executed when you click on a movie
              annotation.  The string "%s" will be replaced  with
              the movie file name.  This has no default value.

       mapNumericCharNames yes | no
              If set to "yes", the Xpdf tools will attempt to map
              various numeric character names sometimes  used  in
              font  subsets.   In some cases this leads to usable
              text, and in other cases it leads to  gibberish  --
              there is no way for Xpdf to tell.  This defaults to
              "yes".

       printCommands yes | no
              If set to "yes", drawing commands  are  printed  as
              they're  executed  (useful  for  debugging).   This
              defaults to "no".

       errQuiet yes | no
              If set to "yes",  this  suppresses  all  error  and
              warning  messages from all of the Xpdf tools.  This
              defaults to "no".

                                                               ()

2 EXAMPLES

       The following is a sample xpdfrc file.

       # from the Thai support package
       nameToUnicode /usr/local/share/xpdf/Thai.nameToUnicode

       # from the Japanese support package
       cidToUnicode Adobe-Japan1 /usr/local/share/xpdf/Adobe-Japan1.cidToUnicode
       unicodeMap   JISX0208     /usr/local/share/xpdf/JISX0208.unicodeMap
       cMapDir      Adobe-Japan1 /usr/local/share/xpdf/cmap/Adobe-Japan1

       # use the Base-14 Type 1 fonts from ghostscript
       displayFontT1 Times-Roman           /usr/local/share/ghostscript/fonts/n021003l.pfb
       displayFontT1 Times-Italic          /usr/local/share/ghostscript/fonts/n021023l.pfb
       displayFontT1 Times-Bold            /usr/local/share/ghostscript/fonts/n021004l.pfb
       displayFontT1 Times-BoldItalic      /usr/local/share/ghostscript/fonts/n021024l.pfb
       displayFontT1 Helvetica             /usr/local/share/ghostscript/fonts/n019003l.pfb
       displayFontT1 Helvetica-Oblique     /usr/local/share/ghostscript/fonts/n019023l.pfb
       displayFontT1 Helvetica-Bold        /usr/local/share/ghostscript/fonts/n019004l.pfb
       displayFontT1 Helvetica-BoldOblique /usr/local/share/ghostscript/fonts/n019024l.pfb
       displayFontT1 Courier               /usr/local/share/ghostscript/fonts/n022003l.pfb
       displayFontT1 Courier-Oblique       /usr/local/share/ghostscript/fonts/n022023l.pfb
       displayFontT1 Courier-Bold          /usr/local/share/ghostscript/fonts/n022004l.pfb
       displayFontT1 Courier-BoldOblique   /usr/local/share/ghostscript/fonts/n022024l.pfb
       displayFontT1 Symbol                /usr/local/share/ghostscript/fonts/s050000l.pfb
       displayFontT1 ZapfDingbats          /usr/local/share/ghostscript/fonts/d050000l.pfb

       # use the Bakoma Type 1 fonts
       # (this assumes they happen to be installed in /usr/local/fonts/bakoma)
       fontDir /usr/local/fonts/bakoma

       # set some PostScript options
       psPaperSize          letter
       psDuplex             no
       psLevel              level2
       psEmbedType1Fonts    yes
       psEmbedTrueTypeFonts yes
       psFile               "| lpr -Pprinter5"

       # assume that the PostScript printer has the Univers and
       # Univers-Bold fonts
       psFont Univers      Univers
       psFont Univers-Bold Univers-Bold

       # set the text output options
       textEncoding UTF-8
       textEOL      unix

       # misc options
       t1libControl    low
       freetypeControl low
       urlCommand      "netscape -remote 'openURL(%s)'"

                                                               ()

2 FILES

       /usr/local/etc/xpdfrc
              This  is  the  default location for the system-wide
              configuration file.  Depending on build options, it
              may be placed elsewhere.

       $HOME/.xpdfrc
              This  is  the  user's  configuration  file.   If it
              exists, it will be read in place of the system-wide
              file.

                                                               ()

2 AUTHOR

       The   Xpdf   software   and  documentation  are  copyright
       1996-2005 Glyph & Cog, LLC.

                                                               ()

2 SEE_ALSO

       xpdf(1),     pdftops(1),     pdftotext(1),     pdfinfo(1),
       pdftoppm(1), pdfimages(1)
       http://www.foolabs.com/xpdf/

                                                               ()

//========================================================================
//
// FoFiBase.cc
//
// Copyright 1999-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stdio.h>
#include "gmem.h"
#include "FoFiBase.h"

//------------------------------------------------------------------------
// FoFiBase
//------------------------------------------------------------------------

FoFiBase::FoFiBase(char *fileA, int lenA, GBool freeFileDataA) {
  fileData = file = (Guchar *)fileA;
  len = lenA;
  freeFileData = freeFileDataA;
}

FoFiBase::~FoFiBase() {
  if (freeFileData) {
    gfree(fileData);
  }
}

char *FoFiBase::readFile(char *fileName, int *fileLen) {
  FILE *f;
  char *buf;
  int n;

  if (!(f = fopen(fileName, "rb"))) {
    return NULL;
  }
  fseek(f, 0, SEEK_END);
  n = (int)ftell(f);
  fseek(f, 0, SEEK_SET);
  buf = (char *)gmalloc(n);
  if ((int)fread(buf, 1, n, f) != n) {
    gfree(buf);
    fclose(f);
    return NULL;
  }
  fclose(f);
  *fileLen = n;
  return buf;
}

int FoFiBase::getS8(int pos, GBool *ok) {
  int x;

  if (pos < 0 || pos >= len) {
    *ok = gFalse;
    return 0;
  }
  x = file[pos];
  if (x & 0x80) {
    x |= ~0xff;
  }
  return x;
}

int FoFiBase::getU8(int pos, GBool *ok) {
  if (pos < 0 || pos >= len) {
    *ok = gFalse;
    return 0;
  }
  return file[pos];
}

int FoFiBase::getS16BE(int pos, GBool *ok) {
  int x;

  if (pos < 0 || pos+1 >= len) {
    *ok = gFalse;
    return 0;
  }
  x = file[pos];
  x = (x << 8) + file[pos+1];
  if (x & 0x8000) {
    x |= ~0xffff;
  }
  return x;
}

int FoFiBase::getU16BE(int pos, GBool *ok) {
  int x;

  if (pos < 0 || pos+1 >= len) {
    *ok = gFalse;
    return 0;
  }
  x = file[pos];
  x = (x << 8) + file[pos+1];
  return x;
}

int FoFiBase::getS32BE(int pos, GBool *ok) {
  int x;

  if (pos < 0 || pos+3 >= len) {
    *ok = gFalse;
    return 0;
  }
  x = file[pos];
  x = (x << 8) + file[pos+1];
  x = (x << 8) + file[pos+2];
  x = (x << 8) + file[pos+3];
  if (x & 0x80000000) {
    x |= ~0xffffffff;
  }
  return x;
}

Guint FoFiBase::getU32BE(int pos, GBool *ok) {
  Guint x;

  if (pos < 0 || pos+3 >= len) {
    *ok = gFalse;
    return 0;
  }
  x = file[pos];
  x = (x << 8) + file[pos+1];
  x = (x << 8) + file[pos+2];
  x = (x << 8) + file[pos+3];
  return x;
}

Guint FoFiBase::getUVarBE(int pos, int size, GBool *ok) {
  Guint x;
  int i;

  if (pos < 0 || pos + size > len) {
    *ok = gFalse;
    return 0;
  }
  x = 0;
  for (i = 0; i < size; ++i) {
    x = (x << 8) + file[pos + i];
  }
  return x;
}

GBool FoFiBase::checkRegion(int pos, int size) {
  return pos >= 0 &&
         pos + size >= pos &&
         pos + size <= len;
}
//========================================================================
//
// FoFiBase.h
//
// Copyright 1999-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef FOFIBASE_H
#define FOFIBASE_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "gtypes.h"

//------------------------------------------------------------------------

typedef void (*FoFiOutputFunc)(void *stream, char *data, int len);

//------------------------------------------------------------------------
// FoFiBase
//------------------------------------------------------------------------

class FoFiBase {
public:

  virtual ~FoFiBase();

protected:

  FoFiBase(char *fileA, int lenA, GBool freeFileDataA);
  static char *readFile(char *fileName, int *fileLen);

  // S = signed / U = unsigned
  // 8/16/32/Var = word length, in bytes
  // BE = big endian
  int getS8(int pos, GBool *ok);
  int getU8(int pos, GBool *ok);
  int getS16BE(int pos, GBool *ok);
  int getU16BE(int pos, GBool *ok);
  int getS32BE(int pos, GBool *ok);
  Guint getU32BE(int pos, GBool *ok);
  Guint getUVarBE(int pos, int size, GBool *ok);

  GBool checkRegion(int pos, int size);

  Guchar *fileData;
  Guchar *file;
  int len;
  GBool freeFileData;
};

#endif
//========================================================================
//
// FoFiEncodings.cc
//
// Copyright 1999-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stdlib.h>
#include "FoFiEncodings.h"

//------------------------------------------------------------------------
// Type 1 and 1C font data
//------------------------------------------------------------------------

char *fofiType1StandardEncoding[256] = {
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  "space",
  "exclam",
  "quotedbl",
  "numbersign",
  "dollar",
  "percent",
  "ampersand",
  "quoteright",
  "parenleft",
  "parenright",
  "asterisk",
  "plus",
  "comma",
  "hyphen",
  "period",
  "slash",
  "zero",
  "one",
  "two",
  "three",
  "four",
  "five",
  "six",
  "seven",
  "eight",
  "nine",
  "colon",
  "semicolon",
  "less",
  "equal",
  "greater",
  "question",
  "at",
  "A",
  "B",
  "C",
  "D",
  "E",
  "F",
  "G",
  "H",
  "I",
  "J",
  "K",
  "L",
  "M",
  "N",
  "O",
  "P",
  "Q",
  "R",
  "S",
  "T",
  "U",
  "V",
  "W",
  "X",
  "Y",
  "Z",
  "bracketleft",
  "backslash",
  "bracketright",
  "asciicircum",
  "underscore",
  "quoteleft",
  "a",
  "b",
  "c",
  "d",
  "e",
  "f",
  "g",
  "h",
  "i",
  "j",
  "k",
  "l",
  "m",
  "n",
  "o",
  "p",
  "q",
  "r",
  "s",
  "t",
  "u",
  "v",
  "w",
  "x",
  "y",
  "z",
  "braceleft",
  "bar",
  "braceright",
  "asciitilde",
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  "exclamdown",
  "cent",
  "sterling",
  "fraction",
  "yen",
  "florin",
  "section",
  "currency",
  "quotesingle",
  "quotedblleft",
  "guillemotleft",
  "guilsinglleft",
  "guilsinglright",
  "fi",
  "fl",
  NULL,
  "endash",
  "dagger",
  "daggerdbl",
  "periodcentered",
  NULL,
  "paragraph",
  "bullet",
  "quotesinglbase",
  "quotedblbase",
  "quotedblright",
  "guillemotright",
  "ellipsis",
  "perthousand",
  NULL,
  "questiondown",
  NULL,
  "grave",
  "acute",
  "circumflex",
  "tilde",
  "macron",
  "breve",
  "dotaccent",
  "dieresis",
  NULL,
  "ring",
  "cedilla",
  NULL,
  "hungarumlaut",
  "ogonek",
  "caron",
  "emdash",
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  "AE",
  NULL,
  "ordfeminine",
  NULL,
  NULL,
  NULL,
  NULL,
  "Lslash",
  "Oslash",
  "OE",
  "ordmasculine",
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  "ae",
  NULL,
  NULL,
  NULL,
  "dotlessi",
  NULL,
  NULL,
  "lslash",
  "oslash",
  "oe",
  "germandbls",
  NULL,
  NULL,
  NULL,
  NULL
};

char *fofiType1ExpertEncoding[256] = {
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  "space",
  "exclamsmall",
  "Hungarumlautsmall",
  NULL,
  "dollaroldstyle",
  "dollarsuperior",
  "ampersandsmall",
  "Acutesmall",
  "parenleftsuperior",
  "parenrightsuperior",
  "twodotenleader",
  "onedotenleader",
  "comma",
  "hyphen",
  "period",
  "fraction",
  "zerooldstyle",
  "oneoldstyle",
  "twooldstyle",
  "threeoldstyle",
  "fouroldstyle",
  "fiveoldstyle",
  "sixoldstyle",
  "sevenoldstyle",
  "eightoldstyle",
  "nineoldstyle",
  "colon",
  "semicolon",
  "commasuperior",
  "threequartersemdash",
  "periodsuperior",
  "questionsmall",
  NULL,
  "asuperior",
  "bsuperior",
  "centsuperior",
  "dsuperior",
  "esuperior",
  NULL,
  NULL,
  NULL,
  "isuperior",
  NULL,
  NULL,
  "lsuperior",
  "msuperior",
  "nsuperior",
  "osuperior",
  NULL,
  NULL,
  "rsuperior",
  "ssuperior",
  "tsuperior",
  NULL,
  "ff",
  "fi",
  "fl",
  "ffi",
  "ffl",
  "parenleftinferior",
  NULL,
  "parenrightinferior",
  "Circumflexsmall",
  "hyphensuperior",
  "Gravesmall",
  "Asmall",
  "Bsmall",
  "Csmall",
  "Dsmall",
  "Esmall",
  "Fsmall",
  "Gsmall",
  "Hsmall",
  "Ismall",
  "Jsmall",
  "Ksmall",
  "Lsmall",
  "Msmall",
  "Nsmall",
  "Osmall",
  "Psmall",
  "Qsmall",
  "Rsmall",
  "Ssmall",
  "Tsmall",
  "Usmall",
  "Vsmall",
  "Wsmall",
  "Xsmall",
  "Ysmall",
  "Zsmall",
  "colonmonetary",
  "onefitted",
  "rupiah",
  "Tildesmall",
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  "exclamdownsmall",
  "centoldstyle",
  "Lslashsmall",
  NULL,
  NULL,
  "Scaronsmall",
  "Zcaronsmall",
  "Dieresissmall",
  "Brevesmall",
  "Caronsmall",
  NULL,
  "Dotaccentsmall",
  NULL,
  NULL,
  "Macronsmall",
  NULL,
  NULL,
  "figuredash",
  "hypheninferior",
  NULL,
  NULL,
  "Ogoneksmall",
  "Ringsmall",
  "Cedillasmall",
  NULL,
  NULL,
  NULL,
  "onequarter",
  "onehalf",
  "threequarters",
  "questiondownsmall",
  "oneeighth",
  "threeeighths",
  "fiveeighths",
  "seveneighths",
  "onethird",
  "twothirds",
  NULL,
  NULL,
  "zerosuperior",
  "onesuperior",
  "twosuperior",
  "threesuperior",
  "foursuperior",
  "fivesuperior",
  "sixsuperior",
  "sevensuperior",
  "eightsuperior",
  "ninesuperior",
  "zeroinferior",
  "oneinferior",
  "twoinferior",
  "threeinferior",
  "fourinferior",
  "fiveinferior",
  "sixinferior",
  "seveninferior",
  "eightinferior",
  "nineinferior",
  "centinferior",
  "dollarinferior",
  "periodinferior",
  "commainferior",
  "Agravesmall",
  "Aacutesmall",
  "Acircumflexsmall",
  "Atildesmall",
  "Adieresissmall",
  "Aringsmall",
  "AEsmall",
  "Ccedillasmall",
  "Egravesmall",
  "Eacutesmall",
  "Ecircumflexsmall",
  "Edieresissmall",
  "Igravesmall",
  "Iacutesmall",
  "Icircumflexsmall",
  "Idieresissmall",
  "Ethsmall",
  "Ntildesmall",
  "Ogravesmall",
  "Oacutesmall",
  "Ocircumflexsmall",
  "Otildesmall",
  "Odieresissmall",
  "OEsmall",
  "Oslashsmall",
  "Ugravesmall",
  "Uacutesmall",
  "Ucircumflexsmall",
  "Udieresissmall",
  "Yacutesmall",
  "Thornsmall",
  "Ydieresissmall"
};

//------------------------------------------------------------------------
// Type 1C font data
//------------------------------------------------------------------------

char *fofiType1CStdStrings[391] = {
  ".notdef",
  "space",
  "exclam",
  "quotedbl",
  "numbersign",
  "dollar",
  "percent",
  "ampersand",
  "quoteright",
  "parenleft",
  "parenright",
  "asterisk",
  "plus",
  "comma",
  "hyphen",
  "period",
  "slash",
  "zero",
  "one",
  "two",
  "three",
  "four",
  "five",
  "six",
  "seven",
  "eight",
  "nine",
  "colon",
  "semicolon",
  "less",
  "equal",
  "greater",
  "question",
  "at",
  "A",
  "B",
  "C",
  "D",
  "E",
  "F",
  "G",
  "H",
  "I",
  "J",
  "K",
  "L",
  "M",
  "N",
  "O",
  "P",
  "Q",
  "R",
  "S",
  "T",
  "U",
  "V",
  "W",
  "X",
  "Y",
  "Z",
  "bracketleft",
  "backslash",
  "bracketright",
  "asciicircum",
  "underscore",
  "quoteleft",
  "a",
  "b",
  "c",
  "d",
  "e",
  "f",
  "g",
  "h",
  "i",
  "j",
  "k",
  "l",
  "m",
  "n",
  "o",
  "p",
  "q",
  "r",
  "s",
  "t",
  "u",
  "v",
  "w",
  "x",
  "y",
  "z",
  "braceleft",
  "bar",
  "braceright",
  "asciitilde",
  "exclamdown",
  "cent",
  "sterling",
  "fraction",
  "yen",
  "florin",
  "section",
  "currency",
  "quotesingle",
  "quotedblleft",
  "guillemotleft",
  "guilsinglleft",
  "guilsinglright",
  "fi",
  "fl",
  "endash",
  "dagger",
  "daggerdbl",
  "periodcentered",
  "paragraph",
  "bullet",
  "quotesinglbase",
  "quotedblbase",
  "quotedblright",
  "guillemotright",
  "ellipsis",
  "perthousand",
  "questiondown",
  "grave",
  "acute",
  "circumflex",
  "tilde",
  "macron",
  "breve",
  "dotaccent",
  "dieresis",
  "ring",
  "cedilla",
  "hungarumlaut",
  "ogonek",
  "caron",
  "emdash",
  "AE",
  "ordfeminine",
  "Lslash",
  "Oslash",
  "OE",
  "ordmasculine",
  "ae",
  "dotlessi",
  "lslash",
  "oslash",
  "oe",
  "germandbls",
  "onesuperior",
  "logicalnot",
  "mu",
  "trademark",
  "Eth",
  "onehalf",
  "plusminus",
  "Thorn",
  "onequarter",
  "divide",
  "brokenbar",
  "degree",
  "thorn",
  "threequarters",
  "twosuperior",
  "registered",
  "minus",
  "eth",
  "multiply",
  "threesuperior",
  "copyright",
  "Aacute",
  "Acircumflex",
  "Adieresis",
  "Agrave",
  "Aring",
  "Atilde",
  "Ccedilla",
  "Eacute",
  "Ecircumflex",
  "Edieresis",
  "Egrave",
  "Iacute",
  "Icircumflex",
  "Idieresis",
  "Igrave",
  "Ntilde",
  "Oacute",
  "Ocircumflex",
  "Odieresis",
  "Ograve",
  "Otilde",
  "Scaron",
  "Uacute",
  "Ucircumflex",
  "Udieresis",
  "Ugrave",
  "Yacute",
  "Ydieresis",
  "Zcaron",
  "aacute",
  "acircumflex",
  "adieresis",
  "agrave",
  "aring",
  "atilde",
  "ccedilla",
  "eacute",
  "ecircumflex",
  "edieresis",
  "egrave",
  "iacute",
  "icircumflex",
  "idieresis",
  "igrave",
  "ntilde",
  "oacute",
  "ocircumflex",
  "odieresis",
  "ograve",
  "otilde",
  "scaron",
  "uacute",
  "ucircumflex",
  "udieresis",
  "ugrave",
  "yacute",
  "ydieresis",
  "zcaron",
  "exclamsmall",
  "Hungarumlautsmall",
  "dollaroldstyle",
  "dollarsuperior",
  "ampersandsmall",
  "Acutesmall",
  "parenleftsuperior",
  "parenrightsuperior",
  "twodotenleader",
  "onedotenleader",
  "zerooldstyle",
  "oneoldstyle",
  "twooldstyle",
  "threeoldstyle",
  "fouroldstyle",
  "fiveoldstyle",
  "sixoldstyle",
  "sevenoldstyle",
  "eightoldstyle",
  "nineoldstyle",
  "commasuperior",
  "threequartersemdash",
  "periodsuperior",
  "questionsmall",
  "asuperior",
  "bsuperior",
  "centsuperior",
  "dsuperior",
  "esuperior",
  "isuperior",
  "lsuperior",
  "msuperior",
  "nsuperior",
  "osuperior",
  "rsuperior",
  "ssuperior",
  "tsuperior",
  "ff",
  "ffi",
  "ffl",
  "parenleftinferior",
  "parenrightinferior",
  "Circumflexsmall",
  "hyphensuperior",
  "Gravesmall",
  "Asmall",
  "Bsmall",
  "Csmall",
  "Dsmall",
  "Esmall",
  "Fsmall",
  "Gsmall",
  "Hsmall",
  "Ismall",
  "Jsmall",
  "Ksmall",
  "Lsmall",
  "Msmall",
  "Nsmall",
  "Osmall",
  "Psmall",
  "Qsmall",
  "Rsmall",
  "Ssmall",
  "Tsmall",
  "Usmall",
  "Vsmall",
  "Wsmall",
  "Xsmall",
  "Ysmall",
  "Zsmall",
  "colonmonetary",
  "onefitted",
  "rupiah",
  "Tildesmall",
  "exclamdownsmall",
  "centoldstyle",
  "Lslashsmall",
  "Scaronsmall",
  "Zcaronsmall",
  "Dieresissmall",
  "Brevesmall",
  "Caronsmall",
  "Dotaccentsmall",
  "Macronsmall",
  "figuredash",
  "hypheninferior",
  "Ogoneksmall",
  "Ringsmall",
  "Cedillasmall",
  "questiondownsmall",
  "oneeighth",
  "threeeighths",
  "fiveeighths",
  "seveneighths",
  "onethird",
  "twothirds",
  "zerosuperior",
  "foursuperior",
  "fivesuperior",
  "sixsuperior",
  "sevensuperior",
  "eightsuperior",
  "ninesuperior",
  "zeroinferior",
  "oneinferior",
  "twoinferior",
  "threeinferior",
  "fourinferior",
  "fiveinferior",
  "sixinferior",
  "seveninferior",
  "eightinferior",
  "nineinferior",
  "centinferior",
  "dollarinferior",
  "periodinferior",
  "commainferior",
  "Agravesmall",
  "Aacutesmall",
  "Acircumflexsmall",
  "Atildesmall",
  "Adieresissmall",
  "Aringsmall",
  "AEsmall",
  "Ccedillasmall",
  "Egravesmall",
  "Eacutesmall",
  "Ecircumflexsmall",
  "Edieresissmall",
  "Igravesmall",
  "Iacutesmall",
  "Icircumflexsmall",
  "Idieresissmall",
  "Ethsmall",
  "Ntildesmall",
  "Ogravesmall",
  "Oacutesmall",
  "Ocircumflexsmall",
  "Otildesmall",
  "Odieresissmall",
  "OEsmall",
  "Oslashsmall",
  "Ugravesmall",
  "Uacutesmall",
  "Ucircumflexsmall",
  "Udieresissmall",
  "Yacutesmall",
  "Thornsmall",
  "Ydieresissmall",
  "001.000",
  "001.001",
  "001.002",
  "001.003",
  "Black",
  "Bold",
  "Book",
  "Light",
  "Medium",
  "Regular",
  "Roman",
  "Semibold"
};

Gushort fofiType1CISOAdobeCharset[229] = {
    0,   1,   2,   3,   4,   5,   6,   7,   8,   9,
   10,  11,  12,  13,  14,  15,  16,  17,  18,  19,
   20,  21,  22,  23,  24,  25,  26,  27,  28,  29,
   30,  31,  32,  33,  34,  35,  36,  37,  38,  39,
   40,  41,  42,  43,  44,  45,  46,  47,  48,  49,
   50,  51,  52,  53,  54,  55,  56,  57,  58,  59,
   60,  61,  62,  63,  64,  65,  66,  67,  68,  69,
   70,  71,  72,  73,  74,  75,  76,  77,  78,  79,
   80,  81,  82,  83,  84,  85,  86,  87,  88,  89,
   90,  91,  92,  93,  94,  95,  96,  97,  98,  99,
  100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
  110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
  120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
  130, 131, 132, 133, 134, 135, 136, 137, 138, 139,
  140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
  150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
  160, 161, 162, 163, 164, 165, 166, 167, 168, 169,
  170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
  180, 181, 182, 183, 184, 185, 186, 187, 188, 189,
  190, 191, 192, 193, 194, 195, 196, 197, 198, 199,
  200, 201, 202, 203, 204, 205, 206, 207, 208, 209,
  210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
  220, 221, 222, 223, 224, 225, 226, 227, 228
};

Gushort fofiType1CExpertCharset[166] = {
    0,   1, 229, 230, 231, 232, 233, 234, 235, 236,
  237, 238,  13,  14,  15,  99, 239, 240, 241, 242,
  243, 244, 245, 246, 247, 248,  27,  28, 249, 250,
  251, 252, 253, 254, 255, 256, 257, 258, 259, 260,
  261, 262, 263, 264, 265, 266, 109, 110, 267, 268,
  269, 270, 271, 272, 273, 274, 275, 276, 277, 278,
  279, 280, 281, 282, 283, 284, 285, 286, 287, 288,
  289, 290, 291, 292, 293, 294, 295, 296, 297, 298,
  299, 300, 301, 302, 303, 304, 305, 306, 307, 308,
  309, 310, 311, 312, 313, 314, 315, 316, 317, 318,
  158, 155, 163, 319, 320, 321, 322, 323, 324, 325,
  326, 150, 164, 169, 327, 328, 329, 330, 331, 332,
  333, 334, 335, 336, 337, 338, 339, 340, 341, 342,
  343, 344, 345, 346, 347, 348, 349, 350, 351, 352,
  353, 354, 355, 356, 357, 358, 359, 360, 361, 362,
  363, 364, 365, 366, 367, 368, 369, 370, 371, 372,
  373, 374, 375, 376, 377, 378
};

Gushort fofiType1CExpertSubsetCharset[87] = {
    0,   1, 231, 232, 235, 236, 237, 238,  13,  14,
   15,  99, 239, 240, 241, 242, 243, 244, 245, 246,
  247, 248,  27,  28, 249, 250, 251, 253, 254, 255,
  256, 257, 258, 259, 260, 261, 262, 263, 264, 265,
  266, 109, 110, 267, 268, 269, 270, 272, 300, 301,
  302, 305, 314, 315, 158, 155, 163, 320, 321, 322,
  323, 324, 325, 326, 150, 164, 169, 327, 328, 329,
  330, 331, 332, 333, 334, 335, 336, 337, 338, 339,
  340, 341, 342, 343, 344, 345, 346
};
//========================================================================
//
// FoFiEncodings.h
//
// Copyright 1999-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef FOFIENCODINGS_H
#define FOFIENCODINGS_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "gtypes.h"

//------------------------------------------------------------------------
// Type 1 and 1C font data
//------------------------------------------------------------------------

extern char *fofiType1StandardEncoding[256];
extern char *fofiType1ExpertEncoding[256];

//------------------------------------------------------------------------
// Type 1C font data
//------------------------------------------------------------------------

extern char *fofiType1CStdStrings[391];
extern Gushort fofiType1CISOAdobeCharset[229];
extern Gushort fofiType1CExpertCharset[166];
extern Gushort fofiType1CExpertSubsetCharset[87];

#endif
//========================================================================
//
// FoFiTrueType.cc
//
// Copyright 1999-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stdlib.h>
#include <string.h>
#include "gtypes.h"
#include "gmem.h"
#include "GString.h"
#include "GHash.h"
#include "FoFiType1C.h"
#include "FoFiTrueType.h"

//
// Terminology
// -----------
//
// character code = number used as an element of a text string
//
// character name = glyph name = name for a particular glyph within a
//                  font
//
// glyph index = GID = position (within some internal table in the font)
//               where the instructions to draw a particular glyph are
//               stored
//
// Type 1 fonts
// ------------
//
// Type 1 fonts contain:
//
// Encoding: array of glyph names, maps char codes to glyph names
//
//           Encoding[charCode] = charName
//
// CharStrings: dictionary of instructions, keyed by character names,
//              maps character name to glyph data
//
//              CharStrings[charName] = glyphData
//
// TrueType fonts
// --------------
//
// TrueType fonts contain:
//
// 'cmap' table: mapping from character code to glyph index; there may
//               be multiple cmaps in a TrueType font
//
//               cmap[charCode] = gid
//
// 'post' table: mapping from glyph index to glyph name
//
//               post[gid] = glyphName
//
// Type 42 fonts
// -------------
//
// Type 42 fonts contain:
//
// Encoding: array of glyph names, maps char codes to glyph names
//
//           Encoding[charCode] = charName
//
// CharStrings: dictionary of glyph indexes, keyed by character names,
//              maps character name to glyph index
//
//              CharStrings[charName] = gid
//

//------------------------------------------------------------------------

#define ttcfTag 0x74746366

//------------------------------------------------------------------------

struct TrueTypeTable {
  Guint tag;
  Guint checksum;
  int offset;
  int origOffset;
  int len;
};

struct TrueTypeCmap {
  int platform;
  int encoding;
  int offset;
  int len;
  int fmt;
};

struct TrueTypeLoca {
  int idx;
  int origOffset;
  int newOffset;
  int len;
};

#define cmapTag 0x636d6170
#define glyfTag 0x676c7966
#define headTag 0x68656164
#define hheaTag 0x68686561
#define hmtxTag 0x686d7478
#define locaTag 0x6c6f6361
#define nameTag 0x6e616d65
#define os2Tag  0x4f532f32
#define postTag 0x706f7374

static int cmpTrueTypeLocaOffset(const void *p1, const void *p2) {
  TrueTypeLoca *loca1 = (TrueTypeLoca *)p1;
  TrueTypeLoca *loca2 = (TrueTypeLoca *)p2;

  if (loca1->origOffset == loca2->origOffset) {
    return loca1->idx - loca2->idx;
  }
  return loca1->origOffset - loca2->origOffset;
}

static int cmpTrueTypeLocaIdx(const void *p1, const void *p2) {
  TrueTypeLoca *loca1 = (TrueTypeLoca *)p1;
  TrueTypeLoca *loca2 = (TrueTypeLoca *)p2;

  return loca1->idx - loca2->idx;
}

static int cmpTrueTypeTableTag(const void *p1, const void *p2) {
  TrueTypeTable *tab1 = (TrueTypeTable *)p1;
  TrueTypeTable *tab2 = (TrueTypeTable *)p2;

  return (int)tab1->tag - (int)tab2->tag;
}

//------------------------------------------------------------------------

struct T42Table {
  char *tag;			// 4-byte tag
  GBool required;		// required by the TrueType spec?
};

// TrueType tables to be embedded in Type 42 fonts.
// NB: the table names must be in alphabetical order here.
#define nT42Tables 11
static T42Table t42Tables[nT42Tables] = {
  { "cvt ", gTrue  },
  { "fpgm", gTrue  },
  { "glyf", gTrue  },
  { "head", gTrue  },
  { "hhea", gTrue  },
  { "hmtx", gTrue  },
  { "loca", gTrue  },
  { "maxp", gTrue  },
  { "prep", gTrue  },
  { "vhea", gFalse },
  { "vmtx", gFalse }
};
#define t42HeadTable  3
#define t42LocaTable  6
#define t42GlyfTable  2
#define t42VheaTable  9
#define t42VmtxTable 10

//------------------------------------------------------------------------

// Glyph names in some arbitrary standard order that Apple uses for
// their TrueType fonts.
static char *macGlyphNames[258] = {
  ".notdef",        "null",           "CR",             "space",
  "exclam",         "quotedbl",       "numbersign",     "dollar",
  "percent",        "ampersand",      "quotesingle",    "parenleft",
  "parenright",     "asterisk",       "plus",           "comma",
  "hyphen",         "period",         "slash",          "zero",
  "one",            "two",            "three",          "four",
  "five",           "six",            "seven",          "eight",
  "nine",           "colon",          "semicolon",      "less",
  "equal",          "greater",        "question",       "at",
  "A",              "B",              "C",              "D",
  "E",              "F",              "G",              "H",
  "I",              "J",              "K",              "L",
  "M",              "N",              "O",              "P",
  "Q",              "R",              "S",              "T",
  "U",              "V",              "W",              "X",
  "Y",              "Z",              "bracketleft",    "backslash",
  "bracketright",   "asciicircum",    "underscore",     "grave",
  "a",              "b",              "c",              "d",
  "e",              "f",              "g",              "h",
  "i",              "j",              "k",              "l",
  "m",              "n",              "o",              "p",
  "q",              "r",              "s",              "t",
  "u",              "v",              "w",              "x",
  "y",              "z",              "braceleft",      "bar",
  "braceright",     "asciitilde",     "Adieresis",      "Aring",
  "Ccedilla",       "Eacute",         "Ntilde",         "Odieresis",
  "Udieresis",      "aacute",         "agrave",         "acircumflex",
  "adieresis",      "atilde",         "aring",          "ccedilla",
  "eacute",         "egrave",         "ecircumflex",    "edieresis",
  "iacute",         "igrave",         "icircumflex",    "idieresis",
  "ntilde",         "oacute",         "ograve",         "ocircumflex",
  "odieresis",      "otilde",         "uacute",         "ugrave",
  "ucircumflex",    "udieresis",      "dagger",         "degree",
  "cent",           "sterling",       "section",        "bullet",
  "paragraph",      "germandbls",     "registered",     "copyright",
  "trademark",      "acute",          "dieresis",       "notequal",
  "AE",             "Oslash",         "infinity",       "plusminus",
  "lessequal",      "greaterequal",   "yen",            "mu1",
  "partialdiff",    "summation",      "product",        "pi",
  "integral",       "ordfeminine",    "ordmasculine",   "Ohm",
  "ae",             "oslash",         "questiondown",   "exclamdown",
  "logicalnot",     "radical",        "florin",         "approxequal",
  "increment",      "guillemotleft",  "guillemotright", "ellipsis",
  "nbspace",        "Agrave",         "Atilde",         "Otilde",
  "OE",             "oe",             "endash",         "emdash",
  "quotedblleft",   "quotedblright",  "quoteleft",      "quoteright",
  "divide",         "lozenge",        "ydieresis",      "Ydieresis",
  "fraction",       "currency",       "guilsinglleft",  "guilsinglright",
  "fi",             "fl",             "daggerdbl",      "periodcentered",
  "quotesinglbase", "quotedblbase",   "perthousand",    "Acircumflex",
  "Ecircumflex",    "Aacute",         "Edieresis",      "Egrave",
  "Iacute",         "Icircumflex",    "Idieresis",      "Igrave",
  "Oacute",         "Ocircumflex",    "applelogo",      "Ograve",
  "Uacute",         "Ucircumflex",    "Ugrave",         "dotlessi",
  "circumflex",     "tilde",          "overscore",      "breve",
  "dotaccent",      "ring",           "cedilla",        "hungarumlaut",
  "ogonek",         "caron",          "Lslash",         "lslash",
  "Scaron",         "scaron",         "Zcaron",         "zcaron",
  "brokenbar",      "Eth",            "eth",            "Yacute",
  "yacute",         "Thorn",          "thorn",          "minus",
  "multiply",       "onesuperior",    "twosuperior",    "threesuperior",
  "onehalf",        "onequarter",     "threequarters",  "franc",
  "Gbreve",         "gbreve",         "Idot",           "Scedilla",
  "scedilla",       "Cacute",         "cacute",         "Ccaron",
  "ccaron",         "dmacron"
};

//------------------------------------------------------------------------
// FoFiTrueType
//------------------------------------------------------------------------

FoFiTrueType *FoFiTrueType::make(char *fileA, int lenA) {
  FoFiTrueType *ff;

  ff = new FoFiTrueType(fileA, lenA, gFalse);
  if (!ff->parsedOk) {
    delete ff;
    return NULL;
  }
  return ff;
}

FoFiTrueType *FoFiTrueType::load(char *fileName) {
  FoFiTrueType *ff;
  char *fileA;
  int lenA;

  if (!(fileA = FoFiBase::readFile(fileName, &lenA))) {
    return NULL;
  }
  ff = new FoFiTrueType(fileA, lenA, gTrue);
  if (!ff->parsedOk) {
    delete ff;
    return NULL;
  }
  return ff;
}

FoFiTrueType::FoFiTrueType(char *fileA, int lenA, GBool freeFileDataA):
  FoFiBase(fileA, lenA, freeFileDataA)
{
  tables = NULL;
  nTables = 0;
  cmaps = NULL;
  nCmaps = 0;
  nameToGID = NULL;
  parsedOk = gFalse;

  parse();
}

FoFiTrueType::~FoFiTrueType() {
  gfree(tables);
  gfree(cmaps);
  if (nameToGID) {
    delete nameToGID;
  }
}

int FoFiTrueType::getNumCmaps() {
  return nCmaps;
}

int FoFiTrueType::getCmapPlatform(int i) {
  return cmaps[i].platform;
}

int FoFiTrueType::getCmapEncoding(int i) {
  return cmaps[i].encoding;
}

int FoFiTrueType::findCmap(int platform, int encoding) {
  int i;

  for (i = 0; i < nCmaps; ++i) {
    if (cmaps[i].platform == platform && cmaps[i].encoding == encoding) {
      return i;
    }
  }
  return -1;
}

Gushort FoFiTrueType::mapCodeToGID(int i, int c) {
  Gushort gid;
  int segCnt, segEnd, segStart, segDelta, segOffset;
  int cmapFirst, cmapLen;
  int pos, a, b, m;
  GBool ok;

  if (i < 0 || i >= nCmaps) {
    return 0;
  }
  ok = gTrue;
  pos = cmaps[i].offset;
  switch (cmaps[i].fmt) {
  case 0:
    if (c < 0 || c >= cmaps[i].len - 6) {
      return 0;
    }
    gid = getU8(cmaps[i].offset + 6 + c, &ok);
    break;
  case 4:
    segCnt = getU16BE(pos + 6, &ok) / 2;
    a = -1;
    b = segCnt - 1;
    segEnd = getU16BE(pos + 14 + 2*b, &ok);
    if (c > segEnd) {
      // malformed font -- the TrueType spec requires the last segEnd
      // to be 0xffff
      return 0;
    }
    // invariant: seg[a].end < code <= seg[b].end
    while (b - a > 1 && ok) {
      m = (a + b) / 2;
      segEnd = getU16BE(pos + 14 + 2*m, &ok);
      if (segEnd < c) {
	a = m;
      } else {
	b = m;
      }
    }
    segStart = getU16BE(pos + 16 + 2*segCnt + 2*b, &ok);
    segDelta = getU16BE(pos + 16 + 4*segCnt + 2*b, &ok);
    segOffset = getU16BE(pos + 16 + 6*segCnt + 2*b, &ok);
    if (c < segStart) {
      return 0;
    }
    if (segOffset == 0) {
      gid = (c + segDelta) & 0xffff;
    } else {
      gid = getU16BE(pos + 16 + 6*segCnt + 2*b +
		       segOffset + 2 * (c - segStart), &ok);
      if (gid != 0) {
	gid = (gid + segDelta) & 0xffff;
      }
    }
    break;
  case 6:
    cmapFirst = getU16BE(pos + 6, &ok);
    cmapLen = getU16BE(pos + 8, &ok);
    if (c < cmapFirst || c >= cmapFirst + cmapLen) {
      return 0;
    }
    gid = getU16BE(pos + 10 + 2 * (c - cmapFirst), &ok);
    break;
  default:
    return 0;
  }
  if (!ok) {
    return 0;
  }
  return gid;
}

int FoFiTrueType::mapNameToGID(char *name) {
  if (!nameToGID) {
    return 0;
  }
  return nameToGID->lookupInt(name);
}

Gushort *FoFiTrueType::getCIDToGIDMap(int *nCIDs) {
  FoFiType1C *ff;
  Gushort *map;
  int i;

  *nCIDs = 0;
  if (!openTypeCFF) {
    return NULL;
  }
  i = seekTable("CFF ");
  if (!checkRegion(tables[i].offset, tables[i].len)) {
    return NULL;
  }
  if (!(ff = FoFiType1C::make((char *)file + tables[i].offset,
			      tables[i].len))) {
    return NULL;
  }
  map = ff->getCIDToGIDMap(nCIDs);
  delete ff;
  return map;
}

int FoFiTrueType::getEmbeddingRights() {
  int i, fsType;
  GBool ok;

  if ((i = seekTable("OS/2")) < 0) {
    return 4;
  }
  ok = gTrue;
  fsType = getU16BE(tables[i].offset + 8, &ok);
  if (!ok) {
    return 4;
  }
  if (fsType & 0x0008) {
    return 2;
  }
  if (fsType & 0x0004) {
    return 1;
  }
  if (fsType & 0x0002) {
    return 0;
  }
  return 3;
}

void FoFiTrueType::convertToType42(char *psName, char **encoding,
				   Gushort *codeToGID,
				   FoFiOutputFunc outputFunc,
				   void *outputStream) {
  GString *buf;
  GBool ok;

  if (openTypeCFF) {
    return;
  }

  // write the header
  ok = gTrue;
  buf = GString::format("%!PS-TrueTypeFont-{0:2g}\n",
			(double)getS32BE(0, &ok) / 65536.0);
  (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
  delete buf;

  // begin the font dictionary
  (*outputFunc)(outputStream, "10 dict begin\n", 14);
  (*outputFunc)(outputStream, "/FontName /", 11);
  (*outputFunc)(outputStream, psName, strlen(psName));
  (*outputFunc)(outputStream, " def\n", 5);
  (*outputFunc)(outputStream, "/FontType 42 def\n", 17);
  (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
  buf = GString::format("/FontBBox [{0:d} {1:d} {2:d} {3:d}] def\n",
			bbox[0], bbox[1], bbox[2], bbox[3]);
  (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
  delete buf;
  (*outputFunc)(outputStream, "/PaintType 0 def\n", 17);

  // write the guts of the dictionary
  cvtEncoding(encoding, outputFunc, outputStream);
  cvtCharStrings(encoding, codeToGID, outputFunc, outputStream);
  cvtSfnts(outputFunc, outputStream, NULL, gFalse);

  // end the dictionary and define the font
  (*outputFunc)(outputStream, "FontName currentdict end definefont pop\n", 40);
}

void FoFiTrueType::convertToType1(char *psName, char **newEncoding,
				  GBool ascii, FoFiOutputFunc outputFunc,
				  void *outputStream) {
  FoFiType1C *ff;
  int i;

  if (!openTypeCFF) {
    return;
  }
  i = seekTable("CFF ");
  if (!checkRegion(tables[i].offset, tables[i].len)) {
    return;
  }
  if (!(ff = FoFiType1C::make((char *)file + tables[i].offset,
			      tables[i].len))) {
    return;
  }
  ff->convertToType1(psName, newEncoding, ascii, outputFunc, outputStream);
  delete ff;
}

void FoFiTrueType::convertToCIDType2(char *psName,
				     Gushort *cidMap, int nCIDs,
				     GBool needVerticalMetrics,
				     FoFiOutputFunc outputFunc,
				     void *outputStream) {
  GString *buf;
  Gushort cid;
  GBool ok;
  int i, j, k;

  if (openTypeCFF) {
    return;
  }

  // write the header
  ok = gTrue;
  buf = GString::format("%!PS-TrueTypeFont-{0:2g}\n",
			(double)getS32BE(0, &ok) / 65536.0);
  (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
  delete buf;

  // begin the font dictionary
  (*outputFunc)(outputStream, "20 dict begin\n", 14);
  (*outputFunc)(outputStream, "/CIDFontName /", 14);
  (*outputFunc)(outputStream, psName, strlen(psName));
  (*outputFunc)(outputStream, " def\n", 5);
  (*outputFunc)(outputStream, "/CIDFontType 2 def\n", 19);
  (*outputFunc)(outputStream, "/FontType 42 def\n", 17);
  (*outputFunc)(outputStream, "/CIDSystemInfo 3 dict dup begin\n", 32);
  (*outputFunc)(outputStream, "  /Registry (Adobe) def\n", 24);
  (*outputFunc)(outputStream, "  /Ordering (Identity) def\n", 27);
  (*outputFunc)(outputStream, "  /Supplement 0 def\n", 20);
  (*outputFunc)(outputStream, "  end def\n", 10);
  (*outputFunc)(outputStream, "/GDBytes 2 def\n", 15);
  if (cidMap) {
    buf = GString::format("/CIDCount {0:d} def\n", nCIDs);
    (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
    delete buf;
    if (nCIDs > 32767) {
      (*outputFunc)(outputStream, "/CIDMap [", 9);
      for (i = 0; i < nCIDs; i += 32768 - 16) {
	(*outputFunc)(outputStream, "<\n", 2);
	for (j = 0; j < 32768 - 16 && i+j < nCIDs; j += 16) {
	  (*outputFunc)(outputStream, "  ", 2);
	  for (k = 0; k < 16 && i+j+k < nCIDs; ++k) {
	    cid = cidMap[i+j+k];
	    buf = GString::format("{0:02x}{1:02x}",
				  (cid >> 8) & 0xff, cid & 0xff);
	    (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
	    delete buf;
	  }
	  (*outputFunc)(outputStream, "\n", 1);
	}
	(*outputFunc)(outputStream, "  >", 3);
      }
      (*outputFunc)(outputStream, "\n", 1);
      (*outputFunc)(outputStream, "] def\n", 6);
    } else {
      (*outputFunc)(outputStream, "/CIDMap <\n", 10);
      for (i = 0; i < nCIDs; i += 16) {
	(*outputFunc)(outputStream, "  ", 2);
	for (j = 0; j < 16 && i+j < nCIDs; ++j) {
	  cid = cidMap[i+j];
	  buf = GString::format("{0:02x}{1:02x}",
				(cid >> 8) & 0xff, cid & 0xff);
	  (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
	  delete buf;
	}
	(*outputFunc)(outputStream, "\n", 1);
      }
      (*outputFunc)(outputStream, "> def\n", 6);
    }
  } else {
    // direct mapping - just fill the string(s) with s[i]=i
    buf = GString::format("/CIDCount {0:d} def\n", nGlyphs);
    (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
    delete buf;
    if (nGlyphs > 32767) {
      (*outputFunc)(outputStream, "/CIDMap [\n", 10);
      for (i = 0; i < nGlyphs; i += 32767) {
	j = nGlyphs - i < 32767 ? nGlyphs - i : 32767;
	buf = GString::format("  {0:d} string 0 1 {1:d} {{\n", 2 * j, j - 1);
	(*outputFunc)(outputStream, buf->getCString(), buf->getLength());
	delete buf;
	buf = GString::format("    2 copy dup 2 mul exch {0:d} add -8 bitshift put\n",
			      i);
	(*outputFunc)(outputStream, buf->getCString(), buf->getLength());
	delete buf;
	buf = GString::format("    1 index exch dup 2 mul 1 add exch {0:d} add"
			      " 255 and put\n", i);
	(*outputFunc)(outputStream, buf->getCString(), buf->getLength());
	delete buf;
	(*outputFunc)(outputStream, "  } for\n", 8);
      }
      (*outputFunc)(outputStream, "] def\n", 6);
    } else {
      buf = GString::format("/CIDMap {0:d} string\n", 2 * nGlyphs);
      (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
      delete buf;
      buf = GString::format("  0 1 {0:d} {{\n", nGlyphs - 1);
      (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
      delete buf;
      (*outputFunc)(outputStream,
		    "    2 copy dup 2 mul exch -8 bitshift put\n", 42);
      (*outputFunc)(outputStream,
		    "    1 index exch dup 2 mul 1 add exch 255 and put\n", 50);
      (*outputFunc)(outputStream, "  } for\n", 8);
      (*outputFunc)(outputStream, "def\n", 4);
    }
  }
  (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
  buf = GString::format("/FontBBox [{0:d} {1:d} {2:d} {3:d}] def\n",
			bbox[0], bbox[1], bbox[2], bbox[3]);
  (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
  delete buf;
  (*outputFunc)(outputStream, "/PaintType 0 def\n", 17);
  (*outputFunc)(outputStream, "/Encoding [] readonly def\n", 26);
  (*outputFunc)(outputStream, "/CharStrings 1 dict dup begin\n", 30);
  (*outputFunc)(outputStream, "  /.notdef 0 def\n", 17);
  (*outputFunc)(outputStream, "  end readonly def\n", 19);

  // write the guts of the dictionary
  cvtSfnts(outputFunc, outputStream, NULL, needVerticalMetrics);

  // end the dictionary and define the font
  (*outputFunc)(outputStream,
		"CIDFontName currentdict end /CIDFont defineresource pop\n",
		56);
}

void FoFiTrueType::convertToCIDType0(char *psName,
				     FoFiOutputFunc outputFunc,
				     void *outputStream) {
  FoFiType1C *ff;
  int i;

  if (!openTypeCFF) {
    return;
  }
  i = seekTable("CFF ");
  if (!checkRegion(tables[i].offset, tables[i].len)) {
    return;
  }
  if (!(ff = FoFiType1C::make((char *)file + tables[i].offset,
			      tables[i].len))) {
    return;
  }
  ff->convertToCIDType0(psName, outputFunc, outputStream);
  delete ff;
}

void FoFiTrueType::convertToType0(char *psName, Gushort *cidMap, int nCIDs,
				  GBool needVerticalMetrics,
				  FoFiOutputFunc outputFunc,
				  void *outputStream) {
  GString *buf;
  GString *sfntsName;
  int n, i, j;

  if (openTypeCFF) {
    return;
  }

  // write the Type 42 sfnts array
  sfntsName = (new GString(psName))->append("_sfnts");
  cvtSfnts(outputFunc, outputStream, sfntsName, needVerticalMetrics);
  delete sfntsName;

  // write the descendant Type 42 fonts
  n = cidMap ? nCIDs : nGlyphs;
  for (i = 0; i < n; i += 256) {
    (*outputFunc)(outputStream, "10 dict begin\n", 14);
    (*outputFunc)(outputStream, "/FontName /", 11);
    (*outputFunc)(outputStream, psName, strlen(psName));
    buf = GString::format("_{0:02x} def\n", i >> 8);
    (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
    delete buf;
    (*outputFunc)(outputStream, "/FontType 42 def\n", 17);
    (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
    buf = GString::format("/FontBBox [{0:d} {1:d} {2:d} {3:d}] def\n",
			  bbox[0], bbox[1], bbox[2], bbox[3]);
    (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
    delete buf;
    (*outputFunc)(outputStream, "/PaintType 0 def\n", 17);
    (*outputFunc)(outputStream, "/sfnts ", 7);
    (*outputFunc)(outputStream, psName, strlen(psName));
    (*outputFunc)(outputStream, "_sfnts def\n", 11);
    (*outputFunc)(outputStream, "/Encoding 256 array\n", 20);
    for (j = 0; j < 256 && i+j < n; ++j) {
      buf = GString::format("dup {0:d} /c{1:02x} put\n", j, j);
      (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
      delete buf;
    }
    (*outputFunc)(outputStream, "readonly def\n", 13);
    (*outputFunc)(outputStream, "/CharStrings 257 dict dup begin\n", 32);
    (*outputFunc)(outputStream, "/.notdef 0 def\n", 15);
    for (j = 0; j < 256 && i+j < n; ++j) {
      buf = GString::format("/c{0:02x} {1:d} def\n",
			    j, cidMap ? cidMap[i+j] : i+j);
      (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
      delete buf;
    }
    (*outputFunc)(outputStream, "end readonly def\n", 17);
    (*outputFunc)(outputStream,
		  "FontName currentdict end definefont pop\n", 40);
  }

  // write the Type 0 parent font
  (*outputFunc)(outputStream, "16 dict begin\n", 14);
  (*outputFunc)(outputStream, "/FontName /", 11);
  (*outputFunc)(outputStream, psName, strlen(psName));
  (*outputFunc)(outputStream, " def\n", 5);
  (*outputFunc)(outputStream, "/FontType 0 def\n", 16);
  (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
  (*outputFunc)(outputStream, "/FMapType 2 def\n", 16);
  (*outputFunc)(outputStream, "/Encoding [\n", 12);
  for (i = 0; i < n; i += 256) {
    buf = GString::format("{0:d}\n", i >> 8);
    (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
    delete buf;
  }
  (*outputFunc)(outputStream, "] def\n", 6);
  (*outputFunc)(outputStream, "/FDepVector [\n", 14);
  for (i = 0; i < n; i += 256) {
    (*outputFunc)(outputStream, "/", 1);
    (*outputFunc)(outputStream, psName, strlen(psName));
    buf = GString::format("_{0:02x} findfont\n", i >> 8);
    (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
    delete buf;
  }
  (*outputFunc)(outputStream, "] def\n", 6);
  (*outputFunc)(outputStream, "FontName currentdict end definefont pop\n", 40);
}

void FoFiTrueType::convertToType0(char *psName,
				  FoFiOutputFunc outputFunc,
				  void *outputStream) {
  FoFiType1C *ff;
  int i;

  if (!openTypeCFF) {
    return;
  }
  i = seekTable("CFF ");
  if (!checkRegion(tables[i].offset, tables[i].len)) {
    return;
  }
  if (!(ff = FoFiType1C::make((char *)file + tables[i].offset,
			      tables[i].len))) {
    return;
  }
  ff->convertToType0(psName, outputFunc, outputStream);
  delete ff;
}

void FoFiTrueType::writeTTF(FoFiOutputFunc outputFunc,
			    void *outputStream, char *name,
			    Gushort *codeToGID) {
  // this substitute cmap table maps char codes 0000-ffff directly to
  // glyphs 0000-ffff
  static char cmapTab[36] = {
    0, 0,			// table version number
    0, 1,			// number of encoding tables
    0, 1,			// platform ID
    0, 0,			// encoding ID
    0, 0, 0, 12,		// offset of subtable
    0, 4,			// subtable format
    0, 24,			// subtable length
    0, 0,			// subtable version
    0, 2,			// segment count * 2
    0, 2,			// 2 * 2 ^ floor(log2(segCount))
    0, 0,			// floor(log2(segCount))
    0, 0,			// 2*segCount - 2*2^floor(log2(segCount))
    (char)0xff, (char)0xff,	// endCount[0]
    0, 0,			// reserved
    0, 0,			// startCount[0]
    0, 0,			// idDelta[0]
    0, 0			// pad to a mulitple of four bytes
  };
  static char nameTab[8] = {
    0, 0,			// format
    0, 0,			// number of name records
    0, 6,			// offset to start of string storage
    0, 0			// pad to multiple of four bytes
  };
  static char postTab[32] = {
    0, 1, 0, 0,			// format
    0, 0, 0, 0,			// italic angle
    0, 0,			// underline position
    0, 0,			// underline thickness
    0, 0, 0, 0,			// fixed pitch
    0, 0, 0, 0,			// min Type 42 memory
    0, 0, 0, 0,			// max Type 42 memory
    0, 0, 0, 0,			// min Type 1 memory
    0, 0, 0, 0			// max Type 1 memory
  };
  static char os2Tab[86] = {
    0, 1,			// version
    0, 1,			// xAvgCharWidth
    0, 0,			// usWeightClass
    0, 0,			// usWidthClass
    0, 0,			// fsType
    0, 0,			// ySubscriptXSize
    0, 0,			// ySubscriptYSize
    0, 0,			// ySubscriptXOffset
    0, 0,			// ySubscriptYOffset
    0, 0,			// ySuperscriptXSize
    0, 0,			// ySuperscriptYSize
    0, 0,			// ySuperscriptXOffset
    0, 0,			// ySuperscriptYOffset
    0, 0,			// yStrikeoutSize
    0, 0,			// yStrikeoutPosition
    0, 0,			// sFamilyClass
    0, 0, 0, 0, 0,		// panose
    0, 0, 0, 0, 0,
    0, 0, 0, 0,			// ulUnicodeRange1
    0, 0, 0, 0,			// ulUnicodeRange2
    0, 0, 0, 0,			// ulUnicodeRange3
    0, 0, 0, 0,			// ulUnicodeRange4
    0, 0, 0, 0,			// achVendID
    0, 0,			// fsSelection
    0, 0,			// usFirstCharIndex
    0, 0,			// usLastCharIndex
    0, 0,			// sTypoAscender
    0, 0,			// sTypoDescender
    0, 0,			// sTypoLineGap
    0, 0,			// usWinAscent
    0, 0,			// usWinDescent
    0, 0, 0, 0,			// ulCodePageRange1
    0, 0, 0, 0			// ulCodePageRange2
  };
  GBool missingCmap, missingName, missingPost, missingOS2;
  GBool unsortedLoca, badCmapLen, abbrevHMTX;
  int nZeroLengthTables;
  int nHMetrics, advWidth, lsb;
  TrueTypeLoca *locaTable;
  TrueTypeTable *newTables;
  char *newNameTab, *newCmapTab, *newHHEATab, *newHMTXTab;
  int nNewTables, cmapIdx, cmapLen, glyfLen, newNameLen, newCmapLen, next;
  int newHHEALen, newHMTXLen;
  Guint locaChecksum, glyfChecksum, fileChecksum;
  char *tableDir;
  char locaBuf[4], checksumBuf[4];
  GBool ok;
  Guint t;
  int pos, i, j, k, n;

  if (openTypeCFF) {
    return;
  }

  // check for missing tables
  // (Note: if the OS/2 table is missing, the Microsoft PCL5 driver
  // will embed a PCL TrueType font with the pitch field set to zero,
  // which apparently causes divide-by-zero errors.  As far as I can
  // tell, the only important field in the OS/2 table is
  // xAvgCharWidth.)
  missingCmap = (cmapIdx = seekTable("cmap")) < 0;
  missingName = seekTable("name") < 0;
  missingPost = seekTable("post") < 0;
  missingOS2 = seekTable("OS/2") < 0;

  // read the loca table, check to see if it's sorted
  locaTable = (TrueTypeLoca *)gmallocn(nGlyphs + 1, sizeof(TrueTypeLoca));
  unsortedLoca = gFalse;
  i = seekTable("loca");
  pos = tables[i].offset;
  ok = gTrue;
  for (i = 0; i <= nGlyphs; ++i) {
    if (locaFmt) {
      locaTable[i].origOffset = (int)getU32BE(pos + i*4, &ok);
    } else {
      locaTable[i].origOffset = 2 * getU16BE(pos + i*2, &ok);
    }
    if (i > 0 && locaTable[i].origOffset < locaTable[i-1].origOffset) {
      unsortedLoca = gTrue;
    }
    // glyph descriptions must be at least 12 bytes long (nContours,
    // xMin, yMin, xMax, yMax, instructionLength - two bytes each);
    // invalid glyph descriptions (even if they're never used) make
    // Windows choke, so we work around that problem here (ideally,
    // this would parse the glyph descriptions in the glyf table and
    // remove any that were invalid, but this quick test is a decent
    // start)
    if (i > 0 &&
	locaTable[i].origOffset - locaTable[i-1].origOffset > 0 &&
	locaTable[i].origOffset - locaTable[i-1].origOffset < 12) {
      locaTable[i-1].origOffset = locaTable[i].origOffset;
      unsortedLoca = gTrue;
    }
    locaTable[i].idx = i;
  }

  // check for zero-length tables
  nZeroLengthTables = 0;
  for (i = 0; i < nTables; ++i) {
    if (tables[i].len == 0) {
      ++nZeroLengthTables;
    }
  }

  // check for an incorrect cmap table length
  badCmapLen = gFalse;
  cmapLen = 0; // make gcc happy
  if (!missingCmap) {
    cmapLen = cmaps[0].offset + cmaps[0].len;
    for (i = 1; i < nCmaps; ++i) {
      if (cmaps[i].offset + cmaps[i].len > cmapLen) {
	cmapLen = cmaps[i].offset + cmaps[i].len;
      }
    }
    cmapLen -= tables[cmapIdx].offset;
    if (cmapLen > tables[cmapIdx].len) {
      badCmapLen = gTrue;
    }
  }

  // check for an abbreviated hmtx table (this is completely legal,
  // but confuses the Microsoft PCL5 printer driver, which generates
  // embedded fonts with the pitch field set to zero)
  i = seekTable("hhea");
  nHMetrics = getU16BE(tables[i].offset + 34, &ok);
  abbrevHMTX = nHMetrics < nGlyphs;

  // if nothing is broken, just write the TTF file as is
  if (!missingCmap && !missingName && !missingPost && !missingOS2 &&
      !unsortedLoca && !badCmapLen && !abbrevHMTX && nZeroLengthTables == 0 &&
      !name && !codeToGID) {
    (*outputFunc)(outputStream, (char *)file, len);
    goto done1;
  }

  // sort the 'loca' table: some (non-compliant) fonts have
  // out-of-order loca tables; in order to correctly handle the case
  // where (compliant) fonts have empty entries in the middle of the
  // table, cmpTrueTypeLocaOffset uses offset as its primary sort key,
  // and idx as its secondary key (ensuring that adjacent entries with
  // the same pos value remain in the same order)
  glyfLen = 0; // make gcc happy
  if (unsortedLoca) {
    qsort(locaTable, nGlyphs + 1, sizeof(TrueTypeLoca),
	  &cmpTrueTypeLocaOffset);
    for (i = 0; i < nGlyphs; ++i) {
      locaTable[i].len = locaTable[i+1].origOffset - locaTable[i].origOffset;
    }
    locaTable[nGlyphs].len = 0;
    qsort(locaTable, nGlyphs + 1, sizeof(TrueTypeLoca),
	  &cmpTrueTypeLocaIdx);
    pos = 0;
    for (i = 0; i <= nGlyphs; ++i) {
      locaTable[i].newOffset = pos;
      pos += locaTable[i].len;
      if (pos & 3) {
	pos += 4 - (pos & 3);
      }
    }
    glyfLen = pos;
  }

  // compute checksums for the loca and glyf tables
  locaChecksum = glyfChecksum = 0;
  if (unsortedLoca) {
    if (locaFmt) {
      for (j = 0; j <= nGlyphs; ++j) {
	locaChecksum += locaTable[j].newOffset;
      }
    } else {
      for (j = 0; j <= nGlyphs; j += 2) {
	locaChecksum += locaTable[j].newOffset << 16;
	if (j + 1 <= nGlyphs) {
	  locaChecksum += locaTable[j+1].newOffset;
	}
      }
    }
    pos = tables[seekTable("glyf")].offset;
    for (j = 0; j < nGlyphs; ++j) {
      n = locaTable[j].len;
      if (n > 0) {
	k = locaTable[j].origOffset;
	if (checkRegion(pos + k, n)) {
	  glyfChecksum += computeTableChecksum(file + pos + k, n);
	}
      }
    }
  }

  // construct the new name table
  if (name) {
    n = strlen(name);
    newNameLen = (6 + 4*12 + 2 * (3*n + 7) + 3) & ~3;
    newNameTab = (char *)gmalloc(newNameLen);
    memset(newNameTab, 0, newNameLen);
    newNameTab[0] = 0;		// format selector
    newNameTab[1] = 0;
    newNameTab[2] = 0;		// number of name records
    newNameTab[3] = 4;
    newNameTab[4] = 0;		// offset to start of string storage
    newNameTab[5] = 6 + 4*12;
    next = 0;
    for (i = 0; i < 4; ++i) {
      newNameTab[6 + i*12 + 0] = 0;	// platform ID = Microsoft
      newNameTab[6 + i*12 + 1] = 3;
      newNameTab[6 + i*12 + 2] = 0;	// encoding ID = Unicode
      newNameTab[6 + i*12 + 3] = 1;
      newNameTab[6 + i*12 + 4] = 0x04;	// language ID = American English
      newNameTab[6 + i*12 + 5] = 0x09;
      newNameTab[6 + i*12 + 6] = 0;	// name ID
      newNameTab[6 + i*12 + 7] = i + 1;
      newNameTab[6 + i*12 + 8] = i+1 == 2 ? 0 : ((2*n) >> 8); // string length
      newNameTab[6 + i*12 + 9] = i+1 == 2 ? 14 : ((2*n) & 0xff);
      newNameTab[6 + i*12 + 10] = next >> 8;		    // string offset
      newNameTab[6 + i*12 + 11] = next & 0xff;
      if (i+1 == 2) {
	memcpy(newNameTab + 6 + 4*12 + next, "\0R\0e\0g\0u\0l\0a\0r", 14);
	next += 14;
      } else {
	for (j = 0; j < n; ++j) {
	  newNameTab[6 + 4*12 + next + 2*j] = 0;
	  newNameTab[6 + 4*12 + next + 2*j + 1] = name[j];
	}
	next += 2*n;
      }
    }
  } else {
    newNameLen = 0;
    newNameTab = NULL;
  }

  // construct the new cmap table
  if (codeToGID) {
    newCmapLen = 44 + 256 * 2;
    newCmapTab = (char *)gmalloc(newCmapLen);
    newCmapTab[0] = 0;		// table version number = 0
    newCmapTab[1] = 0;
    newCmapTab[2] = 0;		// number of encoding tables = 1
    newCmapTab[3] = 1;
    newCmapTab[4] = 0;		// platform ID = Microsoft
    newCmapTab[5] = 3;
    newCmapTab[6] = 0;		// encoding ID = Unicode
    newCmapTab[7] = 1;
    newCmapTab[8] = 0;		// offset of subtable
    newCmapTab[9] = 0;
    newCmapTab[10] = 0;
    newCmapTab[11] = 12;
    newCmapTab[12] = 0;		// subtable format = 4
    newCmapTab[13] = 4;
    newCmapTab[14] = 0x02;	// subtable length
    newCmapTab[15] = 0x20;
    newCmapTab[16] = 0;		// subtable version = 0
    newCmapTab[17] = 0;
    newCmapTab[18] = 0;		// segment count * 2
    newCmapTab[19] = 4;
    newCmapTab[20] = 0;		// 2 * 2 ^ floor(log2(segCount))
    newCmapTab[21] = 4;
    newCmapTab[22] = 0;		// floor(log2(segCount))
    newCmapTab[23] = 1;
    newCmapTab[24] = 0;		// 2*segCount - 2*2^floor(log2(segCount))
    newCmapTab[25] = 0;
    newCmapTab[26] = 0x00;	// endCount[0]
    newCmapTab[27] = (char)0xff;
    newCmapTab[28] = (char)0xff; // endCount[1]
    newCmapTab[29] = (char)0xff;
    newCmapTab[30] = 0;		// reserved
    newCmapTab[31] = 0;
    newCmapTab[32] = 0x00;	// startCount[0]
    newCmapTab[33] = 0x00;
    newCmapTab[34] = (char)0xff; // startCount[1]
    newCmapTab[35] = (char)0xff;
    newCmapTab[36] = 0;		// idDelta[0]
    newCmapTab[37] = 0;
    newCmapTab[38] = 0;		// idDelta[1]
    newCmapTab[39] = 1;
    newCmapTab[40] = 0;		// idRangeOffset[0]
    newCmapTab[41] = 4;
    newCmapTab[42] = 0;		// idRangeOffset[1]
    newCmapTab[43] = 0;
    for (i = 0; i < 256; ++i) {
      newCmapTab[44 + 2*i] = codeToGID[i] >> 8;
      newCmapTab[44 + 2*i + 1] = codeToGID[i] & 0xff;
    }
  } else {
    newCmapLen = 0;
    newCmapTab = NULL;
  }

  // generate the new hmtx table and the updated hhea table
  if (abbrevHMTX) {
    i = seekTable("hhea");
    pos = tables[i].offset;
    newHHEALen = 36;
    newHHEATab = (char *)gmalloc(newHHEALen);
    for (i = 0; i < newHHEALen; ++i) {
      newHHEATab[i] = getU8(pos++, &ok);
    }
    newHHEATab[34] = nGlyphs >> 8;
    newHHEATab[35] = nGlyphs & 0xff;
    i = seekTable("hmtx");
    pos = tables[i].offset;
    newHMTXLen = 4 * nGlyphs;
    newHMTXTab = (char *)gmalloc(newHMTXLen);
    advWidth = 0;
    for (i = 0; i < nHMetrics; ++i) {
      advWidth = getU16BE(pos, &ok);
      lsb = getU16BE(pos + 2, &ok);
      pos += 4;
      newHMTXTab[4*i    ] = advWidth >> 8;
      newHMTXTab[4*i + 1] = advWidth & 0xff;
      newHMTXTab[4*i + 2] = lsb >> 8;
      newHMTXTab[4*i + 3] = lsb & 0xff;
    }
    for (; i < nGlyphs; ++i) {
      lsb = getU16BE(pos, &ok);
      pos += 2;
      newHMTXTab[4*i    ] = advWidth >> 8;
      newHMTXTab[4*i + 1] = advWidth & 0xff;
      newHMTXTab[4*i + 2] = lsb >> 8;
      newHMTXTab[4*i + 3] = lsb & 0xff;
    }
  } else {
    newHHEATab = newHMTXTab = NULL;
    newHHEALen = newHMTXLen = 0; // make gcc happy
  }

  // construct the new table directory:
  // - keep all original tables with non-zero length
  // - fix the cmap table's length, if necessary
  // - add missing tables
  // - sort the table by tag
  // - compute new table positions, including 4-byte alignment
  // - (re)compute table checksums
  nNewTables = nTables - nZeroLengthTables +
               (missingCmap ? 1 : 0) + (missingName ? 1 : 0) +
               (missingPost ? 1 : 0) + (missingOS2 ? 1 : 0);
  newTables = (TrueTypeTable *)gmallocn(nNewTables, sizeof(TrueTypeTable));
  j = 0;
  for (i = 0; i < nTables; ++i) {
    if (tables[i].len > 0) {
      newTables[j] = tables[i];
      newTables[j].origOffset = tables[i].offset;
      if (checkRegion(tables[i].offset, newTables[i].len)) {
	newTables[j].checksum =
	    computeTableChecksum(file + tables[i].offset, tables[i].len);
	if (tables[i].tag == headTag) {
	  // don't include the file checksum
	  newTables[j].checksum -= getU32BE(tables[i].offset + 8, &ok);
	}
      }
      if (newTables[j].tag == cmapTag && codeToGID) {
	newTables[j].len = newCmapLen;
	newTables[j].checksum = computeTableChecksum((Guchar *)newCmapTab,
						     newCmapLen);
      } else if (newTables[j].tag == cmapTag && badCmapLen) {
	newTables[j].len = cmapLen;
      } else if (newTables[j].tag == locaTag && unsortedLoca) {
	newTables[j].len = (nGlyphs + 1) * (locaFmt ? 4 : 2);
	newTables[j].checksum = locaChecksum;
      } else if (newTables[j].tag == glyfTag && unsortedLoca) {
	newTables[j].len = glyfLen;
	newTables[j].checksum = glyfChecksum;
      } else if (newTables[j].tag == nameTag && name) {
	newTables[j].len = newNameLen;
	newTables[j].checksum = computeTableChecksum((Guchar *)newNameTab,
						     newNameLen);
      } else if (newTables[j].tag == hheaTag && abbrevHMTX) {
	newTables[j].len = newHHEALen;
	newTables[j].checksum = computeTableChecksum((Guchar *)newHHEATab,
						     newHHEALen);
      } else if (newTables[j].tag == hmtxTag && abbrevHMTX) {
	newTables[j].len = newHMTXLen;
	newTables[j].checksum = computeTableChecksum((Guchar *)newHMTXTab,
						     newHMTXLen);
      }
      ++j;
    }
  }
  if (missingCmap) {
    newTables[j].tag = cmapTag;
    if (codeToGID) {
      newTables[j].checksum = computeTableChecksum((Guchar *)newCmapTab,
						   newCmapLen);
      newTables[j].len = newCmapLen;
    } else {
      newTables[j].checksum = computeTableChecksum((Guchar *)cmapTab,
						   sizeof(cmapTab));
      newTables[j].len = sizeof(cmapTab);
    }
    ++j;
  }
  if (missingName) {
    newTables[j].tag = nameTag;
    if (name) {
      newTables[j].checksum = computeTableChecksum((Guchar *)newNameTab,
						   newNameLen);
      newTables[j].len = newNameLen;
    } else {
      newTables[j].checksum = computeTableChecksum((Guchar *)nameTab,
						   sizeof(nameTab));
      newTables[j].len = sizeof(nameTab);
    }
    ++j;
  }
  if (missingPost) {
    newTables[j].tag = postTag;
    newTables[j].checksum = computeTableChecksum((Guchar *)postTab,
						 sizeof(postTab));
    newTables[j].len = sizeof(postTab);
    ++j;
  }
  if (missingOS2) {
    newTables[j].tag = os2Tag;
    newTables[j].checksum = computeTableChecksum((Guchar *)os2Tab,
						 sizeof(os2Tab));
    newTables[j].len = sizeof(os2Tab);
    ++j;
  }
  qsort(newTables, nNewTables, sizeof(TrueTypeTable),
	&cmpTrueTypeTableTag);
  pos = 12 + nNewTables * 16;
  for (i = 0; i < nNewTables; ++i) {
    newTables[i].offset = pos;
    pos += newTables[i].len;
    if (pos & 3) {
      pos += 4 - (pos & 3);
    }
  }

  // write the table directory
  tableDir = (char *)gmalloc(12 + nNewTables * 16);
  tableDir[0] = 0x00;					// sfnt version
  tableDir[1] = 0x01;
  tableDir[2] = 0x00;
  tableDir[3] = 0x00;
  tableDir[4] = (char)((nNewTables >> 8) & 0xff);	// numTables
  tableDir[5] = (char)(nNewTables & 0xff);
  for (i = -1, t = (Guint)nNewTables; t; ++i, t >>= 1) ;
  t = 1 << (4 + i);
  tableDir[6] = (char)((t >> 8) & 0xff);		// searchRange
  tableDir[7] = (char)(t & 0xff);
  tableDir[8] = (char)((i >> 8) & 0xff);		// entrySelector
  tableDir[9] = (char)(i & 0xff);
  t = nNewTables * 16 - t;
  tableDir[10] = (char)((t >> 8) & 0xff);		// rangeShift
  tableDir[11] = (char)(t & 0xff);
  pos = 12;
  for (i = 0; i < nNewTables; ++i) {
    tableDir[pos   ] = (char)(newTables[i].tag >> 24);
    tableDir[pos+ 1] = (char)(newTables[i].tag >> 16);
    tableDir[pos+ 2] = (char)(newTables[i].tag >>  8);
    tableDir[pos+ 3] = (char) newTables[i].tag;
    tableDir[pos+ 4] = (char)(newTables[i].checksum >> 24);
    tableDir[pos+ 5] = (char)(newTables[i].checksum >> 16);
    tableDir[pos+ 6] = (char)(newTables[i].checksum >>  8);
    tableDir[pos+ 7] = (char) newTables[i].checksum;
    tableDir[pos+ 8] = (char)(newTables[i].offset >> 24);
    tableDir[pos+ 9] = (char)(newTables[i].offset >> 16);
    tableDir[pos+10] = (char)(newTables[i].offset >>  8);
    tableDir[pos+11] = (char) newTables[i].offset;
    tableDir[pos+12] = (char)(newTables[i].len >> 24);
    tableDir[pos+13] = (char)(newTables[i].len >> 16);
    tableDir[pos+14] = (char)(newTables[i].len >>  8);
    tableDir[pos+15] = (char) newTables[i].len;
    pos += 16;
  }
  (*outputFunc)(outputStream, tableDir, 12 + nNewTables * 16);

  // compute the file checksum
  fileChecksum = computeTableChecksum((Guchar *)tableDir,
				      12 + nNewTables * 16);
  for (i = 0; i < nNewTables; ++i) {
    fileChecksum += newTables[i].checksum;
  }
  fileChecksum = 0xb1b0afba - fileChecksum;

  // write the tables
  for (i = 0; i < nNewTables; ++i) {
    if (newTables[i].tag == headTag) {
      if (checkRegion(newTables[i].origOffset, newTables[i].len)) {
	(*outputFunc)(outputStream, (char *)file + newTables[i].origOffset, 8);
	checksumBuf[0] = fileChecksum >> 24;
	checksumBuf[1] = fileChecksum >> 16;
	checksumBuf[2] = fileChecksum >> 8;
	checksumBuf[3] = fileChecksum;
	(*outputFunc)(outputStream, checksumBuf, 4);
	(*outputFunc)(outputStream,
		      (char *)file + newTables[i].origOffset + 12,
		      newTables[i].len - 12);
      } else {
	for (j = 0; j < newTables[i].len; ++j) {
	  (*outputFunc)(outputStream, "\0", 1);
	}
      }
    } else if (newTables[i].tag == cmapTag && codeToGID) {
      (*outputFunc)(outputStream, newCmapTab, newTables[i].len);
    } else if (newTables[i].tag == cmapTag && missingCmap) {
      (*outputFunc)(outputStream, cmapTab, newTables[i].len);
    } else if (newTables[i].tag == nameTag && name) {
      (*outputFunc)(outputStream, newNameTab, newTables[i].len);
    } else if (newTables[i].tag == nameTag && missingName) {
      (*outputFunc)(outputStream, nameTab, newTables[i].len);
    } else if (newTables[i].tag == postTag && missingPost) {
      (*outputFunc)(outputStream, postTab, newTables[i].len);
    } else if (newTables[i].tag == os2Tag && missingOS2) {
      (*outputFunc)(outputStream, os2Tab, newTables[i].len);
    } else if (newTables[i].tag == hheaTag && abbrevHMTX) {
      (*outputFunc)(outputStream, newHHEATab, newTables[i].len);
    } else if (newTables[i].tag == hmtxTag && abbrevHMTX) {
      (*outputFunc)(outputStream, newHMTXTab, newTables[i].len);
    } else if (newTables[i].tag == locaTag && unsortedLoca) {
      for (j = 0; j <= nGlyphs; ++j) {
	if (locaFmt) {
	  locaBuf[0] = (char)(locaTable[j].newOffset >> 24);
	  locaBuf[1] = (char)(locaTable[j].newOffset >> 16);
	  locaBuf[2] = (char)(locaTable[j].newOffset >>  8);
	  locaBuf[3] = (char) locaTable[j].newOffset;
	  (*outputFunc)(outputStream, locaBuf, 4);
	} else {
	  locaBuf[0] = (char)(locaTable[j].newOffset >> 9);
	  locaBuf[1] = (char)(locaTable[j].newOffset >> 1);
	  (*outputFunc)(outputStream, locaBuf, 2);
	}
      }
    } else if (newTables[i].tag == glyfTag && unsortedLoca) {
      pos = tables[seekTable("glyf")].offset;
      for (j = 0; j < nGlyphs; ++j) {
	n = locaTable[j].len;
	if (n > 0) {
	  k = locaTable[j].origOffset;
	  if (checkRegion(pos + k, n)) {
	    (*outputFunc)(outputStream, (char *)file + pos + k, n);
	  } else {
	    for (k = 0; k < n; ++k) {
	      (*outputFunc)(outputStream, "\0", 1);
	    }
	  }
	  if ((k = locaTable[j].len & 3)) {
	    (*outputFunc)(outputStream, "\0\0\0\0", 4 - k);
	  }
	}
      }
    } else {
      if (checkRegion(newTables[i].origOffset, newTables[i].len)) {
	(*outputFunc)(outputStream, (char *)file + newTables[i].origOffset,
		      newTables[i].len);
      } else {
	for (j = 0; j < newTables[i].len; ++j) {
	  (*outputFunc)(outputStream, "\0", 1);
	}
      }
    }
    if (newTables[i].len & 3) {
      (*outputFunc)(outputStream, "\0\0\0", 4 - (newTables[i].len & 3));
    }
  }

  gfree(newHMTXTab);
  gfree(newHHEATab);
  gfree(newCmapTab);
  gfree(newNameTab);
  gfree(tableDir);
  gfree(newTables);
 done1:
  gfree(locaTable);
}

void FoFiTrueType::cvtEncoding(char **encoding,
			       FoFiOutputFunc outputFunc,
			       void *outputStream) {
  char *name;
  GString *buf;
  int i;

  (*outputFunc)(outputStream, "/Encoding 256 array\n", 20);
  if (encoding) {
    for (i = 0; i < 256; ++i) {
      if (!(name = encoding[i])) {
	name = ".notdef";
      }
      buf = GString::format("dup {0:d} /", i);
      (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
      delete buf;
      (*outputFunc)(outputStream, name, strlen(name));
      (*outputFunc)(outputStream, " put\n", 5);
    }
  } else {
    for (i = 0; i < 256; ++i) {
      buf = GString::format("dup {0:d} /c{1:02x} put\n", i, i);
      (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
      delete buf;
    }
  }
  (*outputFunc)(outputStream, "readonly def\n", 13);
}

void FoFiTrueType::cvtCharStrings(char **encoding,
				  Gushort *codeToGID,
				  FoFiOutputFunc outputFunc,
				  void *outputStream) {
  char *name;
  GString *buf;
  char buf2[16];
  int i, k;

  // always define '.notdef'
  (*outputFunc)(outputStream, "/CharStrings 256 dict dup begin\n", 32);
  (*outputFunc)(outputStream, "/.notdef 0 def\n", 15);

  // if there's no 'cmap' table, punt
  if (nCmaps == 0) {
    goto err;
  }

  // map char name to glyph index:
  // 1. use encoding to map name to char code
  // 2. use codeToGID to map char code to glyph index
  // N.B. We do this in reverse order because font subsets can have
  //      weird encodings that use the same character name twice, and
  //      the first definition is probably the one we want.
  k = 0; // make gcc happy
  for (i = 255; i >= 0; --i) {
    if (encoding) {
      name = encoding[i];
    } else {
      sprintf(buf2, "c%02x", i);
      name = buf2;
    }
    if (name && strcmp(name, ".notdef")) {
      k = codeToGID[i];
      // note: Distiller (maybe Adobe's PS interpreter in general)
      // doesn't like TrueType fonts that have CharStrings entries
      // which point to nonexistent glyphs, hence the (k < nGlyphs)
      // test
      if (k > 0 && k < nGlyphs) {
	(*outputFunc)(outputStream, "/", 1);
	(*outputFunc)(outputStream, name, strlen(name));
	buf = GString::format(" {0:d} def\n", k);
	(*outputFunc)(outputStream, buf->getCString(), buf->getLength());
	delete buf;
      }
    }
  }

 err:
  (*outputFunc)(outputStream, "end readonly def\n", 17);
}

void FoFiTrueType::cvtSfnts(FoFiOutputFunc outputFunc,
			    void *outputStream, GString *name,
			    GBool needVerticalMetrics) {
  Guchar headData[54];
  TrueTypeLoca *locaTable;
  Guchar *locaData;
  TrueTypeTable newTables[nT42Tables];
  Guchar tableDir[12 + nT42Tables*16];
  GBool ok;
  Guint checksum;
  int nNewTables;
  int length, pos, glyfPos, i, j, k;
  Guchar vheaTab[36] = {
    0, 1, 0, 0,			// table version number
    0, 0,			// ascent
    0, 0,			// descent
    0, 0,			// reserved
    0, 0,			// max advance height
    0, 0,			// min top side bearing
    0, 0,			// min bottom side bearing
    0, 0,			// y max extent
    0, 0,			// caret slope rise
    0, 1,			// caret slope run
    0, 0,			// caret offset
    0, 0,			// reserved
    0, 0,			// reserved
    0, 0,			// reserved
    0, 0,			// reserved
    0, 0,			// metric data format
    0, 1			// number of advance heights in vmtx table
  };
  Guchar *vmtxTab;
  GBool needVhea, needVmtx;
  int advance;

  // construct the 'head' table, zero out the font checksum
  i = seekTable("head");
  pos = tables[i].offset;
  if (!checkRegion(pos, 54)) {
    return;
  }
  memcpy(headData, file + pos, 54);
  headData[8] = headData[9] = headData[10] = headData[11] = (Guchar)0;

  // read the original 'loca' table, pad entries out to 4 bytes, and
  // sort it into proper order -- some (non-compliant) fonts have
  // out-of-order loca tables; in order to correctly handle the case
  // where (compliant) fonts have empty entries in the middle of the
  // table, cmpTrueTypeLocaPos uses offset as its primary sort key,
  // and idx as its secondary key (ensuring that adjacent entries with
  // the same pos value remain in the same order)
  locaTable = (TrueTypeLoca *)gmallocn(nGlyphs + 1, sizeof(TrueTypeLoca));
  i = seekTable("loca");
  pos = tables[i].offset;
  ok = gTrue;
  for (i = 0; i <= nGlyphs; ++i) {
    locaTable[i].idx = i;
    if (locaFmt) {
      locaTable[i].origOffset = (int)getU32BE(pos + i*4, &ok);
    } else {
      locaTable[i].origOffset = 2 * getU16BE(pos + i*2, &ok);
    }
  }
  qsort(locaTable, nGlyphs + 1, sizeof(TrueTypeLoca),
	&cmpTrueTypeLocaOffset);
  for (i = 0; i < nGlyphs; ++i) {
    locaTable[i].len = locaTable[i+1].origOffset - locaTable[i].origOffset;
  }
  locaTable[nGlyphs].len = 0;
  qsort(locaTable, nGlyphs + 1, sizeof(TrueTypeLoca),
	&cmpTrueTypeLocaIdx);
  pos = 0;
  for (i = 0; i <= nGlyphs; ++i) {
    locaTable[i].newOffset = pos;
    pos += locaTable[i].len;
    if (pos & 3) {
      pos += 4 - (pos & 3);
    }
  }

  // construct the new 'loca' table
  locaData = (Guchar *)gmallocn(nGlyphs + 1, (locaFmt ? 4 : 2));
  for (i = 0; i <= nGlyphs; ++i) {
    pos = locaTable[i].newOffset;
    if (locaFmt) {
      locaData[4*i  ] = (Guchar)(pos >> 24);
      locaData[4*i+1] = (Guchar)(pos >> 16);
      locaData[4*i+2] = (Guchar)(pos >>  8);
      locaData[4*i+3] = (Guchar) pos;
    } else {
      locaData[2*i  ] = (Guchar)(pos >> 9);
      locaData[2*i+1] = (Guchar)(pos >> 1);
    }
  }

  // count the number of tables
  nNewTables = 0;
  for (i = 0; i < nT42Tables; ++i) {
    if (t42Tables[i].required ||
	seekTable(t42Tables[i].tag) >= 0) {
      ++nNewTables;
    }
  }
  vmtxTab = NULL; // make gcc happy
  advance = 0; // make gcc happy
  if (needVerticalMetrics) {
    needVhea = seekTable("vhea") < 0;
    needVmtx = seekTable("vmtx") < 0;
    if (needVhea || needVmtx) {
      i = seekTable("head");
      advance = getU16BE(tables[i].offset + 18, &ok); // units per em
      if (needVhea) {
	++nNewTables;
      }
      if (needVmtx) {
	++nNewTables;
      }
    }
  }

  // construct the new table headers, including table checksums
  // (pad each table out to a multiple of 4 bytes)
  pos = 12 + nNewTables*16;
  k = 0;
  for (i = 0; i < nT42Tables; ++i) {
    length = -1;
    checksum = 0; // make gcc happy
    if (i == t42HeadTable) {
      length = 54;
      checksum = computeTableChecksum(headData, 54);
    } else if (i == t42LocaTable) {
      length = (nGlyphs + 1) * (locaFmt ? 4 : 2);
      checksum = computeTableChecksum(locaData, length);
    } else if (i == t42GlyfTable) {
      length = 0;
      checksum = 0;
      glyfPos = tables[seekTable("glyf")].offset;
      for (j = 0; j < nGlyphs; ++j) {
	length += locaTable[j].len;
	if (length & 3) {
	  length += 4 - (length & 3);
	}
	if (checkRegion(glyfPos + locaTable[j].origOffset, locaTable[j].len)) {
	  checksum +=
	      computeTableChecksum(file + glyfPos + locaTable[j].origOffset,
				   locaTable[j].len);
	}
      }
    } else {
      if ((j = seekTable(t42Tables[i].tag)) >= 0) {
	length = tables[j].len;
	if (checkRegion(tables[j].offset, length)) {
	  checksum = computeTableChecksum(file + tables[j].offset, length);
	}
      } else if (needVerticalMetrics && i == t42VheaTable) {
	vheaTab[10] = advance / 256;    // max advance height
	vheaTab[11] = advance % 256;
	length = sizeof(vheaTab);
	checksum = computeTableChecksum(vheaTab, length);
      } else if (needVerticalMetrics && i == t42VmtxTable) {
	length = 4 + (nGlyphs - 1) * 4;
	vmtxTab = (Guchar *)gmalloc(length);
	vmtxTab[0] = advance / 256;
	vmtxTab[1] = advance % 256;
	for (j = 2; j < length; j += 2) {
	  vmtxTab[j] = 0;
	  vmtxTab[j+1] = 0;
	}
	checksum = computeTableChecksum(vmtxTab, length);
      } else if (t42Tables[i].required) {
	//~ error(-1, "Embedded TrueType font is missing a required table ('%s')",
	//~       t42Tables[i].tag);
	length = 0;
	checksum = 0;
      }
    }
    if (length >= 0) {
      newTables[k].tag = ((t42Tables[i].tag[0] & 0xff) << 24) |
	                 ((t42Tables[i].tag[1] & 0xff) << 16) |
	                 ((t42Tables[i].tag[2] & 0xff) <<  8) |
	                  (t42Tables[i].tag[3] & 0xff);
      newTables[k].checksum = checksum;
      newTables[k].offset = pos;
      newTables[k].len = length;
      pos += length;
      if (pos & 3) {
	pos += 4 - (length & 3);
      }
      ++k;
    }
  }

  // construct the table directory
  tableDir[0] = 0x00;		// sfnt version
  tableDir[1] = 0x01;
  tableDir[2] = 0x00;
  tableDir[3] = 0x00;
  tableDir[4] = 0;		// numTables
  tableDir[5] = nNewTables;
  tableDir[6] = 0;		// searchRange
  tableDir[7] = (Guchar)128;
  tableDir[8] = 0;		// entrySelector
  tableDir[9] = 3;
  tableDir[10] = 0;		// rangeShift
  tableDir[11] = (Guchar)(16 * nNewTables - 128);
  pos = 12;
  for (i = 0; i < nNewTables; ++i) {
    tableDir[pos   ] = (Guchar)(newTables[i].tag >> 24);
    tableDir[pos+ 1] = (Guchar)(newTables[i].tag >> 16);
    tableDir[pos+ 2] = (Guchar)(newTables[i].tag >>  8);
    tableDir[pos+ 3] = (Guchar) newTables[i].tag;
    tableDir[pos+ 4] = (Guchar)(newTables[i].checksum >> 24);
    tableDir[pos+ 5] = (Guchar)(newTables[i].checksum >> 16);
    tableDir[pos+ 6] = (Guchar)(newTables[i].checksum >>  8);
    tableDir[pos+ 7] = (Guchar) newTables[i].checksum;
    tableDir[pos+ 8] = (Guchar)(newTables[i].offset >> 24);
    tableDir[pos+ 9] = (Guchar)(newTables[i].offset >> 16);
    tableDir[pos+10] = (Guchar)(newTables[i].offset >>  8);
    tableDir[pos+11] = (Guchar) newTables[i].offset;
    tableDir[pos+12] = (Guchar)(newTables[i].len >> 24);
    tableDir[pos+13] = (Guchar)(newTables[i].len >> 16);
    tableDir[pos+14] = (Guchar)(newTables[i].len >>  8);
    tableDir[pos+15] = (Guchar) newTables[i].len;
    pos += 16;
  }

  // compute the font checksum and store it in the head table
  checksum = computeTableChecksum(tableDir, 12 + nNewTables*16);
  for (i = 0; i < nNewTables; ++i) {
    checksum += newTables[i].checksum;
  }
  checksum = 0xb1b0afba - checksum; // because the TrueType spec says so
  headData[ 8] = (Guchar)(checksum >> 24);
  headData[ 9] = (Guchar)(checksum >> 16);
  headData[10] = (Guchar)(checksum >>  8);
  headData[11] = (Guchar) checksum;

  // start the sfnts array
  if (name) {
    (*outputFunc)(outputStream, "/", 1);
    (*outputFunc)(outputStream, name->getCString(), name->getLength());
    (*outputFunc)(outputStream, " [\n", 3);
  } else {
    (*outputFunc)(outputStream, "/sfnts [\n", 9);
  }

  // write the table directory
  dumpString(tableDir, 12 + nNewTables*16, outputFunc, outputStream);

  // write the tables
  for (i = 0; i < nNewTables; ++i) {
    if (i == t42HeadTable) {
      dumpString(headData, 54, outputFunc, outputStream);
    } else if (i == t42LocaTable) {
      length = (nGlyphs + 1) * (locaFmt ? 4 : 2);
      dumpString(locaData, length, outputFunc, outputStream);
    } else if (i == t42GlyfTable) {
      glyfPos = tables[seekTable("glyf")].offset;
      for (j = 0; j < nGlyphs; ++j) {
	if (locaTable[j].len > 0 &&
	    checkRegion(glyfPos + locaTable[j].origOffset, locaTable[j].len)) {
	  dumpString(file + glyfPos + locaTable[j].origOffset,
		     locaTable[j].len, outputFunc, outputStream);
	}
      }
    } else {
      // length == 0 means the table is missing and the error was
      // already reported during the construction of the table
      // headers
      if ((length = newTables[i].len) > 0) {
	if ((j = seekTable(t42Tables[i].tag)) >= 0 &&
	    checkRegion(tables[j].offset, tables[j].len)) {
	  dumpString(file + tables[j].offset, tables[j].len,
		     outputFunc, outputStream);
	} else if (needVerticalMetrics && i == t42VheaTable) {
	  dumpString(vheaTab, length, outputFunc, outputStream);
	} else if (needVerticalMetrics && i == t42VmtxTable) {
	  dumpString(vmtxTab, length, outputFunc, outputStream);
	  gfree(vmtxTab);
	}
      }
    }
  }

  // end the sfnts array
  (*outputFunc)(outputStream, "] def\n", 6);

  gfree(locaData);
  gfree(locaTable);
}

void FoFiTrueType::dumpString(Guchar *s, int length,
			      FoFiOutputFunc outputFunc,
			      void *outputStream) {
  GString *buf;
  int pad, i, j;

  (*outputFunc)(outputStream, "<", 1);
  for (i = 0; i < length; i += 32) {
    for (j = 0; j < 32 && i+j < length; ++j) {
      buf = GString::format("{0:02x}", s[i+j] & 0xff);
      (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
      delete buf;
    }
    if (i % (65536 - 32) == 65536 - 64) {
      (*outputFunc)(outputStream, ">\n<", 3);
    } else if (i+32 < length) {
      (*outputFunc)(outputStream, "\n", 1);
    }
  }
  if (length & 3) {
    pad = 4 - (length & 3);
    for (i = 0; i < pad; ++i) {
      (*outputFunc)(outputStream, "00", 2);
    }
  }
  // add an extra zero byte because the Adobe Type 42 spec says so
  (*outputFunc)(outputStream, "00>\n", 4);
}

Guint FoFiTrueType::computeTableChecksum(Guchar *data, int length) {
  Guint checksum, word;
  int i;

  checksum = 0;
  for (i = 0; i+3 < length; i += 4) {
    word = ((data[i  ] & 0xff) << 24) +
           ((data[i+1] & 0xff) << 16) +
           ((data[i+2] & 0xff) <<  8) +
            (data[i+3] & 0xff);
    checksum += word;
  }
  if (length & 3) {
    word = 0;
    i = length & ~3;
    switch (length & 3) {
    case 3:
      word |= (data[i+2] & 0xff) <<  8;
    case 2:
      word |= (data[i+1] & 0xff) << 16;
    case 1:
      word |= (data[i  ] & 0xff) << 24;
      break;
    }
    checksum += word;
  }
  return checksum;
}

void FoFiTrueType::parse() {
  Guint topTag;
  int pos, ver, i, j;

  parsedOk = gTrue;

  // look for a collection (TTC)
  topTag = getU32BE(0, &parsedOk);
  if (!parsedOk) {
    return;
  }
  if (topTag == ttcfTag) {
    pos = getU32BE(12, &parsedOk);
    if (!parsedOk) {
      return;
    }
  } else {
    pos = 0;
  }

  // check the sfnt version
  ver = getU32BE(pos, &parsedOk);
  if (!parsedOk) {
    return;
  }
  openTypeCFF = ver == 0x4f54544f; // 'OTTO'

  // read the table directory
  nTables = getU16BE(pos + 4, &parsedOk);
  if (!parsedOk) {
    return;
  }
  tables = (TrueTypeTable *)gmallocn(nTables, sizeof(TrueTypeTable));
  pos += 12;
  for (i = 0; i < nTables; ++i) {
    tables[i].tag = getU32BE(pos, &parsedOk);
    tables[i].checksum = getU32BE(pos + 4, &parsedOk);
    tables[i].offset = (int)getU32BE(pos + 8, &parsedOk);
    tables[i].len = (int)getU32BE(pos + 12, &parsedOk);
    if (tables[i].offset + tables[i].len < tables[i].offset ||
	tables[i].offset + tables[i].len > len) {
      parsedOk = gFalse;
    }
    pos += 16;
  }
  if (!parsedOk) {
    return;
  }

  // check for tables that are required by both the TrueType spec and
  // the Type 42 spec
  if (seekTable("head") < 0 ||
      seekTable("hhea") < 0 ||
      seekTable("maxp") < 0 ||
      seekTable("hmtx") < 0 ||
      (!openTypeCFF && seekTable("loca") < 0) ||
      (!openTypeCFF && seekTable("glyf") < 0) ||
      (openTypeCFF && seekTable("CFF ") < 0)) {
    parsedOk = gFalse;
    return;
  }

  // read the cmaps
  if ((i = seekTable("cmap")) >= 0) {
    pos = tables[i].offset + 2;
    nCmaps = getU16BE(pos, &parsedOk);
    pos += 2;
    if (!parsedOk) {
      return;
    }
    cmaps = (TrueTypeCmap *)gmallocn(nCmaps, sizeof(TrueTypeCmap));
    for (j = 0; j < nCmaps; ++j) {
      cmaps[j].platform = getU16BE(pos, &parsedOk);
      cmaps[j].encoding = getU16BE(pos + 2, &parsedOk);
      cmaps[j].offset = tables[i].offset + getU32BE(pos + 4, &parsedOk);
      pos += 8;
      cmaps[j].fmt = getU16BE(cmaps[j].offset, &parsedOk);
      cmaps[j].len = getU16BE(cmaps[j].offset + 2, &parsedOk);
    }
    if (!parsedOk) {
      return;
    }
  } else {
    nCmaps = 0;
  }

  // get the number of glyphs from the maxp table
  i = seekTable("maxp");
  nGlyphs = getU16BE(tables[i].offset + 4, &parsedOk);
  if (!parsedOk) {
    return;
  }

  // get the bbox and loca table format from the head table
  i = seekTable("head");
  bbox[0] = getS16BE(tables[i].offset + 36, &parsedOk);
  bbox[1] = getS16BE(tables[i].offset + 38, &parsedOk);
  bbox[2] = getS16BE(tables[i].offset + 40, &parsedOk);
  bbox[3] = getS16BE(tables[i].offset + 42, &parsedOk);
  locaFmt = getS16BE(tables[i].offset + 50, &parsedOk);
  if (!parsedOk) {
    return;
  }

  // make sure the loca table is sane (correct length and entries are
  // in bounds)
  if (!openTypeCFF) {
    i = seekTable("loca");
    if (tables[i].len < 0) {
      parsedOk = gFalse;
      return;
    }
    if (tables[i].len < (nGlyphs + 1) * (locaFmt ? 4 : 2)) {
      nGlyphs = tables[i].len / (locaFmt ? 4 : 2) - 1;
    }
    for (j = 0; j <= nGlyphs; ++j) {
      if (locaFmt) {
	pos = (int)getU32BE(tables[i].offset + j*4, &parsedOk);
      } else {
	pos = getU16BE(tables[i].offset + j*2, &parsedOk);
      }
      if (pos < 0 || pos > len) {
	parsedOk = gFalse;
      }
    }
    if (!parsedOk) {
      return;
    }
  }

  // read the post table
  readPostTable();
}

void FoFiTrueType::readPostTable() {
  GString *name;
  int tablePos, postFmt, stringIdx, stringPos;
  GBool ok;
  int i, j, n, m;

  ok = gTrue;
  if ((i = seekTable("post")) < 0) {
    return;
  }
  tablePos = tables[i].offset;
  postFmt = getU32BE(tablePos, &ok);
  if (!ok) {
    goto err;
  }
  if (postFmt == 0x00010000) {
    nameToGID = new GHash(gTrue);
    for (i = 0; i < 258; ++i) {
      nameToGID->add(new GString(macGlyphNames[i]), i);
    }
  } else if (postFmt == 0x00020000) {
    nameToGID = new GHash(gTrue);
    n = getU16BE(tablePos + 32, &ok);
    if (!ok) {
      goto err;
    }
    if (n > nGlyphs) {
      n = nGlyphs;
    }
    stringIdx = 0;
    stringPos = tablePos + 34 + 2*n;
    for (i = 0; i < n; ++i) {
      j = getU16BE(tablePos + 34 + 2*i, &ok);
      if (j < 258) {
	nameToGID->removeInt(macGlyphNames[j]);
	nameToGID->add(new GString(macGlyphNames[j]), i);
      } else {
	j -= 258;
	if (j != stringIdx) {
	  for (stringIdx = 0, stringPos = tablePos + 34 + 2*n;
	       stringIdx < j;
	       ++stringIdx, stringPos += 1 + getU8(stringPos, &ok)) ;
	  if (!ok) {
	    goto err;
	  }
	}
	m = getU8(stringPos, &ok);
	if (!ok || !checkRegion(stringPos + 1, m)) {
	  goto err;
	}
	name = new GString((char *)&file[stringPos + 1], m);
	nameToGID->removeInt(name);
	nameToGID->add(name, i);
	++stringIdx;
	stringPos += 1 + m;
      }
    }
  } else if (postFmt == 0x00028000) {
    nameToGID = new GHash(gTrue);
    for (i = 0; i < nGlyphs; ++i) {
      j = getU8(tablePos + 32 + i, &ok);
      if (!ok) {
	goto err;
      }
      if (j < 258) {
	nameToGID->removeInt(macGlyphNames[j]);
	nameToGID->add(new GString(macGlyphNames[j]), i);
      }
    }
  }

  return;

 err:
  if (nameToGID) {
    delete nameToGID;
    nameToGID = NULL;
  }
}

int FoFiTrueType::seekTable(char *tag) {
  Guint tagI;
  int i;

  tagI = ((tag[0] & 0xff) << 24) |
         ((tag[1] & 0xff) << 16) |
         ((tag[2] & 0xff) << 8) |
          (tag[3] & 0xff);
  for (i = 0; i < nTables; ++i) {
    if (tables[i].tag == tagI) {
      return i;
    }
  }
  return -1;
}
//========================================================================
//
// FoFiTrueType.h
//
// Copyright 1999-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef FOFITRUETYPE_H
#define FOFITRUETYPE_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "gtypes.h"
#include "FoFiBase.h"

class GString;
class GHash;
struct TrueTypeTable;
struct TrueTypeCmap;

//------------------------------------------------------------------------
// FoFiTrueType
//------------------------------------------------------------------------

class FoFiTrueType: public FoFiBase {
public:

  // Create a FoFiTrueType object from a memory buffer.
  static FoFiTrueType *make(char *fileA, int lenA);

  // Create a FoFiTrueType object from a file on disk.
  static FoFiTrueType *load(char *fileName);

  virtual ~FoFiTrueType();

  // Returns true if this an OpenType font containing CFF data, false
  // if it's a TrueType font (or OpenType font with TrueType data).
  GBool isOpenTypeCFF() { return openTypeCFF; }

  // Return the number of cmaps defined by this font.
  int getNumCmaps();

  // Return the platform ID of the <i>th cmap.
  int getCmapPlatform(int i);

  // Return the encoding ID of the <i>th cmap.
  int getCmapEncoding(int i);

  // Return the index of the cmap for <platform>, <encoding>.  Returns
  // -1 if there is no corresponding cmap.
  int findCmap(int platform, int encoding);

  // Return the GID corresponding to <c> according to the <i>th cmap.
  Gushort mapCodeToGID(int i, int c);

  // Returns the GID corresponding to <name> according to the post
  // table.  Returns 0 if there is no mapping for <name> or if the
  // font does not have a post table.
  int mapNameToGID(char *name);

  // Return the mapping from CIDs to GIDs, and return the number of
  // CIDs in *<nCIDs>.  This is only useful for CID fonts.  (Only
  // useful for OpenType CFF fonts.)
  Gushort *getCIDToGIDMap(int *nCIDs);

  // Returns the least restrictive embedding licensing right (as
  // defined by the TrueType spec):
  // * 4: OS/2 table is missing or invalid
  // * 3: installable embedding
  // * 2: editable embedding
  // * 1: preview & print embedding
  // * 0: restricted license embedding
  int getEmbeddingRights();

  // Convert to a Type 42 font, suitable for embedding in a PostScript
  // file.  <psName> will be used as the PostScript font name (so we
  // don't need to depend on the 'name' table in the font).  The
  // <encoding> array specifies the mapping from char codes to names.
  // If <encoding> is NULL, the encoding is unknown or undefined.  The
  // <codeToGID> array specifies the mapping from char codes to GIDs.
  // (Not useful for OpenType CFF fonts.)
  void convertToType42(char *psName, char **encoding,
		       Gushort *codeToGID,
		       FoFiOutputFunc outputFunc, void *outputStream);

  // Convert to a Type 1 font, suitable for embedding in a PostScript
  // file.  This is only useful with 8-bit fonts.  If <newEncoding> is
  // not NULL, it will be used in place of the encoding in the Type 1C
  // font.  If <ascii> is true the eexec section will be hex-encoded,
  // otherwise it will be left as binary data.  If <psName> is
  // non-NULL, it will be used as the PostScript font name.  (Only
  // useful for OpenType CFF fonts.)
  void convertToType1(char *psName, char **newEncoding, GBool ascii,
		      FoFiOutputFunc outputFunc, void *outputStream);

  // Convert to a Type 2 CIDFont, suitable for embedding in a
  // PostScript file.  <psName> will be used as the PostScript font
  // name (so we don't need to depend on the 'name' table in the
  // font).  The <cidMap> array maps CIDs to GIDs; it has <nCIDs>
  // entries.  (Not useful for OpenType CFF fonts.)
  void convertToCIDType2(char *psName, Gushort *cidMap, int nCIDs,
			 GBool needVerticalMetrics,
			 FoFiOutputFunc outputFunc, void *outputStream);

  // Convert to a Type 0 CIDFont, suitable for embedding in a
  // PostScript file.  <psName> will be used as the PostScript font
  // name.  (Only useful for OpenType CFF fonts.)
  void convertToCIDType0(char *psName,
			 FoFiOutputFunc outputFunc, void *outputStream);

  // Convert to a Type 0 (but non-CID) composite font, suitable for
  // embedding in a PostScript file.  <psName> will be used as the
  // PostScript font name (so we don't need to depend on the 'name'
  // table in the font).  The <cidMap> array maps CIDs to GIDs; it has
  // <nCIDs> entries.  (Not useful for OpenType CFF fonts.)
  void convertToType0(char *psName, Gushort *cidMap, int nCIDs,
		      GBool needVerticalMetrics,
		      FoFiOutputFunc outputFunc, void *outputStream);

  // Convert to a Type 0 (but non-CID) composite font, suitable for
  // embedding in a PostScript file.  <psName> will be used as the
  // PostScript font name.  (Only useful for OpenType CFF fonts.)
  void convertToType0(char *psName,
		      FoFiOutputFunc outputFunc, void *outputStream);

  // Write a clean TTF file, filling in missing tables and correcting
  // various other errors.  If <name> is non-NULL, the font is renamed
  // to <name>.  If <codeToGID> is non-NULL, the font is re-encoded,
  // using a Windows Unicode cmap.  If <name> is NULL and the font is
  // complete and correct, it will be written unmodified.  (Not useful
  // for OpenType CFF fonts.)
  void writeTTF(FoFiOutputFunc outputFunc, void *outputStream,
		char *name = NULL, Gushort *codeToGID = NULL);

private:

  FoFiTrueType(char *fileA, int lenA, GBool freeFileDataA);
  void cvtEncoding(char **encoding,
		   FoFiOutputFunc outputFunc,
		   void *outputStream);
  void cvtCharStrings(char **encoding,
		      Gushort *codeToGID,
		      FoFiOutputFunc outputFunc,
		      void *outputStream);
  void cvtSfnts(FoFiOutputFunc outputFunc,
		void *outputStream, GString *name,
		GBool needVerticalMetrics);
  void dumpString(Guchar *s, int length,
		  FoFiOutputFunc outputFunc,
		  void *outputStream);
  Guint computeTableChecksum(Guchar *data, int length);
  void parse();
  void readPostTable();
  int seekTable(char *tag);

  TrueTypeTable *tables;
  int nTables;
  TrueTypeCmap *cmaps;
  int nCmaps;
  int nGlyphs;
  int locaFmt;
  int bbox[4];
  GHash *nameToGID;
  GBool openTypeCFF;

  GBool parsedOk;
};

#endif
//========================================================================
//
// FoFiType1.cc
//
// Copyright 1999-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stdlib.h>
#include <string.h>
#include "gmem.h"
#include "FoFiEncodings.h"
#include "FoFiType1.h"

//------------------------------------------------------------------------
// FoFiType1
//------------------------------------------------------------------------

FoFiType1 *FoFiType1::make(char *fileA, int lenA) {
  return new FoFiType1(fileA, lenA, gFalse);
}

FoFiType1 *FoFiType1::load(char *fileName) {
  char *fileA;
  int lenA;

  if (!(fileA = FoFiBase::readFile(fileName, &lenA))) {
    return NULL;
  }
  return new FoFiType1(fileA, lenA, gTrue);
}

FoFiType1::FoFiType1(char *fileA, int lenA, GBool freeFileDataA):
  FoFiBase(fileA, lenA, freeFileDataA)
{
  name = NULL;
  encoding = NULL;
  parsed = gFalse;
}

FoFiType1::~FoFiType1() {
  int i;

  if (name) {
    gfree(name);
  }
  if (encoding && encoding != fofiType1StandardEncoding) {
    for (i = 0; i < 256; ++i) {
      gfree(encoding[i]);
    }
    gfree(encoding);
  }
}

char *FoFiType1::getName() {
  if (!parsed) {
    parse();
  }
  return name;
}

char **FoFiType1::getEncoding() {
  if (!parsed) {
    parse();
  }
  return encoding;
}

void FoFiType1::writeEncoded(char **newEncoding,
			     FoFiOutputFunc outputFunc, void *outputStream) {
  char buf[512];
  char *line, *line2, *p;
  int i;

  // copy everything up to the encoding
  for (line = (char *)file;
       line && strncmp(line, "/Encoding", 9);
       line = getNextLine(line)) ;
  if (!line) {
    // no encoding - just copy the whole font file
    (*outputFunc)(outputStream, (char *)file, len);
    return;
  }
  (*outputFunc)(outputStream, (char *)file, line - (char *)file);

  // write the new encoding
  (*outputFunc)(outputStream, "/Encoding 256 array\n", 20);
  (*outputFunc)(outputStream,
		"0 1 255 {1 index exch /.notdef put} for\n", 40);
  for (i = 0; i < 256; ++i) {
    if (newEncoding[i]) {
      sprintf(buf, "dup %d /%s put\n", i, newEncoding[i]);
      (*outputFunc)(outputStream, buf, strlen(buf));
    }
  }
  (*outputFunc)(outputStream, "readonly def\n", 13);
  
  // find the end of the encoding data
  //~ this ought to parse PostScript tokens
  if (!strncmp(line, "/Encoding StandardEncoding def", 30)) {
    line = getNextLine(line);
  } else {
    // skip "/Encoding" + one whitespace char,
    // then look for 'def' preceded by PostScript whitespace
    p = line + 10;
    line = NULL;
    for (; p < (char *)file + len; ++p) {
      if ((*p == ' ' || *p == '\t' || *p == '\x0a' ||
	   *p == '\x0d' || *p == '\x0c' || *p == '\0') &&
	  p + 4 <= (char *)file + len &&
	  !strncmp(p + 1, "def", 3)) {
	line = p + 4;
	break;
      }
    }
  }

  // some fonts have two /Encoding entries in their dictionary, so we
  // check for a second one here
  if (line) {
    for (line2 = line, i = 0;
	 i < 20 && line2 && strncmp(line2, "/Encoding", 9);
	 line2 = getNextLine(line2), ++i) ;
    if (i < 20 && line2) {
      (*outputFunc)(outputStream, line, line2 - line);
      if (!strncmp(line2, "/Encoding StandardEncoding def", 30)) {
	line = getNextLine(line2);
      } else {
	// skip "/Encoding" + one whitespace char,
	// then look for 'def' preceded by PostScript whitespace
	p = line2 + 10;
	line = NULL;
	for (; p < (char *)file + len; ++p) {
	  if ((*p == ' ' || *p == '\t' || *p == '\x0a' ||
	       *p == '\x0d' || *p == '\x0c' || *p == '\0') &&
	      p + 4 <= (char *)file + len &&
	      !strncmp(p + 1, "def", 3)) {
	    line = p + 4;
	    break;
	  }
	}
      }
    }

    // copy everything after the encoding
    if (line) {
      (*outputFunc)(outputStream, line, ((char *)file + len) - line);
    }
  }
}

char *FoFiType1::getNextLine(char *line) {
  while (line < (char *)file + len && *line != '\x0a' && *line != '\x0d') {
    ++line;
  }
  if (line < (char *)file + len && *line == '\x0d') {
    ++line;
  }
  if (line < (char *)file + len && *line == '\x0a') {
    ++line;
  }
  if (line >= (char *)file + len) {
    return NULL;
  }
  return line;
}

void FoFiType1::parse() {
  char *line, *line1, *p, *p2;
  char buf[256];
  char c;
  int n, code, i, j;

  for (i = 1, line = (char *)file;
       i <= 100 && line && (!name || !encoding);
       ++i) {

    // get font name
    if (!name && !strncmp(line, "/FontName", 9)) {
      strncpy(buf, line, 255);
      buf[255] = '\0';
      if ((p = strchr(buf+9, '/')) &&
	  (p = strtok(p+1, " \t\n\r"))) {
	name = copyString(p);
      }
      line = getNextLine(line);

    // get encoding
    } else if (!encoding &&
	       !strncmp(line, "/Encoding StandardEncoding def", 30)) {
      encoding = fofiType1StandardEncoding;
    } else if (!encoding &&
	       !strncmp(line, "/Encoding 256 array", 19)) {
      encoding = (char **)gmallocn(256, sizeof(char *));
      for (j = 0; j < 256; ++j) {
	encoding[j] = NULL;
      }
      for (j = 0, line = getNextLine(line);
	   j < 300 && line && (line1 = getNextLine(line));
	   ++j, line = line1) {
	if ((n = line1 - line) > 255) {
	  n = 255;
	}
	strncpy(buf, line, n);
	buf[n] = '\0';
	for (p = buf; *p == ' ' || *p == '\t'; ++p) ;
	if (!strncmp(p, "dup", 3)) {
	  for (p += 3; *p == ' ' || *p == '\t'; ++p) ;
	  for (p2 = p; *p2 >= '0' && *p2 <= '9'; ++p2) ;
	  if (*p2) {
	    c = *p2;
	    *p2 = '\0';
	    code = atoi(p);
	    *p2 = c;
	    if (code == 8 && *p2 == '#') {
	      code = 0;
	      for (++p2; *p2 >= '0' && *p2 <= '7'; ++p2) {
		code = code * 8 + (*p2 - '0');
	      }
	    }
	    if (code < 256) {
	      for (p = p2; *p == ' ' || *p == '\t'; ++p) ;
	      if (*p == '/') {
		++p;
		for (p2 = p; *p2 && *p2 != ' ' && *p2 != '\t'; ++p2) ;
		*p2 = '\0';
		encoding[code] = copyString(p);
	      }
	    }
	  }
	} else {
	  if (strtok(buf, " \t") &&
	      (p = strtok(NULL, " \t\n\r")) && !strcmp(p, "def")) {
	    break;
	  }
	}
      }
      //~ check for getinterval/putinterval junk

    } else {
      line = getNextLine(line);
    }
  }

  parsed = gTrue;
}
//========================================================================
//
// FoFiType1.h
//
// Copyright 1999-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef FOFITYPE1_H
#define FOFITYPE1_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "gtypes.h"
#include "FoFiBase.h"

//------------------------------------------------------------------------
// FoFiType1
//------------------------------------------------------------------------

class FoFiType1: public FoFiBase {
public:

  // Create a FoFiType1 object from a memory buffer.
  static FoFiType1 *make(char *fileA, int lenA);

  // Create a FoFiType1 object from a file on disk.
  static FoFiType1 *load(char *fileName);

  virtual ~FoFiType1();

  // Return the font name.
  char *getName();

  // Return the encoding, as an array of 256 names (any of which may
  // be NULL).
  char **getEncoding();

  // Write a version of the Type 1 font file with a new encoding.
  void writeEncoded(char **newEncoding,
		    FoFiOutputFunc outputFunc, void *outputStream);

private:

  FoFiType1(char *fileA, int lenA, GBool freeFileDataA);

  char *getNextLine(char *line);
  void parse();

  char *name;
  char **encoding;
  GBool parsed;
};

#endif
//========================================================================
//
// FoFiType1C.cc
//
// Copyright 1999-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "gmem.h"
#include "GString.h"
#include "FoFiEncodings.h"
#include "FoFiType1C.h"

//------------------------------------------------------------------------

static char hexChars[17] = "0123456789ABCDEF";

//------------------------------------------------------------------------
// FoFiType1C
//------------------------------------------------------------------------

FoFiType1C *FoFiType1C::make(char *fileA, int lenA) {
  FoFiType1C *ff;

  ff = new FoFiType1C(fileA, lenA, gFalse);
  if (!ff->parse()) {
    delete ff;
    return NULL;
  }
  return ff;
}

FoFiType1C *FoFiType1C::load(char *fileName) {
  FoFiType1C *ff;
  char *fileA;
  int lenA;

  if (!(fileA = FoFiBase::readFile(fileName, &lenA))) {
    return NULL;
  }
  ff = new FoFiType1C(fileA, lenA, gTrue);
  if (!ff->parse()) {
    delete ff;
    return NULL;
  }
  return ff;
}

FoFiType1C::FoFiType1C(char *fileA, int lenA, GBool freeFileDataA):
  FoFiBase(fileA, lenA, freeFileDataA)
{
  name = NULL;
  encoding = NULL;
  privateDicts = NULL;
  fdSelect = NULL;
  charset = NULL;
}

FoFiType1C::~FoFiType1C() {
  int i;

  if (name) {
    delete name;
  }
  if (encoding &&
      encoding != fofiType1StandardEncoding &&
      encoding != fofiType1ExpertEncoding) {
    for (i = 0; i < 256; ++i) {
      gfree(encoding[i]);
    }
    gfree(encoding);
  }
  if (privateDicts) {
    gfree(privateDicts);
  }
  if (fdSelect) {
    gfree(fdSelect);
  }
  if (charset &&
      charset != fofiType1CISOAdobeCharset &&
      charset != fofiType1CExpertCharset &&
      charset != fofiType1CExpertSubsetCharset) {
    gfree(charset);
  }
}

char *FoFiType1C::getName() {
  return name ? name->getCString() : (char *)NULL;
}

char **FoFiType1C::getEncoding() {
  return encoding;
}

Gushort *FoFiType1C::getCIDToGIDMap(int *nCIDs) {
  Gushort *map;
  int n, i;

  // a CID font's top dict has ROS as the first operator
  if (topDict.firstOp != 0x0c1e) {
    *nCIDs = 0;
    return NULL;
  }

  // in a CID font, the charset data is the GID-to-CID mapping, so all
  // we have to do is reverse it
  n = 0;
  for (i = 0; i < nGlyphs; ++i) {
    if (charset[i] > n) {
      n = charset[i];
    }
  }
  ++n;
  map = (Gushort *)gmallocn(n, sizeof(Gushort));
  memset(map, 0, n * sizeof(Gushort));
  for (i = 0; i < nGlyphs; ++i) {
    map[charset[i]] = i;
  }
  *nCIDs = n;
  return map;
}

void FoFiType1C::convertToType1(char *psName, char **newEncoding, GBool ascii,
				FoFiOutputFunc outputFunc,
				void *outputStream) {
  int psNameLen;
  Type1CEexecBuf eb;
  Type1CIndex subrIdx;
  Type1CIndexVal val;
  GString *buf;
  char buf2[256];
  char **enc;
  GBool ok;
  int i;

  if (psName) {
    psNameLen = strlen(psName);
  } else {
    psName = name->getCString();
    psNameLen = name->getLength();
  }

  // write header and font dictionary, up to encoding
  ok = gTrue;
  (*outputFunc)(outputStream, "%!FontType1-1.0: ", 17);
  (*outputFunc)(outputStream, psName, psNameLen);
  if (topDict.versionSID != 0) {
    getString(topDict.versionSID, buf2, &ok);
    (*outputFunc)(outputStream, buf2, strlen(buf2));
  }
  (*outputFunc)(outputStream, "\n", 1);
  // the dictionary needs room for 12 entries: the following 9, plus
  // Private and CharStrings (in the eexec section) and FID (which is
  // added by definefont)
  (*outputFunc)(outputStream, "12 dict begin\n", 14);
  (*outputFunc)(outputStream, "/FontInfo 10 dict dup begin\n", 28);
  if (topDict.versionSID != 0) {
    (*outputFunc)(outputStream, "/version (", 10);
    (*outputFunc)(outputStream, buf2, strlen(buf2));
    (*outputFunc)(outputStream, ") readonly def\n", 15);
  }
  if (topDict.noticeSID != 0) {
    getString(topDict.noticeSID, buf2, &ok);
    (*outputFunc)(outputStream, "/Notice (", 9);
    (*outputFunc)(outputStream, buf2, strlen(buf2));
    (*outputFunc)(outputStream, ") readonly def\n", 15);
  }
  if (topDict.copyrightSID != 0) {
    getString(topDict.copyrightSID, buf2, &ok);
    (*outputFunc)(outputStream, "/Copyright (", 12);
    (*outputFunc)(outputStream, buf2, strlen(buf2));
    (*outputFunc)(outputStream, ") readonly def\n", 15);
  }
  if (topDict.fullNameSID != 0) {
    getString(topDict.fullNameSID, buf2, &ok);
    (*outputFunc)(outputStream, "/FullName (", 11);
    (*outputFunc)(outputStream, buf2, strlen(buf2));
    (*outputFunc)(outputStream, ") readonly def\n", 15);
  }
  if (topDict.familyNameSID != 0) {
    getString(topDict.familyNameSID, buf2, &ok);
    (*outputFunc)(outputStream, "/FamilyName (", 13);
    (*outputFunc)(outputStream, buf2, strlen(buf2));
    (*outputFunc)(outputStream, ") readonly def\n", 15);
  }
  if (topDict.weightSID != 0) {
    getString(topDict.weightSID, buf2, &ok);
    (*outputFunc)(outputStream, "/Weight (", 9);
    (*outputFunc)(outputStream, buf2, strlen(buf2));
    (*outputFunc)(outputStream, ") readonly def\n", 15);
  }
  if (topDict.isFixedPitch) {
    (*outputFunc)(outputStream, "/isFixedPitch true def\n", 23);
  } else {
    (*outputFunc)(outputStream, "/isFixedPitch false def\n", 24);
  }
  buf = GString::format("/ItalicAngle {0:.4g} def\n", topDict.italicAngle);
  (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
  delete buf;
  buf = GString::format("/UnderlinePosition {0:.4g} def\n",
			topDict.underlinePosition);
  (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
  delete buf;
  buf = GString::format("/UnderlineThickness {0:.4g} def\n",
			topDict.underlineThickness);
  (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
  delete buf;
  (*outputFunc)(outputStream, "end readonly def\n", 17);
  (*outputFunc)(outputStream, "/FontName /", 11);
  (*outputFunc)(outputStream, psName, psNameLen);
  (*outputFunc)(outputStream, " def\n", 5);
  buf = GString::format("/PaintType {0:d} def\n", topDict.paintType);
  (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
  delete buf;
  (*outputFunc)(outputStream, "/FontType 1 def\n", 16);
  buf = GString::format("/FontMatrix [{0:.8g} {1:.8g} {2:.8g} {3:.8g} {4:.8g} {5:.8g}] readonly def\n",
			topDict.fontMatrix[0], topDict.fontMatrix[1],
			topDict.fontMatrix[2], topDict.fontMatrix[3],
			topDict.fontMatrix[4], topDict.fontMatrix[5]);
  (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
  delete buf;
  buf = GString::format("/FontBBox [{0:.4g} {1:.4g} {2:.4g} {3:.4g}] readonly def\n",
			topDict.fontBBox[0], topDict.fontBBox[1],
			topDict.fontBBox[2], topDict.fontBBox[3]);
  (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
  delete buf;
  buf = GString::format("/StrokeWidth {0:.4g} def\n", topDict.strokeWidth);
  (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
  delete buf;
  if (topDict.uniqueID != 0) {
    buf = GString::format("/UniqueID {0:d} def\n", topDict.uniqueID);
    (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
    delete buf;
  }

  // write the encoding
  (*outputFunc)(outputStream, "/Encoding ", 10);
  if (!newEncoding && encoding == fofiType1StandardEncoding) {
    (*outputFunc)(outputStream, "StandardEncoding def\n", 21);
  } else {
    (*outputFunc)(outputStream, "256 array\n", 10);
    (*outputFunc)(outputStream,
		  "0 1 255 {1 index exch /.notdef put} for\n", 40);
    enc = newEncoding ? newEncoding : encoding;
    for (i = 0; i < 256; ++i) {
      if (enc[i]) {
	buf = GString::format("dup {0:d} /{1:s} put\n", i, enc[i]);
	(*outputFunc)(outputStream, buf->getCString(), buf->getLength());
	delete buf;
      }
    }
    (*outputFunc)(outputStream, "readonly def\n", 13);
  }
  (*outputFunc)(outputStream, "currentdict end\n", 16);

  // start the binary section
  (*outputFunc)(outputStream, "currentfile eexec\n", 18);
  eb.outputFunc = outputFunc;
  eb.outputStream = outputStream;
  eb.ascii = ascii;
  eb.r1 = 55665;
  eb.line = 0;

  // write the private dictionary
  eexecWrite(&eb, "\x83\xca\x73\xd5");
  eexecWrite(&eb, "dup /Private 32 dict dup begin\n");
  eexecWrite(&eb, "/RD {string currentfile exch readstring pop}"
	     " executeonly def\n");
  eexecWrite(&eb, "/ND {noaccess def} executeonly def\n");
  eexecWrite(&eb, "/NP {noaccess put} executeonly def\n");
  eexecWrite(&eb, "/MinFeature {16 16} def\n");
  eexecWrite(&eb, "/password 5839 def\n");
  if (privateDicts[0].nBlueValues) {
    eexecWrite(&eb, "/BlueValues [");
    for (i = 0; i < privateDicts[0].nBlueValues; ++i) {
      buf = GString::format("{0:s}{1:d}",
			    i > 0 ? " " : "", privateDicts[0].blueValues[i]);
      eexecWrite(&eb, buf->getCString());
      delete buf;
    }
    eexecWrite(&eb, "] def\n");
  }
  if (privateDicts[0].nOtherBlues) {
    eexecWrite(&eb, "/OtherBlues [");
    for (i = 0; i < privateDicts[0].nOtherBlues; ++i) {
      buf = GString::format("{0:s}{1:d}",
			    i > 0 ? " " : "", privateDicts[0].otherBlues[i]);
      eexecWrite(&eb, buf->getCString());
      delete buf;
    }
    eexecWrite(&eb, "] def\n");
  }
  if (privateDicts[0].nFamilyBlues) {
    eexecWrite(&eb, "/FamilyBlues [");
    for (i = 0; i < privateDicts[0].nFamilyBlues; ++i) {
      buf = GString::format("{0:s}{1:d}",
			    i > 0 ? " " : "", privateDicts[0].familyBlues[i]);
      eexecWrite(&eb, buf->getCString());
      delete buf;
    }
    eexecWrite(&eb, "] def\n");
  }
  if (privateDicts[0].nFamilyOtherBlues) {
    eexecWrite(&eb, "/FamilyOtherBlues [");
    for (i = 0; i < privateDicts[0].nFamilyOtherBlues; ++i) {
      buf = GString::format("{0:s}{1:d}", i > 0 ? " " : "",
			    privateDicts[0].familyOtherBlues[i]);
      eexecWrite(&eb, buf->getCString());
      delete buf;
    }
    eexecWrite(&eb, "] def\n");
  }
  if (privateDicts[0].blueScale != 0.039625) {
    buf = GString::format("/BlueScale {0:.4g} def\n",
			  privateDicts[0].blueScale);
    eexecWrite(&eb, buf->getCString());
    delete buf;
  }
  if (privateDicts[0].blueShift != 7) {
    buf = GString::format("/BlueShift {0:d} def\n", privateDicts[0].blueShift);
    eexecWrite(&eb, buf->getCString());
    delete buf;
  }
  if (privateDicts[0].blueFuzz != 1) {
    buf = GString::format("/BlueFuzz {0:d} def\n", privateDicts[0].blueFuzz);
    eexecWrite(&eb, buf->getCString());
    delete buf;
  }
  if (privateDicts[0].hasStdHW) {
    buf = GString::format("/StdHW [{0:.4g}] def\n", privateDicts[0].stdHW);
    eexecWrite(&eb, buf->getCString());
    delete buf;
  }
  if (privateDicts[0].hasStdVW) {
    buf = GString::format("/StdVW [{0:.4g}] def\n", privateDicts[0].stdVW);
    eexecWrite(&eb, buf->getCString());
    delete buf;
  }
  if (privateDicts[0].nStemSnapH) {
    eexecWrite(&eb, "/StemSnapH [");
    for (i = 0; i < privateDicts[0].nStemSnapH; ++i) {
      buf = GString::format("{0:s}{1:.4g}",
			    i > 0 ? " " : "", privateDicts[0].stemSnapH[i]);
      eexecWrite(&eb, buf->getCString());
      delete buf;
    }
    eexecWrite(&eb, "] def\n");
  }
  if (privateDicts[0].nStemSnapV) {
    eexecWrite(&eb, "/StemSnapV [");
    for (i = 0; i < privateDicts[0].nStemSnapV; ++i) {
      buf = GString::format("{0:s}{1:.4g}",
			    i > 0 ? " " : "", privateDicts[0].stemSnapV[i]);
      eexecWrite(&eb, buf->getCString());
      delete buf;
    }
    eexecWrite(&eb, "] def\n");
  }
  if (privateDicts[0].hasForceBold) {
    buf = GString::format("/ForceBold {0:s} def\n",
			  privateDicts[0].forceBold ? "true" : "false");
    eexecWrite(&eb, buf->getCString());
    delete buf;
  }
  if (privateDicts[0].forceBoldThreshold != 0) {
    buf = GString::format("/ForceBoldThreshold {0:.4g} def\n",
			  privateDicts[0].forceBoldThreshold);
    eexecWrite(&eb, buf->getCString());
    delete buf;
  }
  if (privateDicts[0].languageGroup != 0) {
    buf = GString::format("/LanguageGroup {0:d} def\n",
			  privateDicts[0].languageGroup);
    eexecWrite(&eb, buf->getCString());
    delete buf;
  }
  if (privateDicts[0].expansionFactor != 0.06) {
    buf = GString::format("/ExpansionFactor {0:.4g} def\n",
			  privateDicts[0].expansionFactor);
    eexecWrite(&eb, buf->getCString());
    delete buf;
  }

  // set up subroutines
  ok = gTrue;
  getIndex(privateDicts[0].subrsOffset, &subrIdx, &ok);
  if (!ok) {
    subrIdx.pos = -1;
  }

  // write the CharStrings
  buf = GString::format("2 index /CharStrings {0:d} dict dup begin\n",
			nGlyphs);
  eexecWrite(&eb, buf->getCString());
  delete buf;
  for (i = 0; i < nGlyphs; ++i) {
    ok = gTrue;
    getIndexVal(&charStringsIdx, i, &val, &ok);
    if (ok) {
      getString(charset[i], buf2, &ok);
      if (ok) {
	eexecCvtGlyph(&eb, buf2, val.pos, val.len, &subrIdx, &privateDicts[0]);
      }
    }
  }
  eexecWrite(&eb, "end\n");
  eexecWrite(&eb, "end\n");
  eexecWrite(&eb, "readonly put\n");
  eexecWrite(&eb, "noaccess put\n");
  eexecWrite(&eb, "dup /FontName get exch definefont pop\n");
  eexecWrite(&eb, "mark currentfile closefile\n");

  // trailer
  if (ascii && eb.line > 0) {
    (*outputFunc)(outputStream, "\n", 1);
  }
  for (i = 0; i < 8; ++i) {
    (*outputFunc)(outputStream, "0000000000000000000000000000000000000000000000000000000000000000\n", 65);
  }
  (*outputFunc)(outputStream, "cleartomark\n", 12);
}

void FoFiType1C::convertToCIDType0(char *psName,
				   FoFiOutputFunc outputFunc,
				   void *outputStream) {
  int *cidMap;
  GString *charStrings;
  int *charStringOffsets;
  Type1CIndex subrIdx;
  Type1CIndexVal val;
  int nCIDs, gdBytes;
  GString *buf;
  char buf2[256];
  GBool ok;
  int gid, offset, n, i, j, k;

  // compute the CID count and build the CID-to-GID mapping
  nCIDs = 0;
  for (i = 0; i < nGlyphs; ++i) {
    if (charset[i] >= nCIDs) {
      nCIDs = charset[i] + 1;
    }
  }
  cidMap = (int *)gmallocn(nCIDs, sizeof(int));
  for (i = 0; i < nCIDs; ++i) {
    cidMap[i] = -1;
  }
  for (i = 0; i < nGlyphs; ++i) {
    cidMap[charset[i]] = i;
  }

  // build the charstrings
  charStrings = new GString();
  charStringOffsets = (int *)gmallocn(nCIDs + 1, sizeof(int));
  for (i = 0; i < nCIDs; ++i) {
    charStringOffsets[i] = charStrings->getLength();
    if ((gid = cidMap[i]) >= 0) {
      ok = gTrue;
      getIndexVal(&charStringsIdx, gid, &val, &ok);
      if (ok) {
	getIndex(privateDicts[fdSelect[gid]].subrsOffset, &subrIdx, &ok);
	if (!ok) {
	  subrIdx.pos = -1;
	}
	cvtGlyph(val.pos, val.len, charStrings,
		 &subrIdx, &privateDicts[fdSelect[gid]], gTrue);
      }
    }
  }
  charStringOffsets[nCIDs] = charStrings->getLength();

  // compute gdBytes = number of bytes needed for charstring offsets
  // (offset size needs to account for the charstring offset table,
  // with a worst case of five bytes per entry, plus the charstrings
  // themselves)
  i = (nCIDs + 1) * 5 + charStrings->getLength();
  if (i < 0x100) {
    gdBytes = 1;
  } else if (i < 0x10000) {
    gdBytes = 2;
  } else if (i < 0x1000000) {
    gdBytes = 3;
  } else {
    gdBytes = 4;
  }

  // begin the font dictionary
  (*outputFunc)(outputStream, "/CIDInit /ProcSet findresource begin\n", 37);
  (*outputFunc)(outputStream, "20 dict begin\n", 14);
  (*outputFunc)(outputStream, "/CIDFontName /", 14);
  (*outputFunc)(outputStream, psName, strlen(psName));
  (*outputFunc)(outputStream, " def\n", 5);
  (*outputFunc)(outputStream, "/CIDFontType 0 def\n", 19);
  (*outputFunc)(outputStream, "/CIDSystemInfo 3 dict dup begin\n", 32);
  if (topDict.registrySID > 0 && topDict.orderingSID > 0) {
    ok = gTrue;
    getString(topDict.registrySID, buf2, &ok);
    if (ok) {
      (*outputFunc)(outputStream, "  /Registry (", 13);
      (*outputFunc)(outputStream, buf2, strlen(buf2));
      (*outputFunc)(outputStream, ") def\n", 6);
    }
    ok = gTrue;
    getString(topDict.orderingSID, buf2, &ok);
    if (ok) {
      (*outputFunc)(outputStream, "  /Ordering (", 13);
      (*outputFunc)(outputStream, buf2, strlen(buf2));
      (*outputFunc)(outputStream, ") def\n", 6);
    }
  } else {
    (*outputFunc)(outputStream, "  /Registry (Adobe) def\n", 24);
    (*outputFunc)(outputStream, "  /Ordering (Identity) def\n", 27);
  }
  buf = GString::format("  /Supplement {0:d} def\n", topDict.supplement);
  (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
  delete buf;
  (*outputFunc)(outputStream, "end def\n", 8);
  if (topDict.hasFontMatrix) {
    buf = GString::format("/FontMatrix [{0:.8g} {1:.8g} {2:.8g} {3:.8g} {4:.8g} {5:.8g}] def\n",
			  topDict.fontMatrix[0], topDict.fontMatrix[1],
			  topDict.fontMatrix[2], topDict.fontMatrix[3],
			  topDict.fontMatrix[4], topDict.fontMatrix[5]);
    (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
    delete buf;
  } else if (privateDicts[0].hasFontMatrix) {
    (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
  } else {
    (*outputFunc)(outputStream,
		  "/FontMatrix [0.001 0 0 0.001 0 0] def\n", 38);
  }
  buf = GString::format("/FontBBox [{0:.4g} {1:.4g} {2:.4g} {3:.4g}] def\n",
			topDict.fontBBox[0], topDict.fontBBox[1],
			topDict.fontBBox[2], topDict.fontBBox[3]);
  (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
  delete buf;
  (*outputFunc)(outputStream, "/FontInfo 1 dict dup begin\n", 27);
  (*outputFunc)(outputStream, "  /FSType 8 def\n", 16);
  (*outputFunc)(outputStream, "end def\n", 8);

  // CIDFont-specific entries
  buf = GString::format("/CIDCount {0:d} def\n", nCIDs);
  (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
  delete buf;
  (*outputFunc)(outputStream, "/FDBytes 1 def\n", 15);
  buf = GString::format("/GDBytes {0:d} def\n", gdBytes);
  (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
  delete buf;
  (*outputFunc)(outputStream, "/CIDMapOffset 0 def\n", 20);
  if (topDict.paintType != 0) {
    buf = GString::format("/PaintType {0:d} def\n", topDict.paintType);
    (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
    delete buf;
    buf = GString::format("/StrokeWidth {0:.4g} def\n", topDict.strokeWidth);
    (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
    delete buf;
  }

  // FDArray entry
  buf = GString::format("/FDArray {0:d} array\n", nFDs);
  (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
  delete buf;
  for (i = 0; i < nFDs; ++i) {
    buf = GString::format("dup {0:d} 10 dict begin\n", i);
    (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
    delete buf;
    (*outputFunc)(outputStream, "/FontType 1 def\n", 16);
    if (privateDicts[i].hasFontMatrix) {
      buf = GString::format("/FontMatrix [{0:.8g} {1:.8g} {2:.8g} {3:.8g} {4:.8g} {5:.8g}] def\n",
			    privateDicts[i].fontMatrix[0],
			    privateDicts[i].fontMatrix[1],
			    privateDicts[i].fontMatrix[2],
			    privateDicts[i].fontMatrix[3],
			    privateDicts[i].fontMatrix[4],
			    privateDicts[i].fontMatrix[5]);
      (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
      delete buf;
    } else {
      (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
    }
    buf = GString::format("/PaintType {0:d} def\n", topDict.paintType);
    (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
    delete buf;
    (*outputFunc)(outputStream, "/Private 32 dict begin\n", 23);
    if (privateDicts[i].nBlueValues) {
      (*outputFunc)(outputStream, "/BlueValues [", 13);
      for (j = 0; j < privateDicts[i].nBlueValues; ++j) {
	buf = GString::format("{0:s}{1:d}",
			      j > 0 ? " " : "", privateDicts[i].blueValues[j]);
	(*outputFunc)(outputStream, buf->getCString(), buf->getLength());
	delete buf;
      }
      (*outputFunc)(outputStream, "] def\n", 6);
    }
    if (privateDicts[i].nOtherBlues) {
      (*outputFunc)(outputStream, "/OtherBlues [", 13);
      for (j = 0; j < privateDicts[i].nOtherBlues; ++j) {
	buf = GString::format("{0:s}{1:d}",
			      j > 0 ? " " : "", privateDicts[i].otherBlues[j]);
	(*outputFunc)(outputStream, buf->getCString(), buf->getLength());
	delete buf;
      }
      (*outputFunc)(outputStream, "] def\n", 6);
    }
    if (privateDicts[i].nFamilyBlues) {
      (*outputFunc)(outputStream, "/FamilyBlues [", 14);
      for (j = 0; j < privateDicts[i].nFamilyBlues; ++j) {
	buf = GString::format("{0:s}{1:d}",
			      j > 0 ? " " : "",
			      privateDicts[i].familyBlues[j]);
	(*outputFunc)(outputStream, buf->getCString(), buf->getLength());
	delete buf;
      }
      (*outputFunc)(outputStream, "] def\n", 6);
    }
    if (privateDicts[i].nFamilyOtherBlues) {
      (*outputFunc)(outputStream, "/FamilyOtherBlues [", 19);
      for (j = 0; j < privateDicts[i].nFamilyOtherBlues; ++j) {
	buf = GString::format("{0:s}{1:d}", j > 0 ? " " : "",
			      privateDicts[i].familyOtherBlues[j]);
	(*outputFunc)(outputStream, buf->getCString(), buf->getLength());
	delete buf;
      }
      (*outputFunc)(outputStream, "] def\n", 6);
    }
    if (privateDicts[i].blueScale != 0.039625) {
      buf = GString::format("/BlueScale {0:.4g} def\n",
			    privateDicts[i].blueScale);
      (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
      delete buf;
    }
    if (privateDicts[i].blueShift != 7) {
      buf = GString::format("/BlueShift {0:d} def\n",
			    privateDicts[i].blueShift);
      (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
      delete buf;
    }
    if (privateDicts[i].blueFuzz != 1) {
      buf = GString::format("/BlueFuzz {0:d} def\n", privateDicts[i].blueFuzz);
      (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
      delete buf;
    }
    if (privateDicts[i].hasStdHW) {
      buf = GString::format("/StdHW [{0:.4g}] def\n", privateDicts[i].stdHW);
      (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
      delete buf;
    }
    if (privateDicts[i].hasStdVW) {
      buf = GString::format("/StdVW [{0:.4g}] def\n", privateDicts[i].stdVW);
      (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
      delete buf;
    }
    if (privateDicts[i].nStemSnapH) {
      (*outputFunc)(outputStream, "/StemSnapH [", 12);
      for (j = 0; j < privateDicts[i].nStemSnapH; ++j) {
	buf = GString::format("{0:s}{1:.4g}",
			      j > 0 ? " " : "", privateDicts[i].stemSnapH[j]);
	(*outputFunc)(outputStream, buf->getCString(), buf->getLength());
	delete buf;
      }
      (*outputFunc)(outputStream, "] def\n", 6);
    }
    if (privateDicts[i].nStemSnapV) {
      (*outputFunc)(outputStream, "/StemSnapV [", 12);
      for (j = 0; j < privateDicts[i].nStemSnapV; ++j) {
	buf = GString::format("{0:s}{1:.4g}",
			      j > 0 ? " " : "", privateDicts[i].stemSnapV[j]);
	(*outputFunc)(outputStream, buf->getCString(), buf->getLength());
	delete buf;
      }
      (*outputFunc)(outputStream, "] def\n", 6);
    }
    if (privateDicts[i].hasForceBold) {
      buf = GString::format("/ForceBold {0:s} def\n",
			    privateDicts[i].forceBold ? "true" : "false");
      (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
      delete buf;
    }
    if (privateDicts[i].forceBoldThreshold != 0) {
      buf = GString::format("/ForceBoldThreshold {0:.4g} def\n",
			    privateDicts[i].forceBoldThreshold);
      (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
      delete buf;
    }
    if (privateDicts[i].languageGroup != 0) {
      buf = GString::format("/LanguageGroup {0:d} def\n",
			    privateDicts[i].languageGroup);
      (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
      delete buf;
    }
    if (privateDicts[i].expansionFactor != 0.06) {
      buf = GString::format("/ExpansionFactor {0:.4g} def\n",
			    privateDicts[i].expansionFactor);
      (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
      delete buf;
    }
    (*outputFunc)(outputStream, "currentdict end def\n", 20);
    (*outputFunc)(outputStream, "currentdict end put\n", 20);
  }
  (*outputFunc)(outputStream, "def\n", 4);

  // start the binary section
  offset = (nCIDs + 1) * (1 + gdBytes);
  buf = GString::format("(Hex) {0:d} StartData\n",
			offset + charStrings->getLength());
  (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
  delete buf;

  // write the charstring offset (CIDMap) table
  for (i = 0; i <= nCIDs; i += 6) {
    for (j = 0; j < 6 && i+j <= nCIDs; ++j) {
      if (i+j < nCIDs && cidMap[i+j] >= 0) {
	buf2[0] = (char)fdSelect[cidMap[i+j]];
      } else {
	buf2[0] = (char)0;
      }
      n = offset + charStringOffsets[i+j];
      for (k = gdBytes; k >= 1; --k) {
	buf2[k] = (char)(n & 0xff);
	n >>= 8;
      }
      for (k = 0; k <= gdBytes; ++k) {
	buf = GString::format("{0:02x}", buf2[k] & 0xff);
	(*outputFunc)(outputStream, buf->getCString(), buf->getLength());
	delete buf;
      }
    }
    (*outputFunc)(outputStream, "\n", 1);
  }

  // write the charstring data
  n = charStrings->getLength();
  for (i = 0; i < n; i += 32) {
    for (j = 0; j < 32 && i+j < n; ++j) {
      buf = GString::format("{0:02x}", charStrings->getChar(i+j) & 0xff);
      (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
      delete buf;
    }
    if (i + 32 >= n) {
      (*outputFunc)(outputStream, ">", 1);
    }
    (*outputFunc)(outputStream, "\n", 1);
  }

  gfree(charStringOffsets);
  delete charStrings;
  gfree(cidMap);
}

void FoFiType1C::convertToType0(char *psName,
				FoFiOutputFunc outputFunc,
				void *outputStream) {
  int *cidMap;
  Type1CIndex subrIdx;
  Type1CIndexVal val;
  int nCIDs;
  GString *buf;
  Type1CEexecBuf eb;
  GBool ok;
  int fd, i, j, k;

  // compute the CID count and build the CID-to-GID mapping
  nCIDs = 0;
  for (i = 0; i < nGlyphs; ++i) {
    if (charset[i] >= nCIDs) {
      nCIDs = charset[i] + 1;
    }
  }
  cidMap = (int *)gmallocn(nCIDs, sizeof(int));
  for (i = 0; i < nCIDs; ++i) {
    cidMap[i] = -1;
  }
  for (i = 0; i < nGlyphs; ++i) {
    cidMap[charset[i]] = i;
  }

  // write the descendant Type 1 fonts
  for (i = 0; i < nCIDs; i += 256) {

    //~ this assumes that all CIDs in this block have the same FD --
    //~ to handle multiple FDs correctly, need to somehow divide the
    //~ font up by FD; as a kludge we ignore CID 0, which is .notdef
    fd = 0;
    for (j = i==0 ? 1 : 0; j < 256 && i+j < nCIDs; ++j) {
      if (cidMap[i+j] >= 0) {
	fd = fdSelect[cidMap[i+j]];
	break;
      }
    }

    // font dictionary (unencrypted section)
    (*outputFunc)(outputStream, "16 dict begin\n", 14);
    (*outputFunc)(outputStream, "/FontName /", 11);
    (*outputFunc)(outputStream, psName, strlen(psName));
    buf = GString::format("_{0:02x} def\n", i >> 8);
    (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
    delete buf;
    (*outputFunc)(outputStream, "/FontType 1 def\n", 16);
    if (privateDicts[fd].hasFontMatrix) {
      buf = GString::format("/FontMatrix [{0:.8g} {1:.8g} {2:.8g} {3:.8g} {4:.8g} {5:.8g}] def\n",
			    privateDicts[fd].fontMatrix[0],
			    privateDicts[fd].fontMatrix[1],
			    privateDicts[fd].fontMatrix[2],
			    privateDicts[fd].fontMatrix[3],
			    privateDicts[fd].fontMatrix[4],
			    privateDicts[fd].fontMatrix[5]);
      (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
      delete buf;
    } else if (topDict.hasFontMatrix) {
      (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
    } else {
      (*outputFunc)(outputStream,
		    "/FontMatrix [0.001 0 0 0.001 0 0] def\n", 38);
    }
    buf = GString::format("/FontBBox [{0:.4g} {1:.4g} {2:.4g} {3:.4g}] def\n",
			  topDict.fontBBox[0], topDict.fontBBox[1],
			  topDict.fontBBox[2], topDict.fontBBox[3]);
    (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
    delete buf;
    buf = GString::format("/PaintType {0:d} def\n", topDict.paintType);
    (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
    delete buf;
    if (topDict.paintType != 0) {
      buf = GString::format("/StrokeWidth {0:.4g} def\n", topDict.strokeWidth);
      (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
      delete buf;
    }
    (*outputFunc)(outputStream, "/Encoding 256 array\n", 20);
    for (j = 0; j < 256 && i+j < nCIDs; ++j) {
      buf = GString::format("dup {0:d} /c{1:02x} put\n", j, j);
      (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
      delete buf;
    }
    if (j < 256) {
      buf = GString::format("{0:d} 1 255 {{ 1 index exch /.notdef put }} for\n",
			    j);
      (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
      delete buf;
    }
    (*outputFunc)(outputStream, "readonly def\n", 13);
    (*outputFunc)(outputStream, "currentdict end\n", 16);

    // start the binary section
    (*outputFunc)(outputStream, "currentfile eexec\n", 18);
    eb.outputFunc = outputFunc;
    eb.outputStream = outputStream;
    eb.ascii = gTrue;
    eb.r1 = 55665;
    eb.line = 0;

    // start the private dictionary
    eexecWrite(&eb, "\x83\xca\x73\xd5");
    eexecWrite(&eb, "dup /Private 32 dict dup begin\n");
    eexecWrite(&eb, "/RD {string currentfile exch readstring pop}"
	       " executeonly def\n");
    eexecWrite(&eb, "/ND {noaccess def} executeonly def\n");
    eexecWrite(&eb, "/NP {noaccess put} executeonly def\n");
    eexecWrite(&eb, "/MinFeature {16 16} def\n");
    eexecWrite(&eb, "/password 5839 def\n");
    if (privateDicts[fd].nBlueValues) {
      eexecWrite(&eb, "/BlueValues [");
      for (k = 0; k < privateDicts[fd].nBlueValues; ++k) {
	buf = GString::format("{0:s}{1:d}",
			      k > 0 ? " " : "",
			      privateDicts[fd].blueValues[k]);
	eexecWrite(&eb, buf->getCString());
	delete buf;
      }
      eexecWrite(&eb, "] def\n");
    }
    if (privateDicts[fd].nOtherBlues) {
      eexecWrite(&eb, "/OtherBlues [");
      for (k = 0; k < privateDicts[fd].nOtherBlues; ++k) {
	buf = GString::format("{0:s}{1:d}",
			      k > 0 ? " " : "",
			      privateDicts[fd].otherBlues[k]);
	eexecWrite(&eb, buf->getCString());
	delete buf;
      }
      eexecWrite(&eb, "] def\n");
    }
    if (privateDicts[fd].nFamilyBlues) {
      eexecWrite(&eb, "/FamilyBlues [");
      for (k = 0; k < privateDicts[fd].nFamilyBlues; ++k) {
	buf = GString::format("{0:s}{1:d}", k > 0 ? " " : "",
			      privateDicts[fd].familyBlues[k]);
	eexecWrite(&eb, buf->getCString());
	delete buf;
      }
      eexecWrite(&eb, "] def\n");
    }
    if (privateDicts[fd].nFamilyOtherBlues) {
      eexecWrite(&eb, "/FamilyOtherBlues [");
      for (k = 0; k < privateDicts[fd].nFamilyOtherBlues; ++k) {
	buf = GString::format("{0:s}{1:d}", k > 0 ? " " : "",
			      privateDicts[fd].familyOtherBlues[k]);
	eexecWrite(&eb, buf->getCString());
	delete buf;
      }
      eexecWrite(&eb, "] def\n");
    }
    if (privateDicts[fd].blueScale != 0.039625) {
      buf = GString::format("/BlueScale {0:.4g} def\n",
			    privateDicts[fd].blueScale);
      eexecWrite(&eb, buf->getCString());
      delete buf;
    }
    if (privateDicts[fd].blueShift != 7) {
      buf = GString::format("/BlueShift {0:d} def\n",
			    privateDicts[fd].blueShift);
      eexecWrite(&eb, buf->getCString());
      delete buf;
    }
    if (privateDicts[fd].blueFuzz != 1) {
      buf = GString::format("/BlueFuzz {0:d} def\n",
			    privateDicts[fd].blueFuzz);
      eexecWrite(&eb, buf->getCString());
      delete buf;
    }
    if (privateDicts[fd].hasStdHW) {
      buf = GString::format("/StdHW [{0:.4g}] def\n", privateDicts[fd].stdHW);
      eexecWrite(&eb, buf->getCString());
      delete buf;
    }
    if (privateDicts[fd].hasStdVW) {
      buf = GString::format("/StdVW [{0:.4g}] def\n", privateDicts[fd].stdVW);
      eexecWrite(&eb, buf->getCString());
      delete buf;
    }
    if (privateDicts[fd].nStemSnapH) {
      eexecWrite(&eb, "/StemSnapH [");
      for (k = 0; k < privateDicts[fd].nStemSnapH; ++k) {
	buf = GString::format("{0:s}{1:.4g}",
			      k > 0 ? " " : "", privateDicts[fd].stemSnapH[k]);
	eexecWrite(&eb, buf->getCString());
	delete buf;
      }
      eexecWrite(&eb, "] def\n");
    }
    if (privateDicts[fd].nStemSnapV) {
      eexecWrite(&eb, "/StemSnapV [");
      for (k = 0; k < privateDicts[fd].nStemSnapV; ++k) {
	buf = GString::format("{0:s}{1:.4g}",
			      k > 0 ? " " : "", privateDicts[fd].stemSnapV[k]);
	eexecWrite(&eb, buf->getCString());
	delete buf;
      }
      eexecWrite(&eb, "] def\n");
    }
    if (privateDicts[fd].hasForceBold) {
      buf = GString::format("/ForceBold {0:s} def\n",
			    privateDicts[fd].forceBold ? "true" : "false");
      eexecWrite(&eb, buf->getCString());
      delete buf;
    }
    if (privateDicts[fd].forceBoldThreshold != 0) {
      buf = GString::format("/ForceBoldThreshold {0:.4g} def\n",
			    privateDicts[fd].forceBoldThreshold);
      eexecWrite(&eb, buf->getCString());
      delete buf;
    }
    if (privateDicts[fd].languageGroup != 0) {
      buf = GString::format("/LanguageGroup {0:d} def\n",
			    privateDicts[fd].languageGroup);
      eexecWrite(&eb, buf->getCString());
      delete buf;
    }
    if (privateDicts[fd].expansionFactor != 0.06) {
      buf = GString::format("/ExpansionFactor {0:.4g} def\n",
			    privateDicts[fd].expansionFactor);
      eexecWrite(&eb, buf->getCString());
      delete buf;
    }

    // set up the subroutines
    ok = gTrue;
    getIndex(privateDicts[fd].subrsOffset, &subrIdx, &ok);
    if (!ok) {
      subrIdx.pos = -1;
    }

    // start the CharStrings
    eexecWrite(&eb, "2 index /CharStrings 256 dict dup begin\n");

    // write the .notdef CharString
    ok = gTrue;
    getIndexVal(&charStringsIdx, 0, &val, &ok);
    if (ok) {
      eexecCvtGlyph(&eb, ".notdef", val.pos, val.len,
		    &subrIdx, &privateDicts[fd]);
    }

    // write the CharStrings
    for (j = 0; j < 256 && i+j < nCIDs; ++j) {
      if (cidMap[i+j] >= 0) {
	ok = gTrue;
	getIndexVal(&charStringsIdx, cidMap[i+j], &val, &ok);
	if (ok) {
	  buf = GString::format("c{0:02x}", j);
	  eexecCvtGlyph(&eb, buf->getCString(), val.pos, val.len,
			&subrIdx, &privateDicts[fd]);
	  delete buf;
	}
      }
    }
    eexecWrite(&eb, "end\n");
    eexecWrite(&eb, "end\n");
    eexecWrite(&eb, "readonly put\n");
    eexecWrite(&eb, "noaccess put\n");
    eexecWrite(&eb, "dup /FontName get exch definefont pop\n");
    eexecWrite(&eb, "mark currentfile closefile\n");

    // trailer
    if (eb.line > 0) {
      (*outputFunc)(outputStream, "\n", 1);
    }
    for (j = 0; j < 8; ++j) {
      (*outputFunc)(outputStream, "0000000000000000000000000000000000000000000000000000000000000000\n", 65);
    }
    (*outputFunc)(outputStream, "cleartomark\n", 12);
  }

  // write the Type 0 parent font
  (*outputFunc)(outputStream, "16 dict begin\n", 14);
  (*outputFunc)(outputStream, "/FontName /", 11);
  (*outputFunc)(outputStream, psName, strlen(psName));
  (*outputFunc)(outputStream, " def\n", 5);
  (*outputFunc)(outputStream, "/FontType 0 def\n", 16);
  if (topDict.hasFontMatrix) {
    buf = GString::format("/FontMatrix [{0:.8g} {1:.8g} {2:.8g} {3:.8g} {4:.8g} {5:.8g}] def\n",
			  topDict.fontMatrix[0], topDict.fontMatrix[1],
			  topDict.fontMatrix[2], topDict.fontMatrix[3],
			  topDict.fontMatrix[4], topDict.fontMatrix[5]);
    (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
    delete buf;
  } else {
    (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
  }
  (*outputFunc)(outputStream, "/FMapType 2 def\n", 16);
  (*outputFunc)(outputStream, "/Encoding [\n", 12);
  for (i = 0; i < nCIDs; i += 256) {
    buf = GString::format("{0:d}\n", i >> 8);
    (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
    delete buf;
  }
  (*outputFunc)(outputStream, "] def\n", 6);
  (*outputFunc)(outputStream, "/FDepVector [\n", 14);
  for (i = 0; i < nCIDs; i += 256) {
    (*outputFunc)(outputStream, "/", 1);
    (*outputFunc)(outputStream, psName, strlen(psName));
    buf = GString::format("_{0:02x} findfont\n", i >> 8);
    (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
    delete buf;
  }
  (*outputFunc)(outputStream, "] def\n", 6);
  (*outputFunc)(outputStream, "FontName currentdict end definefont pop\n", 40);

  gfree(cidMap);
}

void FoFiType1C::eexecCvtGlyph(Type1CEexecBuf *eb, char *glyphName,
			       int offset, int nBytes,
			       Type1CIndex *subrIdx,
			       Type1CPrivateDict *pDict) {
  GString *buf;
  GString *charBuf;

  // generate the charstring
  charBuf = new GString();
  cvtGlyph(offset, nBytes, charBuf, subrIdx, pDict, gTrue);

  buf = GString::format("/{0:s} {1:d} RD ", glyphName, charBuf->getLength());
  eexecWrite(eb, buf->getCString());
  delete buf;
  eexecWriteCharstring(eb, (Guchar *)charBuf->getCString(),
		       charBuf->getLength());
  eexecWrite(eb, " ND\n");

  delete charBuf;
}

void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf,
			  Type1CIndex *subrIdx, Type1CPrivateDict *pDict,
			  GBool top) {
  Type1CIndexVal val;
  GBool ok, dFP;
  double d, dx, dy;
  Gushort r2;
  Guchar byte;
  int pos, subrBias, start, i, k;

  start = charBuf->getLength();
  if (top) {
    charBuf->append((char)73);
    charBuf->append((char)58);
    charBuf->append((char)147);
    charBuf->append((char)134);
    nOps = 0;
    nHints = 0;
    firstOp = gTrue;
    openPath = gFalse;
  }

  pos = offset;
  while (pos < offset + nBytes) {
    ok = gTrue;
    pos = getOp(pos, gTrue, &ok);
    if (!ok) {
      break;
    }
    if (!ops[nOps - 1].isNum) {
      --nOps; // drop the operator
      switch (ops[nOps].op) {
      case 0x0001:		// hstem
	if (firstOp) {
	  cvtGlyphWidth(nOps & 1, charBuf, pDict);
	  firstOp = gFalse;
	}
	if (nOps & 1) {
	  //~ error(-1, "Wrong number of args (%d) to Type 2 hstem", nOps);
	}
	d = 0;
	dFP = gFalse;
	for (k = 0; k < nOps; k += 2) {
	  // convert Type 2 edge hints (-20 or -21) to Type 1 ghost hints
	  if (ops[k+1].num < 0) {
	    d += ops[k].num + ops[k+1].num;
	    dFP |= ops[k].isFP | ops[k+1].isFP;
	    cvtNum(d, dFP, charBuf);
	    cvtNum(-ops[k+1].num, ops[k+1].isFP, charBuf);
	  } else {
	    d += ops[k].num;
	    dFP |= ops[k].isFP;
	    cvtNum(d, dFP, charBuf);
	    cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
	    d += ops[k+1].num;
	    dFP |= ops[k+1].isFP;
	  }
	  charBuf->append((char)1);
	}
	nHints += nOps / 2;
	nOps = 0;
	break;
      case 0x0003:		// vstem
	if (firstOp) {
	  cvtGlyphWidth(nOps & 1, charBuf, pDict);
	  firstOp = gFalse;
	}
	if (nOps & 1) {
	  //~ error(-1, "Wrong number of args (%d) to Type 2 vstem", nOps);
	}
	d = 0;
	dFP = gFalse;
	for (k = 0; k < nOps; k += 2) {
	  // convert Type 2 edge hints (-20 or -21) to Type 1 ghost hints
	  if (ops[k+1].num < 0) {
	    d += ops[k].num + ops[k+1].num;
	    dFP |= ops[k].isFP | ops[k+1].isFP;
	    cvtNum(d, dFP, charBuf);
	    cvtNum(-ops[k+1].num, ops[k+1].isFP, charBuf);
	  } else {
	    d += ops[k].num;
	    dFP |= ops[k].isFP;
	    cvtNum(d, dFP, charBuf);
	    cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
	    d += ops[k+1].num;
	    dFP |= ops[k+1].isFP;
	  }
	  charBuf->append((char)3);
	}
	nHints += nOps / 2;
	nOps = 0;
	break;
      case 0x0004:		// vmoveto
	if (firstOp) {
	  cvtGlyphWidth(nOps == 2, charBuf, pDict);
	  firstOp = gFalse;
	}
	if (openPath) {
	  charBuf->append((char)9);
	  openPath = gFalse;
	}
	if (nOps != 1) {
	  //~ error(-1, "Wrong number of args (%d) to Type 2 vmoveto", nOps);
	}
	cvtNum(ops[0].num, ops[0].isFP, charBuf);
	charBuf->append((char)4);
	nOps = 0;
	break;
      case 0x0005:		// rlineto
	if (nOps < 2 || nOps % 2 != 0) {
	  //~ error(-1, "Wrong number of args (%d) to Type 2 rlineto", nOps);
	}
	for (k = 0; k < nOps; k += 2) {
	  cvtNum(ops[k].num, ops[k].isFP, charBuf);
	  cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
	  charBuf->append((char)5);
	}
	nOps = 0;
	openPath = gTrue;
	break;
      case 0x0006:		// hlineto
	if (nOps < 1) {
	  //~ error(-1, "Wrong number of args (%d) to Type 2 hlineto", nOps);
	}
	for (k = 0; k < nOps; ++k) {
	  cvtNum(ops[k].num, ops[k].isFP, charBuf);
	  charBuf->append((char)((k & 1) ? 7 : 6));
	}
	nOps = 0;
	openPath = gTrue;
	break;
      case 0x0007:		// vlineto
	if (nOps < 1) {
	  //~ error(-1, "Wrong number of args (%d) to Type 2 vlineto", nOps);
	}
	for (k = 0; k < nOps; ++k) {
	  cvtNum(ops[k].num, ops[k].isFP, charBuf);
	  charBuf->append((char)((k & 1) ? 6 : 7));
	}
	nOps = 0;
	openPath = gTrue;
	break;
      case 0x0008:		// rrcurveto
	if (nOps < 6 || nOps % 6 != 0) {
	  //~ error(-1, "Wrong number of args (%d) to Type 2 rrcurveto", nOps);
	}
	for (k = 0; k < nOps; k += 6) {
	  cvtNum(ops[k].num, ops[k].isFP, charBuf);
	  cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
	  cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
	  cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
	  cvtNum(ops[k+4].num, ops[k+4].isFP, charBuf);
	  cvtNum(ops[k+5].num, ops[k+5].isFP, charBuf);
	  charBuf->append((char)8);
	}
	nOps = 0;
	openPath = gTrue;
	break;
      case 0x000a:		// callsubr
	if (nOps >= 1) {
	  subrBias = (subrIdx->len < 1240)
	               ? 107 : (subrIdx->len < 33900) ? 1131 : 32768;
	  k = subrBias + (int)ops[nOps - 1].num;
	  --nOps;
	  ok = gTrue;
	  getIndexVal(subrIdx, k, &val, &ok);
	  if (ok) {
	    cvtGlyph(val.pos, val.len, charBuf, subrIdx, pDict, gFalse);
	  }
	} else {
	  //~ error(-1, "Too few args to Type 2 callsubr");
	}
	// don't clear the stack
	break;
      case 0x000b:		// return
	// don't clear the stack
	break;
      case 0x000e:		// endchar / seac
	if (firstOp) {
	  cvtGlyphWidth(nOps == 1 || nOps == 5, charBuf, pDict);
	  firstOp = gFalse;
	}
	if (openPath) {
	  charBuf->append((char)9);
	  openPath = gFalse;
	}
	if (nOps == 4) {
	  cvtNum(0, gFalse, charBuf);
	  cvtNum(ops[0].num, ops[0].isFP, charBuf);
	  cvtNum(ops[1].num, ops[1].isFP, charBuf);
	  cvtNum(ops[2].num, ops[2].isFP, charBuf);
	  cvtNum(ops[3].num, ops[3].isFP, charBuf);
	  charBuf->append((char)12)->append((char)6);
	} else if (nOps == 0) {
	  charBuf->append((char)14);
	} else {
	  //~ error(-1, "Wrong number of args (%d) to Type 2 endchar", nOps);
	}
	nOps = 0;
	break;
      case 0x000f:		// (obsolete)
	// this op is ignored, but we need the glyph width
	if (firstOp) {
	  cvtGlyphWidth(nOps > 0, charBuf, pDict);
	  firstOp = gFalse;
	}
	nOps = 0;
	break;
      case 0x0010:		// blend
	//~ error(-1, "Unimplemented Type 2 charstring op: %d", file[i]);
	nOps = 0;
	break;
      case 0x0012:		// hstemhm
	// ignored
	if (firstOp) {
	  cvtGlyphWidth(nOps & 1, charBuf, pDict);
	  firstOp = gFalse;
	}
	if (nOps & 1) {
	  //~ error(-1, "Wrong number of args (%d) to Type 2 hstemhm", nOps);
	}
	nHints += nOps / 2;
	nOps = 0;
	break;
      case 0x0013:		// hintmask
	// ignored
	if (firstOp) {
	  cvtGlyphWidth(nOps & 1, charBuf, pDict);
	  firstOp = gFalse;
	}
	if (nOps > 0) {
	  if (nOps & 1) {
	    //~ error(-1, "Wrong number of args (%d) to Type 2 hintmask/vstemhm",
	    //~       nOps);
	  }
	  nHints += nOps / 2;
	}
	pos += (nHints + 7) >> 3;
	nOps = 0;
	break;
      case 0x0014:		// cntrmask
	// ignored
	if (firstOp) {
	  cvtGlyphWidth(nOps & 1, charBuf, pDict);
	  firstOp = gFalse;
	}
	if (nOps > 0) {
	  if (nOps & 1) {
	    //~ error(-1, "Wrong number of args (%d) to Type 2 cntrmask/vstemhm",
	    //~       nOps);
	  }
	  nHints += nOps / 2;
	}
	pos += (nHints + 7) >> 3;
	nOps = 0;
	break;
      case 0x0015:		// rmoveto
	if (firstOp) {
	  cvtGlyphWidth(nOps == 3, charBuf, pDict);
	  firstOp = gFalse;
	}
	if (openPath) {
	  charBuf->append((char)9);
	  openPath = gFalse;
	}
	if (nOps != 2) {
	  //~ error(-1, "Wrong number of args (%d) to Type 2 rmoveto", nOps);
	}
	cvtNum(ops[0].num, ops[0].isFP, charBuf);
	cvtNum(ops[1].num, ops[1].isFP, charBuf);
	charBuf->append((char)21);
	nOps = 0;
	break;
      case 0x0016:		// hmoveto
	if (firstOp) {
	  cvtGlyphWidth(nOps == 2, charBuf, pDict);
	  firstOp = gFalse;
	}
	if (openPath) {
	  charBuf->append((char)9);
	  openPath = gFalse;
	}
	if (nOps != 1) {
	  //~ error(-1, "Wrong number of args (%d) to Type 2 hmoveto", nOps);
	}
	cvtNum(ops[0].num, ops[0].isFP, charBuf);
	charBuf->append((char)22);
	nOps = 0;
	break;
      case 0x0017:		// vstemhm
	// ignored
	if (firstOp) {
	  cvtGlyphWidth(nOps & 1, charBuf, pDict);
	  firstOp = gFalse;
	}
	if (nOps & 1) {
	  //~ error(-1, "Wrong number of args (%d) to Type 2 vstemhm", nOps);
	}
	nHints += nOps / 2;
	nOps = 0;
	break;
      case 0x0018:		// rcurveline
	if (nOps < 8 || (nOps - 2) % 6 != 0) {
	  //~ error(-1, "Wrong number of args (%d) to Type 2 rcurveline", nOps);
	}
	for (k = 0; k < nOps - 2; k += 6) {
	  cvtNum(ops[k].num, ops[k].isFP, charBuf);
	  cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
	  cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
	  cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
	  cvtNum(ops[k+4].num, ops[k+4].isFP, charBuf);
	  cvtNum(ops[k+5].num, ops[k+5].isFP, charBuf);
	  charBuf->append((char)8);
	}
	cvtNum(ops[k].num, ops[k].isFP, charBuf);
	cvtNum(ops[k+1].num, ops[k].isFP, charBuf);
	charBuf->append((char)5);
	nOps = 0;
	openPath = gTrue;
	break;
      case 0x0019:		// rlinecurve
	if (nOps < 8 || (nOps - 6) % 2 != 0) {
	  //~ error(-1, "Wrong number of args (%d) to Type 2 rlinecurve", nOps);
	}
	for (k = 0; k < nOps - 6; k += 2) {
	  cvtNum(ops[k].num, ops[k].isFP, charBuf);
	  cvtNum(ops[k+1].num, ops[k].isFP, charBuf);
	  charBuf->append((char)5);
	}
	cvtNum(ops[k].num, ops[k].isFP, charBuf);
	cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
	cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
	cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
	cvtNum(ops[k+4].num, ops[k+4].isFP, charBuf);
	cvtNum(ops[k+5].num, ops[k+5].isFP, charBuf);
	charBuf->append((char)8);
	nOps = 0;
	openPath = gTrue;
	break;
      case 0x001a:		// vvcurveto
	if (nOps < 4 || !(nOps % 4 == 0 || (nOps-1) % 4 == 0)) {
	  //~ error(-1, "Wrong number of args (%d) to Type 2 vvcurveto", nOps);
	}
	if (nOps % 2 == 1) {
	  cvtNum(ops[0].num, ops[0].isFP, charBuf);
	  cvtNum(ops[1].num, ops[1].isFP, charBuf);
	  cvtNum(ops[2].num, ops[2].isFP, charBuf);
	  cvtNum(ops[3].num, ops[3].isFP, charBuf);
	  cvtNum(0, gFalse, charBuf);
	  cvtNum(ops[4].num, ops[4].isFP, charBuf);
	  charBuf->append((char)8);
	  k = 5;
	} else {
	  k = 0;
	}
	for (; k < nOps; k += 4) {
	  cvtNum(0, gFalse, charBuf);
	  cvtNum(ops[k].num, ops[k].isFP, charBuf);
	  cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
	  cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
	  cvtNum(0, gFalse, charBuf);
	  cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
	  charBuf->append((char)8);
	}
	nOps = 0;
	openPath = gTrue;
	break;
      case 0x001b:		// hhcurveto
	if (nOps < 4 || !(nOps % 4 == 0 || (nOps-1) % 4 == 0)) {
	  //~ error(-1, "Wrong number of args (%d) to Type 2 hhcurveto", nOps);
	}
	if (nOps % 2 == 1) {
	  cvtNum(ops[1].num, ops[1].isFP, charBuf);
	  cvtNum(ops[0].num, ops[0].isFP, charBuf);
	  cvtNum(ops[2].num, ops[2].isFP, charBuf);
	  cvtNum(ops[3].num, ops[3].isFP, charBuf);
	  cvtNum(ops[4].num, ops[4].isFP, charBuf);
	  cvtNum(0, gFalse, charBuf);
	  charBuf->append((char)8);
	  k = 5;
	} else {
	  k = 0;
	}
	for (; k < nOps; k += 4) {
	  cvtNum(ops[k].num, ops[k].isFP, charBuf);
	  cvtNum(0, gFalse, charBuf);
	  cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
	  cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
	  cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
	  cvtNum(0, gFalse, charBuf);
	  charBuf->append((char)8);
	}
	nOps = 0;
	openPath = gTrue;
	break;
      case 0x001d:		// callgsubr
	if (nOps >= 1) {
	  k = gsubrBias + (int)ops[nOps - 1].num;
	  --nOps;
	  ok = gTrue;
	  getIndexVal(&gsubrIdx, k, &val, &ok);
	  if (ok) {
	    cvtGlyph(val.pos, val.len, charBuf, subrIdx, pDict, gFalse);
	  }
	} else {
	  //~ error(-1, "Too few args to Type 2 callgsubr");
	}
	// don't clear the stack
	break;
      case 0x001e:		// vhcurveto
	if (nOps < 4 || !(nOps % 4 == 0 || (nOps-1) % 4 == 0)) {
	  //~ error(-1, "Wrong number of args (%d) to Type 2 vhcurveto", nOps);
	}
	for (k = 0; k < nOps && k != nOps-5; k += 4) {
	  if (k % 8 == 0) {
	    cvtNum(ops[k].num, ops[k].isFP, charBuf);
	    cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
	    cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
	    cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
	    charBuf->append((char)30);
	  } else {
	    cvtNum(ops[k].num, ops[k].isFP, charBuf);
	    cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
	    cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
	    cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
	    charBuf->append((char)31);
	  }
	}
	if (k == nOps-5) {
	  if (k % 8 == 0) {
	    cvtNum(0, gFalse, charBuf);
	    cvtNum(ops[k].num, ops[k].isFP, charBuf);
	    cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
	    cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
	    cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
	    cvtNum(ops[k+4].num, ops[k+4].isFP, charBuf);
	  } else {
	    cvtNum(ops[k].num, ops[k].isFP, charBuf);
	    cvtNum(0, gFalse, charBuf);
	    cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
	    cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
	    cvtNum(ops[k+4].num, ops[k+4].isFP, charBuf);
	    cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
	  }
	  charBuf->append((char)8);
	}
	nOps = 0;
	openPath = gTrue;
	break;
      case 0x001f:		// hvcurveto
	if (nOps < 4 || !(nOps % 4 == 0 || (nOps-1) % 4 == 0)) {
	  //~ error(-1, "Wrong number of args (%d) to Type 2 hvcurveto", nOps);
	}
	for (k = 0; k < nOps && k != nOps-5; k += 4) {
	  if (k % 8 == 0) {
	    cvtNum(ops[k].num, ops[k].isFP, charBuf);
	    cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
	    cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
	    cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
	    charBuf->append((char)31);
	  } else {
	    cvtNum(ops[k].num, ops[k].isFP, charBuf);
	    cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
	    cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
	    cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
	    charBuf->append((char)30);
	  }
	}
	if (k == nOps-5) {
	  if (k % 8 == 0) {
	    cvtNum(ops[k].num, ops[k].isFP, charBuf);
	    cvtNum(0, gFalse, charBuf);
	    cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
	    cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
	    cvtNum(ops[k+4].num, ops[k+4].isFP, charBuf);
	    cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
	  } else {
	    cvtNum(0, gFalse, charBuf);
	    cvtNum(ops[k].num, ops[k].isFP, charBuf);
	    cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
	    cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
	    cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
	    cvtNum(ops[k+4].num, ops[k+4].isFP, charBuf);
	  }
	  charBuf->append((char)8);
	}
	nOps = 0;
	openPath = gTrue;
	break;
      case 0x0c00:		// dotsection (should be Type 1 only?)
	// ignored
	nOps = 0;
	break;
      case 0x0c03:		// and
      case 0x0c04:		// or
      case 0x0c05:		// not
      case 0x0c08:		// store
      case 0x0c09:		// abs
      case 0x0c0a:		// add
      case 0x0c0b:		// sub
      case 0x0c0c:		// div
      case 0x0c0d:		// load
      case 0x0c0e:		// neg
      case 0x0c0f:		// eq
      case 0x0c12:		// drop
      case 0x0c14:		// put
      case 0x0c15:		// get
      case 0x0c16:		// ifelse
      case 0x0c17:		// random
      case 0x0c18:		// mul
      case 0x0c1a:		// sqrt
      case 0x0c1b:		// dup
      case 0x0c1c:		// exch
      case 0x0c1d:		// index
      case 0x0c1e:		// roll
	//~ error(-1, "Unimplemented Type 2 charstring op: 12.%d", file[i+1]);
	nOps = 0;
	break;
      case 0x0c22:		// hflex
	if (nOps != 7) {
	  //~ error(-1, "Wrong number of args (%d) to Type 2 hflex", nOps);
	}
	cvtNum(ops[0].num, ops[0].isFP, charBuf);
	cvtNum(0, gFalse, charBuf);
	cvtNum(ops[1].num, ops[1].isFP, charBuf);
	cvtNum(ops[2].num, ops[2].isFP, charBuf);
	cvtNum(ops[3].num, ops[3].isFP, charBuf);
	cvtNum(0, gFalse, charBuf);
	charBuf->append((char)8);
	cvtNum(ops[4].num, ops[4].isFP, charBuf);
	cvtNum(0, gFalse, charBuf);
	cvtNum(ops[5].num, ops[5].isFP, charBuf);
	cvtNum(-ops[2].num, ops[2].isFP, charBuf);
	cvtNum(ops[6].num, ops[6].isFP, charBuf);
	cvtNum(0, gFalse, charBuf);
	charBuf->append((char)8);
	nOps = 0;
	openPath = gTrue;
	break;
      case 0x0c23:		// flex
	if (nOps != 13) {
	  //~ error(-1, "Wrong number of args (%d) to Type 2 flex", nOps);
	}
	cvtNum(ops[0].num, ops[0].isFP, charBuf);
	cvtNum(ops[1].num, ops[1].isFP, charBuf);
	cvtNum(ops[2].num, ops[2].isFP, charBuf);
	cvtNum(ops[3].num, ops[3].isFP, charBuf);
	cvtNum(ops[4].num, ops[4].isFP, charBuf);
	cvtNum(ops[5].num, ops[5].isFP, charBuf);
	charBuf->append((char)8);
	cvtNum(ops[6].num, ops[6].isFP, charBuf);
	cvtNum(ops[7].num, ops[7].isFP, charBuf);
	cvtNum(ops[8].num, ops[8].isFP, charBuf);
	cvtNum(ops[9].num, ops[9].isFP, charBuf);
	cvtNum(ops[10].num, ops[10].isFP, charBuf);
	cvtNum(ops[11].num, ops[11].isFP, charBuf);
	charBuf->append((char)8);
	nOps = 0;
	openPath = gTrue;
	break;
      case 0x0c24:		// hflex1
	if (nOps != 9) {
	  //~ error(-1, "Wrong number of args (%d) to Type 2 hflex1", nOps);
	}
	cvtNum(ops[0].num, ops[0].isFP, charBuf);
	cvtNum(ops[1].num, ops[1].isFP, charBuf);
	cvtNum(ops[2].num, ops[2].isFP, charBuf);
	cvtNum(ops[3].num, ops[3].isFP, charBuf);
	cvtNum(ops[4].num, ops[4].isFP, charBuf);
	cvtNum(0, gFalse, charBuf);
	charBuf->append((char)8);
	cvtNum(ops[5].num, ops[5].isFP, charBuf);
	cvtNum(0, gFalse, charBuf);
	cvtNum(ops[6].num, ops[6].isFP, charBuf);
	cvtNum(ops[7].num, ops[7].isFP, charBuf);
	cvtNum(ops[8].num, ops[8].isFP, charBuf);
	cvtNum(-(ops[1].num + ops[3].num + ops[7].num),
	       ops[1].isFP | ops[3].isFP | ops[7].isFP, charBuf);
	charBuf->append((char)8);
	nOps = 0;
	openPath = gTrue;
	break;
      case 0x0c25:		// flex1
	if (nOps != 11) {
	  //~ error(-1, "Wrong number of args (%d) to Type 2 flex1", nOps);
	}
	cvtNum(ops[0].num, ops[0].isFP, charBuf);
	cvtNum(ops[1].num, ops[1].isFP, charBuf);
	cvtNum(ops[2].num, ops[2].isFP, charBuf);
	cvtNum(ops[3].num, ops[3].isFP, charBuf);
	cvtNum(ops[4].num, ops[4].isFP, charBuf);
	cvtNum(ops[5].num, ops[5].isFP, charBuf);
	charBuf->append((char)8);
	cvtNum(ops[6].num, ops[6].isFP, charBuf);
	cvtNum(ops[7].num, ops[7].isFP, charBuf);
	cvtNum(ops[8].num, ops[8].isFP, charBuf);
	cvtNum(ops[9].num, ops[9].isFP, charBuf);
	dx = ops[0].num + ops[2].num + ops[4].num + ops[6].num + ops[8].num;
	dy = ops[1].num + ops[3].num + ops[5].num + ops[7].num + ops[9].num;
	if (fabs(dx) > fabs(dy)) {
	  cvtNum(ops[10].num, ops[10].isFP, charBuf);
	  cvtNum(-dy, ops[1].isFP | ops[3].isFP | ops[5].isFP |
		      ops[7].isFP | ops[9].isFP, charBuf);
	} else {
	  cvtNum(-dx, ops[0].isFP | ops[2].isFP | ops[4].isFP |
		      ops[6].isFP | ops[8].isFP, charBuf);
	  cvtNum(ops[10].num, ops[10].isFP, charBuf);
	}
	charBuf->append((char)8);
	nOps = 0;
	openPath = gTrue;
	break;
      default:
	//~ error(-1, "Illegal Type 2 charstring op: %04x",
	//~       ops[nOps].op);
	nOps = 0;
	break;
      }
    }
  }

  // charstring encryption
  if (top) {
    r2 = 4330;
    for (i = start; i < charBuf->getLength(); ++i) {
      byte = charBuf->getChar(i) ^ (r2 >> 8);
      charBuf->setChar(i, byte);
      r2 = (byte + r2) * 52845 + 22719;
    }
  }
}

void FoFiType1C::cvtGlyphWidth(GBool useOp, GString *charBuf,
			       Type1CPrivateDict *pDict) {
  double w;
  GBool wFP;
  int i;

  if (useOp) {
    w = pDict->nominalWidthX + ops[0].num;
    wFP = pDict->nominalWidthXFP | ops[0].isFP;
    for (i = 1; i < nOps; ++i) {
      ops[i-1] = ops[i];
    }
    --nOps;
  } else {
    w = pDict->defaultWidthX;
    wFP = pDict->defaultWidthXFP;
  }
  cvtNum(0, gFalse, charBuf);
  cvtNum(w, wFP, charBuf);
  charBuf->append((char)13);
}

void FoFiType1C::cvtNum(double x, GBool isFP, GString *charBuf) {
  Guchar buf[12];
  int y, n;

  n = 0;
  if (isFP) {
    if (x >= -32768 && x < 32768) {
      y = (int)(x * 256.0);
      buf[0] = 255;
      buf[1] = (Guchar)(y >> 24);
      buf[2] = (Guchar)(y >> 16);
      buf[3] = (Guchar)(y >> 8);
      buf[4] = (Guchar)y;
      buf[5] = 255;
      buf[6] = 0;
      buf[7] = 0;
      buf[8] = 1;
      buf[9] = 0;
      buf[10] = 12;
      buf[11] = 12;
      n = 12;
    } else {
      //~ error(-1, "Type 2 fixed point constant out of range");
    }
  } else {
    y = (int)x;
    if (y >= -107 && y <= 107) {
      buf[0] = (Guchar)(y + 139);
      n = 1;
    } else if (y > 107 && y <= 1131) {
      y -= 108;
      buf[0] = (Guchar)((y >> 8) + 247);
      buf[1] = (Guchar)(y & 0xff);
      n = 2;
    } else if (y < -107 && y >= -1131) {
      y = -y - 108;
      buf[0] = (Guchar)((y >> 8) + 251);
      buf[1] = (Guchar)(y & 0xff);
      n = 2;
    } else {
      buf[0] = 255;
      buf[1] = (Guchar)(y >> 24);
      buf[2] = (Guchar)(y >> 16);
      buf[3] = (Guchar)(y >> 8);
      buf[4] = (Guchar)y;
      n = 5;
    }
  }
  charBuf->append((char *)buf, n);
}

void FoFiType1C::eexecWrite(Type1CEexecBuf *eb, char *s) {
  Guchar *p;
  Guchar x;

  for (p = (Guchar *)s; *p; ++p) {
    x = *p ^ (eb->r1 >> 8);
    eb->r1 = (x + eb->r1) * 52845 + 22719;
    if (eb->ascii) {
      (*eb->outputFunc)(eb->outputStream, &hexChars[x >> 4], 1);
      (*eb->outputFunc)(eb->outputStream, &hexChars[x & 0x0f], 1);
      eb->line += 2;
      if (eb->line == 64) {
	(*eb->outputFunc)(eb->outputStream, "\n", 1);
	eb->line = 0;
      }
    } else {
      (*eb->outputFunc)(eb->outputStream, (char *)&x, 1);
    }
  }
}

void FoFiType1C::eexecWriteCharstring(Type1CEexecBuf *eb,
				      Guchar *s, int n) {
  Guchar x;
  int i;

  // eexec encryption
  for (i = 0; i < n; ++i) {
    x = s[i] ^ (eb->r1 >> 8);
    eb->r1 = (x + eb->r1) * 52845 + 22719;
    if (eb->ascii) {
      (*eb->outputFunc)(eb->outputStream, &hexChars[x >> 4], 1);
      (*eb->outputFunc)(eb->outputStream, &hexChars[x & 0x0f], 1);
      eb->line += 2;
      if (eb->line == 64) {
	(*eb->outputFunc)(eb->outputStream, "\n", 1);
	eb->line = 0;
      }
    } else {
      (*eb->outputFunc)(eb->outputStream, (char *)&x, 1);
    }
  }
}

GBool FoFiType1C::parse() {
  Type1CIndex fdIdx;
  Type1CIndexVal val;
  int i;

  parsedOk = gTrue;

  // some tools embed Type 1C fonts with an extra whitespace char at
  // the beginning
  if (len > 0 && file[0] != '\x01') {
    ++file;
    --len;
  }

  // find the indexes
  getIndex(getU8(2, &parsedOk), &nameIdx, &parsedOk);
  getIndex(nameIdx.endPos, &topDictIdx, &parsedOk);
  getIndex(topDictIdx.endPos, &stringIdx, &parsedOk);
  getIndex(stringIdx.endPos, &gsubrIdx, &parsedOk);
  if (!parsedOk) {
    return gFalse;
  }
  gsubrBias = (gsubrIdx.len < 1240) ? 107
                                    : (gsubrIdx.len < 33900) ? 1131 : 32768;

  // read the first font name
  getIndexVal(&nameIdx, 0, &val, &parsedOk);
  if (!parsedOk) {
    return gFalse;
  }
  name = new GString((char *)&file[val.pos], val.len);

  // read the top dict for the first font
  readTopDict();

  // for CID fonts: read the FDArray dicts and private dicts
  if (topDict.firstOp == 0x0c1e) {
    if (topDict.fdArrayOffset == 0) {
      nFDs = 1;
      privateDicts = (Type1CPrivateDict *)gmalloc(sizeof(Type1CPrivateDict));
      readPrivateDict(0, 0, &privateDicts[0]);
    } else {
      getIndex(topDict.fdArrayOffset, &fdIdx, &parsedOk);
      if (!parsedOk) {
	return gFalse;
      }
      nFDs = fdIdx.len;
      privateDicts = (Type1CPrivateDict *)
	                 gmallocn(nFDs, sizeof(Type1CPrivateDict));
      for (i = 0; i < nFDs; ++i) {
	getIndexVal(&fdIdx, i, &val, &parsedOk);
	if (!parsedOk) {
	  return gFalse;
	}
	readFD(val.pos, val.len, &privateDicts[i]);
      }
    }

  // for 8-bit fonts: read the private dict
  } else {
    privateDicts = (Type1CPrivateDict *)gmalloc(sizeof(Type1CPrivateDict));
    readPrivateDict(topDict.privateOffset, topDict.privateSize,
		    &privateDicts[0]);
  }

  // check for parse errors in the private dict(s)
  if (!parsedOk) {
    return gFalse;
  }

  // get the charstrings index
  if (topDict.charStringsOffset <= 0) {
    parsedOk = gFalse;
    return gFalse;
  }
  getIndex(topDict.charStringsOffset, &charStringsIdx, &parsedOk);
  if (!parsedOk) {
    return gFalse;
  }
  nGlyphs = charStringsIdx.len;

  // for CID fonts: read the FDSelect table
  if (topDict.firstOp == 0x0c1e) {
    readFDSelect();
    if (!parsedOk) {
      return gFalse;
    }
  }

  // read the charset
  if (!readCharset()) {
    parsedOk = gFalse;
    return gFalse;
  }

  // for 8-bit fonts: build the encoding
  if (topDict.firstOp != 0x0c14 && topDict.firstOp != 0x0c1e) {
    buildEncoding();
    if (!parsedOk) {
      return gFalse;
    }
  }

  return parsedOk;
}

void FoFiType1C::readTopDict() {
  Type1CIndexVal topDictPtr;
  int pos;

  topDict.firstOp = -1;
  topDict.versionSID = 0;
  topDict.noticeSID = 0;
  topDict.copyrightSID = 0;
  topDict.fullNameSID = 0;
  topDict.familyNameSID = 0;
  topDict.weightSID = 0;
  topDict.isFixedPitch = 0;
  topDict.italicAngle = 0;
  topDict.underlinePosition = -100;
  topDict.underlineThickness = 50;
  topDict.paintType = 0;
  topDict.charstringType = 2;
  topDict.fontMatrix[0] = 0.001;
  topDict.fontMatrix[1] = 0;
  topDict.fontMatrix[2] = 0;
  topDict.fontMatrix[3] = 0.001;
  topDict.fontMatrix[4] = 0;
  topDict.fontMatrix[5] = 0;
  topDict.hasFontMatrix = gFalse;
  topDict.uniqueID = 0;
  topDict.fontBBox[0] = 0;
  topDict.fontBBox[1] = 0;
  topDict.fontBBox[2] = 0;
  topDict.fontBBox[3] = 0;
  topDict.strokeWidth = 0;
  topDict.charsetOffset = 0;
  topDict.encodingOffset = 0;
  topDict.charStringsOffset = 0;
  topDict.privateSize = 0;
  topDict.privateOffset = 0;
  topDict.registrySID = 0;
  topDict.orderingSID = 0;
  topDict.supplement = 0;
  topDict.fdArrayOffset = 0;
  topDict.fdSelectOffset = 0;

  getIndexVal(&topDictIdx, 0, &topDictPtr, &parsedOk);
  pos = topDictPtr.pos;
  nOps = 0;
  while (pos < topDictPtr.pos + topDictPtr.len) {
    pos = getOp(pos, gFalse, &parsedOk);
    if (!parsedOk) {
      break;
    }
    if (!ops[nOps - 1].isNum) {
      --nOps; // drop the operator
      if (topDict.firstOp < 0) {
	topDict.firstOp = ops[nOps].op;
      }
      switch (ops[nOps].op) {
      case 0x0000: topDict.versionSID = (int)ops[0].num; break;
      case 0x0001: topDict.noticeSID = (int)ops[0].num; break;
      case 0x0c00: topDict.copyrightSID = (int)ops[0].num; break;
      case 0x0002: topDict.fullNameSID = (int)ops[0].num; break;
      case 0x0003: topDict.familyNameSID = (int)ops[0].num; break;
      case 0x0004: topDict.weightSID = (int)ops[0].num; break;
      case 0x0c01: topDict.isFixedPitch = (int)ops[0].num; break;
      case 0x0c02: topDict.italicAngle = ops[0].num; break;
      case 0x0c03: topDict.underlinePosition = ops[0].num; break;
      case 0x0c04: topDict.underlineThickness = ops[0].num; break;
      case 0x0c05: topDict.paintType = (int)ops[0].num; break;
      case 0x0c06: topDict.charstringType = (int)ops[0].num; break;
      case 0x0c07: topDict.fontMatrix[0] = ops[0].num;
	           topDict.fontMatrix[1] = ops[1].num;
	           topDict.fontMatrix[2] = ops[2].num;
	           topDict.fontMatrix[3] = ops[3].num;
	           topDict.fontMatrix[4] = ops[4].num;
	           topDict.fontMatrix[5] = ops[5].num;
		   topDict.hasFontMatrix = gTrue; break;
      case 0x000d: topDict.uniqueID = (int)ops[0].num; break;
      case 0x0005: topDict.fontBBox[0] = ops[0].num;
	           topDict.fontBBox[1] = ops[1].num;
	           topDict.fontBBox[2] = ops[2].num;
	           topDict.fontBBox[3] = ops[3].num; break;
      case 0x0c08: topDict.strokeWidth = ops[0].num; break;
      case 0x000f: topDict.charsetOffset = (int)ops[0].num; break;
      case 0x0010: topDict.encodingOffset = (int)ops[0].num; break;
      case 0x0011: topDict.charStringsOffset = (int)ops[0].num; break;
      case 0x0012: topDict.privateSize = (int)ops[0].num;
	           topDict.privateOffset = (int)ops[1].num; break;
      case 0x0c1e: topDict.registrySID = (int)ops[0].num;
	           topDict.orderingSID = (int)ops[1].num;
		   topDict.supplement = (int)ops[2].num; break;
      case 0x0c24: topDict.fdArrayOffset = (int)ops[0].num; break;
      case 0x0c25: topDict.fdSelectOffset = (int)ops[0].num; break;
      }
      nOps = 0;
    }
  }
}

// Read a CID font dict (FD) - this pulls out the private dict
// pointer, and reads the private dict.  It also pulls the FontMatrix
// (if any) out of the FD.
void FoFiType1C::readFD(int offset, int length, Type1CPrivateDict *pDict) {
  int pos, pSize, pOffset;
  double fontMatrix[6];
  GBool hasFontMatrix;

  hasFontMatrix = gFalse;
  pSize = pOffset = 0;
  pos = offset;
  nOps = 0;
  while (pos < offset + length) {
    pos = getOp(pos, gFalse, &parsedOk);
    if (!parsedOk) {
      return;
    }
    if (!ops[nOps - 1].isNum) {
      if (ops[nOps - 1].op == 0x0012) {
	if (nOps < 3) {
	  parsedOk = gFalse;
	  return;
	}
	pSize = (int)ops[0].num;
	pOffset = (int)ops[1].num;
	break;
      } else if (ops[nOps - 1].op == 0x0c07) {
	fontMatrix[0] = ops[0].num;
	fontMatrix[1] = ops[1].num;
	fontMatrix[2] = ops[2].num;
	fontMatrix[3] = ops[3].num;
	fontMatrix[4] = ops[4].num;
	fontMatrix[5] = ops[5].num;
	hasFontMatrix = gTrue;
      }
      nOps = 0;
    }
  }
  readPrivateDict(pOffset, pSize, pDict);
  if (hasFontMatrix) {
    pDict->fontMatrix[0] = fontMatrix[0];
    pDict->fontMatrix[1] = fontMatrix[1];
    pDict->fontMatrix[2] = fontMatrix[2];
    pDict->fontMatrix[3] = fontMatrix[3];
    pDict->fontMatrix[4] = fontMatrix[4];
    pDict->fontMatrix[5] = fontMatrix[5];
    pDict->hasFontMatrix = gTrue;
  }
}

void FoFiType1C::readPrivateDict(int offset, int length,
				 Type1CPrivateDict *pDict) {
  int pos;

  pDict->hasFontMatrix = gFalse;
  pDict->nBlueValues = 0;
  pDict->nOtherBlues = 0;
  pDict->nFamilyBlues = 0;
  pDict->nFamilyOtherBlues = 0;
  pDict->blueScale = 0.039625;
  pDict->blueShift = 7;
  pDict->blueFuzz = 1;
  pDict->hasStdHW = gFalse;
  pDict->hasStdVW = gFalse;
  pDict->nStemSnapH = 0;
  pDict->nStemSnapV = 0;
  pDict->hasForceBold = gFalse;
  pDict->forceBoldThreshold = 0;
  pDict->languageGroup = 0;
  pDict->expansionFactor = 0.06;
  pDict->initialRandomSeed = 0;
  pDict->subrsOffset = 0;
  pDict->defaultWidthX = 0;
  pDict->defaultWidthXFP = gFalse;
  pDict->nominalWidthX = 0;
  pDict->nominalWidthXFP = gFalse;

  // no dictionary
  if (offset == 0 || length == 0) {
    return;
  }

  pos = offset;
  nOps = 0;
  while (pos < offset + length) {
    pos = getOp(pos, gFalse, &parsedOk);
    if (!parsedOk) {
      break;
    }
    if (!ops[nOps - 1].isNum) {
      --nOps; // drop the operator
      switch (ops[nOps].op) {
      case 0x0006:
	pDict->nBlueValues = getDeltaIntArray(pDict->blueValues,
					      type1CMaxBlueValues);
	break;
      case 0x0007:
	pDict->nOtherBlues = getDeltaIntArray(pDict->otherBlues,
					      type1CMaxOtherBlues);
	break;
      case 0x0008:
	pDict->nFamilyBlues = getDeltaIntArray(pDict->familyBlues,
					       type1CMaxBlueValues);
	break;
      case 0x0009:
	pDict->nFamilyOtherBlues = getDeltaIntArray(pDict->familyOtherBlues,
						    type1CMaxOtherBlues);
	break;
      case 0x0c09:
	pDict->blueScale = ops[0].num;
	break;
      case 0x0c0a:
	pDict->blueShift = (int)ops[0].num;
	break;
      case 0x0c0b:
	pDict->blueFuzz = (int)ops[0].num;
	break;
      case 0x000a:
	pDict->stdHW = ops[0].num;
	pDict->hasStdHW = gTrue;
	break;
      case 0x000b:
	pDict->stdVW = ops[0].num;
	pDict->hasStdVW = gTrue;
	break;
      case 0x0c0c:
	pDict->nStemSnapH = getDeltaFPArray(pDict->stemSnapH,
					    type1CMaxStemSnap);
	break;
      case 0x0c0d:
	pDict->nStemSnapV = getDeltaFPArray(pDict->stemSnapV,
					    type1CMaxStemSnap);
	break;
      case 0x0c0e:
	pDict->forceBold = ops[0].num != 0;
	pDict->hasForceBold = gTrue;
	break;
      case 0x0c0f:
	pDict->forceBoldThreshold = ops[0].num;
	break;
      case 0x0c11:
	pDict->languageGroup = (int)ops[0].num;
	break;
      case 0x0c12:
	pDict->expansionFactor = ops[0].num;
	break;
      case 0x0c13:
	pDict->initialRandomSeed = (int)ops[0].num;
	break;
      case 0x0013:
	pDict->subrsOffset = offset + (int)ops[0].num;
	break;
      case 0x0014:
	pDict->defaultWidthX = ops[0].num;
	pDict->defaultWidthXFP = ops[0].isFP;
	break;
      case 0x0015:
	pDict->nominalWidthX = ops[0].num;
	pDict->nominalWidthXFP = ops[0].isFP;
	break;
      }
      nOps = 0;
    }
  }
}

void FoFiType1C::readFDSelect() {
  int fdSelectFmt, pos, nRanges, gid0, gid1, fd, i, j;

  fdSelect = (Guchar *)gmalloc(nGlyphs);
  if (topDict.fdSelectOffset == 0) {
    for (i = 0; i < nGlyphs; ++i) {
      fdSelect[i] = 0;
    }
  } else {
    pos = topDict.fdSelectOffset;
    fdSelectFmt = getU8(pos++, &parsedOk);
    if (!parsedOk) {
      return;
    }
    if (fdSelectFmt == 0) {
      if (!checkRegion(pos, nGlyphs)) {
	parsedOk = gFalse;
	return;
      }
      memcpy(fdSelect, file + pos, nGlyphs);
    } else if (fdSelectFmt == 3) {
      nRanges = getU16BE(pos, &parsedOk);
      pos += 2;
      gid0 = getU16BE(pos, &parsedOk);
      pos += 2;
      for (i = 1; i <= nRanges; ++i) {
	fd = getU8(pos++, &parsedOk);
	gid1 = getU16BE(pos, &parsedOk);
	if (!parsedOk) {
	  return;
	}
	pos += 2;
	if (gid0 > gid1 || gid1 > nGlyphs) {
	  //~ error(-1, "Bad FDSelect table in CID font");
	  parsedOk = gFalse;
	  return;
	}
	for (j = gid0; j < gid1; ++j) {
	  fdSelect[j] = fd;
	}
	gid0 = gid1;
      }
    } else {
      //~ error(-1, "Unknown FDSelect table format in CID font");
      for (i = 0; i < nGlyphs; ++i) {
	fdSelect[i] = 0;
      }
    }
  }
}

void FoFiType1C::buildEncoding() {
  char buf[256];
  int nCodes, nRanges, encFormat;
  int pos, c, sid, nLeft, nSups, i, j;

  if (topDict.encodingOffset == 0) {
    encoding = fofiType1StandardEncoding;

  } else if (topDict.encodingOffset == 1) {
    encoding = fofiType1ExpertEncoding;

  } else {
    encoding = (char **)gmallocn(256, sizeof(char *));
    for (i = 0; i < 256; ++i) {
      encoding[i] = NULL;
    }
    pos = topDict.encodingOffset;
    encFormat = getU8(pos++, &parsedOk);
    if (!parsedOk) {
      return;
    }
    if ((encFormat & 0x7f) == 0) {
      nCodes = 1 + getU8(pos++, &parsedOk);
      if (!parsedOk) {
	return;
      }
      if (nCodes > nGlyphs) {
	nCodes = nGlyphs;
      }
      for (i = 1; i < nCodes; ++i) {
	c = getU8(pos++, &parsedOk);
	if (!parsedOk) {
	  return;
	}
	if (encoding[c]) {
	  gfree(encoding[c]);
	}
	encoding[c] = copyString(getString(charset[i], buf, &parsedOk));
      }
    } else if ((encFormat & 0x7f) == 1) {
      nRanges = getU8(pos++, &parsedOk);
      if (!parsedOk) {
	return;
      }
      nCodes = 1;
      for (i = 0; i < nRanges; ++i) {
	c = getU8(pos++, &parsedOk);
	nLeft = getU8(pos++, &parsedOk);
	if (!parsedOk) {
	  return;
	}
	for (j = 0; j <= nLeft && nCodes < nGlyphs; ++j) {
	  if (c < 256) {
	    if (encoding[c]) {
	      gfree(encoding[c]);
	    }
	    encoding[c] = copyString(getString(charset[nCodes], buf,
					       &parsedOk));
	  }
	  ++nCodes;
	  ++c;
	}
      }
    }
    if (encFormat & 0x80) {
      nSups = getU8(pos++, &parsedOk);
      if (!parsedOk) {
	return;
      }
      for (i = 0; i < nSups; ++i) {
	c = getU8(pos++, &parsedOk);;
	if (!parsedOk) {
	  return;;
	}
	sid = getU16BE(pos, &parsedOk);
	pos += 2;
	if (!parsedOk) {
	  return;
	}
	if (encoding[c]) {
	  gfree(encoding[c]);
	}
	encoding[c] = copyString(getString(sid, buf, &parsedOk));
      }
    }
  }
}

GBool FoFiType1C::readCharset() {
  int charsetFormat, c, pos;
  int nLeft, i, j;

  if (topDict.charsetOffset == 0) {
    charset = fofiType1CISOAdobeCharset;
  } else if (topDict.charsetOffset == 1) {
    charset = fofiType1CExpertCharset;
  } else if (topDict.charsetOffset == 2) {
    charset = fofiType1CExpertSubsetCharset;
  } else {
    charset = (Gushort *)gmallocn(nGlyphs, sizeof(Gushort));
    for (i = 0; i < nGlyphs; ++i) {
      charset[i] = 0;
    }
    pos = topDict.charsetOffset;
    charsetFormat = getU8(pos++, &parsedOk);
    if (charsetFormat == 0) {
      for (i = 1; i < nGlyphs; ++i) {
	charset[i] = (Gushort)getU16BE(pos, &parsedOk);
	pos += 2;
	if (!parsedOk) {
	  break;
	}
      }
    } else if (charsetFormat == 1) {
      i = 1;
      while (i < nGlyphs) {
	c = getU16BE(pos, &parsedOk);
	pos += 2;
	nLeft = getU8(pos++, &parsedOk);
	if (!parsedOk) {
	  break;
	}
	for (j = 0; j <= nLeft && i < nGlyphs; ++j) {
	  charset[i++] = (Gushort)c++;
	}
      }
    } else if (charsetFormat == 2) {
      i = 1;
      while (i < nGlyphs) {
	c = getU16BE(pos, &parsedOk);
	pos += 2;
	nLeft = getU16BE(pos, &parsedOk);
	pos += 2;
	if (!parsedOk) {
	  break;
	}
	for (j = 0; j <= nLeft && i < nGlyphs; ++j) {
	  charset[i++] = (Gushort)c++;
	}
      }
    }
    if (!parsedOk) {
      gfree(charset);
      charset = NULL;
      return gFalse;
    }
  }
  return gTrue;
}

int FoFiType1C::getOp(int pos, GBool charstring, GBool *ok) {
  static char nybChars[16] = "0123456789.ee -";
  Type1COp op;
  char buf[65];
  int b0, b1, nyb0, nyb1, x, i;

  b0 = getU8(pos++, ok);
  op.isNum = gTrue;
  op.isFP = gFalse;

  if (b0 == 28) {
    x = getU8(pos++, ok);
    x = (x << 8) | getU8(pos++, ok);
    if (x & 0x8000) {
      x |= ~0xffff;
    }
    op.num = x;

  } else if (!charstring && b0 == 29) {
    x = getU8(pos++, ok);
    x = (x << 8) | getU8(pos++, ok);
    x = (x << 8) | getU8(pos++, ok);
    x = (x << 8) | getU8(pos++, ok);
    if (x & 0x80000000) {
      x |= ~0xffffffff;
    }
    op.num = x;

  } else if (!charstring && b0 == 30) {
    i = 0;
    do {
      b1 = getU8(pos++, ok);
      nyb0 = b1 >> 4;
      nyb1 = b1 & 0x0f;
      if (nyb0 == 0xf) {
	break;
      }
      buf[i++] = nybChars[nyb0];
      if (i == 64) {
	break;
      }
      if (nyb0 == 0xc) {
	buf[i++] = '-';
      }
      if (i == 64) {
	break;
      }
      if (nyb1 == 0xf) {
	break;
      }
      buf[i++] = nybChars[nyb1];
      if (i == 64) {
	break;
      }
      if (nyb1 == 0xc) {
	buf[i++] = '-';
      }
    } while (i < 64);
    buf[i] = '\0';
    op.num = atof(buf);
    op.isFP = gTrue;

  } else if (b0 >= 32 && b0 <= 246) {
    op.num = b0 - 139;

  } else if (b0 >= 247 && b0 <= 250) {
    op.num = ((b0 - 247) << 8) + getU8(pos++, ok) + 108;

  } else if (b0 >= 251 && b0 <= 254) {
    op.num = -((b0 - 251) << 8) - getU8(pos++, ok) - 108;

  } else if (charstring && b0 == 255) {
    x = getU8(pos++, ok);
    x = (x << 8) | getU8(pos++, ok);
    x = (x << 8) | getU8(pos++, ok);
    x = (x << 8) | getU8(pos++, ok);
    if (x & 0x80000000) {
      x |= ~0xffffffff;
    }
    op.num = (double)x / 65536.0;
    op.isFP = gTrue;

  } else if (b0 == 12) {
    op.isNum = gFalse;
    op.op = 0x0c00 + getU8(pos++, ok);

  } else {
    op.isNum = gFalse;
    op.op = b0;
  }

  if (nOps < 49) {
    ops[nOps++] = op;
  }

  return pos;
}

// Convert the delta-encoded ops array to an array of ints.
int FoFiType1C::getDeltaIntArray(int *arr, int maxLen) {
  int x;
  int n, i;

  if ((n = nOps) > maxLen) {
    n = maxLen;
  }
  x = 0;
  for (i = 0; i < n; ++i) {
    x += (int)ops[i].num;
    arr[i] = x;
  }
  return n;
}

// Convert the delta-encoded ops array to an array of doubles.
int FoFiType1C::getDeltaFPArray(double *arr, int maxLen) {
  double x;
  int n, i;

  if ((n = nOps) > maxLen) {
    n = maxLen;
  }
  x = 0;
  for (i = 0; i < n; ++i) {
    x += ops[i].num;
    arr[i] = x;
  }
  return n;
}

void FoFiType1C::getIndex(int pos, Type1CIndex *idx, GBool *ok) {
  idx->pos = pos;
  idx->len = getU16BE(pos, ok);
  if (idx->len == 0) {
    // empty indexes are legal and contain just the length field
    idx->offSize = 0;
    idx->startPos = idx->endPos = pos + 2;
  } else {
    idx->offSize = getU8(pos + 2, ok);
    if (idx->offSize < 1 || idx->offSize > 4) {
      *ok = gFalse;
    }
    idx->startPos = pos + 3 + (idx->len + 1) * idx->offSize - 1;
    if (idx->startPos < 0 || idx->startPos >= len) {
      *ok = gFalse;
    }
    idx->endPos = idx->startPos + getUVarBE(pos + 3 + idx->len * idx->offSize,
					    idx->offSize, ok);
    if (idx->endPos < idx->startPos || idx->endPos > len) {
      *ok = gFalse;
    }
  }
}

void FoFiType1C::getIndexVal(Type1CIndex *idx, int i,
			     Type1CIndexVal *val, GBool *ok) {
  int pos0, pos1;

  if (i < 0 || i >= idx->len) {
    *ok = gFalse;
    return;
  }
  pos0 = idx->startPos + getUVarBE(idx->pos + 3 + i * idx->offSize,
				   idx->offSize, ok);
  pos1 = idx->startPos + getUVarBE(idx->pos + 3 + (i + 1) * idx->offSize,
				   idx->offSize, ok);
  if (pos0 < idx->startPos || pos0 > idx->endPos ||
      pos1 <= idx->startPos || pos1 > idx->endPos ||
      pos1 < pos0) {
    *ok = gFalse;
  }
  val->pos = pos0;
  val->len = pos1 - pos0;
}

char *FoFiType1C::getString(int sid, char *buf, GBool *ok) {
  Type1CIndexVal val;
  int n;

  if (sid < 391) {
    strcpy(buf, fofiType1CStdStrings[sid]);
  } else {
    sid -= 391;
    getIndexVal(&stringIdx, sid, &val, ok);
    if (*ok) {
      if ((n = val.len) > 255) {
	n = 255;
      }
      strncpy(buf, (char *)&file[val.pos], n);
      buf[n] = '\0';
    } else {
      buf[0] = '\0';
    }
  }
  return buf;
}
//========================================================================
//
// FoFiType1C.h
//
// Copyright 1999-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef FOFITYPE1C_H
#define FOFITYPE1C_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "gtypes.h"
#include "FoFiBase.h"

class GString;

//------------------------------------------------------------------------

struct Type1CIndex {
  int pos;			// absolute position in file
  int len;			// length (number of entries)
  int offSize;			// offset size
  int startPos;			// position of start of index data - 1
  int endPos;			// position one byte past end of the index
};

struct Type1CIndexVal {
  int pos;			// absolute position in file
  int len;			// length, in bytes
};

struct Type1CTopDict {
  int firstOp;

  int versionSID;
  int noticeSID;
  int copyrightSID;
  int fullNameSID;
  int familyNameSID;
  int weightSID;
  int isFixedPitch;
  double italicAngle;
  double underlinePosition;
  double underlineThickness;
  int paintType;
  int charstringType;
  double fontMatrix[6];
  GBool hasFontMatrix;		// CID fonts are allowed to put their
				//   FontMatrix in the FD instead of the
				//   top dict
  int uniqueID;
  double fontBBox[4];
  double strokeWidth;
  int charsetOffset;
  int encodingOffset;
  int charStringsOffset;
  int privateSize;
  int privateOffset;

  // CIDFont entries
  int registrySID;
  int orderingSID;
  int supplement;
  int fdArrayOffset;
  int fdSelectOffset;
};

#define type1CMaxBlueValues 14
#define type1CMaxOtherBlues 10
#define type1CMaxStemSnap   12

struct Type1CPrivateDict {
  double fontMatrix[6];
  GBool hasFontMatrix;
  int blueValues[type1CMaxBlueValues];
  int nBlueValues;
  int otherBlues[type1CMaxOtherBlues];
  int nOtherBlues;
  int familyBlues[type1CMaxBlueValues];
  int nFamilyBlues;
  int familyOtherBlues[type1CMaxOtherBlues];
  int nFamilyOtherBlues;
  double blueScale;
  int blueShift;
  int blueFuzz;
  double stdHW;
  GBool hasStdHW;
  double stdVW;
  GBool hasStdVW;
  double stemSnapH[type1CMaxStemSnap];
  int nStemSnapH;
  double stemSnapV[type1CMaxStemSnap];
  int nStemSnapV;
  GBool forceBold;
  GBool hasForceBold;
  double forceBoldThreshold;
  int languageGroup;
  double expansionFactor;
  int initialRandomSeed;
  int subrsOffset;
  double defaultWidthX;
  GBool defaultWidthXFP;
  double nominalWidthX;
  GBool nominalWidthXFP;
};

struct Type1COp {
  GBool isNum;			// true -> number, false -> operator
  GBool isFP;			// true -> floating point number, false -> int
  union {
    double num;			// if num is true
    int op;			// if num is false
  };
};

struct Type1CEexecBuf {
  FoFiOutputFunc outputFunc;
  void *outputStream;
  GBool ascii;			// ASCII encoding?
  Gushort r1;			// eexec encryption key
  int line;			// number of eexec chars left on current line
};

//------------------------------------------------------------------------
// FoFiType1C
//------------------------------------------------------------------------

class FoFiType1C: public FoFiBase {
public:

  // Create a FoFiType1C object from a memory buffer.
  static FoFiType1C *make(char *fileA, int lenA);

  // Create a FoFiType1C object from a file on disk.
  static FoFiType1C *load(char *fileName);

  virtual ~FoFiType1C();

  // Return the font name.
  char *getName();

  // Return the encoding, as an array of 256 names (any of which may
  // be NULL).  This is only useful with 8-bit fonts.
  char **getEncoding();

  // Return the mapping from CIDs to GIDs, and return the number of
  // CIDs in *<nCIDs>.  This is only useful for CID fonts.
  Gushort *getCIDToGIDMap(int *nCIDs);

  // Convert to a Type 1 font, suitable for embedding in a PostScript
  // file.  This is only useful with 8-bit fonts.  If <newEncoding> is
  // not NULL, it will be used in place of the encoding in the Type 1C
  // font.  If <ascii> is true the eexec section will be hex-encoded,
  // otherwise it will be left as binary data.  If <psName> is non-NULL,
  // it will be used as the PostScript font name.
  void convertToType1(char *psName, char **newEncoding, GBool ascii,
		      FoFiOutputFunc outputFunc, void *outputStream);

  // Convert to a Type 0 CIDFont, suitable for embedding in a
  // PostScript file.  <psName> will be used as the PostScript font
  // name.
  void convertToCIDType0(char *psName,
			 FoFiOutputFunc outputFunc, void *outputStream);

  // Convert to a Type 0 (but non-CID) composite font, suitable for
  // embedding in a PostScript file.  <psName> will be used as the
  // PostScript font name.
  void convertToType0(char *psName,
		      FoFiOutputFunc outputFunc, void *outputStream);

private:

  FoFiType1C(char *fileA, int lenA, GBool freeFileDataA);
  void eexecCvtGlyph(Type1CEexecBuf *eb, char *glyphName,
		     int offset, int nBytes,
		     Type1CIndex *subrIdx,
		     Type1CPrivateDict *pDict);
  void cvtGlyph(int offset, int nBytes, GString *charBuf,
		Type1CIndex *subrIdx, Type1CPrivateDict *pDict,
		GBool top);
  void cvtGlyphWidth(GBool useOp, GString *charBuf,
		     Type1CPrivateDict *pDict);
  void cvtNum(double x, GBool isFP, GString *charBuf);
  void eexecWrite(Type1CEexecBuf *eb, char *s);
  void eexecWriteCharstring(Type1CEexecBuf *eb, Guchar *s, int n);
  GBool parse();
  void readTopDict();
  void readFD(int offset, int length, Type1CPrivateDict *pDict);
  void readPrivateDict(int offset, int length, Type1CPrivateDict *pDict);
  void readFDSelect();
  void buildEncoding();
  GBool readCharset();
  int getOp(int pos, GBool charstring, GBool *ok);
  int getDeltaIntArray(int *arr, int maxLen);
  int getDeltaFPArray(double *arr, int maxLen);
  void getIndex(int pos, Type1CIndex *idx, GBool *ok);
  void getIndexVal(Type1CIndex *idx, int i, Type1CIndexVal *val, GBool *ok);
  char *getString(int sid, char *buf, GBool *ok);

  GString *name;
  char **encoding;

  Type1CIndex nameIdx;
  Type1CIndex topDictIdx;
  Type1CIndex stringIdx;
  Type1CIndex gsubrIdx;
  Type1CIndex charStringsIdx;

  Type1CTopDict topDict;
  Type1CPrivateDict *privateDicts;

  int nGlyphs;
  int nFDs;
  Guchar *fdSelect;
  Gushort *charset;
  int gsubrBias;

  GBool parsedOk;

  Type1COp ops[49];		// operands and operator
  int nOps;			// number of operands
  int nHints;			// number of hints for the current glyph
  GBool firstOp;		// true if we haven't hit the first op yet
  GBool openPath;		// true if there is an unclosed path
};

#endif
#========================================================================
#
# FoFi library Makefile
#
# Copyright 2003 Glyph & Cog, LLC
#
#========================================================================

SHELL = /bin/sh

srcdir = @srcdir@
VPATH = @srcdir@

GOOSRCDIR = $(srcdir)/../goo
GOOLIBDIR = ../goo

CXXFLAGS = -I/usr/local/include @CXXFLAGS@ @DEFS@ -I.. -I$(GOOSRCDIR) -I$(srcdir)

CXX = @CXX@
AR = ar rc
RANLIB = ranlib

LIBPREFIX = lib

#------------------------------------------------------------------------

.SUFFIXES: .cc

.cc.o:
	$(CXX) $(CXXFLAGS) -c $<

#------------------------------------------------------------------------

CXX_SRC = \
	$(srcdir)/FoFiBase.cc \
	$(srcdir)/FoFiEncodings.cc \
	$(srcdir)/FoFiTrueType.cc \
	$(srcdir)/FoFiType1.cc \
	$(srcdir)/FoFiType1C.cc

#------------------------------------------------------------------------

all: $(LIBPREFIX)fofi.a

#------------------------------------------------------------------------

FOFI_OBJS = \
	FoFiBase.o \
	FoFiEncodings.o \
	FoFiTrueType.o \
	FoFiType1.o \
	FoFiType1C.o

$(LIBPREFIX)fofi.a: $(FOFI_OBJS)
	rm -f $(LIBPREFIX)fofi.a
	$(AR) $(LIBPREFIX)fofi.a $(FOFI_OBJS)
	$(RANLIB) $(LIBPREFIX)fofi.a

#------------------------------------------------------------------------
FOFI_WINOBJS = libfofi.lib FoFiBase.obj FoFiEncodings.obj FoFiTrueType.obj FoFiType1C.obj FoFiType1.obj

clean:
	rm -f $(FOFI_OBJS) $(LIBPREFIX)fofi.a $(FOFI_WINOBJS)

#------------------------------------------------------------------------

depend:
	$(CXX) $(CXXFLAGS) -MM $(CXX_SRC) >Makefile.dep

include Makefile.dep
/***************************************************************************/
/*                                                                         */
/*  ftconfig.h                                                             */
/*                                                                         */
/*    ANSI-specific configuration file (specification only).               */
/*                                                                         */
/*  Copyright 1996-2001, 2002, 2003, 2004, 2006, 2007, 2008, 2010 by       */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* This header file contains a number of macro definitions that are used */
  /* by the rest of the engine.  Most of the macros here are automatically */
  /* determined at compile time, and you should not need to change it to   */
  /* port FreeType, except to compile the library with a non-ANSI          */
  /* compiler.                                                             */
  /*                                                                       */
  /* Note however that if some specific modifications are needed, we       */
  /* advise you to place a modified copy in your build directory.          */
  /*                                                                       */
  /* The build directory is usually `freetype/builds/<system>', and        */
  /* contains system-specific files that are always included first when    */
  /* building the library.                                                 */
  /*                                                                       */
  /* This ANSI version should stay in `include/freetype/config'.           */
  /*                                                                       */
  /*************************************************************************/

#ifndef __FTCONFIG_H__
#define __FTCONFIG_H__

#include <ft2build.h>
#include FT_CONFIG_OPTIONS_H
#include FT_CONFIG_STANDARD_LIBRARY_H


FT_BEGIN_HEADER


  /*************************************************************************/
  /*                                                                       */
  /*               PLATFORM-SPECIFIC CONFIGURATION MACROS                  */
  /*                                                                       */
  /* These macros can be toggled to suit a specific system.  The current   */
  /* ones are defaults used to compile FreeType in an ANSI C environment   */
  /* (16bit compilers are also supported).  Copy this file to your own     */
  /* `freetype/builds/<system>' directory, and edit it to port the engine. */
  /*                                                                       */
  /*************************************************************************/


  /* There are systems (like the Texas Instruments 'C54x) where a `char' */
  /* has 16 bits.  ANSI C says that sizeof(char) is always 1.  Since an  */
  /* `int' has 16 bits also for this system, sizeof(int) gives 1 which   */
  /* is probably unexpected.                                             */
  /*                                                                     */
  /* `CHAR_BIT' (defined in limits.h) gives the number of bits in a      */
  /* `char' type.                                                        */

#ifndef FT_CHAR_BIT
#define FT_CHAR_BIT  CHAR_BIT
#endif


  /* The size of an `int' type.  */
#if                                 FT_UINT_MAX == 0xFFFFUL
#define FT_SIZEOF_INT  (16 / FT_CHAR_BIT)
#elif                               FT_UINT_MAX == 0xFFFFFFFFUL
#define FT_SIZEOF_INT  (32 / FT_CHAR_BIT)
#elif FT_UINT_MAX > 0xFFFFFFFFUL && FT_UINT_MAX == 0xFFFFFFFFFFFFFFFFUL
#define FT_SIZEOF_INT  (64 / FT_CHAR_BIT)
#else
#error "Unsupported size of `int' type!"
#endif

  /* The size of a `long' type.  A five-byte `long' (as used e.g. on the */
  /* DM642) is recognized but avoided.                                   */
#if                                  FT_ULONG_MAX == 0xFFFFFFFFUL
#define FT_SIZEOF_LONG  (32 / FT_CHAR_BIT)
#elif FT_ULONG_MAX > 0xFFFFFFFFUL && FT_ULONG_MAX == 0xFFFFFFFFFFUL
#define FT_SIZEOF_LONG  (32 / FT_CHAR_BIT)
#elif FT_ULONG_MAX > 0xFFFFFFFFUL && FT_ULONG_MAX == 0xFFFFFFFFFFFFFFFFUL
#define FT_SIZEOF_LONG  (64 / FT_CHAR_BIT)
#else
#error "Unsupported size of `long' type!"
#endif


  /* Preferred alignment of data */
#define FT_ALIGNMENT  8


  /* FT_UNUSED is a macro used to indicate that a given parameter is not  */
  /* used -- this is only used to get rid of unpleasant compiler warnings */
#ifndef FT_UNUSED
#define FT_UNUSED( arg )  ( (arg) = (arg) )
#endif


  /*************************************************************************/
  /*                                                                       */
  /*                     AUTOMATIC CONFIGURATION MACROS                    */
  /*                                                                       */
  /* These macros are computed from the ones defined above.  Don't touch   */
  /* their definition, unless you know precisely what you are doing.  No   */
  /* porter should need to mess with them.                                 */
  /*                                                                       */
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* Mac support                                                           */
  /*                                                                       */
  /*   This is the only necessary change, so it is defined here instead    */
  /*   providing a new configuration file.                                 */
  /*                                                                       */
#if ( defined( __APPLE__ ) && !defined( DARWIN_NO_CARBON ) ) || \
    ( defined( __MWERKS__ ) && defined( macintosh )        )
  /* no Carbon frameworks for 64bit 10.4.x */
#include "AvailabilityMacros.h"
#if defined( __LP64__ ) && \
    ( MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4 )
#define DARWIN_NO_CARBON 1
#else
#define FT_MACINTOSH 1
#endif

#elif defined( __SC__ ) || defined( __MRC__ )
  /* Classic MacOS compilers */
#include "ConditionalMacros.h"
#if TARGET_OS_MAC
#define FT_MACINTOSH 1
#endif

#endif


  /*************************************************************************/
  /*                                                                       */
  /* <Section>                                                             */
  /*    basic_types                                                        */
  /*                                                                       */
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FT_Int16                                                           */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A typedef for a 16bit signed integer type.                         */
  /*                                                                       */
  typedef signed short  FT_Int16;


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FT_UInt16                                                          */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A typedef for a 16bit unsigned integer type.                       */
  /*                                                                       */
  typedef unsigned short  FT_UInt16;

  /* */


  /* this #if 0 ... #endif clause is for documentation purposes */
#if 0

  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FT_Int32                                                           */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A typedef for a 32bit signed integer type.  The size depends on    */
  /*    the configuration.                                                 */
  /*                                                                       */
  typedef signed XXX  FT_Int32;


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FT_UInt32                                                          */
  /*                                                                       */
  /*    A typedef for a 32bit unsigned integer type.  The size depends on  */
  /*    the configuration.                                                 */
  /*                                                                       */
  typedef unsigned XXX  FT_UInt32;

  /* */

#endif

#if FT_SIZEOF_INT == (32 / FT_CHAR_BIT)

  typedef signed int      FT_Int32;
  typedef unsigned int    FT_UInt32;

#elif FT_SIZEOF_LONG == (32 / FT_CHAR_BIT)

  typedef signed long     FT_Int32;
  typedef unsigned long   FT_UInt32;

#else
#error "no 32bit type found -- please check your configuration files"
#endif


  /* look up an integer type that is at least 32 bits */
#if FT_SIZEOF_INT >= (32 / FT_CHAR_BIT)

  typedef int            FT_Fast;
  typedef unsigned int   FT_UFast;

#elif FT_SIZEOF_LONG >= (32 / FT_CHAR_BIT)

  typedef long           FT_Fast;
  typedef unsigned long  FT_UFast;

#endif


  /* determine whether we have a 64-bit int type for platforms without */
  /* Autoconf                                                          */
#if FT_SIZEOF_LONG == (64 / FT_CHAR_BIT)

  /* FT_LONG64 must be defined if a 64-bit type is available */
#define FT_LONG64
#define FT_INT64  long

#elif defined( _MSC_VER ) && _MSC_VER >= 900  /* Visual C++ (and Intel C++) */

  /* this compiler provides the __int64 type */
#define FT_LONG64
#define FT_INT64  __int64

#elif defined( __BORLANDC__ )  /* Borland C++ */

  /* XXXX: We should probably check the value of __BORLANDC__ in order */
  /*       to test the compiler version.                               */

  /* this compiler provides the __int64 type */
#define FT_LONG64
#define FT_INT64  __int64

#elif defined( __WATCOMC__ )   /* Watcom C++ */

  /* Watcom doesn't provide 64-bit data types */

#elif defined( __MWERKS__ )    /* Metrowerks CodeWarrior */

#define FT_LONG64
#define FT_INT64  long long int

#elif defined( __GNUC__ )

  /* GCC provides the `long long' type */
#define FT_LONG64
#define FT_INT64  long long int

#endif /* FT_SIZEOF_LONG == (64 / FT_CHAR_BIT) */


  /*************************************************************************/
  /*                                                                       */
  /* A 64-bit data type will create compilation problems if you compile    */
  /* in strict ANSI mode.  To avoid them, we disable its use if __STDC__   */
  /* is defined.  You can however ignore this rule by defining the         */
  /* FT_CONFIG_OPTION_FORCE_INT64 configuration macro.                     */
  /*                                                                       */
#if defined( FT_LONG64 ) && !defined( FT_CONFIG_OPTION_FORCE_INT64 )

#ifdef __STDC__

  /* undefine the 64-bit macros in strict ANSI compilation mode */
#undef FT_LONG64
#undef FT_INT64

#endif /* __STDC__ */

#endif /* FT_LONG64 && !FT_CONFIG_OPTION_FORCE_INT64 */


#define FT_BEGIN_STMNT  do {
#define FT_END_STMNT    } while ( 0 )
#define FT_DUMMY_STMNT  FT_BEGIN_STMNT FT_END_STMNT


#ifndef  FT_CONFIG_OPTION_NO_ASSEMBLER
  /* Provide assembler fragments for performance-critical functions. */
  /* These must be defined `static __inline__' with GCC.             */

#if defined( __CC_ARM ) || defined( __ARMCC__ )  /* RVCT */
#define FT_MULFIX_ASSEMBLER  FT_MulFix_arm

  /* documentation is in freetype.h */

  static __inline FT_Int32
  FT_MulFix_arm( FT_Int32  a,
                 FT_Int32  b )
  {
    register FT_Int32  t, t2;


    __asm
    {
      smull t2, t,  b,  a           /* (lo=t2,hi=t) = a*b */
      mov   a,  t,  asr #31         /* a   = (hi >> 31) */
      add   a,  a,  #0x8000         /* a  += 0x8000 */
      adds  t2, t2, a               /* t2 += a */
      adc   t,  t,  #0              /* t  += carry */
      mov   a,  t2, lsr #16         /* a   = t2 >> 16 */
      orr   a,  a,  t,  lsl #16     /* a  |= t << 16 */
    }
    return a;
  }

#endif /* __CC_ARM || __ARMCC__ */


#ifdef __GNUC__

#if defined( __arm__ ) && !defined( __thumb__ )    && \
    !( defined( __CC_ARM ) || defined( __ARMCC__ ) )
#define FT_MULFIX_ASSEMBLER  FT_MulFix_arm

  /* documentation is in freetype.h */

  static __inline__ FT_Int32
  FT_MulFix_arm( FT_Int32  a,
                 FT_Int32  b )
  {
    register FT_Int32  t, t2;


    asm __volatile__ (
      "smull  %1, %2, %4, %3\n\t"   /* (lo=%1,hi=%2) = a*b */
      "mov    %0, %2, asr #31\n\t"  /* %0  = (hi >> 31) */
      "add    %0, %0, #0x8000\n\t"  /* %0 += 0x8000 */
      "adds   %1, %1, %0\n\t"       /* %1 += %0 */
      "adc    %2, %2, #0\n\t"       /* %2 += carry */
      "mov    %0, %1, lsr #16\n\t"  /* %0  = %1 >> 16 */
      "orr    %0, %2, lsl #16\n\t"  /* %0 |= %2 << 16 */
      : "=r"(a), "=&r"(t2), "=&r"(t)
      : "r"(a), "r"(b) );
    return a;
  }

#endif /* __arm__ && !__thumb__ && !( __CC_ARM || __ARMCC__ ) */

#if defined( __i386__ )
#define FT_MULFIX_ASSEMBLER  FT_MulFix_i386

  /* documentation is in freetype.h */

  static __inline__ FT_Int32
  FT_MulFix_i386( FT_Int32  a,
                  FT_Int32  b )
  {
    register FT_Int32  result;


    __asm__ __volatile__ (
      "imul  %%edx\n"
      "movl  %%edx, %%ecx\n"
      "sarl  $31, %%ecx\n"
      "addl  $0x8000, %%ecx\n"
      "addl  %%ecx, %%eax\n"
      "adcl  $0, %%edx\n"
      "shrl  $16, %%eax\n"
      "shll  $16, %%edx\n"
      "addl  %%edx, %%eax\n"
      : "=a"(result), "=d"(b)
      : "a"(a), "d"(b)
      : "%ecx", "cc" );
    return result;
  }

#endif /* i386 */

#endif /* __GNUC__ */

#endif /* !FT_CONFIG_OPTION_NO_ASSEMBLER */


#ifdef FT_CONFIG_OPTION_INLINE_MULFIX
#ifdef FT_MULFIX_ASSEMBLER
#define FT_MULFIX_INLINED  FT_MULFIX_ASSEMBLER
#endif
#endif


#ifdef FT_MAKE_OPTION_SINGLE_OBJECT

#define FT_LOCAL( x )      static  x
#define FT_LOCAL_DEF( x )  static  x

#else

#ifdef __cplusplus
#define FT_LOCAL( x )      extern "C"  x
#define FT_LOCAL_DEF( x )  extern "C"  x
#else
#define FT_LOCAL( x )      extern  x
#define FT_LOCAL_DEF( x )  x
#endif

#endif /* FT_MAKE_OPTION_SINGLE_OBJECT */


#ifndef FT_BASE

#ifdef __cplusplus
#define FT_BASE( x )  extern "C"  x
#else
#define FT_BASE( x )  extern  x
#endif

#endif /* !FT_BASE */


#ifndef FT_BASE_DEF

#ifdef __cplusplus
#define FT_BASE_DEF( x )  x
#else
#define FT_BASE_DEF( x )  x
#endif

#endif /* !FT_BASE_DEF */


#ifndef FT_EXPORT

#ifdef __cplusplus
#define FT_EXPORT( x )  extern "C"  x
#else
#define FT_EXPORT( x )  extern  x
#endif

#endif /* !FT_EXPORT */


#ifndef FT_EXPORT_DEF

#ifdef __cplusplus
#define FT_EXPORT_DEF( x )  extern "C"  x
#else
#define FT_EXPORT_DEF( x )  extern  x
#endif

#endif /* !FT_EXPORT_DEF */


#ifndef FT_EXPORT_VAR

#ifdef __cplusplus
#define FT_EXPORT_VAR( x )  extern "C"  x
#else
#define FT_EXPORT_VAR( x )  extern  x
#endif

#endif /* !FT_EXPORT_VAR */

  /* The following macros are needed to compile the library with a   */
  /* C++ compiler and with 16bit compilers.                          */
  /*                                                                 */

  /* This is special.  Within C++, you must specify `extern "C"' for */
  /* functions which are used via function pointers, and you also    */
  /* must do that for structures which contain function pointers to  */
  /* assure C linkage -- it's not possible to have (local) anonymous */
  /* functions which are accessed by (global) function pointers.     */
  /*                                                                 */
  /*                                                                 */
  /* FT_CALLBACK_DEF is used to _define_ a callback function.        */
  /*                                                                 */
  /* FT_CALLBACK_TABLE is used to _declare_ a constant variable that */
  /* contains pointers to callback functions.                        */
  /*                                                                 */
  /* FT_CALLBACK_TABLE_DEF is used to _define_ a constant variable   */
  /* that contains pointers to callback functions.                   */
  /*                                                                 */
  /*                                                                 */
  /* Some 16bit compilers have to redefine these macros to insert    */
  /* the infamous `_cdecl' or `__fastcall' declarations.             */
  /*                                                                 */
#ifndef FT_CALLBACK_DEF
#ifdef __cplusplus
#define FT_CALLBACK_DEF( x )  extern "C"  x
#else
#define FT_CALLBACK_DEF( x )  static  x
#endif
#endif /* FT_CALLBACK_DEF */

#ifndef FT_CALLBACK_TABLE
#ifdef __cplusplus
#define FT_CALLBACK_TABLE      extern "C"
#define FT_CALLBACK_TABLE_DEF  extern "C"
#else
#define FT_CALLBACK_TABLE      extern
#define FT_CALLBACK_TABLE_DEF  /* nothing */
#endif
#endif /* FT_CALLBACK_TABLE */


FT_END_HEADER


#endif /* __FTCONFIG_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  ftheader.h                                                             */
/*                                                                         */
/*    Build macros of the FreeType 2 library.                              */
/*                                                                         */
/*  Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 by       */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/

#ifndef __FT_HEADER_H__
#define __FT_HEADER_H__


  /*@***********************************************************************/
  /*                                                                       */
  /* <Macro>                                                               */
  /*    FT_BEGIN_HEADER                                                    */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This macro is used in association with @FT_END_HEADER in header    */
  /*    files to ensure that the declarations within are properly          */
  /*    encapsulated in an `extern "C" { .. }' block when included from a  */
  /*    C++ compiler.                                                      */
  /*                                                                       */
#ifdef __cplusplus
#define FT_BEGIN_HEADER  extern "C" {
#else
#define FT_BEGIN_HEADER  /* nothing */
#endif


  /*@***********************************************************************/
  /*                                                                       */
  /* <Macro>                                                               */
  /*    FT_END_HEADER                                                      */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This macro is used in association with @FT_BEGIN_HEADER in header  */
  /*    files to ensure that the declarations within are properly          */
  /*    encapsulated in an `extern "C" { .. }' block when included from a  */
  /*    C++ compiler.                                                      */
  /*                                                                       */
#ifdef __cplusplus
#define FT_END_HEADER  }
#else
#define FT_END_HEADER  /* nothing */
#endif


  /*************************************************************************/
  /*                                                                       */
  /* Aliases for the FreeType 2 public and configuration files.            */
  /*                                                                       */
  /*************************************************************************/

  /*************************************************************************/
  /*                                                                       */
  /* <Section>                                                             */
  /*    header_file_macros                                                 */
  /*                                                                       */
  /* <Title>                                                               */
  /*    Header File Macros                                                 */
  /*                                                                       */
  /* <Abstract>                                                            */
  /*    Macro definitions used to #include specific header files.          */
  /*                                                                       */
  /* <Description>                                                         */
  /*    The following macros are defined to the name of specific           */
  /*    FreeType~2 header files.  They can be used directly in #include    */
  /*    statements as in:                                                  */
  /*                                                                       */
  /*    {                                                                  */
  /*      #include FT_FREETYPE_H                                           */
  /*      #include FT_MULTIPLE_MASTERS_H                                   */
  /*      #include FT_GLYPH_H                                              */
  /*    }                                                                  */
  /*                                                                       */
  /*    There are several reasons why we are now using macros to name      */
  /*    public header files.  The first one is that such macros are not    */
  /*    limited to the infamous 8.3~naming rule required by DOS (and       */
  /*    `FT_MULTIPLE_MASTERS_H' is a lot more meaningful than `ftmm.h').   */
  /*                                                                       */
  /*    The second reason is that it allows for more flexibility in the    */
  /*    way FreeType~2 is installed on a given system.                     */
  /*                                                                       */
  /*************************************************************************/


  /* configuration files */

  /*************************************************************************
   *
   * @macro:
   *   FT_CONFIG_CONFIG_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing
   *   FreeType~2 configuration data.
   *
   */
#ifndef FT_CONFIG_CONFIG_H
#define FT_CONFIG_CONFIG_H  <freetype/config/ftconfig.h>
#endif


  /*************************************************************************
   *
   * @macro:
   *   FT_CONFIG_STANDARD_LIBRARY_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing
   *   FreeType~2 interface to the standard C library functions.
   *
   */
#ifndef FT_CONFIG_STANDARD_LIBRARY_H
#define FT_CONFIG_STANDARD_LIBRARY_H  <freetype/config/ftstdlib.h>
#endif


  /*************************************************************************
   *
   * @macro:
   *   FT_CONFIG_OPTIONS_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing
   *   FreeType~2 project-specific configuration options.
   *
   */
#ifndef FT_CONFIG_OPTIONS_H
#define FT_CONFIG_OPTIONS_H  <freetype/config/ftoption.h>
#endif


  /*************************************************************************
   *
   * @macro:
   *   FT_CONFIG_MODULES_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing the
   *   list of FreeType~2 modules that are statically linked to new library
   *   instances in @FT_Init_FreeType.
   *
   */
#ifndef FT_CONFIG_MODULES_H
#define FT_CONFIG_MODULES_H  <freetype/config/ftmodule.h>
#endif

  /* */

  /* public headers */

  /*************************************************************************
   *
   * @macro:
   *   FT_FREETYPE_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing the
   *   base FreeType~2 API.
   *
   */
#define FT_FREETYPE_H  <freetype/freetype.h>


  /*************************************************************************
   *
   * @macro:
   *   FT_ERRORS_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing the
   *   list of FreeType~2 error codes (and messages).
   *
   *   It is included by @FT_FREETYPE_H.
   *
   */
#define FT_ERRORS_H  <freetype/fterrors.h>


  /*************************************************************************
   *
   * @macro:
   *   FT_MODULE_ERRORS_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing the
   *   list of FreeType~2 module error offsets (and messages).
   *
   */
#define FT_MODULE_ERRORS_H  <freetype/ftmoderr.h>


  /*************************************************************************
   *
   * @macro:
   *   FT_SYSTEM_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing the
   *   FreeType~2 interface to low-level operations (i.e., memory management
   *   and stream i/o).
   *
   *   It is included by @FT_FREETYPE_H.
   *
   */
#define FT_SYSTEM_H  <freetype/ftsystem.h>


  /*************************************************************************
   *
   * @macro:
   *   FT_IMAGE_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing type
   *   definitions related to glyph images (i.e., bitmaps, outlines,
   *   scan-converter parameters).
   *
   *   It is included by @FT_FREETYPE_H.
   *
   */
#define FT_IMAGE_H  <freetype/ftimage.h>


  /*************************************************************************
   *
   * @macro:
   *   FT_TYPES_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing the
   *   basic data types defined by FreeType~2.
   *
   *   It is included by @FT_FREETYPE_H.
   *
   */
#define FT_TYPES_H  <freetype/fttypes.h>


  /*************************************************************************
   *
   * @macro:
   *   FT_LIST_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing the
   *   list management API of FreeType~2.
   *
   *   (Most applications will never need to include this file.)
   *
   */
#define FT_LIST_H  <freetype/ftlist.h>


  /*************************************************************************
   *
   * @macro:
   *   FT_OUTLINE_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing the
   *   scalable outline management API of FreeType~2.
   *
   */
#define FT_OUTLINE_H  <freetype/ftoutln.h>


  /*************************************************************************
   *
   * @macro:
   *   FT_SIZES_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing the
   *   API which manages multiple @FT_Size objects per face.
   *
   */
#define FT_SIZES_H  <freetype/ftsizes.h>


  /*************************************************************************
   *
   * @macro:
   *   FT_MODULE_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing the
   *   module management API of FreeType~2.
   *
   */
#define FT_MODULE_H  <freetype/ftmodapi.h>


  /*************************************************************************
   *
   * @macro:
   *   FT_RENDER_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing the
   *   renderer module management API of FreeType~2.
   *
   */
#define FT_RENDER_H  <freetype/ftrender.h>


  /*************************************************************************
   *
   * @macro:
   *   FT_TYPE1_TABLES_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing the
   *   types and API specific to the Type~1 format.
   *
   */
#define FT_TYPE1_TABLES_H  <freetype/t1tables.h>


  /*************************************************************************
   *
   * @macro:
   *   FT_TRUETYPE_IDS_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing the
   *   enumeration values which identify name strings, languages, encodings,
   *   etc.  This file really contains a _large_ set of constant macro
   *   definitions, taken from the TrueType and OpenType specifications.
   *
   */
#define FT_TRUETYPE_IDS_H  <freetype/ttnameid.h>


  /*************************************************************************
   *
   * @macro:
   *   FT_TRUETYPE_TABLES_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing the
   *   types and API specific to the TrueType (as well as OpenType) format.
   *
   */
#define FT_TRUETYPE_TABLES_H  <freetype/tttables.h>


  /*************************************************************************
   *
   * @macro:
   *   FT_TRUETYPE_TAGS_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing the
   *   definitions of TrueType four-byte `tags' which identify blocks in
   *   SFNT-based font formats (i.e., TrueType and OpenType).
   *
   */
#define FT_TRUETYPE_TAGS_H  <freetype/tttags.h>


  /*************************************************************************
   *
   * @macro:
   *   FT_BDF_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing the
   *   definitions of an API which accesses BDF-specific strings from a
   *   face.
   *
   */
#define FT_BDF_H  <freetype/ftbdf.h>


  /*************************************************************************
   *
   * @macro:
   *   FT_CID_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing the
   *   definitions of an API which access CID font information from a
   *   face.
   *
   */
#define FT_CID_H  <freetype/ftcid.h>


  /*************************************************************************
   *
   * @macro:
   *   FT_GZIP_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing the
   *   definitions of an API which supports gzip-compressed files.
   *
   */
#define FT_GZIP_H  <freetype/ftgzip.h>


  /*************************************************************************
   *
   * @macro:
   *   FT_LZW_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing the
   *   definitions of an API which supports LZW-compressed files.
   *
   */
#define FT_LZW_H  <freetype/ftlzw.h>


  /*************************************************************************
   *
   * @macro:
   *   FT_WINFONTS_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing the
   *   definitions of an API which supports Windows FNT files.
   *
   */
#define FT_WINFONTS_H   <freetype/ftwinfnt.h>


  /*************************************************************************
   *
   * @macro:
   *   FT_GLYPH_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing the
   *   API of the optional glyph management component.
   *
   */
#define FT_GLYPH_H  <freetype/ftglyph.h>


  /*************************************************************************
   *
   * @macro:
   *   FT_BITMAP_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing the
   *   API of the optional bitmap conversion component.
   *
   */
#define FT_BITMAP_H  <freetype/ftbitmap.h>


  /*************************************************************************
   *
   * @macro:
   *   FT_BBOX_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing the
   *   API of the optional exact bounding box computation routines.
   *
   */
#define FT_BBOX_H  <freetype/ftbbox.h>


  /*************************************************************************
   *
   * @macro:
   *   FT_CACHE_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing the
   *   API of the optional FreeType~2 cache sub-system.
   *
   */
#define FT_CACHE_H  <freetype/ftcache.h>


  /*************************************************************************
   *
   * @macro:
   *   FT_CACHE_IMAGE_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing the
   *   `glyph image' API of the FreeType~2 cache sub-system.
   *
   *   It is used to define a cache for @FT_Glyph elements.  You can also
   *   use the API defined in @FT_CACHE_SMALL_BITMAPS_H if you only need to
   *   store small glyph bitmaps, as it will use less memory.
   *
   *   This macro is deprecated.  Simply include @FT_CACHE_H to have all
   *   glyph image-related cache declarations.
   *
   */
#define FT_CACHE_IMAGE_H  FT_CACHE_H


  /*************************************************************************
   *
   * @macro:
   *   FT_CACHE_SMALL_BITMAPS_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing the
   *   `small bitmaps' API of the FreeType~2 cache sub-system.
   *
   *   It is used to define a cache for small glyph bitmaps in a relatively
   *   memory-efficient way.  You can also use the API defined in
   *   @FT_CACHE_IMAGE_H if you want to cache arbitrary glyph images,
   *   including scalable outlines.
   *
   *   This macro is deprecated.  Simply include @FT_CACHE_H to have all
   *   small bitmaps-related cache declarations.
   *
   */
#define FT_CACHE_SMALL_BITMAPS_H  FT_CACHE_H


  /*************************************************************************
   *
   * @macro:
   *   FT_CACHE_CHARMAP_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing the
   *   `charmap' API of the FreeType~2 cache sub-system.
   *
   *   This macro is deprecated.  Simply include @FT_CACHE_H to have all
   *   charmap-based cache declarations.
   *
   */
#define FT_CACHE_CHARMAP_H  FT_CACHE_H


  /*************************************************************************
   *
   * @macro:
   *   FT_MAC_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing the
   *   Macintosh-specific FreeType~2 API.  The latter is used to access
   *   fonts embedded in resource forks.
   *
   *   This header file must be explicitly included by client applications
   *   compiled on the Mac (note that the base API still works though).
   *
   */
#define FT_MAC_H  <freetype/ftmac.h>


  /*************************************************************************
   *
   * @macro:
   *   FT_MULTIPLE_MASTERS_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing the
   *   optional multiple-masters management API of FreeType~2.
   *
   */
#define FT_MULTIPLE_MASTERS_H  <freetype/ftmm.h>


  /*************************************************************************
   *
   * @macro:
   *   FT_SFNT_NAMES_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing the
   *   optional FreeType~2 API which accesses embedded `name' strings in
   *   SFNT-based font formats (i.e., TrueType and OpenType).
   *
   */
#define FT_SFNT_NAMES_H  <freetype/ftsnames.h>


  /*************************************************************************
   *
   * @macro:
   *   FT_OPENTYPE_VALIDATE_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing the
   *   optional FreeType~2 API which validates OpenType tables (BASE, GDEF,
   *   GPOS, GSUB, JSTF).
   *
   */
#define FT_OPENTYPE_VALIDATE_H  <freetype/ftotval.h>


  /*************************************************************************
   *
   * @macro:
   *   FT_GX_VALIDATE_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing the
   *   optional FreeType~2 API which validates TrueTypeGX/AAT tables (feat,
   *   mort, morx, bsln, just, kern, opbd, trak, prop).
   *
   */
#define FT_GX_VALIDATE_H  <freetype/ftgxval.h>


  /*************************************************************************
   *
   * @macro:
   *   FT_PFR_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing the
   *   FreeType~2 API which accesses PFR-specific data.
   *
   */
#define FT_PFR_H  <freetype/ftpfr.h>


  /*************************************************************************
   *
   * @macro:
   *   FT_STROKER_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing the
   *   FreeType~2 API which provides functions to stroke outline paths.
   */
#define FT_STROKER_H  <freetype/ftstroke.h>


  /*************************************************************************
   *
   * @macro:
   *   FT_SYNTHESIS_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing the
   *   FreeType~2 API which performs artificial obliquing and emboldening.
   */
#define FT_SYNTHESIS_H  <freetype/ftsynth.h>


  /*************************************************************************
   *
   * @macro:
   *   FT_XFREE86_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing the
   *   FreeType~2 API which provides functions specific to the XFree86 and
   *   X.Org X11 servers.
   */
#define FT_XFREE86_H  <freetype/ftxf86.h>


  /*************************************************************************
   *
   * @macro:
   *   FT_TRIGONOMETRY_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing the
   *   FreeType~2 API which performs trigonometric computations (e.g.,
   *   cosines and arc tangents).
   */
#define FT_TRIGONOMETRY_H  <freetype/fttrigon.h>


  /*************************************************************************
   *
   * @macro:
   *   FT_LCD_FILTER_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing the
   *   FreeType~2 API which performs color filtering for subpixel rendering.
   */
#define FT_LCD_FILTER_H  <freetype/ftlcdfil.h>


  /*************************************************************************
   *
   * @macro:
   *   FT_UNPATENTED_HINTING_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing the
   *   FreeType~2 API which performs color filtering for subpixel rendering.
   */
#define FT_UNPATENTED_HINTING_H  <freetype/ttunpat.h>


  /*************************************************************************
   *
   * @macro:
   *   FT_INCREMENTAL_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing the
   *   FreeType~2 API which performs color filtering for subpixel rendering.
   */
#define FT_INCREMENTAL_H  <freetype/ftincrem.h>


  /*************************************************************************
   *
   * @macro:
   *   FT_GASP_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing the
   *   FreeType~2 API which returns entries from the TrueType GASP table.
   */
#define FT_GASP_H  <freetype/ftgasp.h>


  /*************************************************************************
   *
   * @macro:
   *   FT_ADVANCES_H
   *
   * @description:
   *   A macro used in #include statements to name the file containing the
   *   FreeType~2 API which returns individual and ranged glyph advances.
   */
#define FT_ADVANCES_H  <freetype/ftadvanc.h>


  /* */

#define FT_ERROR_DEFINITIONS_H  <freetype/fterrdef.h>


  /* The internals of the cache sub-system are no longer exposed.  We */
  /* default to FT_CACHE_H at the moment just in case, but we know of */
  /* no rogue client that uses them.                                  */
  /*                                                                  */
#define FT_CACHE_MANAGER_H           <freetype/ftcache.h>
#define FT_CACHE_INTERNAL_MRU_H      <freetype/ftcache.h>
#define FT_CACHE_INTERNAL_MANAGER_H  <freetype/ftcache.h>
#define FT_CACHE_INTERNAL_CACHE_H    <freetype/ftcache.h>
#define FT_CACHE_INTERNAL_GLYPH_H    <freetype/ftcache.h>
#define FT_CACHE_INTERNAL_IMAGE_H    <freetype/ftcache.h>
#define FT_CACHE_INTERNAL_SBITS_H    <freetype/ftcache.h>


#define FT_INCREMENTAL_H          <freetype/ftincrem.h>

#define FT_TRUETYPE_UNPATENTED_H  <freetype/ttunpat.h>


  /*
   * Include internal headers definitions from <freetype/internal/...>
   * only when building the library.
   */
#ifdef FT2_BUILD_LIBRARY
#define  FT_INTERNAL_INTERNAL_H  <freetype/internal/internal.h>
#include FT_INTERNAL_INTERNAL_H
#endif /* FT2_BUILD_LIBRARY */


#endif /* __FT2_BUILD_H__ */


/* END */
/*
 *  This file registers the FreeType modules compiled into the library.
 *
 *  If you use GNU make, this file IS NOT USED!  Instead, it is created in
 *  the objects directory (normally `<topdir>/objs/') based on information
 *  from `<topdir>/modules.cfg'.
 *
 *  Please read `docs/INSTALL.ANY' and `docs/CUSTOMIZE' how to compile
 *  FreeType without GNU make.
 *
 */

FT_USE_MODULE( FT_Module_Class, autofit_module_class )
FT_USE_MODULE( FT_Driver_ClassRec, tt_driver_class )
FT_USE_MODULE( FT_Driver_ClassRec, t1_driver_class )
FT_USE_MODULE( FT_Driver_ClassRec, cff_driver_class )
FT_USE_MODULE( FT_Driver_ClassRec, t1cid_driver_class )
FT_USE_MODULE( FT_Driver_ClassRec, pfr_driver_class )
FT_USE_MODULE( FT_Driver_ClassRec, t42_driver_class )
FT_USE_MODULE( FT_Driver_ClassRec, winfnt_driver_class )
FT_USE_MODULE( FT_Driver_ClassRec, pcf_driver_class )
FT_USE_MODULE( FT_Module_Class, psaux_module_class )
FT_USE_MODULE( FT_Module_Class, psnames_module_class )
FT_USE_MODULE( FT_Module_Class, pshinter_module_class )
FT_USE_MODULE( FT_Renderer_Class, ft_raster1_renderer_class )
FT_USE_MODULE( FT_Module_Class, sfnt_module_class )
FT_USE_MODULE( FT_Renderer_Class, ft_smooth_renderer_class )
FT_USE_MODULE( FT_Renderer_Class, ft_smooth_lcd_renderer_class )
FT_USE_MODULE( FT_Renderer_Class, ft_smooth_lcdv_renderer_class )
FT_USE_MODULE( FT_Driver_ClassRec, bdf_driver_class )

/* EOF */
/***************************************************************************/
/*                                                                         */
/*  ftoption.h                                                             */
/*                                                                         */
/*    User-selectable configuration macros (specification only).           */
/*                                                                         */
/*  Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,   */
/*            2010 by                                                      */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __FTOPTION_H__
#define __FTOPTION_H__


#include <ft2build.h>


FT_BEGIN_HEADER

  /*************************************************************************/
  /*                                                                       */
  /*                 USER-SELECTABLE CONFIGURATION MACROS                  */
  /*                                                                       */
  /* This file contains the default configuration macro definitions for    */
  /* a standard build of the FreeType library.  There are three ways to    */
  /* use this file to build project-specific versions of the library:      */
  /*                                                                       */
  /*  - You can modify this file by hand, but this is not recommended in   */
  /*    cases where you would like to build several versions of the        */
  /*    library from a single source directory.                            */
  /*                                                                       */
  /*  - You can put a copy of this file in your build directory, more      */
  /*    precisely in `$BUILD/freetype/config/ftoption.h', where `$BUILD'   */
  /*    is the name of a directory that is included _before_ the FreeType  */
  /*    include path during compilation.                                   */
  /*                                                                       */
  /*    The default FreeType Makefiles and Jamfiles use the build          */
  /*    directory `builds/<system>' by default, but you can easily change  */
  /*    that for your own projects.                                        */
  /*                                                                       */
  /*  - Copy the file <ft2build.h> to `$BUILD/ft2build.h' and modify it    */
  /*    slightly to pre-define the macro FT_CONFIG_OPTIONS_H used to       */
  /*    locate this file during the build.  For example,                   */
  /*                                                                       */
  /*      #define FT_CONFIG_OPTIONS_H  <myftoptions.h>                     */
  /*      #include <freetype/config/ftheader.h>                            */
  /*                                                                       */
  /*    will use `$BUILD/myftoptions.h' instead of this file for macro     */
  /*    definitions.                                                       */
  /*                                                                       */
  /*    Note also that you can similarly pre-define the macro              */
  /*    FT_CONFIG_MODULES_H used to locate the file listing of the modules */
  /*    that are statically linked to the library at compile time.  By     */
  /*    default, this file is <freetype/config/ftmodule.h>.                */
  /*                                                                       */
  /*  We highly recommend using the third method whenever possible.        */
  /*                                                                       */
  /*************************************************************************/


  /*************************************************************************/
  /*************************************************************************/
  /****                                                                 ****/
  /**** G E N E R A L   F R E E T Y P E   2   C O N F I G U R A T I O N ****/
  /****                                                                 ****/
  /*************************************************************************/
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* Uncomment the line below if you want to activate sub-pixel rendering  */
  /* (a.k.a. LCD rendering, or ClearType) in this build of the library.    */
  /*                                                                       */
  /* Note that this feature is covered by several Microsoft patents        */
  /* and should not be activated in any default build of the library.      */
  /*                                                                       */
  /* This macro has no impact on the FreeType API, only on its             */
  /* _implementation_.  For example, using FT_RENDER_MODE_LCD when calling */
  /* FT_Render_Glyph still generates a bitmap that is 3 times wider than   */
  /* the original size in case this macro isn't defined; however, each     */
  /* triplet of subpixels has R=G=B.                                       */
  /*                                                                       */
  /* This is done to allow FreeType clients to run unmodified, forcing     */
  /* them to display normal gray-level anti-aliased glyphs.                */
  /*                                                                       */
/* #define FT_CONFIG_OPTION_SUBPIXEL_RENDERING */


  /*************************************************************************/
  /*                                                                       */
  /* Many compilers provide a non-ANSI 64-bit data type that can be used   */
  /* by FreeType to speed up some computations.  However, this will create */
  /* some problems when compiling the library in strict ANSI mode.         */
  /*                                                                       */
  /* For this reason, the use of 64-bit integers is normally disabled when */
  /* the __STDC__ macro is defined.  You can however disable this by       */
  /* defining the macro FT_CONFIG_OPTION_FORCE_INT64 here.                 */
  /*                                                                       */
  /* For most compilers, this will only create compilation warnings when   */
  /* building the library.                                                 */
  /*                                                                       */
  /* ObNote: The compiler-specific 64-bit integers are detected in the     */
  /*         file `ftconfig.h' either statically or through the            */
  /*         `configure' script on supported platforms.                    */
  /*                                                                       */
#undef FT_CONFIG_OPTION_FORCE_INT64


  /*************************************************************************/
  /*                                                                       */
  /* If this macro is defined, do not try to use an assembler version of   */
  /* performance-critical functions (e.g. FT_MulFix).  You should only do  */
  /* that to verify that the assembler function works properly, or to      */
  /* execute benchmark tests of the various implementations.               */
/* #define FT_CONFIG_OPTION_NO_ASSEMBLER */


  /*************************************************************************/
  /*                                                                       */
  /* If this macro is defined, try to use an inlined assembler version of  */
  /* the `FT_MulFix' function, which is a `hotspot' when loading and       */
  /* hinting glyphs, and which should be executed as fast as possible.     */
  /*                                                                       */
  /* Note that if your compiler or CPU is not supported, this will default */
  /* to the standard and portable implementation found in `ftcalc.c'.      */
  /*                                                                       */
#define FT_CONFIG_OPTION_INLINE_MULFIX


  /*************************************************************************/
  /*                                                                       */
  /* LZW-compressed file support.                                          */
  /*                                                                       */
  /*   FreeType now handles font files that have been compressed with the  */
  /*   `compress' program.  This is mostly used to parse many of the PCF   */
  /*   files that come with various X11 distributions.  The implementation */
  /*   uses NetBSD's `zopen' to partially uncompress the file on the fly   */
  /*   (see src/lzw/ftgzip.c).                                             */
  /*                                                                       */
  /*   Define this macro if you want to enable this `feature'.             */
  /*                                                                       */
#define FT_CONFIG_OPTION_USE_LZW


  /*************************************************************************/
  /*                                                                       */
  /* Gzip-compressed file support.                                         */
  /*                                                                       */
  /*   FreeType now handles font files that have been compressed with the  */
  /*   `gzip' program.  This is mostly used to parse many of the PCF files */
  /*   that come with XFree86.  The implementation uses `zlib' to          */
  /*   partially uncompress the file on the fly (see src/gzip/ftgzip.c).   */
  /*                                                                       */
  /*   Define this macro if you want to enable this `feature'.  See also   */
  /*   the macro FT_CONFIG_OPTION_SYSTEM_ZLIB below.                       */
  /*                                                                       */
#define FT_CONFIG_OPTION_USE_ZLIB


  /*************************************************************************/
  /*                                                                       */
  /* ZLib library selection                                                */
  /*                                                                       */
  /*   This macro is only used when FT_CONFIG_OPTION_USE_ZLIB is defined.  */
  /*   It allows FreeType's `ftgzip' component to link to the system's     */
  /*   installation of the ZLib library.  This is useful on systems like   */
  /*   Unix or VMS where it generally is already available.                */
  /*                                                                       */
  /*   If you let it undefined, the component will use its own copy        */
  /*   of the zlib sources instead.  These have been modified to be        */
  /*   included directly within the component and *not* export external    */
  /*   function names.  This allows you to link any program with FreeType  */
  /*   _and_ ZLib without linking conflicts.                               */
  /*                                                                       */
  /*   Do not #undef this macro here since the build system might define   */
  /*   it for certain configurations only.                                 */
  /*                                                                       */
/* #define FT_CONFIG_OPTION_SYSTEM_ZLIB */


  /*************************************************************************/
  /*                                                                       */
  /* DLL export compilation                                                */
  /*                                                                       */
  /*   When compiling FreeType as a DLL, some systems/compilers need a     */
  /*   special keyword in front OR after the return type of function       */
  /*   declarations.                                                       */
  /*                                                                       */
  /*   Two macros are used within the FreeType source code to define       */
  /*   exported library functions: FT_EXPORT and FT_EXPORT_DEF.            */
  /*                                                                       */
  /*     FT_EXPORT( return_type )                                          */
  /*                                                                       */
  /*       is used in a function declaration, as in                        */
  /*                                                                       */
  /*         FT_EXPORT( FT_Error )                                         */
  /*         FT_Init_FreeType( FT_Library*  alibrary );                    */
  /*                                                                       */
  /*                                                                       */
  /*     FT_EXPORT_DEF( return_type )                                      */
  /*                                                                       */
  /*       is used in a function definition, as in                         */
  /*                                                                       */
  /*         FT_EXPORT_DEF( FT_Error )                                     */
  /*         FT_Init_FreeType( FT_Library*  alibrary )                     */
  /*         {                                                             */
  /*           ... some code ...                                           */
  /*           return FT_Err_Ok;                                           */
  /*         }                                                             */
  /*                                                                       */
  /*   You can provide your own implementation of FT_EXPORT and            */
  /*   FT_EXPORT_DEF here if you want.  If you leave them undefined, they  */
  /*   will be later automatically defined as `extern return_type' to      */
  /*   allow normal compilation.                                           */
  /*                                                                       */
  /*   Do not #undef these macros here since the build system might define */
  /*   them for certain configurations only.                               */
  /*                                                                       */
/* #define FT_EXPORT(x)      extern x */
/* #define FT_EXPORT_DEF(x)  x */


  /*************************************************************************/
  /*                                                                       */
  /* Glyph Postscript Names handling                                       */
  /*                                                                       */
  /*   By default, FreeType 2 is compiled with the `psnames' module.  This */
  /*   module is in charge of converting a glyph name string into a        */
  /*   Unicode value, or return a Macintosh standard glyph name for the    */
  /*   use with the TrueType `post' table.                                 */
  /*                                                                       */
  /*   Undefine this macro if you do not want `psnames' compiled in your   */
  /*   build of FreeType.  This has the following effects:                 */
  /*                                                                       */
  /*   - The TrueType driver will provide its own set of glyph names,      */
  /*     if you build it to support postscript names in the TrueType       */
  /*     `post' table.                                                     */
  /*                                                                       */
  /*   - The Type 1 driver will not be able to synthesize a Unicode        */
  /*     charmap out of the glyphs found in the fonts.                     */
  /*                                                                       */
  /*   You would normally undefine this configuration macro when building  */
  /*   a version of FreeType that doesn't contain a Type 1 or CFF driver.  */
  /*                                                                       */
#define FT_CONFIG_OPTION_POSTSCRIPT_NAMES


  /*************************************************************************/
  /*                                                                       */
  /* Postscript Names to Unicode Values support                            */
  /*                                                                       */
  /*   By default, FreeType 2 is built with the `PSNames' module compiled  */
  /*   in.  Among other things, the module is used to convert a glyph name */
  /*   into a Unicode value.  This is especially useful in order to        */
  /*   synthesize on the fly a Unicode charmap from the CFF/Type 1 driver  */
  /*   through a big table named the `Adobe Glyph List' (AGL).             */
  /*                                                                       */
  /*   Undefine this macro if you do not want the Adobe Glyph List         */
  /*   compiled in your `PSNames' module.  The Type 1 driver will not be   */
  /*   able to synthesize a Unicode charmap out of the glyphs found in the */
  /*   fonts.                                                              */
  /*                                                                       */
#define FT_CONFIG_OPTION_ADOBE_GLYPH_LIST


  /*************************************************************************/
  /*                                                                       */
  /* Support for Mac fonts                                                 */
  /*                                                                       */
  /*   Define this macro if you want support for outline fonts in Mac      */
  /*   format (mac dfont, mac resource, macbinary containing a mac         */
  /*   resource) on non-Mac platforms.                                     */
  /*                                                                       */
  /*   Note that the `FOND' resource isn't checked.                        */
  /*                                                                       */
#define FT_CONFIG_OPTION_MAC_FONTS


  /*************************************************************************/
  /*                                                                       */
  /* Guessing methods to access embedded resource forks                    */
  /*                                                                       */
  /*   Enable extra Mac fonts support on non-Mac platforms (e.g.           */
  /*   GNU/Linux).                                                         */
  /*                                                                       */
  /*   Resource forks which include fonts data are stored sometimes in     */
  /*   locations which users or developers don't expected.  In some cases, */
  /*   resource forks start with some offset from the head of a file.  In  */
  /*   other cases, the actual resource fork is stored in file different   */
  /*   from what the user specifies.  If this option is activated,         */
  /*   FreeType tries to guess whether such offsets or different file      */
  /*   names must be used.                                                 */
  /*                                                                       */
  /*   Note that normal, direct access of resource forks is controlled via */
  /*   the FT_CONFIG_OPTION_MAC_FONTS option.                              */
  /*                                                                       */
#ifdef FT_CONFIG_OPTION_MAC_FONTS
#define FT_CONFIG_OPTION_GUESSING_EMBEDDED_RFORK
#endif


  /*************************************************************************/
  /*                                                                       */
  /* Allow the use of FT_Incremental_Interface to load typefaces that      */
  /* contain no glyph data, but supply it via a callback function.         */
  /* This is required by clients supporting document formats which         */
  /* supply font data incrementally as the document is parsed, such        */
  /* as the Ghostscript interpreter for the PostScript language.           */
  /*                                                                       */
#define FT_CONFIG_OPTION_INCREMENTAL


  /*************************************************************************/
  /*                                                                       */
  /* The size in bytes of the render pool used by the scan-line converter  */
  /* to do all of its work.                                                */
  /*                                                                       */
  /* This must be greater than 4KByte if you use FreeType to rasterize     */
  /* glyphs; otherwise, you may set it to zero to avoid unnecessary        */
  /* allocation of the render pool.                                        */
  /*                                                                       */
#define FT_RENDER_POOL_SIZE  16384L


  /*************************************************************************/
  /*                                                                       */
  /* FT_MAX_MODULES                                                        */
  /*                                                                       */
  /*   The maximum number of modules that can be registered in a single    */
  /*   FreeType library object.  32 is the default.                        */
  /*                                                                       */
#define FT_MAX_MODULES  32


  /*************************************************************************/
  /*                                                                       */
  /* Debug level                                                           */
  /*                                                                       */
  /*   FreeType can be compiled in debug or trace mode.  In debug mode,    */
  /*   errors are reported through the `ftdebug' component.  In trace      */
  /*   mode, additional messages are sent to the standard output during    */
  /*   execution.                                                          */
  /*                                                                       */
  /*   Define FT_DEBUG_LEVEL_ERROR to build the library in debug mode.     */
  /*   Define FT_DEBUG_LEVEL_TRACE to build it in trace mode.              */
  /*                                                                       */
  /*   Don't define any of these macros to compile in `release' mode!      */
  /*                                                                       */
  /*   Do not #undef these macros here since the build system might define */
  /*   them for certain configurations only.                               */
  /*                                                                       */
/* #define FT_DEBUG_LEVEL_ERROR */
/* #define FT_DEBUG_LEVEL_TRACE */


  /*************************************************************************/
  /*                                                                       */
  /* Memory Debugging                                                      */
  /*                                                                       */
  /*   FreeType now comes with an integrated memory debugger that is       */
  /*   capable of detecting simple errors like memory leaks or double      */
  /*   deletes.  To compile it within your build of the library, you       */
  /*   should define FT_DEBUG_MEMORY here.                                 */
  /*                                                                       */
  /*   Note that the memory debugger is only activated at runtime when     */
  /*   when the _environment_ variable `FT2_DEBUG_MEMORY' is defined also! */
  /*                                                                       */
  /*   Do not #undef this macro here since the build system might define   */
  /*   it for certain configurations only.                                 */
  /*                                                                       */
/* #define FT_DEBUG_MEMORY */


  /*************************************************************************/
  /*                                                                       */
  /* Module errors                                                         */
  /*                                                                       */
  /*   If this macro is set (which is _not_ the default), the higher byte  */
  /*   of an error code gives the module in which the error has occurred,  */
  /*   while the lower byte is the real error code.                        */
  /*                                                                       */
  /*   Setting this macro makes sense for debugging purposes only, since   */
  /*   it would break source compatibility of certain programs that use    */
  /*   FreeType 2.                                                         */
  /*                                                                       */
  /*   More details can be found in the files ftmoderr.h and fterrors.h.   */
  /*                                                                       */
#undef FT_CONFIG_OPTION_USE_MODULE_ERRORS


  /*************************************************************************/
  /*                                                                       */
  /* Position Independent Code                                             */
  /*                                                                       */
  /*   If this macro is set (which is _not_ the default), FreeType2 will   */
  /*   avoid creating constants that require address fixups.  Instead the  */
  /*   constants will be moved into a struct and additional intialization  */
  /*   code will be used.                                                  */
  /*                                                                       */
  /*   Setting this macro is needed for systems that prohibit address      */
  /*   fixups, such as BREW.                                               */
  /*                                                                       */
/* #define FT_CONFIG_OPTION_PIC */


  /*************************************************************************/
  /*************************************************************************/
  /****                                                                 ****/
  /****        S F N T   D R I V E R    C O N F I G U R A T I O N       ****/
  /****                                                                 ****/
  /*************************************************************************/
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* Define TT_CONFIG_OPTION_EMBEDDED_BITMAPS if you want to support       */
  /* embedded bitmaps in all formats using the SFNT module (namely         */
  /* TrueType & OpenType).                                                 */
  /*                                                                       */
#define TT_CONFIG_OPTION_EMBEDDED_BITMAPS


  /*************************************************************************/
  /*                                                                       */
  /* Define TT_CONFIG_OPTION_POSTSCRIPT_NAMES if you want to be able to    */
  /* load and enumerate the glyph Postscript names in a TrueType or        */
  /* OpenType file.                                                        */
  /*                                                                       */
  /* Note that when you do not compile the `PSNames' module by undefining  */
  /* the above FT_CONFIG_OPTION_POSTSCRIPT_NAMES, the `sfnt' module will   */
  /* contain additional code used to read the PS Names table from a font.  */
  /*                                                                       */
  /* (By default, the module uses `PSNames' to extract glyph names.)       */
  /*                                                                       */
#define TT_CONFIG_OPTION_POSTSCRIPT_NAMES


  /*************************************************************************/
  /*                                                                       */
  /* Define TT_CONFIG_OPTION_SFNT_NAMES if your applications need to       */
  /* access the internal name table in a SFNT-based format like TrueType   */
  /* or OpenType.  The name table contains various strings used to         */
  /* describe the font, like family name, copyright, version, etc.  It     */
  /* does not contain any glyph name though.                               */
  /*                                                                       */
  /* Accessing SFNT names is done through the functions declared in        */
  /* `freetype/ftsnames.h'.                                                */
  /*                                                                       */
#define TT_CONFIG_OPTION_SFNT_NAMES


  /*************************************************************************/
  /*                                                                       */
  /* TrueType CMap support                                                 */
  /*                                                                       */
  /*   Here you can fine-tune which TrueType CMap table format shall be    */
  /*   supported.                                                          */
#define TT_CONFIG_CMAP_FORMAT_0
#define TT_CONFIG_CMAP_FORMAT_2
#define TT_CONFIG_CMAP_FORMAT_4
#define TT_CONFIG_CMAP_FORMAT_6
#define TT_CONFIG_CMAP_FORMAT_8
#define TT_CONFIG_CMAP_FORMAT_10
#define TT_CONFIG_CMAP_FORMAT_12
#define TT_CONFIG_CMAP_FORMAT_13
#define TT_CONFIG_CMAP_FORMAT_14


  /*************************************************************************/
  /*************************************************************************/
  /****                                                                 ****/
  /****    T R U E T Y P E   D R I V E R    C O N F I G U R A T I O N   ****/
  /****                                                                 ****/
  /*************************************************************************/
  /*************************************************************************/

  /*************************************************************************/
  /*                                                                       */
  /* Define TT_CONFIG_OPTION_BYTECODE_INTERPRETER if you want to compile   */
  /* a bytecode interpreter in the TrueType driver.                        */
  /*                                                                       */
  /* By undefining this, you will only compile the code necessary to load  */
  /* TrueType glyphs without hinting.                                      */
  /*                                                                       */
  /*   Do not #undef this macro here, since the build system might         */
  /*   define it for certain configurations only.                          */
  /*                                                                       */
#define TT_CONFIG_OPTION_BYTECODE_INTERPRETER


  /*************************************************************************/
  /*                                                                       */
  /* If you define TT_CONFIG_OPTION_UNPATENTED_HINTING, a special version  */
  /* of the TrueType bytecode interpreter is used that doesn't implement   */
  /* any of the patented opcodes and algorithms.  The patents related to   */
  /* TrueType hinting have expired worldwide since May 2010; this option   */
  /* is now deprecated.                                                    */
  /*                                                                       */
  /* Note that the TT_CONFIG_OPTION_UNPATENTED_HINTING macro is *ignored*  */
  /* if you define TT_CONFIG_OPTION_BYTECODE_INTERPRETER; in other words,  */
  /* either define TT_CONFIG_OPTION_BYTECODE_INTERPRETER or                */
  /* TT_CONFIG_OPTION_UNPATENTED_HINTING but not both at the same time.    */
  /*                                                                       */
  /* This macro is only useful for a small number of font files (mostly    */
  /* for Asian scripts) that require bytecode interpretation to properly   */
  /* load glyphs.  For all other fonts, this produces unpleasant results,  */
  /* thus the unpatented interpreter is never used to load glyphs from     */
  /* TrueType fonts unless one of the following two options is used.       */
  /*                                                                       */
  /*   - The unpatented interpreter is explicitly activated by the user    */
  /*     through the FT_PARAM_TAG_UNPATENTED_HINTING parameter tag         */
  /*     when opening the FT_Face.                                         */
  /*                                                                       */
  /*   - FreeType detects that the FT_Face corresponds to one of the       */
  /*     `trick' fonts (e.g., `Mingliu') it knows about.  The font engine  */
  /*     contains a hard-coded list of font names and other matching       */
  /*     parameters (see function `tt_face_init' in file                   */
  /*     `src/truetype/ttobjs.c').                                         */
  /*                                                                       */
  /* Here a sample code snippet for using FT_PARAM_TAG_UNPATENTED_HINTING. */
  /*                                                                       */
  /*   {                                                                   */
  /*     FT_Parameter  parameter;                                          */
  /*     FT_Open_Args  open_args;                                          */
  /*                                                                       */
  /*                                                                       */
  /*     parameter.tag = FT_PARAM_TAG_UNPATENTED_HINTING;                  */
  /*                                                                       */
  /*     open_args.flags      = FT_OPEN_PATHNAME | FT_OPEN_PARAMS;         */
  /*     open_args.pathname   = my_font_pathname;                          */
  /*     open_args.num_params = 1;                                         */
  /*     open_args.params     = &parameter;                                */
  /*                                                                       */
  /*     error = FT_Open_Face( library, &open_args, index, &face );        */
  /*     ...                                                               */
  /*   }                                                                   */
  /*                                                                       */
/* #define TT_CONFIG_OPTION_UNPATENTED_HINTING */


  /*************************************************************************/
  /*                                                                       */
  /* Define TT_CONFIG_OPTION_INTERPRETER_SWITCH to compile the TrueType    */
  /* bytecode interpreter with a huge switch statement, rather than a call */
  /* table.  This results in smaller and faster code for a number of       */
  /* architectures.                                                        */
  /*                                                                       */
  /* Note however that on some compiler/processor combinations, undefining */
  /* this macro will generate faster, though larger, code.                 */
  /*                                                                       */
#define TT_CONFIG_OPTION_INTERPRETER_SWITCH


  /*************************************************************************/
  /*                                                                       */
  /* Define TT_CONFIG_OPTION_COMPONENT_OFFSET_SCALED to compile the        */
  /* TrueType glyph loader to use Apple's definition of how to handle      */
  /* component offsets in composite glyphs.                                */
  /*                                                                       */
  /* Apple and MS disagree on the default behavior of component offsets    */
  /* in composites.  Apple says that they should be scaled by the scaling  */
  /* factors in the transformation matrix (roughly, it's more complex)     */
  /* while MS says they should not.  OpenType defines two bits in the      */
  /* composite flags array which can be used to disambiguate, but old      */
  /* fonts will not have them.                                             */
  /*                                                                       */
  /*   http://partners.adobe.com/asn/developer/opentype/glyf.html          */
  /*   http://fonts.apple.com/TTRefMan/RM06/Chap6glyf.html                 */
  /*                                                                       */
#undef TT_CONFIG_OPTION_COMPONENT_OFFSET_SCALED


  /*************************************************************************/
  /*                                                                       */
  /* Define TT_CONFIG_OPTION_GX_VAR_SUPPORT if you want to include         */
  /* support for Apple's distortable font technology (fvar, gvar, cvar,    */
  /* and avar tables).  This has many similarities to Type 1 Multiple      */
  /* Masters support.                                                      */
  /*                                                                       */
#define TT_CONFIG_OPTION_GX_VAR_SUPPORT


  /*************************************************************************/
  /*                                                                       */
  /* Define TT_CONFIG_OPTION_BDF if you want to include support for        */
  /* an embedded `BDF ' table within SFNT-based bitmap formats.            */
  /*                                                                       */
#define TT_CONFIG_OPTION_BDF


  /*************************************************************************/
  /*************************************************************************/
  /****                                                                 ****/
  /****      T Y P E 1   D R I V E R    C O N F I G U R A T I O N       ****/
  /****                                                                 ****/
  /*************************************************************************/
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* T1_MAX_DICT_DEPTH is the maximal depth of nest dictionaries and       */
  /* arrays in the Type 1 stream (see t1load.c).  A minimum of 4 is        */
  /* required.                                                             */
  /*                                                                       */
#define T1_MAX_DICT_DEPTH  5


  /*************************************************************************/
  /*                                                                       */
  /* T1_MAX_SUBRS_CALLS details the maximum number of nested sub-routine   */
  /* calls during glyph loading.                                           */
  /*                                                                       */
#define T1_MAX_SUBRS_CALLS  16


  /*************************************************************************/
  /*                                                                       */
  /* T1_MAX_CHARSTRING_OPERANDS is the charstring stack's capacity.  A     */
  /* minimum of 16 is required.                                            */
  /*                                                                       */
  /* The Chinese font MingTiEG-Medium (CNS 11643 character set) needs 256. */
  /*                                                                       */
#define T1_MAX_CHARSTRINGS_OPERANDS  256


  /*************************************************************************/
  /*                                                                       */
  /* Define this configuration macro if you want to prevent the            */
  /* compilation of `t1afm', which is in charge of reading Type 1 AFM      */
  /* files into an existing face.  Note that if set, the T1 driver will be */
  /* unable to produce kerning distances.                                  */
  /*                                                                       */
#undef T1_CONFIG_OPTION_NO_AFM


  /*************************************************************************/
  /*                                                                       */
  /* Define this configuration macro if you want to prevent the            */
  /* compilation of the Multiple Masters font support in the Type 1        */
  /* driver.                                                               */
  /*                                                                       */
#undef T1_CONFIG_OPTION_NO_MM_SUPPORT


  /*************************************************************************/
  /*************************************************************************/
  /****                                                                 ****/
  /****    A U T O F I T   M O D U L E    C O N F I G U R A T I O N     ****/
  /****                                                                 ****/
  /*************************************************************************/
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* Compile autofit module with CJK (Chinese, Japanese, Korean) script    */
  /* support.                                                              */
  /*                                                                       */
#define AF_CONFIG_OPTION_CJK

  /*************************************************************************/
  /*                                                                       */
  /* Compile autofit module with Indic script support.                     */
  /*                                                                       */
#define AF_CONFIG_OPTION_INDIC

  /* */


  /*
   * Define this variable if you want to keep the layout of internal
   * structures that was used prior to FreeType 2.2.  This also compiles in
   * a few obsolete functions to avoid linking problems on typical Unix
   * distributions.
   *
   * For embedded systems or building a new distribution from scratch, it
   * is recommended to disable the macro since it reduces the library's code
   * size and activates a few memory-saving optimizations as well.
   */
#define FT_CONFIG_OPTION_OLD_INTERNALS


  /*
   *  To detect legacy cache-lookup call from a rogue client (<= 2.1.7),
   *  we restrict the number of charmaps in a font.  The current API of
   *  FTC_CMapCache_Lookup() takes cmap_index & charcode, but old API
   *  takes charcode only.  To determine the passed value is for cmap_index
   *  or charcode, the possible cmap_index is restricted not to exceed
   *  the minimum possible charcode by a rogue client.  It is also very
   *  unlikely that a rogue client is interested in Unicode values 0 to 15.
   *
   *  NOTE: The original threshold was 4 deduced from popular number of
   *        cmap subtables in UCS-4 TrueType fonts, but now it is not
   *        irregular for OpenType fonts to have more than 4 subtables,
   *        because variation selector subtables are available for Apple
   *        and Microsoft platforms.
   */

#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
#define FT_MAX_CHARMAP_CACHEABLE 15
#endif


  /*
   * This macro is defined if either unpatented or native TrueType
   * hinting is requested by the definitions above.
   */
#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER
#define  TT_USE_BYTECODE_INTERPRETER
#undef   TT_CONFIG_OPTION_UNPATENTED_HINTING
#elif defined TT_CONFIG_OPTION_UNPATENTED_HINTING
#define  TT_USE_BYTECODE_INTERPRETER
#endif

FT_END_HEADER


#endif /* __FTOPTION_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  ftstdlib.h                                                             */
/*                                                                         */
/*    ANSI-specific library and header configuration file (specification   */
/*    only).                                                               */
/*                                                                         */
/*  Copyright 2002, 2003, 2004, 2005, 2006, 2007, 2009 by                  */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* This file is used to group all #includes to the ANSI C library that   */
  /* FreeType normally requires.  It also defines macros to rename the     */
  /* standard functions within the FreeType source code.                   */
  /*                                                                       */
  /* Load a file which defines __FTSTDLIB_H__ before this one to override  */
  /* it.                                                                   */
  /*                                                                       */
  /*************************************************************************/


#ifndef __FTSTDLIB_H__
#define __FTSTDLIB_H__


#include <stddef.h>

#define ft_ptrdiff_t  ptrdiff_t


  /**********************************************************************/
  /*                                                                    */
  /*                           integer limits                           */
  /*                                                                    */
  /* UINT_MAX and ULONG_MAX are used to automatically compute the size  */
  /* of `int' and `long' in bytes at compile-time.  So far, this works  */
  /* for all platforms the library has been tested on.                  */
  /*                                                                    */
  /* Note that on the extremely rare platforms that do not provide      */
  /* integer types that are _exactly_ 16 and 32 bits wide (e.g. some    */
  /* old Crays where `int' is 36 bits), we do not make any guarantee    */
  /* about the correct behaviour of FT2 with all fonts.                 */
  /*                                                                    */
  /* In these case, `ftconfig.h' will refuse to compile anyway with a   */
  /* message like `couldn't find 32-bit type' or something similar.     */
  /*                                                                    */
  /**********************************************************************/


#include <limits.h>

#define FT_CHAR_BIT   CHAR_BIT
#define FT_INT_MAX    INT_MAX
#define FT_INT_MIN    INT_MIN
#define FT_UINT_MAX   UINT_MAX
#define FT_ULONG_MAX  ULONG_MAX


  /**********************************************************************/
  /*                                                                    */
  /*                 character and string processing                    */
  /*                                                                    */
  /**********************************************************************/


#include <string.h>

#define ft_memchr   memchr
#define ft_memcmp   memcmp
#define ft_memcpy   memcpy
#define ft_memmove  memmove
#define ft_memset   memset
#define ft_strcat   strcat
#define ft_strcmp   strcmp
#define ft_strcpy   strcpy
#define ft_strlen   strlen
#define ft_strncmp  strncmp
#define ft_strncpy  strncpy
#define ft_strrchr  strrchr
#define ft_strstr   strstr


  /**********************************************************************/
  /*                                                                    */
  /*                           file handling                            */
  /*                                                                    */
  /**********************************************************************/


#include <stdio.h>

#define FT_FILE     FILE
#define ft_fclose   fclose
#define ft_fopen    fopen
#define ft_fread    fread
#define ft_fseek    fseek
#define ft_ftell    ftell
#define ft_sprintf  sprintf


  /**********************************************************************/
  /*                                                                    */
  /*                             sorting                                */
  /*                                                                    */
  /**********************************************************************/


#include <stdlib.h>

#define ft_qsort  qsort


  /**********************************************************************/
  /*                                                                    */
  /*                        memory allocation                           */
  /*                                                                    */
  /**********************************************************************/


#define ft_scalloc   calloc
#define ft_sfree     free
#define ft_smalloc   malloc
#define ft_srealloc  realloc


  /**********************************************************************/
  /*                                                                    */
  /*                          miscellaneous                             */
  /*                                                                    */
  /**********************************************************************/


#define ft_atol   atol
#define ft_labs   labs


  /**********************************************************************/
  /*                                                                    */
  /*                         execution control                          */
  /*                                                                    */
  /**********************************************************************/


#include <setjmp.h>

#define ft_jmp_buf     jmp_buf  /* note: this cannot be a typedef since */
                                /*       jmp_buf is defined as a macro  */
                                /*       on certain platforms           */

#define ft_longjmp     longjmp
#define ft_setjmp( b ) setjmp( *(jmp_buf*) &(b) )    /* same thing here */


  /* the following is only used for debugging purposes, i.e., if */
  /* FT_DEBUG_LEVEL_ERROR or FT_DEBUG_LEVEL_TRACE are defined    */

#include <stdarg.h>


#endif /* __FTSTDLIB_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  freetype.h                                                             */
/*                                                                         */
/*    FreeType high-level API and common types (specification only).       */
/*                                                                         */
/*  Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,   */
/*            2010 by                                                      */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef FT_FREETYPE_H
#error "`ft2build.h' hasn't been included yet!"
#error "Please always use macros to include FreeType header files."
#error "Example:"
#error "  #include <ft2build.h>"
#error "  #include FT_FREETYPE_H"
#endif


#ifndef __FREETYPE_H__
#define __FREETYPE_H__


#include <ft2build.h>
#include FT_CONFIG_CONFIG_H
#include FT_ERRORS_H
#include FT_TYPES_H


FT_BEGIN_HEADER



  /*************************************************************************/
  /*                                                                       */
  /* <Section>                                                             */
  /*    user_allocation                                                    */
  /*                                                                       */
  /* <Title>                                                               */
  /*    User allocation                                                    */
  /*                                                                       */
  /* <Abstract>                                                            */
  /*    How client applications should allocate FreeType data structures.  */
  /*                                                                       */
  /* <Description>                                                         */
  /*    FreeType assumes that structures allocated by the user and passed  */
  /*    as arguments are zeroed out except for the actual data.  In other  */
  /*    words, it is recommended to use `calloc' (or variants of it)       */
  /*    instead of `malloc' for allocation.                                */
  /*                                                                       */
  /*************************************************************************/



  /*************************************************************************/
  /*************************************************************************/
  /*                                                                       */
  /*                        B A S I C   T Y P E S                          */
  /*                                                                       */
  /*************************************************************************/
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* <Section>                                                             */
  /*    base_interface                                                     */
  /*                                                                       */
  /* <Title>                                                               */
  /*    Base Interface                                                     */
  /*                                                                       */
  /* <Abstract>                                                            */
  /*    The FreeType~2 base font interface.                                */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This section describes the public high-level API of FreeType~2.    */
  /*                                                                       */
  /* <Order>                                                               */
  /*    FT_Library                                                         */
  /*    FT_Face                                                            */
  /*    FT_Size                                                            */
  /*    FT_GlyphSlot                                                       */
  /*    FT_CharMap                                                         */
  /*    FT_Encoding                                                        */
  /*                                                                       */
  /*    FT_FaceRec                                                         */
  /*                                                                       */
  /*    FT_FACE_FLAG_SCALABLE                                              */
  /*    FT_FACE_FLAG_FIXED_SIZES                                           */
  /*    FT_FACE_FLAG_FIXED_WIDTH                                           */
  /*    FT_FACE_FLAG_HORIZONTAL                                            */
  /*    FT_FACE_FLAG_VERTICAL                                              */
  /*    FT_FACE_FLAG_SFNT                                                  */
  /*    FT_FACE_FLAG_KERNING                                               */
  /*    FT_FACE_FLAG_MULTIPLE_MASTERS                                      */
  /*    FT_FACE_FLAG_GLYPH_NAMES                                           */
  /*    FT_FACE_FLAG_EXTERNAL_STREAM                                       */
  /*    FT_FACE_FLAG_FAST_GLYPHS                                           */
  /*    FT_FACE_FLAG_HINTER                                                */
  /*                                                                       */
  /*    FT_STYLE_FLAG_BOLD                                                 */
  /*    FT_STYLE_FLAG_ITALIC                                               */
  /*                                                                       */
  /*    FT_SizeRec                                                         */
  /*    FT_Size_Metrics                                                    */
  /*                                                                       */
  /*    FT_GlyphSlotRec                                                    */
  /*    FT_Glyph_Metrics                                                   */
  /*    FT_SubGlyph                                                        */
  /*                                                                       */
  /*    FT_Bitmap_Size                                                     */
  /*                                                                       */
  /*    FT_Init_FreeType                                                   */
  /*    FT_Done_FreeType                                                   */
  /*                                                                       */
  /*    FT_New_Face                                                        */
  /*    FT_Done_Face                                                       */
  /*    FT_New_Memory_Face                                                 */
  /*    FT_Open_Face                                                       */
  /*    FT_Open_Args                                                       */
  /*    FT_Parameter                                                       */
  /*    FT_Attach_File                                                     */
  /*    FT_Attach_Stream                                                   */
  /*                                                                       */
  /*    FT_Set_Char_Size                                                   */
  /*    FT_Set_Pixel_Sizes                                                 */
  /*    FT_Request_Size                                                    */
  /*    FT_Select_Size                                                     */
  /*    FT_Size_Request_Type                                               */
  /*    FT_Size_Request                                                    */
  /*    FT_Set_Transform                                                   */
  /*    FT_Load_Glyph                                                      */
  /*    FT_Get_Char_Index                                                  */
  /*    FT_Get_Name_Index                                                  */
  /*    FT_Load_Char                                                       */
  /*                                                                       */
  /*    FT_OPEN_MEMORY                                                     */
  /*    FT_OPEN_STREAM                                                     */
  /*    FT_OPEN_PATHNAME                                                   */
  /*    FT_OPEN_DRIVER                                                     */
  /*    FT_OPEN_PARAMS                                                     */
  /*                                                                       */
  /*    FT_LOAD_DEFAULT                                                    */
  /*    FT_LOAD_RENDER                                                     */
  /*    FT_LOAD_MONOCHROME                                                 */
  /*    FT_LOAD_LINEAR_DESIGN                                              */
  /*    FT_LOAD_NO_SCALE                                                   */
  /*    FT_LOAD_NO_HINTING                                                 */
  /*    FT_LOAD_NO_BITMAP                                                  */
  /*    FT_LOAD_CROP_BITMAP                                                */
  /*                                                                       */
  /*    FT_LOAD_VERTICAL_LAYOUT                                            */
  /*    FT_LOAD_IGNORE_TRANSFORM                                           */
  /*    FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH                                */
  /*    FT_LOAD_FORCE_AUTOHINT                                             */
  /*    FT_LOAD_NO_RECURSE                                                 */
  /*    FT_LOAD_PEDANTIC                                                   */
  /*                                                                       */
  /*    FT_LOAD_TARGET_NORMAL                                              */
  /*    FT_LOAD_TARGET_LIGHT                                               */
  /*    FT_LOAD_TARGET_MONO                                                */
  /*    FT_LOAD_TARGET_LCD                                                 */
  /*    FT_LOAD_TARGET_LCD_V                                               */
  /*                                                                       */
  /*    FT_Render_Glyph                                                    */
  /*    FT_Render_Mode                                                     */
  /*    FT_Get_Kerning                                                     */
  /*    FT_Kerning_Mode                                                    */
  /*    FT_Get_Track_Kerning                                               */
  /*    FT_Get_Glyph_Name                                                  */
  /*    FT_Get_Postscript_Name                                             */
  /*                                                                       */
  /*    FT_CharMapRec                                                      */
  /*    FT_Select_Charmap                                                  */
  /*    FT_Set_Charmap                                                     */
  /*    FT_Get_Charmap_Index                                               */
  /*                                                                       */
  /*    FT_FSTYPE_INSTALLABLE_EMBEDDING                                    */
  /*    FT_FSTYPE_RESTRICTED_LICENSE_EMBEDDING                             */
  /*    FT_FSTYPE_PREVIEW_AND_PRINT_EMBEDDING                              */
  /*    FT_FSTYPE_EDITABLE_EMBEDDING                                       */
  /*    FT_FSTYPE_NO_SUBSETTING                                            */
  /*    FT_FSTYPE_BITMAP_EMBEDDING_ONLY                                    */
  /*                                                                       */
  /*    FT_Get_FSType_Flags                                                */
  /*                                                                       */
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_Glyph_Metrics                                                   */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A structure used to model the metrics of a single glyph.  The      */
  /*    values are expressed in 26.6 fractional pixel format; if the flag  */
  /*    @FT_LOAD_NO_SCALE has been used while loading the glyph, values    */
  /*    are expressed in font units instead.                               */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    width ::                                                           */
  /*      The glyph's width.                                               */
  /*                                                                       */
  /*    height ::                                                          */
  /*      The glyph's height.                                              */
  /*                                                                       */
  /*    horiBearingX ::                                                    */
  /*      Left side bearing for horizontal layout.                         */
  /*                                                                       */
  /*    horiBearingY ::                                                    */
  /*      Top side bearing for horizontal layout.                          */
  /*                                                                       */
  /*    horiAdvance ::                                                     */
  /*      Advance width for horizontal layout.                             */
  /*                                                                       */
  /*    vertBearingX ::                                                    */
  /*      Left side bearing for vertical layout.                           */
  /*                                                                       */
  /*    vertBearingY ::                                                    */
  /*      Top side bearing for vertical layout.                            */
  /*                                                                       */
  /*    vertAdvance ::                                                     */
  /*      Advance height for vertical layout.                              */
  /*                                                                       */
  /* <Note>                                                                */
  /*    If not disabled with @FT_LOAD_NO_HINTING, the values represent     */
  /*    dimensions of the hinted glyph (in case hinting is applicable).    */
  /*                                                                       */
  typedef struct  FT_Glyph_Metrics_
  {
    FT_Pos  width;
    FT_Pos  height;

    FT_Pos  horiBearingX;
    FT_Pos  horiBearingY;
    FT_Pos  horiAdvance;

    FT_Pos  vertBearingX;
    FT_Pos  vertBearingY;
    FT_Pos  vertAdvance;

  } FT_Glyph_Metrics;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_Bitmap_Size                                                     */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This structure models the metrics of a bitmap strike (i.e., a set  */
  /*    of glyphs for a given point size and resolution) in a bitmap font. */
  /*    It is used for the `available_sizes' field of @FT_Face.            */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    height :: The vertical distance, in pixels, between two            */
  /*              consecutive baselines.  It is always positive.           */
  /*                                                                       */
  /*    width  :: The average width, in pixels, of all glyphs in the       */
  /*              strike.                                                  */
  /*                                                                       */
  /*    size   :: The nominal size of the strike in 26.6 fractional        */
  /*              points.  This field is not very useful.                  */
  /*                                                                       */
  /*    x_ppem :: The horizontal ppem (nominal width) in 26.6 fractional   */
  /*              pixels.                                                  */
  /*                                                                       */
  /*    y_ppem :: The vertical ppem (nominal height) in 26.6 fractional    */
  /*              pixels.                                                  */
  /*                                                                       */
  /* <Note>                                                                */
  /*    Windows FNT:                                                       */
  /*      The nominal size given in a FNT font is not reliable.  Thus when */
  /*      the driver finds it incorrect, it sets `size' to some calculated */
  /*      values and sets `x_ppem' and `y_ppem' to the pixel width and     */
  /*      height given in the font, respectively.                          */
  /*                                                                       */
  /*    TrueType embedded bitmaps:                                         */
  /*      `size', `width', and `height' values are not contained in the    */
  /*      bitmap strike itself.  They are computed from the global font    */
  /*      parameters.                                                      */
  /*                                                                       */
  typedef struct  FT_Bitmap_Size_
  {
    FT_Short  height;
    FT_Short  width;

    FT_Pos    size;

    FT_Pos    x_ppem;
    FT_Pos    y_ppem;

  } FT_Bitmap_Size;


  /*************************************************************************/
  /*************************************************************************/
  /*                                                                       */
  /*                     O B J E C T   C L A S S E S                       */
  /*                                                                       */
  /*************************************************************************/
  /*************************************************************************/

  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FT_Library                                                         */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A handle to a FreeType library instance.  Each `library' is        */
  /*    completely independent from the others; it is the `root' of a set  */
  /*    of objects like fonts, faces, sizes, etc.                          */
  /*                                                                       */
  /*    It also embeds a memory manager (see @FT_Memory), as well as a     */
  /*    scan-line converter object (see @FT_Raster).                       */
  /*                                                                       */
  /*    For multi-threading applications each thread should have its own   */
  /*    FT_Library object.                                                 */
  /*                                                                       */
  /* <Note>                                                                */
  /*    Library objects are normally created by @FT_Init_FreeType, and     */
  /*    destroyed with @FT_Done_FreeType.                                  */
  /*                                                                       */
  typedef struct FT_LibraryRec_  *FT_Library;


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FT_Module                                                          */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A handle to a given FreeType module object.  Each module can be a  */
  /*    font driver, a renderer, or anything else that provides services   */
  /*    to the formers.                                                    */
  /*                                                                       */
  typedef struct FT_ModuleRec_*  FT_Module;


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FT_Driver                                                          */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A handle to a given FreeType font driver object.  Each font driver */
  /*    is a special module capable of creating faces from font files.     */
  /*                                                                       */
  typedef struct FT_DriverRec_*  FT_Driver;


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FT_Renderer                                                        */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A handle to a given FreeType renderer.  A renderer is a special    */
  /*    module in charge of converting a glyph image to a bitmap, when     */
  /*    necessary.  Each renderer supports a given glyph image format, and */
  /*    one or more target surface depths.                                 */
  /*                                                                       */
  typedef struct FT_RendererRec_*  FT_Renderer;


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FT_Face                                                            */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A handle to a given typographic face object.  A face object models */
  /*    a given typeface, in a given style.                                */
  /*                                                                       */
  /* <Note>                                                                */
  /*    Each face object also owns a single @FT_GlyphSlot object, as well  */
  /*    as one or more @FT_Size objects.                                   */
  /*                                                                       */
  /*    Use @FT_New_Face or @FT_Open_Face to create a new face object from */
  /*    a given filepathname or a custom input stream.                     */
  /*                                                                       */
  /*    Use @FT_Done_Face to destroy it (along with its slot and sizes).   */
  /*                                                                       */
  /* <Also>                                                                */
  /*    See @FT_FaceRec for the publicly accessible fields of a given face */
  /*    object.                                                            */
  /*                                                                       */
  typedef struct FT_FaceRec_*  FT_Face;


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FT_Size                                                            */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A handle to an object used to model a face scaled to a given       */
  /*    character size.                                                    */
  /*                                                                       */
  /* <Note>                                                                */
  /*    Each @FT_Face has an _active_ @FT_Size object that is used by      */
  /*    functions like @FT_Load_Glyph to determine the scaling             */
  /*    transformation which is used to load and hint glyphs and metrics.  */
  /*                                                                       */
  /*    You can use @FT_Set_Char_Size, @FT_Set_Pixel_Sizes,                */
  /*    @FT_Request_Size or even @FT_Select_Size to change the content     */
  /*    (i.e., the scaling values) of the active @FT_Size.                 */
  /*                                                                       */
  /*    You can use @FT_New_Size to create additional size objects for a   */
  /*    given @FT_Face, but they won't be used by other functions until    */
  /*    you activate it through @FT_Activate_Size.  Only one size can be   */
  /*    activated at any given time per face.                              */
  /*                                                                       */
  /* <Also>                                                                */
  /*    See @FT_SizeRec for the publicly accessible fields of a given size */
  /*    object.                                                            */
  /*                                                                       */
  typedef struct FT_SizeRec_*  FT_Size;


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FT_GlyphSlot                                                       */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A handle to a given `glyph slot'.  A slot is a container where it  */
  /*    is possible to load any of the glyphs contained in its parent      */
  /*    face.                                                              */
  /*                                                                       */
  /*    In other words, each time you call @FT_Load_Glyph or               */
  /*    @FT_Load_Char, the slot's content is erased by the new glyph data, */
  /*    i.e., the glyph's metrics, its image (bitmap or outline), and      */
  /*    other control information.                                         */
  /*                                                                       */
  /* <Also>                                                                */
  /*    See @FT_GlyphSlotRec for the publicly accessible glyph fields.     */
  /*                                                                       */
  typedef struct FT_GlyphSlotRec_*  FT_GlyphSlot;


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FT_CharMap                                                         */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A handle to a given character map.  A charmap is used to translate */
  /*    character codes in a given encoding into glyph indexes for its     */
  /*    parent's face.  Some font formats may provide several charmaps per */
  /*    font.                                                              */
  /*                                                                       */
  /*    Each face object owns zero or more charmaps, but only one of them  */
  /*    can be `active' and used by @FT_Get_Char_Index or @FT_Load_Char.   */
  /*                                                                       */
  /*    The list of available charmaps in a face is available through the  */
  /*    `face->num_charmaps' and `face->charmaps' fields of @FT_FaceRec.   */
  /*                                                                       */
  /*    The currently active charmap is available as `face->charmap'.      */
  /*    You should call @FT_Set_Charmap to change it.                      */
  /*                                                                       */
  /* <Note>                                                                */
  /*    When a new face is created (either through @FT_New_Face or         */
  /*    @FT_Open_Face), the library looks for a Unicode charmap within     */
  /*    the list and automatically activates it.                           */
  /*                                                                       */
  /* <Also>                                                                */
  /*    See @FT_CharMapRec for the publicly accessible fields of a given   */
  /*    character map.                                                     */
  /*                                                                       */
  typedef struct FT_CharMapRec_*  FT_CharMap;


  /*************************************************************************/
  /*                                                                       */
  /* <Macro>                                                               */
  /*    FT_ENC_TAG                                                         */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This macro converts four-letter tags into an unsigned long.  It is */
  /*    used to define `encoding' identifiers (see @FT_Encoding).          */
  /*                                                                       */
  /* <Note>                                                                */
  /*    Since many 16-bit compilers don't like 32-bit enumerations, you    */
  /*    should redefine this macro in case of problems to something like   */
  /*    this:                                                              */
  /*                                                                       */
  /*    {                                                                  */
  /*      #define FT_ENC_TAG( value, a, b, c, d )  value                   */
  /*    }                                                                  */
  /*                                                                       */
  /*    to get a simple enumeration without assigning special numbers.     */
  /*                                                                       */

#ifndef FT_ENC_TAG
#define FT_ENC_TAG( value, a, b, c, d )         \
          value = ( ( (FT_UInt32)(a) << 24 ) |  \
                    ( (FT_UInt32)(b) << 16 ) |  \
                    ( (FT_UInt32)(c) <<  8 ) |  \
                      (FT_UInt32)(d)         )

#endif /* FT_ENC_TAG */


  /*************************************************************************/
  /*                                                                       */
  /* <Enum>                                                                */
  /*    FT_Encoding                                                        */
  /*                                                                       */
  /* <Description>                                                         */
  /*    An enumeration used to specify character sets supported by         */
  /*    charmaps.  Used in the @FT_Select_Charmap API function.            */
  /*                                                                       */
  /* <Note>                                                                */
  /*    Despite the name, this enumeration lists specific character        */
  /*    repertories (i.e., charsets), and not text encoding methods (e.g., */
  /*    UTF-8, UTF-16, etc.).                                              */
  /*                                                                       */
  /*    Other encodings might be defined in the future.                    */
  /*                                                                       */
  /* <Values>                                                              */
  /*    FT_ENCODING_NONE ::                                                */
  /*      The encoding value~0 is reserved.                                */
  /*                                                                       */
  /*    FT_ENCODING_UNICODE ::                                             */
  /*      Corresponds to the Unicode character set.  This value covers     */
  /*      all versions of the Unicode repertoire, including ASCII and      */
  /*      Latin-1.  Most fonts include a Unicode charmap, but not all      */
  /*      of them.                                                         */
  /*                                                                       */
  /*      For example, if you want to access Unicode value U+1F028 (and    */
  /*      the font contains it), use value 0x1F028 as the input value for  */
  /*      @FT_Get_Char_Index.                                              */
  /*                                                                       */
  /*    FT_ENCODING_MS_SYMBOL ::                                           */
  /*      Corresponds to the Microsoft Symbol encoding, used to encode     */
  /*      mathematical symbols in the 32..255 character code range.  For   */
  /*      more information, see `http://www.ceviz.net/symbol.htm'.         */
  /*                                                                       */
  /*    FT_ENCODING_SJIS ::                                                */
  /*      Corresponds to Japanese SJIS encoding.  More info at             */
  /*      at `http://langsupport.japanreference.com/encoding.shtml'.       */
  /*      See note on multi-byte encodings below.                          */
  /*                                                                       */
  /*    FT_ENCODING_GB2312 ::                                              */
  /*      Corresponds to an encoding system for Simplified Chinese as used */
  /*      used in mainland China.                                          */
  /*                                                                       */
  /*    FT_ENCODING_BIG5 ::                                                */
  /*      Corresponds to an encoding system for Traditional Chinese as     */
  /*      used in Taiwan and Hong Kong.                                    */
  /*                                                                       */
  /*    FT_ENCODING_WANSUNG ::                                             */
  /*      Corresponds to the Korean encoding system known as Wansung.      */
  /*      For more information see                                         */
  /*      `http://www.microsoft.com/typography/unicode/949.txt'.           */
  /*                                                                       */
  /*    FT_ENCODING_JOHAB ::                                               */
  /*      The Korean standard character set (KS~C 5601-1992), which        */
  /*      corresponds to MS Windows code page 1361.  This character set    */
  /*      includes all possible Hangeul character combinations.            */
  /*                                                                       */
  /*    FT_ENCODING_ADOBE_LATIN_1 ::                                       */
  /*      Corresponds to a Latin-1 encoding as defined in a Type~1         */
  /*      PostScript font.  It is limited to 256 character codes.          */
  /*                                                                       */
  /*    FT_ENCODING_ADOBE_STANDARD ::                                      */
  /*      Corresponds to the Adobe Standard encoding, as found in Type~1,  */
  /*      CFF, and OpenType/CFF fonts.  It is limited to 256 character     */
  /*      codes.                                                           */
  /*                                                                       */
  /*    FT_ENCODING_ADOBE_EXPERT ::                                        */
  /*      Corresponds to the Adobe Expert encoding, as found in Type~1,    */
  /*      CFF, and OpenType/CFF fonts.  It is limited to 256 character     */
  /*      codes.                                                           */
  /*                                                                       */
  /*    FT_ENCODING_ADOBE_CUSTOM ::                                        */
  /*      Corresponds to a custom encoding, as found in Type~1, CFF, and   */
  /*      OpenType/CFF fonts.  It is limited to 256 character codes.       */
  /*                                                                       */
  /*    FT_ENCODING_APPLE_ROMAN ::                                         */
  /*      Corresponds to the 8-bit Apple roman encoding.  Many TrueType    */
  /*      and OpenType fonts contain a charmap for this encoding, since    */
  /*      older versions of Mac OS are able to use it.                     */
  /*                                                                       */
  /*    FT_ENCODING_OLD_LATIN_2 ::                                         */
  /*      This value is deprecated and was never used nor reported by      */
  /*      FreeType.  Don't use or test for it.                             */
  /*                                                                       */
  /*    FT_ENCODING_MS_SJIS ::                                             */
  /*      Same as FT_ENCODING_SJIS.  Deprecated.                           */
  /*                                                                       */
  /*    FT_ENCODING_MS_GB2312 ::                                           */
  /*      Same as FT_ENCODING_GB2312.  Deprecated.                         */
  /*                                                                       */
  /*    FT_ENCODING_MS_BIG5 ::                                             */
  /*      Same as FT_ENCODING_BIG5.  Deprecated.                           */
  /*                                                                       */
  /*    FT_ENCODING_MS_WANSUNG ::                                          */
  /*      Same as FT_ENCODING_WANSUNG.  Deprecated.                        */
  /*                                                                       */
  /*    FT_ENCODING_MS_JOHAB ::                                            */
  /*      Same as FT_ENCODING_JOHAB.  Deprecated.                          */
  /*                                                                       */
  /* <Note>                                                                */
  /*    By default, FreeType automatically synthesizes a Unicode charmap   */
  /*    for PostScript fonts, using their glyph names dictionaries.        */
  /*    However, it also reports the encodings defined explicitly in the   */
  /*    font file, for the cases when they are needed, with the Adobe      */
  /*    values as well.                                                    */
  /*                                                                       */
  /*    FT_ENCODING_NONE is set by the BDF and PCF drivers if the charmap  */
  /*    is neither Unicode nor ISO-8859-1 (otherwise it is set to          */
  /*    FT_ENCODING_UNICODE).  Use @FT_Get_BDF_Charset_ID to find out      */
  /*    which encoding is really present.  If, for example, the            */
  /*    `cs_registry' field is `KOI8' and the `cs_encoding' field is `R',  */
  /*    the font is encoded in KOI8-R.                                     */
  /*                                                                       */
  /*    FT_ENCODING_NONE is always set (with a single exception) by the    */
  /*    winfonts driver.  Use @FT_Get_WinFNT_Header and examine the        */
  /*    `charset' field of the @FT_WinFNT_HeaderRec structure to find out  */
  /*    which encoding is really present.  For example,                    */
  /*    @FT_WinFNT_ID_CP1251 (204) means Windows code page 1251 (for       */
  /*    Russian).                                                          */
  /*                                                                       */
  /*    FT_ENCODING_NONE is set if `platform_id' is @TT_PLATFORM_MACINTOSH */
  /*    and `encoding_id' is not @TT_MAC_ID_ROMAN (otherwise it is set to  */
  /*    FT_ENCODING_APPLE_ROMAN).                                          */
  /*                                                                       */
  /*    If `platform_id' is @TT_PLATFORM_MACINTOSH, use the function       */
  /*    @FT_Get_CMap_Language_ID  to query the Mac language ID which may   */
  /*    be needed to be able to distinguish Apple encoding variants.  See  */
  /*                                                                       */
  /*      http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/README.TXT  */
  /*                                                                       */
  /*    to get an idea how to do that.  Basically, if the language ID      */
  /*    is~0, don't use it, otherwise subtract 1 from the language ID.     */
  /*    Then examine `encoding_id'.  If, for example, `encoding_id' is     */
  /*    @TT_MAC_ID_ROMAN and the language ID (minus~1) is                  */
  /*    `TT_MAC_LANGID_GREEK', it is the Greek encoding, not Roman.        */
  /*    @TT_MAC_ID_ARABIC with `TT_MAC_LANGID_FARSI' means the Farsi       */
  /*    variant the Arabic encoding.                                       */
  /*                                                                       */
  typedef enum  FT_Encoding_
  {
    FT_ENC_TAG( FT_ENCODING_NONE, 0, 0, 0, 0 ),

    FT_ENC_TAG( FT_ENCODING_MS_SYMBOL, 's', 'y', 'm', 'b' ),
    FT_ENC_TAG( FT_ENCODING_UNICODE,   'u', 'n', 'i', 'c' ),

    FT_ENC_TAG( FT_ENCODING_SJIS,    's', 'j', 'i', 's' ),
    FT_ENC_TAG( FT_ENCODING_GB2312,  'g', 'b', ' ', ' ' ),
    FT_ENC_TAG( FT_ENCODING_BIG5,    'b', 'i', 'g', '5' ),
    FT_ENC_TAG( FT_ENCODING_WANSUNG, 'w', 'a', 'n', 's' ),
    FT_ENC_TAG( FT_ENCODING_JOHAB,   'j', 'o', 'h', 'a' ),

    /* for backwards compatibility */
    FT_ENCODING_MS_SJIS    = FT_ENCODING_SJIS,
    FT_ENCODING_MS_GB2312  = FT_ENCODING_GB2312,
    FT_ENCODING_MS_BIG5    = FT_ENCODING_BIG5,
    FT_ENCODING_MS_WANSUNG = FT_ENCODING_WANSUNG,
    FT_ENCODING_MS_JOHAB   = FT_ENCODING_JOHAB,

    FT_ENC_TAG( FT_ENCODING_ADOBE_STANDARD, 'A', 'D', 'O', 'B' ),
    FT_ENC_TAG( FT_ENCODING_ADOBE_EXPERT,   'A', 'D', 'B', 'E' ),
    FT_ENC_TAG( FT_ENCODING_ADOBE_CUSTOM,   'A', 'D', 'B', 'C' ),
    FT_ENC_TAG( FT_ENCODING_ADOBE_LATIN_1,  'l', 'a', 't', '1' ),

    FT_ENC_TAG( FT_ENCODING_OLD_LATIN_2, 'l', 'a', 't', '2' ),

    FT_ENC_TAG( FT_ENCODING_APPLE_ROMAN, 'a', 'r', 'm', 'n' )

  } FT_Encoding;


  /*************************************************************************/
  /*                                                                       */
  /* <Enum>                                                                */
  /*    ft_encoding_xxx                                                    */
  /*                                                                       */
  /* <Description>                                                         */
  /*    These constants are deprecated; use the corresponding @FT_Encoding */
  /*    values instead.                                                    */
  /*                                                                       */
#define ft_encoding_none            FT_ENCODING_NONE
#define ft_encoding_unicode         FT_ENCODING_UNICODE
#define ft_encoding_symbol          FT_ENCODING_MS_SYMBOL
#define ft_encoding_latin_1         FT_ENCODING_ADOBE_LATIN_1
#define ft_encoding_latin_2         FT_ENCODING_OLD_LATIN_2
#define ft_encoding_sjis            FT_ENCODING_SJIS
#define ft_encoding_gb2312          FT_ENCODING_GB2312
#define ft_encoding_big5            FT_ENCODING_BIG5
#define ft_encoding_wansung         FT_ENCODING_WANSUNG
#define ft_encoding_johab           FT_ENCODING_JOHAB

#define ft_encoding_adobe_standard  FT_ENCODING_ADOBE_STANDARD
#define ft_encoding_adobe_expert    FT_ENCODING_ADOBE_EXPERT
#define ft_encoding_adobe_custom    FT_ENCODING_ADOBE_CUSTOM
#define ft_encoding_apple_roman     FT_ENCODING_APPLE_ROMAN


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_CharMapRec                                                      */
  /*                                                                       */
  /* <Description>                                                         */
  /*    The base charmap structure.                                        */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    face        :: A handle to the parent face object.                 */
  /*                                                                       */
  /*    encoding    :: An @FT_Encoding tag identifying the charmap.  Use   */
  /*                   this with @FT_Select_Charmap.                       */
  /*                                                                       */
  /*    platform_id :: An ID number describing the platform for the        */
  /*                   following encoding ID.  This comes directly from    */
  /*                   the TrueType specification and should be emulated   */
  /*                   for other formats.                                  */
  /*                                                                       */
  /*    encoding_id :: A platform specific encoding number.  This also     */
  /*                   comes from the TrueType specification and should be */
  /*                   emulated similarly.                                 */
  /*                                                                       */
  typedef struct  FT_CharMapRec_
  {
    FT_Face      face;
    FT_Encoding  encoding;
    FT_UShort    platform_id;
    FT_UShort    encoding_id;

  } FT_CharMapRec;


  /*************************************************************************/
  /*************************************************************************/
  /*                                                                       */
  /*                 B A S E   O B J E C T   C L A S S E S                 */
  /*                                                                       */
  /*************************************************************************/
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FT_Face_Internal                                                   */
  /*                                                                       */
  /* <Description>                                                         */
  /*    An opaque handle to an `FT_Face_InternalRec' structure, used to    */
  /*    model private data of a given @FT_Face object.                     */
  /*                                                                       */
  /*    This structure might change between releases of FreeType~2 and is  */
  /*    not generally available to client applications.                    */
  /*                                                                       */
  typedef struct FT_Face_InternalRec_*  FT_Face_Internal;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_FaceRec                                                         */
  /*                                                                       */
  /* <Description>                                                         */
  /*    FreeType root face class structure.  A face object models a        */
  /*    typeface in a font file.                                           */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    num_faces           :: The number of faces in the font file.  Some */
  /*                           font formats can have multiple faces in     */
  /*                           a font file.                                */
  /*                                                                       */
  /*    face_index          :: The index of the face in the font file.  It */
  /*                           is set to~0 if there is only one face in    */
  /*                           the font file.                              */
  /*                                                                       */
  /*    face_flags          :: A set of bit flags that give important      */
  /*                           information about the face; see             */
  /*                           @FT_FACE_FLAG_XXX for the details.          */
  /*                                                                       */
  /*    style_flags         :: A set of bit flags indicating the style of  */
  /*                           the face; see @FT_STYLE_FLAG_XXX for the    */
  /*                           details.                                    */
  /*                                                                       */
  /*    num_glyphs          :: The number of glyphs in the face.  If the   */
  /*                           face is scalable and has sbits (see         */
  /*                           `num_fixed_sizes'), it is set to the number */
  /*                           of outline glyphs.                          */
  /*                                                                       */
  /*                           For CID-keyed fonts, this value gives the   */
  /*                           highest CID used in the font.               */
  /*                                                                       */
  /*    family_name         :: The face's family name.  This is an ASCII   */
  /*                           string, usually in English, which describes */
  /*                           the typeface's family (like `Times New      */
  /*                           Roman', `Bodoni', `Garamond', etc).  This   */
  /*                           is a least common denominator used to list  */
  /*                           fonts.  Some formats (TrueType & OpenType)  */
  /*                           provide localized and Unicode versions of   */
  /*                           this string.  Applications should use the   */
  /*                           format specific interface to access them.   */
  /*                           Can be NULL (e.g., in fonts embedded in a   */
  /*                           PDF file).                                  */
  /*                                                                       */
  /*    style_name          :: The face's style name.  This is an ASCII    */
  /*                           string, usually in English, which describes */
  /*                           the typeface's style (like `Italic',        */
  /*                           `Bold', `Condensed', etc).  Not all font    */
  /*                           formats provide a style name, so this field */
  /*                           is optional, and can be set to NULL.  As    */
  /*                           for `family_name', some formats provide     */
  /*                           localized and Unicode versions of this      */
  /*                           string.  Applications should use the format */
  /*                           specific interface to access them.          */
  /*                                                                       */
  /*    num_fixed_sizes     :: The number of bitmap strikes in the face.   */
  /*                           Even if the face is scalable, there might   */
  /*                           still be bitmap strikes, which are called   */
  /*                           `sbits' in that case.                       */
  /*                                                                       */
  /*    available_sizes     :: An array of @FT_Bitmap_Size for all bitmap  */
  /*                           strikes in the face.  It is set to NULL if  */
  /*                           there is no bitmap strike.                  */
  /*                                                                       */
  /*    num_charmaps        :: The number of charmaps in the face.         */
  /*                                                                       */
  /*    charmaps            :: An array of the charmaps of the face.       */
  /*                                                                       */
  /*    generic             :: A field reserved for client uses.  See the  */
  /*                           @FT_Generic type description.               */
  /*                                                                       */
  /*    bbox                :: The font bounding box.  Coordinates are     */
  /*                           expressed in font units (see                */
  /*                           `units_per_EM').  The box is large enough   */
  /*                           to contain any glyph from the font.  Thus,  */
  /*                           `bbox.yMax' can be seen as the `maximal     */
  /*                           ascender', and `bbox.yMin' as the `minimal  */
  /*                           descender'.  Only relevant for scalable     */
  /*                           formats.                                    */
  /*                                                                       */
  /*                           Note that the bounding box might be off by  */
  /*                           (at least) one pixel for hinted fonts.  See */
  /*                           @FT_Size_Metrics for further discussion.    */
  /*                                                                       */
  /*    units_per_EM        :: The number of font units per EM square for  */
  /*                           this face.  This is typically 2048 for      */
  /*                           TrueType fonts, and 1000 for Type~1 fonts.  */
  /*                           Only relevant for scalable formats.         */
  /*                                                                       */
  /*    ascender            :: The typographic ascender of the face,       */
  /*                           expressed in font units.  For font formats  */
  /*                           not having this information, it is set to   */
  /*                           `bbox.yMax'.  Only relevant for scalable    */
  /*                           formats.                                    */
  /*                                                                       */
  /*    descender           :: The typographic descender of the face,      */
  /*                           expressed in font units.  For font formats  */
  /*                           not having this information, it is set to   */
  /*                           `bbox.yMin'.  Note that this field is       */
  /*                           usually negative.  Only relevant for        */
  /*                           scalable formats.                           */
  /*                                                                       */
  /*    height              :: The height is the vertical distance         */
  /*                           between two consecutive baselines,          */
  /*                           expressed in font units.  It is always      */
  /*                           positive.  Only relevant for scalable       */
  /*                           formats.                                    */
  /*                                                                       */
  /*    max_advance_width   :: The maximal advance width, in font units,   */
  /*                           for all glyphs in this face.  This can be   */
  /*                           used to make word wrapping computations     */
  /*                           faster.  Only relevant for scalable         */
  /*                           formats.                                    */
  /*                                                                       */
  /*    max_advance_height  :: The maximal advance height, in font units,  */
  /*                           for all glyphs in this face.  This is only  */
  /*                           relevant for vertical layouts, and is set   */
  /*                           to `height' for fonts that do not provide   */
  /*                           vertical metrics.  Only relevant for        */
  /*                           scalable formats.                           */
  /*                                                                       */
  /*    underline_position  :: The position, in font units, of the         */
  /*                           underline line for this face.  It is the    */
  /*                           center of the underlining stem.  Only       */
  /*                           relevant for scalable formats.              */
  /*                                                                       */
  /*    underline_thickness :: The thickness, in font units, of the        */
  /*                           underline for this face.  Only relevant for */
  /*                           scalable formats.                           */
  /*                                                                       */
  /*    glyph               :: The face's associated glyph slot(s).        */
  /*                                                                       */
  /*    size                :: The current active size for this face.      */
  /*                                                                       */
  /*    charmap             :: The current active charmap for this face.   */
  /*                                                                       */
  /* <Note>                                                                */
  /*    Fields may be changed after a call to @FT_Attach_File or           */
  /*    @FT_Attach_Stream.                                                 */
  /*                                                                       */
  typedef struct  FT_FaceRec_
  {
    FT_Long           num_faces;
    FT_Long           face_index;

    FT_Long           face_flags;
    FT_Long           style_flags;

    FT_Long           num_glyphs;

    FT_String*        family_name;
    FT_String*        style_name;

    FT_Int            num_fixed_sizes;
    FT_Bitmap_Size*   available_sizes;

    FT_Int            num_charmaps;
    FT_CharMap*       charmaps;

    FT_Generic        generic;

    /*# The following member variables (down to `underline_thickness') */
    /*# are only relevant to scalable outlines; cf. @FT_Bitmap_Size    */
    /*# for bitmap fonts.                                              */
    FT_BBox           bbox;

    FT_UShort         units_per_EM;
    FT_Short          ascender;
    FT_Short          descender;
    FT_Short          height;

    FT_Short          max_advance_width;
    FT_Short          max_advance_height;

    FT_Short          underline_position;
    FT_Short          underline_thickness;

    FT_GlyphSlot      glyph;
    FT_Size           size;
    FT_CharMap        charmap;

    /*@private begin */

    FT_Driver         driver;
    FT_Memory         memory;
    FT_Stream         stream;

    FT_ListRec        sizes_list;

    FT_Generic        autohint;
    void*             extensions;

    FT_Face_Internal  internal;

    /*@private end */

  } FT_FaceRec;


  /*************************************************************************/
  /*                                                                       */
  /* <Enum>                                                                */
  /*    FT_FACE_FLAG_XXX                                                   */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A list of bit flags used in the `face_flags' field of the          */
  /*    @FT_FaceRec structure.  They inform client applications of         */
  /*    properties of the corresponding face.                              */
  /*                                                                       */
  /* <Values>                                                              */
  /*    FT_FACE_FLAG_SCALABLE ::                                           */
  /*      Indicates that the face contains outline glyphs.  This doesn't   */
  /*      prevent bitmap strikes, i.e., a face can have both this and      */
  /*      and @FT_FACE_FLAG_FIXED_SIZES set.                               */
  /*                                                                       */
  /*    FT_FACE_FLAG_FIXED_SIZES ::                                        */
  /*      Indicates that the face contains bitmap strikes.  See also the   */
  /*      `num_fixed_sizes' and `available_sizes' fields of @FT_FaceRec.   */
  /*                                                                       */
  /*    FT_FACE_FLAG_FIXED_WIDTH ::                                        */
  /*      Indicates that the face contains fixed-width characters (like    */
  /*      Courier, Lucido, MonoType, etc.).                                */
  /*                                                                       */
  /*    FT_FACE_FLAG_SFNT ::                                               */
  /*      Indicates that the face uses the `sfnt' storage scheme.  For     */
  /*      now, this means TrueType and OpenType.                           */
  /*                                                                       */
  /*    FT_FACE_FLAG_HORIZONTAL ::                                         */
  /*      Indicates that the face contains horizontal glyph metrics.  This */
  /*      should be set for all common formats.                            */
  /*                                                                       */
  /*    FT_FACE_FLAG_VERTICAL ::                                           */
  /*      Indicates that the face contains vertical glyph metrics.  This   */
  /*      is only available in some formats, not all of them.              */
  /*                                                                       */
  /*    FT_FACE_FLAG_KERNING ::                                            */
  /*      Indicates that the face contains kerning information.  If set,   */
  /*      the kerning distance can be retrieved through the function       */
  /*      @FT_Get_Kerning.  Otherwise the function always return the       */
  /*      vector (0,0).  Note that FreeType doesn't handle kerning data    */
  /*      from the `GPOS' table (as present in some OpenType fonts).       */
  /*                                                                       */
  /*    FT_FACE_FLAG_FAST_GLYPHS ::                                        */
  /*      THIS FLAG IS DEPRECATED.  DO NOT USE OR TEST IT.                 */
  /*                                                                       */
  /*    FT_FACE_FLAG_MULTIPLE_MASTERS ::                                   */
  /*      Indicates that the font contains multiple masters and is capable */
  /*      of interpolating between them.  See the multiple-masters         */
  /*      specific API for details.                                        */
  /*                                                                       */
  /*    FT_FACE_FLAG_GLYPH_NAMES ::                                        */
  /*      Indicates that the font contains glyph names that can be         */
  /*      retrieved through @FT_Get_Glyph_Name.  Note that some TrueType   */
  /*      fonts contain broken glyph name tables.  Use the function        */
  /*      @FT_Has_PS_Glyph_Names when needed.                              */
  /*                                                                       */
  /*    FT_FACE_FLAG_EXTERNAL_STREAM ::                                    */
  /*      Used internally by FreeType to indicate that a face's stream was */
  /*      provided by the client application and should not be destroyed   */
  /*      when @FT_Done_Face is called.  Don't read or test this flag.     */
  /*                                                                       */
  /*    FT_FACE_FLAG_HINTER ::                                             */
  /*      Set if the font driver has a hinting machine of its own.  For    */
  /*      example, with TrueType fonts, it makes sense to use data from    */
  /*      the SFNT `gasp' table only if the native TrueType hinting engine */
  /*      (with the bytecode interpreter) is available and active.         */
  /*                                                                       */
  /*    FT_FACE_FLAG_CID_KEYED ::                                          */
  /*      Set if the font is CID-keyed.  In that case, the font is not     */
  /*      accessed by glyph indices but by CID values.  For subsetted      */
  /*      CID-keyed fonts this has the consequence that not all index      */
  /*      values are a valid argument to FT_Load_Glyph.  Only the CID      */
  /*      values for which corresponding glyphs in the subsetted font      */
  /*      exist make FT_Load_Glyph return successfully; in all other cases */
  /*      you get an `FT_Err_Invalid_Argument' error.                      */
  /*                                                                       */
  /*      Note that CID-keyed fonts which are in an SFNT wrapper don't     */
  /*      have this flag set since the glyphs are accessed in the normal   */
  /*      way (using contiguous indices); the `CID-ness' isn't visible to  */
  /*      the application.                                                 */
  /*                                                                       */
  /*    FT_FACE_FLAG_TRICKY ::                                             */
  /*      Set if the font is `tricky', this is, it always needs the        */
  /*      font format's native hinting engine to get a reasonable result.  */
  /*      A typical example is the Chinese font `mingli.ttf' which uses    */
  /*      TrueType bytecode instructions to move and scale all of its      */
  /*      subglyphs.                                                       */
  /*                                                                       */
  /*      It is not possible to autohint such fonts using                  */
  /*      @FT_LOAD_FORCE_AUTOHINT; it will also ignore                     */
  /*      @FT_LOAD_NO_HINTING.  You have to set both FT_LOAD_NO_HINTING    */
  /*      and @FT_LOAD_NO_AUTOHINT to really disable hinting; however, you */
  /*      probably never want this except for demonstration purposes.      */
  /*                                                                       */
  /*      Currently, there are six TrueType fonts in the list of tricky    */
  /*      fonts; they are hard-coded in file `ttobjs.c'.                   */
  /*                                                                       */
#define FT_FACE_FLAG_SCALABLE          ( 1L <<  0 )
#define FT_FACE_FLAG_FIXED_SIZES       ( 1L <<  1 )
#define FT_FACE_FLAG_FIXED_WIDTH       ( 1L <<  2 )
#define FT_FACE_FLAG_SFNT              ( 1L <<  3 )
#define FT_FACE_FLAG_HORIZONTAL        ( 1L <<  4 )
#define FT_FACE_FLAG_VERTICAL          ( 1L <<  5 )
#define FT_FACE_FLAG_KERNING           ( 1L <<  6 )
#define FT_FACE_FLAG_FAST_GLYPHS       ( 1L <<  7 )
#define FT_FACE_FLAG_MULTIPLE_MASTERS  ( 1L <<  8 )
#define FT_FACE_FLAG_GLYPH_NAMES       ( 1L <<  9 )
#define FT_FACE_FLAG_EXTERNAL_STREAM   ( 1L << 10 )
#define FT_FACE_FLAG_HINTER            ( 1L << 11 )
#define FT_FACE_FLAG_CID_KEYED         ( 1L << 12 )
#define FT_FACE_FLAG_TRICKY            ( 1L << 13 )


  /*************************************************************************
   *
   * @macro:
   *   FT_HAS_HORIZONTAL( face )
   *
   * @description:
   *   A macro that returns true whenever a face object contains
   *   horizontal metrics (this is true for all font formats though).
   *
   * @also:
   *   @FT_HAS_VERTICAL can be used to check for vertical metrics.
   *
   */
#define FT_HAS_HORIZONTAL( face ) \
          ( face->face_flags & FT_FACE_FLAG_HORIZONTAL )


  /*************************************************************************
   *
   * @macro:
   *   FT_HAS_VERTICAL( face )
   *
   * @description:
   *   A macro that returns true whenever a face object contains vertical
   *   metrics.
   *
   */
#define FT_HAS_VERTICAL( face ) \
          ( face->face_flags & FT_FACE_FLAG_VERTICAL )


  /*************************************************************************
   *
   * @macro:
   *   FT_HAS_KERNING( face )
   *
   * @description:
   *   A macro that returns true whenever a face object contains kerning
   *   data that can be accessed with @FT_Get_Kerning.
   *
   */
#define FT_HAS_KERNING( face ) \
          ( face->face_flags & FT_FACE_FLAG_KERNING )


  /*************************************************************************
   *
   * @macro:
   *   FT_IS_SCALABLE( face )
   *
   * @description:
   *   A macro that returns true whenever a face object contains a scalable
   *   font face (true for TrueType, Type~1, Type~42, CID, OpenType/CFF,
   *   and PFR font formats.
   *
   */
#define FT_IS_SCALABLE( face ) \
          ( face->face_flags & FT_FACE_FLAG_SCALABLE )


  /*************************************************************************
   *
   * @macro:
   *   FT_IS_SFNT( face )
   *
   * @description:
   *   A macro that returns true whenever a face object contains a font
   *   whose format is based on the SFNT storage scheme.  This usually
   *   means: TrueType fonts, OpenType fonts, as well as SFNT-based embedded
   *   bitmap fonts.
   *
   *   If this macro is true, all functions defined in @FT_SFNT_NAMES_H and
   *   @FT_TRUETYPE_TABLES_H are available.
   *
   */
#define FT_IS_SFNT( face ) \
          ( face->face_flags & FT_FACE_FLAG_SFNT )


  /*************************************************************************
   *
   * @macro:
   *   FT_IS_FIXED_WIDTH( face )
   *
   * @description:
   *   A macro that returns true whenever a face object contains a font face
   *   that contains fixed-width (or `monospace', `fixed-pitch', etc.)
   *   glyphs.
   *
   */
#define FT_IS_FIXED_WIDTH( face ) \
          ( face->face_flags & FT_FACE_FLAG_FIXED_WIDTH )


  /*************************************************************************
   *
   * @macro:
   *   FT_HAS_FIXED_SIZES( face )
   *
   * @description:
   *   A macro that returns true whenever a face object contains some
   *   embedded bitmaps.  See the `available_sizes' field of the
   *   @FT_FaceRec structure.
   *
   */
#define FT_HAS_FIXED_SIZES( face ) \
          ( face->face_flags & FT_FACE_FLAG_FIXED_SIZES )


  /*************************************************************************
   *
   * @macro:
   *   FT_HAS_FAST_GLYPHS( face )
   *
   * @description:
   *   Deprecated.
   *
   */
#define FT_HAS_FAST_GLYPHS( face )  0


  /*************************************************************************
   *
   * @macro:
   *   FT_HAS_GLYPH_NAMES( face )
   *
   * @description:
   *   A macro that returns true whenever a face object contains some glyph
   *   names that can be accessed through @FT_Get_Glyph_Name.
   *
   */
#define FT_HAS_GLYPH_NAMES( face ) \
          ( face->face_flags & FT_FACE_FLAG_GLYPH_NAMES )


  /*************************************************************************
   *
   * @macro:
   *   FT_HAS_MULTIPLE_MASTERS( face )
   *
   * @description:
   *   A macro that returns true whenever a face object contains some
   *   multiple masters.  The functions provided by @FT_MULTIPLE_MASTERS_H
   *   are then available to choose the exact design you want.
   *
   */
#define FT_HAS_MULTIPLE_MASTERS( face ) \
          ( face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS )


  /*************************************************************************
   *
   * @macro:
   *   FT_IS_CID_KEYED( face )
   *
   * @description:
   *   A macro that returns true whenever a face object contains a CID-keyed
   *   font.  See the discussion of @FT_FACE_FLAG_CID_KEYED for more
   *   details.
   *
   *   If this macro is true, all functions defined in @FT_CID_H are
   *   available.
   *
   */
#define FT_IS_CID_KEYED( face ) \
          ( face->face_flags & FT_FACE_FLAG_CID_KEYED )


  /*************************************************************************
   *
   * @macro:
   *   FT_IS_TRICKY( face )
   *
   * @description:
   *   A macro that returns true whenever a face represents a `tricky' font.
   *   See the discussion of @FT_FACE_FLAG_TRICKY for more details.
   *
   */
#define FT_IS_TRICKY( face ) \
          ( face->face_flags & FT_FACE_FLAG_TRICKY )


  /*************************************************************************/
  /*                                                                       */
  /* <Const>                                                               */
  /*    FT_STYLE_FLAG_XXX                                                  */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A list of bit-flags used to indicate the style of a given face.    */
  /*    These are used in the `style_flags' field of @FT_FaceRec.          */
  /*                                                                       */
  /* <Values>                                                              */
  /*    FT_STYLE_FLAG_ITALIC ::                                            */
  /*      Indicates that a given face style is italic or oblique.          */
  /*                                                                       */
  /*    FT_STYLE_FLAG_BOLD ::                                              */
  /*      Indicates that a given face is bold.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The style information as provided by FreeType is very basic.  More */
  /*    details are beyond the scope and should be done on a higher level  */
  /*    (for example, by analyzing various fields of the `OS/2' table in   */
  /*    SFNT based fonts).                                                 */
  /*                                                                       */
#define FT_STYLE_FLAG_ITALIC  ( 1 << 0 )
#define FT_STYLE_FLAG_BOLD    ( 1 << 1 )


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FT_Size_Internal                                                   */
  /*                                                                       */
  /* <Description>                                                         */
  /*    An opaque handle to an `FT_Size_InternalRec' structure, used to    */
  /*    model private data of a given @FT_Size object.                     */
  /*                                                                       */
  typedef struct FT_Size_InternalRec_*  FT_Size_Internal;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_Size_Metrics                                                    */
  /*                                                                       */
  /* <Description>                                                         */
  /*    The size metrics structure gives the metrics of a size object.     */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    x_ppem       :: The width of the scaled EM square in pixels, hence */
  /*                    the term `ppem' (pixels per EM).  It is also       */
  /*                    referred to as `nominal width'.                    */
  /*                                                                       */
  /*    y_ppem       :: The height of the scaled EM square in pixels,      */
  /*                    hence the term `ppem' (pixels per EM).  It is also */
  /*                    referred to as `nominal height'.                   */
  /*                                                                       */
  /*    x_scale      :: A 16.16 fractional scaling value used to convert   */
  /*                    horizontal metrics from font units to 26.6         */
  /*                    fractional pixels.  Only relevant for scalable     */
  /*                    font formats.                                      */
  /*                                                                       */
  /*    y_scale      :: A 16.16 fractional scaling value used to convert   */
  /*                    vertical metrics from font units to 26.6           */
  /*                    fractional pixels.  Only relevant for scalable     */
  /*                    font formats.                                      */
  /*                                                                       */
  /*    ascender     :: The ascender in 26.6 fractional pixels.  See       */
  /*                    @FT_FaceRec for the details.                       */
  /*                                                                       */
  /*    descender    :: The descender in 26.6 fractional pixels.  See      */
  /*                    @FT_FaceRec for the details.                       */
  /*                                                                       */
  /*    height       :: The height in 26.6 fractional pixels.  See         */
  /*                    @FT_FaceRec for the details.                       */
  /*                                                                       */
  /*    max_advance  :: The maximal advance width in 26.6 fractional       */
  /*                    pixels.  See @FT_FaceRec for the details.          */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The scaling values, if relevant, are determined first during a     */
  /*    size changing operation.  The remaining fields are then set by the */
  /*    driver.  For scalable formats, they are usually set to scaled      */
  /*    values of the corresponding fields in @FT_FaceRec.                 */
  /*                                                                       */
  /*    Note that due to glyph hinting, these values might not be exact    */
  /*    for certain fonts.  Thus they must be treated as unreliable        */
  /*    with an error margin of at least one pixel!                        */
  /*                                                                       */
  /*    Indeed, the only way to get the exact metrics is to render _all_   */
  /*    glyphs.  As this would be a definite performance hit, it is up to  */
  /*    client applications to perform such computations.                  */
  /*                                                                       */
  /*    The FT_Size_Metrics structure is valid for bitmap fonts also.      */
  /*                                                                       */
  typedef struct  FT_Size_Metrics_
  {
    FT_UShort  x_ppem;      /* horizontal pixels per EM               */
    FT_UShort  y_ppem;      /* vertical pixels per EM                 */

    FT_Fixed   x_scale;     /* scaling values used to convert font    */
    FT_Fixed   y_scale;     /* units to 26.6 fractional pixels        */

    FT_Pos     ascender;    /* ascender in 26.6 frac. pixels          */
    FT_Pos     descender;   /* descender in 26.6 frac. pixels         */
    FT_Pos     height;      /* text height in 26.6 frac. pixels       */
    FT_Pos     max_advance; /* max horizontal advance, in 26.6 pixels */

  } FT_Size_Metrics;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_SizeRec                                                         */
  /*                                                                       */
  /* <Description>                                                         */
  /*    FreeType root size class structure.  A size object models a face   */
  /*    object at a given size.                                            */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    face    :: Handle to the parent face object.                       */
  /*                                                                       */
  /*    generic :: A typeless pointer, which is unused by the FreeType     */
  /*               library or any of its drivers.  It can be used by       */
  /*               client applications to link their own data to each size */
  /*               object.                                                 */
  /*                                                                       */
  /*    metrics :: Metrics for this size object.  This field is read-only. */
  /*                                                                       */
  typedef struct  FT_SizeRec_
  {
    FT_Face           face;      /* parent face object              */
    FT_Generic        generic;   /* generic pointer for client uses */
    FT_Size_Metrics   metrics;   /* size metrics                    */
    FT_Size_Internal  internal;

  } FT_SizeRec;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_SubGlyph                                                        */
  /*                                                                       */
  /* <Description>                                                         */
  /*    The subglyph structure is an internal object used to describe      */
  /*    subglyphs (for example, in the case of composites).                */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The subglyph implementation is not part of the high-level API,     */
  /*    hence the forward structure declaration.                           */
  /*                                                                       */
  /*    You can however retrieve subglyph information with                 */
  /*    @FT_Get_SubGlyph_Info.                                             */
  /*                                                                       */
  typedef struct FT_SubGlyphRec_*  FT_SubGlyph;


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FT_Slot_Internal                                                   */
  /*                                                                       */
  /* <Description>                                                         */
  /*    An opaque handle to an `FT_Slot_InternalRec' structure, used to    */
  /*    model private data of a given @FT_GlyphSlot object.                */
  /*                                                                       */
  typedef struct FT_Slot_InternalRec_*  FT_Slot_Internal;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_GlyphSlotRec                                                    */
  /*                                                                       */
  /* <Description>                                                         */
  /*    FreeType root glyph slot class structure.  A glyph slot is a       */
  /*    container where individual glyphs can be loaded, be they in        */
  /*    outline or bitmap format.                                          */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    library           :: A handle to the FreeType library instance     */
  /*                         this slot belongs to.                         */
  /*                                                                       */
  /*    face              :: A handle to the parent face object.           */
  /*                                                                       */
  /*    next              :: In some cases (like some font tools), several */
  /*                         glyph slots per face object can be a good     */
  /*                         thing.  As this is rare, the glyph slots are  */
  /*                         listed through a direct, single-linked list   */
  /*                         using its `next' field.                       */
  /*                                                                       */
  /*    generic           :: A typeless pointer which is unused by the     */
  /*                         FreeType library or any of its drivers.  It   */
  /*                         can be used by client applications to link    */
  /*                         their own data to each glyph slot object.     */
  /*                                                                       */
  /*    metrics           :: The metrics of the last loaded glyph in the   */
  /*                         slot.  The returned values depend on the last */
  /*                         load flags (see the @FT_Load_Glyph API        */
  /*                         function) and can be expressed either in 26.6 */
  /*                         fractional pixels or font units.              */
  /*                                                                       */
  /*                         Note that even when the glyph image is        */
  /*                         transformed, the metrics are not.             */
  /*                                                                       */
  /*    linearHoriAdvance :: The advance width of the unhinted glyph.      */
  /*                         Its value is expressed in 16.16 fractional    */
  /*                         pixels, unless @FT_LOAD_LINEAR_DESIGN is set  */
  /*                         when loading the glyph.  This field can be    */
  /*                         important to perform correct WYSIWYG layout.  */
  /*                         Only relevant for outline glyphs.             */
  /*                                                                       */
  /*    linearVertAdvance :: The advance height of the unhinted glyph.     */
  /*                         Its value is expressed in 16.16 fractional    */
  /*                         pixels, unless @FT_LOAD_LINEAR_DESIGN is set  */
  /*                         when loading the glyph.  This field can be    */
  /*                         important to perform correct WYSIWYG layout.  */
  /*                         Only relevant for outline glyphs.             */
  /*                                                                       */
  /*    advance           :: This shorthand is, depending on               */
  /*                         @FT_LOAD_IGNORE_TRANSFORM, the transformed    */
  /*                         advance width for the glyph (in 26.6          */
  /*                         fractional pixel format).  As specified with  */
  /*                         @FT_LOAD_VERTICAL_LAYOUT, it uses either the  */
  /*                         `horiAdvance' or the `vertAdvance' value of   */
  /*                         `metrics' field.                              */
  /*                                                                       */
  /*    format            :: This field indicates the format of the image  */
  /*                         contained in the glyph slot.  Typically       */
  /*                         @FT_GLYPH_FORMAT_BITMAP,                      */
  /*                         @FT_GLYPH_FORMAT_OUTLINE, or                  */
  /*                         @FT_GLYPH_FORMAT_COMPOSITE, but others are    */
  /*                         possible.                                     */
  /*                                                                       */
  /*    bitmap            :: This field is used as a bitmap descriptor     */
  /*                         when the slot format is                       */
  /*                         @FT_GLYPH_FORMAT_BITMAP.  Note that the       */
  /*                         address and content of the bitmap buffer can  */
  /*                         change between calls of @FT_Load_Glyph and a  */
  /*                         few other functions.                          */
  /*                                                                       */
  /*    bitmap_left       :: This is the bitmap's left bearing expressed   */
  /*                         in integer pixels.  Of course, this is only   */
  /*                         valid if the format is                        */
  /*                         @FT_GLYPH_FORMAT_BITMAP.                      */
  /*                                                                       */
  /*    bitmap_top        :: This is the bitmap's top bearing expressed in */
  /*                         integer pixels.  Remember that this is the    */
  /*                         distance from the baseline to the top-most    */
  /*                         glyph scanline, upwards y~coordinates being   */
  /*                         *positive*.                                   */
  /*                                                                       */
  /*    outline           :: The outline descriptor for the current glyph  */
  /*                         image if its format is                        */
  /*                         @FT_GLYPH_FORMAT_OUTLINE.  Once a glyph is    */
  /*                         loaded, `outline' can be transformed,         */
  /*                         distorted, embolded, etc.  However, it must   */
  /*                         not be freed.                                 */
  /*                                                                       */
  /*    num_subglyphs     :: The number of subglyphs in a composite glyph. */
  /*                         This field is only valid for the composite    */
  /*                         glyph format that should normally only be     */
  /*                         loaded with the @FT_LOAD_NO_RECURSE flag.     */
  /*                         For now this is internal to FreeType.         */
  /*                                                                       */
  /*    subglyphs         :: An array of subglyph descriptors for          */
  /*                         composite glyphs.  There are `num_subglyphs'  */
  /*                         elements in there.  Currently internal to     */
  /*                         FreeType.                                     */
  /*                                                                       */
  /*    control_data      :: Certain font drivers can also return the      */
  /*                         control data for a given glyph image (e.g.    */
  /*                         TrueType bytecode, Type~1 charstrings, etc.). */
  /*                         This field is a pointer to such data.         */
  /*                                                                       */
  /*    control_len       :: This is the length in bytes of the control    */
  /*                         data.                                         */
  /*                                                                       */
  /*    other             :: Really wicked formats can use this pointer to */
  /*                         present their own glyph image to client       */
  /*                         applications.  Note that the application      */
  /*                         needs to know about the image format.         */
  /*                                                                       */
  /*    lsb_delta         :: The difference between hinted and unhinted    */
  /*                         left side bearing while autohinting is        */
  /*                         active.  Zero otherwise.                      */
  /*                                                                       */
  /*    rsb_delta         :: The difference between hinted and unhinted    */
  /*                         right side bearing while autohinting is       */
  /*                         active.  Zero otherwise.                      */
  /*                                                                       */
  /* <Note>                                                                */
  /*    If @FT_Load_Glyph is called with default flags (see                */
  /*    @FT_LOAD_DEFAULT) the glyph image is loaded in the glyph slot in   */
  /*    its native format (e.g., an outline glyph for TrueType and Type~1  */
  /*    formats).                                                          */
  /*                                                                       */
  /*    This image can later be converted into a bitmap by calling         */
  /*    @FT_Render_Glyph.  This function finds the current renderer for    */
  /*    the native image's format, then invokes it.                        */
  /*                                                                       */
  /*    The renderer is in charge of transforming the native image through */
  /*    the slot's face transformation fields, then converting it into a   */
  /*    bitmap that is returned in `slot->bitmap'.                         */
  /*                                                                       */
  /*    Note that `slot->bitmap_left' and `slot->bitmap_top' are also used */
  /*    to specify the position of the bitmap relative to the current pen  */
  /*    position (e.g., coordinates (0,0) on the baseline).  Of course,    */
  /*    `slot->format' is also changed to @FT_GLYPH_FORMAT_BITMAP.         */
  /*                                                                       */
  /* <Note>                                                                */
  /*    Here a small pseudo code fragment which shows how to use           */
  /*    `lsb_delta' and `rsb_delta':                                       */
  /*                                                                       */
  /*    {                                                                  */
  /*      FT_Pos  origin_x       = 0;                                      */
  /*      FT_Pos  prev_rsb_delta = 0;                                      */
  /*                                                                       */
  /*                                                                       */
  /*      for all glyphs do                                                */
  /*        <compute kern between current and previous glyph and add it to */
  /*         `origin_x'>                                                   */
  /*                                                                       */
  /*        <load glyph with `FT_Load_Glyph'>                              */
  /*                                                                       */
  /*        if ( prev_rsb_delta - face->glyph->lsb_delta >= 32 )           */
  /*          origin_x -= 64;                                              */
  /*        else if ( prev_rsb_delta - face->glyph->lsb_delta < -32 )      */
  /*          origin_x += 64;                                              */
  /*                                                                       */
  /*        prev_rsb_delta = face->glyph->rsb_delta;                       */
  /*                                                                       */
  /*        <save glyph image, or render glyph, or ...>                    */
  /*                                                                       */
  /*        origin_x += face->glyph->advance.x;                            */
  /*      endfor                                                           */
  /*    }                                                                  */
  /*                                                                       */
  typedef struct  FT_GlyphSlotRec_
  {
    FT_Library        library;
    FT_Face           face;
    FT_GlyphSlot      next;
    FT_UInt           reserved;       /* retained for binary compatibility */
    FT_Generic        generic;

    FT_Glyph_Metrics  metrics;
    FT_Fixed          linearHoriAdvance;
    FT_Fixed          linearVertAdvance;
    FT_Vector         advance;

    FT_Glyph_Format   format;

    FT_Bitmap         bitmap;
    FT_Int            bitmap_left;
    FT_Int            bitmap_top;

    FT_Outline        outline;

    FT_UInt           num_subglyphs;
    FT_SubGlyph       subglyphs;

    void*             control_data;
    long              control_len;

    FT_Pos            lsb_delta;
    FT_Pos            rsb_delta;

    void*             other;

    FT_Slot_Internal  internal;

  } FT_GlyphSlotRec;


  /*************************************************************************/
  /*************************************************************************/
  /*                                                                       */
  /*                         F U N C T I O N S                             */
  /*                                                                       */
  /*************************************************************************/
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Init_FreeType                                                   */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Initialize a new FreeType library object.  The set of modules      */
  /*    that are registered by this function is determined at build time.  */
  /*                                                                       */
  /* <Output>                                                              */
  /*    alibrary :: A handle to a new library object.                      */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    In case you want to provide your own memory allocating routines,   */
  /*    use @FT_New_Library instead, followed by a call to                 */
  /*    @FT_Add_Default_Modules (or a series of calls to @FT_Add_Module).  */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Init_FreeType( FT_Library  *alibrary );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Done_FreeType                                                   */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Destroy a given FreeType library object and all of its children,   */
  /*    including resources, drivers, faces, sizes, etc.                   */
  /*                                                                       */
  /* <Input>                                                               */
  /*    library :: A handle to the target library object.                  */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Done_FreeType( FT_Library  library );


  /*************************************************************************/
  /*                                                                       */
  /* <Enum>                                                                */
  /*    FT_OPEN_XXX                                                        */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A list of bit-field constants used within the `flags' field of the */
  /*    @FT_Open_Args structure.                                           */
  /*                                                                       */
  /* <Values>                                                              */
  /*    FT_OPEN_MEMORY   :: This is a memory-based stream.                 */
  /*                                                                       */
  /*    FT_OPEN_STREAM   :: Copy the stream from the `stream' field.       */
  /*                                                                       */
  /*    FT_OPEN_PATHNAME :: Create a new input stream from a C~path        */
  /*                        name.                                          */
  /*                                                                       */
  /*    FT_OPEN_DRIVER   :: Use the `driver' field.                        */
  /*                                                                       */
  /*    FT_OPEN_PARAMS   :: Use the `num_params' and `params' fields.      */
  /*                                                                       */
  /*    ft_open_memory   :: Deprecated; use @FT_OPEN_MEMORY instead.       */
  /*                                                                       */
  /*    ft_open_stream   :: Deprecated; use @FT_OPEN_STREAM instead.       */
  /*                                                                       */
  /*    ft_open_pathname :: Deprecated; use @FT_OPEN_PATHNAME instead.     */
  /*                                                                       */
  /*    ft_open_driver   :: Deprecated; use @FT_OPEN_DRIVER instead.       */
  /*                                                                       */
  /*    ft_open_params   :: Deprecated; use @FT_OPEN_PARAMS instead.       */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The `FT_OPEN_MEMORY', `FT_OPEN_STREAM', and `FT_OPEN_PATHNAME'     */
  /*    flags are mutually exclusive.                                      */
  /*                                                                       */
#define FT_OPEN_MEMORY    0x1
#define FT_OPEN_STREAM    0x2
#define FT_OPEN_PATHNAME  0x4
#define FT_OPEN_DRIVER    0x8
#define FT_OPEN_PARAMS    0x10

#define ft_open_memory    FT_OPEN_MEMORY     /* deprecated */
#define ft_open_stream    FT_OPEN_STREAM     /* deprecated */
#define ft_open_pathname  FT_OPEN_PATHNAME   /* deprecated */
#define ft_open_driver    FT_OPEN_DRIVER     /* deprecated */
#define ft_open_params    FT_OPEN_PARAMS     /* deprecated */


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_Parameter                                                       */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A simple structure used to pass more or less generic parameters to */
  /*    @FT_Open_Face.                                                     */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    tag  :: A four-byte identification tag.                            */
  /*                                                                       */
  /*    data :: A pointer to the parameter data.                           */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The ID and function of parameters are driver-specific.  See the    */
  /*    various FT_PARAM_TAG_XXX flags for more information.               */
  /*                                                                       */
  typedef struct  FT_Parameter_
  {
    FT_ULong    tag;
    FT_Pointer  data;

  } FT_Parameter;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_Open_Args                                                       */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A structure used to indicate how to open a new font file or        */
  /*    stream.  A pointer to such a structure can be used as a parameter  */
  /*    for the functions @FT_Open_Face and @FT_Attach_Stream.             */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    flags       :: A set of bit flags indicating how to use the        */
  /*                   structure.                                          */
  /*                                                                       */
  /*    memory_base :: The first byte of the file in memory.               */
  /*                                                                       */
  /*    memory_size :: The size in bytes of the file in memory.            */
  /*                                                                       */
  /*    pathname    :: A pointer to an 8-bit file pathname.                */
  /*                                                                       */
  /*    stream      :: A handle to a source stream object.                 */
  /*                                                                       */
  /*    driver      :: This field is exclusively used by @FT_Open_Face;    */
  /*                   it simply specifies the font driver to use to open  */
  /*                   the face.  If set to~0, FreeType tries to load the  */
  /*                   face with each one of the drivers in its list.      */
  /*                                                                       */
  /*    num_params  :: The number of extra parameters.                     */
  /*                                                                       */
  /*    params      :: Extra parameters passed to the font driver when     */
  /*                   opening a new face.                                 */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The stream type is determined by the contents of `flags' which     */
  /*    are tested in the following order by @FT_Open_Face:                */
  /*                                                                       */
  /*    If the `FT_OPEN_MEMORY' bit is set, assume that this is a          */
  /*    memory file of `memory_size' bytes, located at `memory_address'.   */
  /*    The data are are not copied, and the client is responsible for     */
  /*    releasing and destroying them _after_ the corresponding call to    */
  /*    @FT_Done_Face.                                                     */
  /*                                                                       */
  /*    Otherwise, if the `FT_OPEN_STREAM' bit is set, assume that a       */
  /*    custom input stream `stream' is used.                              */
  /*                                                                       */
  /*    Otherwise, if the `FT_OPEN_PATHNAME' bit is set, assume that this  */
  /*    is a normal file and use `pathname' to open it.                    */
  /*                                                                       */
  /*    If the `FT_OPEN_DRIVER' bit is set, @FT_Open_Face only tries to    */
  /*    open the file with the driver whose handler is in `driver'.        */
  /*                                                                       */
  /*    If the `FT_OPEN_PARAMS' bit is set, the parameters given by        */
  /*    `num_params' and `params' is used.  They are ignored otherwise.    */
  /*                                                                       */
  /*    Ideally, both the `pathname' and `params' fields should be tagged  */
  /*    as `const'; this is missing for API backwards compatibility.  In   */
  /*    other words, applications should treat them as read-only.          */
  /*                                                                       */
  typedef struct  FT_Open_Args_
  {
    FT_UInt         flags;
    const FT_Byte*  memory_base;
    FT_Long         memory_size;
    FT_String*      pathname;
    FT_Stream       stream;
    FT_Module       driver;
    FT_Int          num_params;
    FT_Parameter*   params;

  } FT_Open_Args;


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_New_Face                                                        */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This function calls @FT_Open_Face to open a font by its pathname.  */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    library    :: A handle to the library resource.                    */
  /*                                                                       */
  /* <Input>                                                               */
  /*    pathname   :: A path to the font file.                             */
  /*                                                                       */
  /*    face_index :: The index of the face within the font.  The first    */
  /*                  face has index~0.                                    */
  /*                                                                       */
  /* <Output>                                                              */
  /*    aface      :: A handle to a new face object.  If `face_index' is   */
  /*                  greater than or equal to zero, it must be non-NULL.  */
  /*                  See @FT_Open_Face for more details.                  */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_New_Face( FT_Library   library,
               const char*  filepathname,
               FT_Long      face_index,
               FT_Face     *aface );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_New_Memory_Face                                                 */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This function calls @FT_Open_Face to open a font which has been    */
  /*    loaded into memory.                                                */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    library    :: A handle to the library resource.                    */
  /*                                                                       */
  /* <Input>                                                               */
  /*    file_base  :: A pointer to the beginning of the font data.         */
  /*                                                                       */
  /*    file_size  :: The size of the memory chunk used by the font data.  */
  /*                                                                       */
  /*    face_index :: The index of the face within the font.  The first    */
  /*                  face has index~0.                                    */
  /*                                                                       */
  /* <Output>                                                              */
  /*    aface      :: A handle to a new face object.  If `face_index' is   */
  /*                  greater than or equal to zero, it must be non-NULL.  */
  /*                  See @FT_Open_Face for more details.                  */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    You must not deallocate the memory before calling @FT_Done_Face.   */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_New_Memory_Face( FT_Library      library,
                      const FT_Byte*  file_base,
                      FT_Long         file_size,
                      FT_Long         face_index,
                      FT_Face        *aface );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Open_Face                                                       */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Create a face object from a given resource described by            */
  /*    @FT_Open_Args.                                                     */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    library    :: A handle to the library resource.                    */
  /*                                                                       */
  /* <Input>                                                               */
  /*    args       :: A pointer to an `FT_Open_Args' structure which must  */
  /*                  be filled by the caller.                             */
  /*                                                                       */
  /*    face_index :: The index of the face within the font.  The first    */
  /*                  face has index~0.                                    */
  /*                                                                       */
  /* <Output>                                                              */
  /*    aface      :: A handle to a new face object.  If `face_index' is   */
  /*                  greater than or equal to zero, it must be non-NULL.  */
  /*                  See note below.                                      */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    Unlike FreeType 1.x, this function automatically creates a glyph   */
  /*    slot for the face object which can be accessed directly through    */
  /*    `face->glyph'.                                                     */
  /*                                                                       */
  /*    FT_Open_Face can be used to quickly check whether the font         */
  /*    format of a given font resource is supported by FreeType.  If the  */
  /*    `face_index' field is negative, the function's return value is~0   */
  /*    if the font format is recognized, or non-zero otherwise;           */
  /*    the function returns a more or less empty face handle in `*aface'  */
  /*    (if `aface' isn't NULL).  The only useful field in this special    */
  /*    case is `face->num_faces' which gives the number of faces within   */
  /*    the font file.  After examination, the returned @FT_Face structure */
  /*    should be deallocated with a call to @FT_Done_Face.                */
  /*                                                                       */
  /*    Each new face object created with this function also owns a        */
  /*    default @FT_Size object, accessible as `face->size'.               */
  /*                                                                       */
  /*    See the discussion of reference counters in the description of     */
  /*    @FT_Reference_Face.                                                */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Open_Face( FT_Library           library,
                const FT_Open_Args*  args,
                FT_Long              face_index,
                FT_Face             *aface );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Attach_File                                                     */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This function calls @FT_Attach_Stream to attach a file.            */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    face         :: The target face object.                            */
  /*                                                                       */
  /* <Input>                                                               */
  /*    filepathname :: The pathname.                                      */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Attach_File( FT_Face      face,
                  const char*  filepathname );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Attach_Stream                                                   */
  /*                                                                       */
  /* <Description>                                                         */
  /*    `Attach' data to a face object.  Normally, this is used to read    */
  /*    additional information for the face object.  For example, you can  */
  /*    attach an AFM file that comes with a Type~1 font to get the        */
  /*    kerning values and other metrics.                                  */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    face       :: The target face object.                              */
  /*                                                                       */
  /* <Input>                                                               */
  /*    parameters :: A pointer to @FT_Open_Args which must be filled by   */
  /*                  the caller.                                          */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The meaning of the `attach' (i.e., what really happens when the    */
  /*    new file is read) is not fixed by FreeType itself.  It really      */
  /*    depends on the font format (and thus the font driver).             */
  /*                                                                       */
  /*    Client applications are expected to know what they are doing       */
  /*    when invoking this function.  Most drivers simply do not implement */
  /*    file attachments.                                                  */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Attach_Stream( FT_Face        face,
                    FT_Open_Args*  parameters );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Reference_Face                                                  */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A counter gets initialized to~1 at the time an @FT_Face structure  */
  /*    is created.  This function increments the counter.  @FT_Done_Face  */
  /*    then only destroys a face if the counter is~1, otherwise it simply */
  /*    decrements the counter.                                            */
  /*                                                                       */
  /*    This function helps in managing life-cycles of structures which    */
  /*    reference @FT_Face objects.                                        */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face :: A handle to a target face object.                          */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  /* <Since>                                                               */
  /*    2.4.2                                                              */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Reference_Face( FT_Face  face );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Done_Face                                                       */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Discard a given face object, as well as all of its child slots and */
  /*    sizes.                                                             */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face :: A handle to a target face object.                          */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    See the discussion of reference counters in the description of     */
  /*    @FT_Reference_Face.                                                */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Done_Face( FT_Face  face );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Select_Size                                                     */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Select a bitmap strike.                                            */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    face         :: A handle to a target face object.                  */
  /*                                                                       */
  /* <Input>                                                               */
  /*    strike_index :: The index of the bitmap strike in the              */
  /*                    `available_sizes' field of @FT_FaceRec structure.  */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Select_Size( FT_Face  face,
                  FT_Int   strike_index );


  /*************************************************************************/
  /*                                                                       */
  /* <Enum>                                                                */
  /*    FT_Size_Request_Type                                               */
  /*                                                                       */
  /* <Description>                                                         */
  /*    An enumeration type that lists the supported size request types.   */
  /*                                                                       */
  /* <Values>                                                              */
  /*    FT_SIZE_REQUEST_TYPE_NOMINAL ::                                    */
  /*      The nominal size.  The `units_per_EM' field of @FT_FaceRec is    */
  /*      used to determine both scaling values.                           */
  /*                                                                       */
  /*    FT_SIZE_REQUEST_TYPE_REAL_DIM ::                                   */
  /*      The real dimension.  The sum of the the `Ascender' and (minus    */
  /*      of) the `Descender' fields of @FT_FaceRec are used to determine  */
  /*      both scaling values.                                             */
  /*                                                                       */
  /*    FT_SIZE_REQUEST_TYPE_BBOX ::                                       */
  /*      The font bounding box.  The width and height of the `bbox' field */
  /*      of @FT_FaceRec are used to determine the horizontal and vertical */
  /*      scaling value, respectively.                                     */
  /*                                                                       */
  /*    FT_SIZE_REQUEST_TYPE_CELL ::                                       */
  /*      The `max_advance_width' field of @FT_FaceRec is used to          */
  /*      determine the horizontal scaling value; the vertical scaling     */
  /*      value is determined the same way as                              */
  /*      @FT_SIZE_REQUEST_TYPE_REAL_DIM does.  Finally, both scaling      */
  /*      values are set to the smaller one.  This type is useful if you   */
  /*      want to specify the font size for, say, a window of a given      */
  /*      dimension and 80x24 cells.                                       */
  /*                                                                       */
  /*    FT_SIZE_REQUEST_TYPE_SCALES ::                                     */
  /*      Specify the scaling values directly.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The above descriptions only apply to scalable formats.  For bitmap */
  /*    formats, the behaviour is up to the driver.                        */
  /*                                                                       */
  /*    See the note section of @FT_Size_Metrics if you wonder how size    */
  /*    requesting relates to scaling values.                              */
  /*                                                                       */
  typedef enum  FT_Size_Request_Type_
  {
    FT_SIZE_REQUEST_TYPE_NOMINAL,
    FT_SIZE_REQUEST_TYPE_REAL_DIM,
    FT_SIZE_REQUEST_TYPE_BBOX,
    FT_SIZE_REQUEST_TYPE_CELL,
    FT_SIZE_REQUEST_TYPE_SCALES,

    FT_SIZE_REQUEST_TYPE_MAX

  } FT_Size_Request_Type;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_Size_RequestRec                                                 */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A structure used to model a size request.                          */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    type           :: See @FT_Size_Request_Type.                       */
  /*                                                                       */
  /*    width          :: The desired width.                               */
  /*                                                                       */
  /*    height         :: The desired height.                              */
  /*                                                                       */
  /*    horiResolution :: The horizontal resolution.  If set to zero,      */
  /*                      `width' is treated as a 26.6 fractional pixel    */
  /*                      value.                                           */
  /*                                                                       */
  /*    vertResolution :: The vertical resolution.  If set to zero,        */
  /*                      `height' is treated as a 26.6 fractional pixel   */
  /*                      value.                                           */
  /*                                                                       */
  /* <Note>                                                                */
  /*    If `width' is zero, then the horizontal scaling value is set equal */
  /*    to the vertical scaling value, and vice versa.                     */
  /*                                                                       */
  typedef struct  FT_Size_RequestRec_
  {
    FT_Size_Request_Type  type;
    FT_Long               width;
    FT_Long               height;
    FT_UInt               horiResolution;
    FT_UInt               vertResolution;

  } FT_Size_RequestRec;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_Size_Request                                                    */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A handle to a size request structure.                              */
  /*                                                                       */
  typedef struct FT_Size_RequestRec_  *FT_Size_Request;


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Request_Size                                                    */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Resize the scale of the active @FT_Size object in a face.          */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    face :: A handle to a target face object.                          */
  /*                                                                       */
  /* <Input>                                                               */
  /*    req  :: A pointer to a @FT_Size_RequestRec.                        */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    Although drivers may select the bitmap strike matching the         */
  /*    request, you should not rely on this if you intend to select a     */
  /*    particular bitmap strike.  Use @FT_Select_Size instead in that     */
  /*    case.                                                              */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Request_Size( FT_Face          face,
                   FT_Size_Request  req );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Set_Char_Size                                                   */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This function calls @FT_Request_Size to request the nominal size   */
  /*    (in points).                                                       */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    face            :: A handle to a target face object.               */
  /*                                                                       */
  /* <Input>                                                               */
  /*    char_width      :: The nominal width, in 26.6 fractional points.   */
  /*                                                                       */
  /*    char_height     :: The nominal height, in 26.6 fractional points.  */
  /*                                                                       */
  /*    horz_resolution :: The horizontal resolution in dpi.               */
  /*                                                                       */
  /*    vert_resolution :: The vertical resolution in dpi.                 */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    If either the character width or height is zero, it is set equal   */
  /*    to the other value.                                                */
  /*                                                                       */
  /*    If either the horizontal or vertical resolution is zero, it is set */
  /*    equal to the other value.                                          */
  /*                                                                       */
  /*    A character width or height smaller than 1pt is set to 1pt; if     */
  /*    both resolution values are zero, they are set to 72dpi.            */
  /*                                                                       */
  /*    Don't use this function if you are using the FreeType cache API.   */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Set_Char_Size( FT_Face     face,
                    FT_F26Dot6  char_width,
                    FT_F26Dot6  char_height,
                    FT_UInt     horz_resolution,
                    FT_UInt     vert_resolution );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Set_Pixel_Sizes                                                 */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This function calls @FT_Request_Size to request the nominal size   */
  /*    (in pixels).                                                       */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    face         :: A handle to the target face object.                */
  /*                                                                       */
  /* <Input>                                                               */
  /*    pixel_width  :: The nominal width, in pixels.                      */
  /*                                                                       */
  /*    pixel_height :: The nominal height, in pixels.                     */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Set_Pixel_Sizes( FT_Face  face,
                      FT_UInt  pixel_width,
                      FT_UInt  pixel_height );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Load_Glyph                                                      */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A function used to load a single glyph into the glyph slot of a    */
  /*    face object.                                                       */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    face        :: A handle to the target face object where the glyph  */
  /*                   is loaded.                                          */
  /*                                                                       */
  /* <Input>                                                               */
  /*    glyph_index :: The index of the glyph in the font file.  For       */
  /*                   CID-keyed fonts (either in PS or in CFF format)     */
  /*                   this argument specifies the CID value.              */
  /*                                                                       */
  /*    load_flags  :: A flag indicating what to load for this glyph.  The */
  /*                   @FT_LOAD_XXX constants can be used to control the   */
  /*                   glyph loading process (e.g., whether the outline    */
  /*                   should be scaled, whether to load bitmaps or not,   */
  /*                   whether to hint the outline, etc).                  */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The loaded glyph may be transformed.  See @FT_Set_Transform for    */
  /*    the details.                                                       */
  /*                                                                       */
  /*    For subsetted CID-keyed fonts, `FT_Err_Invalid_Argument' is        */
  /*    returned for invalid CID values (this is, for CID values which     */
  /*    don't have a corresponding glyph in the font).  See the discussion */
  /*    of the @FT_FACE_FLAG_CID_KEYED flag for more details.              */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Load_Glyph( FT_Face   face,
                 FT_UInt   glyph_index,
                 FT_Int32  load_flags );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Load_Char                                                       */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A function used to load a single glyph into the glyph slot of a    */
  /*    face object, according to its character code.                      */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    face        :: A handle to a target face object where the glyph    */
  /*                   is loaded.                                          */
  /*                                                                       */
  /* <Input>                                                               */
  /*    char_code   :: The glyph's character code, according to the        */
  /*                   current charmap used in the face.                   */
  /*                                                                       */
  /*    load_flags  :: A flag indicating what to load for this glyph.  The */
  /*                   @FT_LOAD_XXX constants can be used to control the   */
  /*                   glyph loading process (e.g., whether the outline    */
  /*                   should be scaled, whether to load bitmaps or not,   */
  /*                   whether to hint the outline, etc).                  */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    This function simply calls @FT_Get_Char_Index and @FT_Load_Glyph.  */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Load_Char( FT_Face   face,
                FT_ULong  char_code,
                FT_Int32  load_flags );


  /*************************************************************************
   *
   * @enum:
   *   FT_LOAD_XXX
   *
   * @description:
   *   A list of bit-field constants used with @FT_Load_Glyph to indicate
   *   what kind of operations to perform during glyph loading.
   *
   * @values:
   *   FT_LOAD_DEFAULT ::
   *     Corresponding to~0, this value is used as the default glyph load
   *     operation.  In this case, the following happens:
   *
   *     1. FreeType looks for a bitmap for the glyph corresponding to the
   *        face's current size.  If one is found, the function returns.
   *        The bitmap data can be accessed from the glyph slot (see note
   *        below).
   *
   *     2. If no embedded bitmap is searched or found, FreeType looks for a
   *        scalable outline.  If one is found, it is loaded from the font
   *        file, scaled to device pixels, then `hinted' to the pixel grid
   *        in order to optimize it.  The outline data can be accessed from
   *        the glyph slot (see note below).
   *
   *     Note that by default, the glyph loader doesn't render outlines into
   *     bitmaps.  The following flags are used to modify this default
   *     behaviour to more specific and useful cases.
   *
   *   FT_LOAD_NO_SCALE ::
   *     Don't scale the outline glyph loaded, but keep it in font units.
   *
   *     This flag implies @FT_LOAD_NO_HINTING and @FT_LOAD_NO_BITMAP, and
   *     unsets @FT_LOAD_RENDER.
   *
   *   FT_LOAD_NO_HINTING ::
   *     Disable hinting.  This generally generates `blurrier' bitmap glyph
   *     when the glyph is rendered in any of the anti-aliased modes.  See
   *     also the note below.
   *
   *     This flag is implied by @FT_LOAD_NO_SCALE.
   *
   *   FT_LOAD_RENDER ::
   *     Call @FT_Render_Glyph after the glyph is loaded.  By default, the
   *     glyph is rendered in @FT_RENDER_MODE_NORMAL mode.  This can be
   *     overridden by @FT_LOAD_TARGET_XXX or @FT_LOAD_MONOCHROME.
   *
   *     This flag is unset by @FT_LOAD_NO_SCALE.
   *
   *   FT_LOAD_NO_BITMAP ::
   *     Ignore bitmap strikes when loading.  Bitmap-only fonts ignore this
   *     flag.
   *
   *     @FT_LOAD_NO_SCALE always sets this flag.
   *
   *   FT_LOAD_VERTICAL_LAYOUT ::
   *     Load the glyph for vertical text layout.  _Don't_ use it as it is
   *     problematic currently.
   *
   *   FT_LOAD_FORCE_AUTOHINT ::
   *     Indicates that the auto-hinter is preferred over the font's native
   *     hinter.  See also the note below.
   *
   *   FT_LOAD_CROP_BITMAP ::
   *     Indicates that the font driver should crop the loaded bitmap glyph
   *     (i.e., remove all space around its black bits).  Not all drivers
   *     implement this.
   *
   *   FT_LOAD_PEDANTIC ::
   *     Indicates that the font driver should perform pedantic verifications
   *     during glyph loading.  This is mostly used to detect broken glyphs
   *     in fonts.  By default, FreeType tries to handle broken fonts also.
   *
   *   FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH ::
   *     Indicates that the font driver should ignore the global advance
   *     width defined in the font.  By default, that value is used as the
   *     advance width for all glyphs when the face has
   *     @FT_FACE_FLAG_FIXED_WIDTH set.
   *
   *     This flag exists for historical reasons (to support buggy CJK
   *     fonts).
   *
   *   FT_LOAD_NO_RECURSE ::
   *     This flag is only used internally.  It merely indicates that the
   *     font driver should not load composite glyphs recursively.  Instead,
   *     it should set the `num_subglyph' and `subglyphs' values of the
   *     glyph slot accordingly, and set `glyph->format' to
   *     @FT_GLYPH_FORMAT_COMPOSITE.
   *
   *     The description of sub-glyphs is not available to client
   *     applications for now.
   *
   *     This flag implies @FT_LOAD_NO_SCALE and @FT_LOAD_IGNORE_TRANSFORM.
   *
   *   FT_LOAD_IGNORE_TRANSFORM ::
   *     Indicates that the transform matrix set by @FT_Set_Transform should
   *     be ignored.
   *
   *   FT_LOAD_MONOCHROME ::
   *     This flag is used with @FT_LOAD_RENDER to indicate that you want to
   *     render an outline glyph to a 1-bit monochrome bitmap glyph, with
   *     8~pixels packed into each byte of the bitmap data.
   *
   *     Note that this has no effect on the hinting algorithm used.  You
   *     should rather use @FT_LOAD_TARGET_MONO so that the
   *     monochrome-optimized hinting algorithm is used.
   *
   *   FT_LOAD_LINEAR_DESIGN ::
   *     Indicates that the `linearHoriAdvance' and `linearVertAdvance'
   *     fields of @FT_GlyphSlotRec should be kept in font units.  See
   *     @FT_GlyphSlotRec for details.
   *
   *   FT_LOAD_NO_AUTOHINT ::
   *     Disable auto-hinter.  See also the note below.
   *
   * @note:
   *   By default, hinting is enabled and the font's native hinter (see
   *   @FT_FACE_FLAG_HINTER) is preferred over the auto-hinter.  You can
   *   disable hinting by setting @FT_LOAD_NO_HINTING or change the
   *   precedence by setting @FT_LOAD_FORCE_AUTOHINT.  You can also set
   *   @FT_LOAD_NO_AUTOHINT in case you don't want the auto-hinter to be
   *   used at all.
   *
   *   See the description of @FT_FACE_FLAG_TRICKY for a special exception
   *   (affecting only a handful of Asian fonts).
   *
   *   Besides deciding which hinter to use, you can also decide which
   *   hinting algorithm to use.  See @FT_LOAD_TARGET_XXX for details.
   *
   */
#define FT_LOAD_DEFAULT                      0x0
#define FT_LOAD_NO_SCALE                     0x1
#define FT_LOAD_NO_HINTING                   0x2
#define FT_LOAD_RENDER                       0x4
#define FT_LOAD_NO_BITMAP                    0x8
#define FT_LOAD_VERTICAL_LAYOUT              0x10
#define FT_LOAD_FORCE_AUTOHINT               0x20
#define FT_LOAD_CROP_BITMAP                  0x40
#define FT_LOAD_PEDANTIC                     0x80
#define FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH  0x200
#define FT_LOAD_NO_RECURSE                   0x400
#define FT_LOAD_IGNORE_TRANSFORM             0x800
#define FT_LOAD_MONOCHROME                   0x1000
#define FT_LOAD_LINEAR_DESIGN                0x2000
#define FT_LOAD_NO_AUTOHINT                  0x8000U

  /* */

  /* used internally only by certain font drivers! */
#define FT_LOAD_ADVANCE_ONLY                 0x100
#define FT_LOAD_SBITS_ONLY                   0x4000


  /**************************************************************************
   *
   * @enum:
   *   FT_LOAD_TARGET_XXX
   *
   * @description:
   *   A list of values that are used to select a specific hinting algorithm
   *   to use by the hinter.  You should OR one of these values to your
   *   `load_flags' when calling @FT_Load_Glyph.
   *
   *   Note that font's native hinters may ignore the hinting algorithm you
   *   have specified (e.g., the TrueType bytecode interpreter).  You can set
   *   @FT_LOAD_FORCE_AUTOHINT to ensure that the auto-hinter is used.
   *
   *   Also note that @FT_LOAD_TARGET_LIGHT is an exception, in that it
   *   always implies @FT_LOAD_FORCE_AUTOHINT.
   *
   * @values:
   *   FT_LOAD_TARGET_NORMAL ::
   *     This corresponds to the default hinting algorithm, optimized for
   *     standard gray-level rendering.  For monochrome output, use
   *     @FT_LOAD_TARGET_MONO instead.
   *
   *   FT_LOAD_TARGET_LIGHT ::
   *     A lighter hinting algorithm for non-monochrome modes.  Many
   *     generated glyphs are more fuzzy but better resemble its original
   *     shape.  A bit like rendering on Mac OS~X.
   *
   *     As a special exception, this target implies @FT_LOAD_FORCE_AUTOHINT.
   *
   *   FT_LOAD_TARGET_MONO ::
   *     Strong hinting algorithm that should only be used for monochrome
   *     output.  The result is probably unpleasant if the glyph is rendered
   *     in non-monochrome modes.
   *
   *   FT_LOAD_TARGET_LCD ::
   *     A variant of @FT_LOAD_TARGET_NORMAL optimized for horizontally
   *     decimated LCD displays.
   *
   *   FT_LOAD_TARGET_LCD_V ::
   *     A variant of @FT_LOAD_TARGET_NORMAL optimized for vertically
   *     decimated LCD displays.
   *
   * @note:
   *   You should use only _one_ of the FT_LOAD_TARGET_XXX values in your
   *   `load_flags'.  They can't be ORed.
   *
   *   If @FT_LOAD_RENDER is also set, the glyph is rendered in the
   *   corresponding mode (i.e., the mode which matches the used algorithm
   *   best) unless @FT_LOAD_MONOCHROME is set.
   *
   *   You can use a hinting algorithm that doesn't correspond to the same
   *   rendering mode.  As an example, it is possible to use the `light'
   *   hinting algorithm and have the results rendered in horizontal LCD
   *   pixel mode, with code like
   *
   *     {
   *       FT_Load_Glyph( face, glyph_index,
   *                      load_flags | FT_LOAD_TARGET_LIGHT );
   *
   *       FT_Render_Glyph( face->glyph, FT_RENDER_MODE_LCD );
   *     }
   *
   */
#define FT_LOAD_TARGET_( x )   ( (FT_Int32)( (x) & 15 ) << 16 )

#define FT_LOAD_TARGET_NORMAL  FT_LOAD_TARGET_( FT_RENDER_MODE_NORMAL )
#define FT_LOAD_TARGET_LIGHT   FT_LOAD_TARGET_( FT_RENDER_MODE_LIGHT  )
#define FT_LOAD_TARGET_MONO    FT_LOAD_TARGET_( FT_RENDER_MODE_MONO   )
#define FT_LOAD_TARGET_LCD     FT_LOAD_TARGET_( FT_RENDER_MODE_LCD    )
#define FT_LOAD_TARGET_LCD_V   FT_LOAD_TARGET_( FT_RENDER_MODE_LCD_V  )


  /**************************************************************************
   *
   * @macro:
   *   FT_LOAD_TARGET_MODE
   *
   * @description:
   *   Return the @FT_Render_Mode corresponding to a given
   *   @FT_LOAD_TARGET_XXX value.
   *
   */
#define FT_LOAD_TARGET_MODE( x )  ( (FT_Render_Mode)( ( (x) >> 16 ) & 15 ) )


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Set_Transform                                                   */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A function used to set the transformation that is applied to glyph */
  /*    images when they are loaded into a glyph slot through              */
  /*    @FT_Load_Glyph.                                                    */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    face   :: A handle to the source face object.                      */
  /*                                                                       */
  /* <Input>                                                               */
  /*    matrix :: A pointer to the transformation's 2x2 matrix.  Use~0 for */
  /*              the identity matrix.                                     */
  /*    delta  :: A pointer to the translation vector.  Use~0 for the null */
  /*              vector.                                                  */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The transformation is only applied to scalable image formats after */
  /*    the glyph has been loaded.  It means that hinting is unaltered by  */
  /*    the transformation and is performed on the character size given in */
  /*    the last call to @FT_Set_Char_Size or @FT_Set_Pixel_Sizes.         */
  /*                                                                       */
  /*    Note that this also transforms the `face.glyph.advance' field, but */
  /*    *not* the values in `face.glyph.metrics'.                          */
  /*                                                                       */
  FT_EXPORT( void )
  FT_Set_Transform( FT_Face     face,
                    FT_Matrix*  matrix,
                    FT_Vector*  delta );


  /*************************************************************************/
  /*                                                                       */
  /* <Enum>                                                                */
  /*    FT_Render_Mode                                                     */
  /*                                                                       */
  /* <Description>                                                         */
  /*    An enumeration type that lists the render modes supported by       */
  /*    FreeType~2.  Each mode corresponds to a specific type of scanline  */
  /*    conversion performed on the outline.                               */
  /*                                                                       */
  /*    For bitmap fonts and embedded bitmaps the `bitmap->pixel_mode'     */
  /*    field in the @FT_GlyphSlotRec structure gives the format of the    */
  /*    returned bitmap.                                                   */
  /*                                                                       */
  /*    All modes except @FT_RENDER_MODE_MONO use 256 levels of opacity.   */
  /*                                                                       */
  /* <Values>                                                              */
  /*    FT_RENDER_MODE_NORMAL ::                                           */
  /*      This is the default render mode; it corresponds to 8-bit         */
  /*      anti-aliased bitmaps.                                            */
  /*                                                                       */
  /*    FT_RENDER_MODE_LIGHT ::                                            */
  /*      This is equivalent to @FT_RENDER_MODE_NORMAL.  It is only        */
  /*      defined as a separate value because render modes are also used   */
  /*      indirectly to define hinting algorithm selectors.  See           */
  /*      @FT_LOAD_TARGET_XXX for details.                                 */
  /*                                                                       */
  /*    FT_RENDER_MODE_MONO ::                                             */
  /*      This mode corresponds to 1-bit bitmaps (with 2~levels of         */
  /*      opacity).                                                        */
  /*                                                                       */
  /*    FT_RENDER_MODE_LCD ::                                              */
  /*      This mode corresponds to horizontal RGB and BGR sub-pixel        */
  /*      displays like LCD screens.  It produces 8-bit bitmaps that are   */
  /*      3~times the width of the original glyph outline in pixels, and   */
  /*      which use the @FT_PIXEL_MODE_LCD mode.                           */
  /*                                                                       */
  /*    FT_RENDER_MODE_LCD_V ::                                            */
  /*      This mode corresponds to vertical RGB and BGR sub-pixel displays */
  /*      (like PDA screens, rotated LCD displays, etc.).  It produces     */
  /*      8-bit bitmaps that are 3~times the height of the original        */
  /*      glyph outline in pixels and use the @FT_PIXEL_MODE_LCD_V mode.   */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The LCD-optimized glyph bitmaps produced by FT_Render_Glyph can be */
  /*    filtered to reduce color-fringes by using @FT_Library_SetLcdFilter */
  /*    (not active in the default builds).  It is up to the caller to     */
  /*    either call @FT_Library_SetLcdFilter (if available) or do the      */
  /*    filtering itself.                                                  */
  /*                                                                       */
  /*    The selected render mode only affects vector glyphs of a font.     */
  /*    Embedded bitmaps often have a different pixel mode like            */
  /*    @FT_PIXEL_MODE_MONO.  You can use @FT_Bitmap_Convert to transform  */
  /*    them into 8-bit pixmaps.                                           */
  /*                                                                       */
  typedef enum  FT_Render_Mode_
  {
    FT_RENDER_MODE_NORMAL = 0,
    FT_RENDER_MODE_LIGHT,
    FT_RENDER_MODE_MONO,
    FT_RENDER_MODE_LCD,
    FT_RENDER_MODE_LCD_V,

    FT_RENDER_MODE_MAX

  } FT_Render_Mode;


  /*************************************************************************/
  /*                                                                       */
  /* <Enum>                                                                */
  /*    ft_render_mode_xxx                                                 */
  /*                                                                       */
  /* <Description>                                                         */
  /*    These constants are deprecated.  Use the corresponding             */
  /*    @FT_Render_Mode values instead.                                    */
  /*                                                                       */
  /* <Values>                                                              */
  /*    ft_render_mode_normal :: see @FT_RENDER_MODE_NORMAL                */
  /*    ft_render_mode_mono   :: see @FT_RENDER_MODE_MONO                  */
  /*                                                                       */
#define ft_render_mode_normal  FT_RENDER_MODE_NORMAL
#define ft_render_mode_mono    FT_RENDER_MODE_MONO


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Render_Glyph                                                    */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Convert a given glyph image to a bitmap.  It does so by inspecting */
  /*    the glyph image format, finding the relevant renderer, and         */
  /*    invoking it.                                                       */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    slot        :: A handle to the glyph slot containing the image to  */
  /*                   convert.                                            */
  /*                                                                       */
  /* <Input>                                                               */
  /*    render_mode :: This is the render mode used to render the glyph    */
  /*                   image into a bitmap.  See @FT_Render_Mode for a     */
  /*                   list of possible values.                            */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Render_Glyph( FT_GlyphSlot    slot,
                   FT_Render_Mode  render_mode );


  /*************************************************************************/
  /*                                                                       */
  /* <Enum>                                                                */
  /*    FT_Kerning_Mode                                                    */
  /*                                                                       */
  /* <Description>                                                         */
  /*    An enumeration used to specify which kerning values to return in   */
  /*    @FT_Get_Kerning.                                                   */
  /*                                                                       */
  /* <Values>                                                              */
  /*    FT_KERNING_DEFAULT  :: Return scaled and grid-fitted kerning       */
  /*                           distances (value is~0).                     */
  /*                                                                       */
  /*    FT_KERNING_UNFITTED :: Return scaled but un-grid-fitted kerning    */
  /*                           distances.                                  */
  /*                                                                       */
  /*    FT_KERNING_UNSCALED :: Return the kerning vector in original font  */
  /*                           units.                                      */
  /*                                                                       */
  typedef enum  FT_Kerning_Mode_
  {
    FT_KERNING_DEFAULT  = 0,
    FT_KERNING_UNFITTED,
    FT_KERNING_UNSCALED

  } FT_Kerning_Mode;


  /*************************************************************************/
  /*                                                                       */
  /* <Const>                                                               */
  /*    ft_kerning_default                                                 */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This constant is deprecated.  Please use @FT_KERNING_DEFAULT       */
  /*    instead.                                                           */
  /*                                                                       */
#define ft_kerning_default   FT_KERNING_DEFAULT


  /*************************************************************************/
  /*                                                                       */
  /* <Const>                                                               */
  /*    ft_kerning_unfitted                                                */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This constant is deprecated.  Please use @FT_KERNING_UNFITTED      */
  /*    instead.                                                           */
  /*                                                                       */
#define ft_kerning_unfitted  FT_KERNING_UNFITTED


  /*************************************************************************/
  /*                                                                       */
  /* <Const>                                                               */
  /*    ft_kerning_unscaled                                                */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This constant is deprecated.  Please use @FT_KERNING_UNSCALED      */
  /*    instead.                                                           */
  /*                                                                       */
#define ft_kerning_unscaled  FT_KERNING_UNSCALED


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Get_Kerning                                                     */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Return the kerning vector between two glyphs of a same face.       */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face        :: A handle to a source face object.                   */
  /*                                                                       */
  /*    left_glyph  :: The index of the left glyph in the kern pair.       */
  /*                                                                       */
  /*    right_glyph :: The index of the right glyph in the kern pair.      */
  /*                                                                       */
  /*    kern_mode   :: See @FT_Kerning_Mode for more information.          */
  /*                   Determines the scale and dimension of the returned  */
  /*                   kerning vector.                                     */
  /*                                                                       */
  /* <Output>                                                              */
  /*    akerning    :: The kerning vector.  This is either in font units   */
  /*                   or in pixels (26.6 format) for scalable formats,    */
  /*                   and in pixels for fixed-sizes formats.              */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    Only horizontal layouts (left-to-right & right-to-left) are        */
  /*    supported by this method.  Other layouts, or more sophisticated    */
  /*    kernings, are out of the scope of this API function -- they can be */
  /*    implemented through format-specific interfaces.                    */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Get_Kerning( FT_Face     face,
                  FT_UInt     left_glyph,
                  FT_UInt     right_glyph,
                  FT_UInt     kern_mode,
                  FT_Vector  *akerning );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Get_Track_Kerning                                               */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Return the track kerning for a given face object at a given size.  */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face       :: A handle to a source face object.                    */
  /*                                                                       */
  /*    point_size :: The point size in 16.16 fractional points.           */
  /*                                                                       */
  /*    degree     :: The degree of tightness.                             */
  /*                                                                       */
  /* <Output>                                                              */
  /*    akerning   :: The kerning in 16.16 fractional points.              */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Get_Track_Kerning( FT_Face    face,
                        FT_Fixed   point_size,
                        FT_Int     degree,
                        FT_Fixed*  akerning );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Get_Glyph_Name                                                  */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Retrieve the ASCII name of a given glyph in a face.  This only     */
  /*    works for those faces where @FT_HAS_GLYPH_NAMES(face) returns~1.   */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face        :: A handle to a source face object.                   */
  /*                                                                       */
  /*    glyph_index :: The glyph index.                                    */
  /*                                                                       */
  /*    buffer_max  :: The maximal number of bytes available in the        */
  /*                   buffer.                                             */
  /*                                                                       */
  /* <Output>                                                              */
  /*    buffer      :: A pointer to a target buffer where the name is      */
  /*                   copied to.                                          */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    An error is returned if the face doesn't provide glyph names or if */
  /*    the glyph index is invalid.  In all cases of failure, the first    */
  /*    byte of `buffer' is set to~0 to indicate an empty name.            */
  /*                                                                       */
  /*    The glyph name is truncated to fit within the buffer if it is too  */
  /*    long.  The returned string is always zero-terminated.              */
  /*                                                                       */
  /*    This function is not compiled within the library if the config     */
  /*    macro `FT_CONFIG_OPTION_NO_GLYPH_NAMES' is defined in              */
  /*    `include/freetype/config/ftoptions.h'.                             */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Get_Glyph_Name( FT_Face     face,
                     FT_UInt     glyph_index,
                     FT_Pointer  buffer,
                     FT_UInt     buffer_max );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Get_Postscript_Name                                             */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Retrieve the ASCII PostScript name of a given face, if available.  */
  /*    This only works with PostScript and TrueType fonts.                */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face :: A handle to the source face object.                        */
  /*                                                                       */
  /* <Return>                                                              */
  /*    A pointer to the face's PostScript name.  NULL if unavailable.     */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The returned pointer is owned by the face and is destroyed with    */
  /*    it.                                                                */
  /*                                                                       */
  FT_EXPORT( const char* )
  FT_Get_Postscript_Name( FT_Face  face );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Select_Charmap                                                  */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Select a given charmap by its encoding tag (as listed in           */
  /*    `freetype.h').                                                     */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    face     :: A handle to the source face object.                    */
  /*                                                                       */
  /* <Input>                                                               */
  /*    encoding :: A handle to the selected encoding.                     */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    This function returns an error if no charmap in the face           */
  /*    corresponds to the encoding queried here.                          */
  /*                                                                       */
  /*    Because many fonts contain more than a single cmap for Unicode     */
  /*    encoding, this function has some special code to select the one    */
  /*    which covers Unicode best (`best' in the sense that a UCS-4 cmap   */
  /*    is preferred to a UCS-2 cmap).  It is thus preferable to           */
  /*    @FT_Set_Charmap in this case.                                      */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Select_Charmap( FT_Face      face,
                     FT_Encoding  encoding );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Set_Charmap                                                     */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Select a given charmap for character code to glyph index mapping.  */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    face    :: A handle to the source face object.                     */
  /*                                                                       */
  /* <Input>                                                               */
  /*    charmap :: A handle to the selected charmap.                       */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    This function returns an error if the charmap is not part of       */
  /*    the face (i.e., if it is not listed in the `face->charmaps'        */
  /*    table).                                                            */
  /*                                                                       */
  /*    It also fails if a type~14 charmap is selected.                    */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Set_Charmap( FT_Face     face,
                  FT_CharMap  charmap );


  /*************************************************************************
   *
   * @function:
   *   FT_Get_Charmap_Index
   *
   * @description:
   *   Retrieve index of a given charmap.
   *
   * @input:
   *   charmap ::
   *     A handle to a charmap.
   *
   * @return:
   *   The index into the array of character maps within the face to which
   *   `charmap' belongs.  If an error occurs, -1 is returned.
   *
   */
  FT_EXPORT( FT_Int )
  FT_Get_Charmap_Index( FT_CharMap  charmap );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Get_Char_Index                                                  */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Return the glyph index of a given character code.  This function   */
  /*    uses a charmap object to do the mapping.                           */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face     :: A handle to the source face object.                    */
  /*                                                                       */
  /*    charcode :: The character code.                                    */
  /*                                                                       */
  /* <Return>                                                              */
  /*    The glyph index.  0~means `undefined character code'.              */
  /*                                                                       */
  /* <Note>                                                                */
  /*    If you use FreeType to manipulate the contents of font files       */
  /*    directly, be aware that the glyph index returned by this function  */
  /*    doesn't always correspond to the internal indices used within      */
  /*    the file.  This is done to ensure that value~0 always corresponds  */
  /*    to the `missing glyph'.                                            */
  /*                                                                       */
  FT_EXPORT( FT_UInt )
  FT_Get_Char_Index( FT_Face   face,
                     FT_ULong  charcode );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Get_First_Char                                                  */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This function is used to return the first character code in the    */
  /*    current charmap of a given face.  It also returns the              */
  /*    corresponding glyph index.                                         */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face    :: A handle to the source face object.                     */
  /*                                                                       */
  /* <Output>                                                              */
  /*    agindex :: Glyph index of first character code.  0~if charmap is   */
  /*               empty.                                                  */
  /*                                                                       */
  /* <Return>                                                              */
  /*    The charmap's first character code.                                */
  /*                                                                       */
  /* <Note>                                                                */
  /*    You should use this function with @FT_Get_Next_Char to be able to  */
  /*    parse all character codes available in a given charmap.  The code  */
  /*    should look like this:                                             */
  /*                                                                       */
  /*    {                                                                  */
  /*      FT_ULong  charcode;                                              */
  /*      FT_UInt   gindex;                                                */
  /*                                                                       */
  /*                                                                       */
  /*      charcode = FT_Get_First_Char( face, &gindex );                   */
  /*      while ( gindex != 0 )                                            */
  /*      {                                                                */
  /*        ... do something with (charcode,gindex) pair ...               */
  /*                                                                       */
  /*        charcode = FT_Get_Next_Char( face, charcode, &gindex );        */
  /*      }                                                                */
  /*    }                                                                  */
  /*                                                                       */
  /*    Note that `*agindex' is set to~0 if the charmap is empty.  The     */
  /*    result itself can be~0 in two cases: if the charmap is empty or    */
  /*    if the value~0 is the first valid character code.                  */
  /*                                                                       */
  FT_EXPORT( FT_ULong )
  FT_Get_First_Char( FT_Face   face,
                     FT_UInt  *agindex );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Get_Next_Char                                                   */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This function is used to return the next character code in the     */
  /*    current charmap of a given face following the value `char_code',   */
  /*    as well as the corresponding glyph index.                          */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face      :: A handle to the source face object.                   */
  /*    char_code :: The starting character code.                          */
  /*                                                                       */
  /* <Output>                                                              */
  /*    agindex   :: Glyph index of next character code.  0~if charmap     */
  /*                 is empty.                                             */
  /*                                                                       */
  /* <Return>                                                              */
  /*    The charmap's next character code.                                 */
  /*                                                                       */
  /* <Note>                                                                */
  /*    You should use this function with @FT_Get_First_Char to walk       */
  /*    over all character codes available in a given charmap.  See the    */
  /*    note for this function for a simple code example.                  */
  /*                                                                       */
  /*    Note that `*agindex' is set to~0 when there are no more codes in   */
  /*    the charmap.                                                       */
  /*                                                                       */
  FT_EXPORT( FT_ULong )
  FT_Get_Next_Char( FT_Face    face,
                    FT_ULong   char_code,
                    FT_UInt   *agindex );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Get_Name_Index                                                  */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Return the glyph index of a given glyph name.  This function uses  */
  /*    driver specific objects to do the translation.                     */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face       :: A handle to the source face object.                  */
  /*                                                                       */
  /*    glyph_name :: The glyph name.                                      */
  /*                                                                       */
  /* <Return>                                                              */
  /*    The glyph index.  0~means `undefined character code'.              */
  /*                                                                       */
  FT_EXPORT( FT_UInt )
  FT_Get_Name_Index( FT_Face     face,
                     FT_String*  glyph_name );


  /*************************************************************************
   *
   * @macro:
   *   FT_SUBGLYPH_FLAG_XXX
   *
   * @description:
   *   A list of constants used to describe subglyphs.  Please refer to the
   *   TrueType specification for the meaning of the various flags.
   *
   * @values:
   *   FT_SUBGLYPH_FLAG_ARGS_ARE_WORDS ::
   *   FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES ::
   *   FT_SUBGLYPH_FLAG_ROUND_XY_TO_GRID ::
   *   FT_SUBGLYPH_FLAG_SCALE ::
   *   FT_SUBGLYPH_FLAG_XY_SCALE ::
   *   FT_SUBGLYPH_FLAG_2X2 ::
   *   FT_SUBGLYPH_FLAG_USE_MY_METRICS ::
   *
   */
#define FT_SUBGLYPH_FLAG_ARGS_ARE_WORDS          1
#define FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES      2
#define FT_SUBGLYPH_FLAG_ROUND_XY_TO_GRID        4
#define FT_SUBGLYPH_FLAG_SCALE                   8
#define FT_SUBGLYPH_FLAG_XY_SCALE             0x40
#define FT_SUBGLYPH_FLAG_2X2                  0x80
#define FT_SUBGLYPH_FLAG_USE_MY_METRICS      0x200


  /*************************************************************************
   *
   * @func:
   *   FT_Get_SubGlyph_Info
   *
   * @description:
   *   Retrieve a description of a given subglyph.  Only use it if
   *   `glyph->format' is @FT_GLYPH_FORMAT_COMPOSITE; an error is
   *   returned otherwise.
   *
   * @input:
   *   glyph ::
   *     The source glyph slot.
   *
   *   sub_index ::
   *     The index of the subglyph.  Must be less than
   *     `glyph->num_subglyphs'.
   *
   * @output:
   *   p_index ::
   *     The glyph index of the subglyph.
   *
   *   p_flags ::
   *     The subglyph flags, see @FT_SUBGLYPH_FLAG_XXX.
   *
   *   p_arg1 ::
   *     The subglyph's first argument (if any).
   *
   *   p_arg2 ::
   *     The subglyph's second argument (if any).
   *
   *   p_transform ::
   *     The subglyph transformation (if any).
   *
   * @return:
   *   FreeType error code.  0~means success.
   *
   * @note:
   *   The values of `*p_arg1', `*p_arg2', and `*p_transform' must be
   *   interpreted depending on the flags returned in `*p_flags'.  See the
   *   TrueType specification for details.
   *
   */
  FT_EXPORT( FT_Error )
  FT_Get_SubGlyph_Info( FT_GlyphSlot  glyph,
                        FT_UInt       sub_index,
                        FT_Int       *p_index,
                        FT_UInt      *p_flags,
                        FT_Int       *p_arg1,
                        FT_Int       *p_arg2,
                        FT_Matrix    *p_transform );


  /*************************************************************************/
  /*                                                                       */
  /* <Enum>                                                                */
  /*    FT_FSTYPE_XXX                                                      */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A list of bit flags used in the `fsType' field of the OS/2 table   */
  /*    in a TrueType or OpenType font and the `FSType' entry in a         */
  /*    PostScript font.  These bit flags are returned by                  */
  /*    @FT_Get_FSType_Flags; they inform client applications of embedding */
  /*    and subsetting restrictions associated with a font.                */
  /*                                                                       */
  /*    See http://www.adobe.com/devnet/acrobat/pdfs/FontPolicies.pdf for  */
  /*    more details.                                                      */
  /*                                                                       */
  /* <Values>                                                              */
  /*    FT_FSTYPE_INSTALLABLE_EMBEDDING ::                                 */
  /*      Fonts with no fsType bit set may be embedded and permanently     */
  /*      installed on the remote system by an application.                */
  /*                                                                       */
  /*    FT_FSTYPE_RESTRICTED_LICENSE_EMBEDDING ::                          */
  /*      Fonts that have only this bit set must not be modified, embedded */
  /*      or exchanged in any manner without first obtaining permission of */
  /*      the font software copyright owner.                               */
  /*                                                                       */
  /*    FT_FSTYPE_PREVIEW_AND_PRINT_EMBEDDING ::                           */
  /*      If this bit is set, the font may be embedded and temporarily     */
  /*      loaded on the remote system.  Documents containing Preview &     */
  /*      Print fonts must be opened `read-only'; no edits can be applied  */
  /*      to the document.                                                 */
  /*                                                                       */
  /*    FT_FSTYPE_EDITABLE_EMBEDDING ::                                    */
  /*      If this bit is set, the font may be embedded but must only be    */
  /*      installed temporarily on other systems.  In contrast to Preview  */
  /*      & Print fonts, documents containing editable fonts may be opened */
  /*      for reading, editing is permitted, and changes may be saved.     */
  /*                                                                       */
  /*    FT_FSTYPE_NO_SUBSETTING ::                                         */
  /*      If this bit is set, the font may not be subsetted prior to       */
  /*      embedding.                                                       */
  /*                                                                       */
  /*    FT_FSTYPE_BITMAP_EMBEDDING_ONLY ::                                 */
  /*      If this bit is set, only bitmaps contained in the font may be    */
  /*      embedded; no outline data may be embedded.  If there are no      */
  /*      bitmaps available in the font, then the font is unembeddable.    */
  /*                                                                       */
  /* <Note>                                                                */
  /*    While the fsType flags can indicate that a font may be embedded, a */
  /*    license with the font vendor may be separately required to use the */
  /*    font in this way.                                                  */
  /*                                                                       */
#define FT_FSTYPE_INSTALLABLE_EMBEDDING         0x0000
#define FT_FSTYPE_RESTRICTED_LICENSE_EMBEDDING  0x0002
#define FT_FSTYPE_PREVIEW_AND_PRINT_EMBEDDING   0x0004
#define FT_FSTYPE_EDITABLE_EMBEDDING            0x0008
#define FT_FSTYPE_NO_SUBSETTING                 0x0100
#define FT_FSTYPE_BITMAP_EMBEDDING_ONLY         0x0200


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Get_FSType_Flags                                                */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Return the fsType flags for a font.                                */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face :: A handle to the source face object.                        */
  /*                                                                       */
  /* <Return>                                                              */
  /*    The fsType flags, @FT_FSTYPE_XXX.                                  */
  /*                                                                       */
  /* <Note>                                                                */
  /*    Use this function rather than directly reading the `fs_type' field */
  /*    in the @PS_FontInfoRec structure which is only guaranteed to       */
  /*    return the correct results for Type~1 fonts.                       */
  /*                                                                       */
  FT_EXPORT( FT_UShort )
  FT_Get_FSType_Flags( FT_Face  face );


  /*************************************************************************/
  /*                                                                       */
  /* <Section>                                                             */
  /*    glyph_variants                                                     */
  /*                                                                       */
  /* <Title>                                                               */
  /*    Glyph Variants                                                     */
  /*                                                                       */
  /* <Abstract>                                                            */
  /*    The FreeType~2 interface to Unicode Ideographic Variation          */
  /*    Sequences (IVS), using the SFNT cmap format~14.                    */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Many CJK characters have variant forms.  They are a sort of grey   */
  /*    area somewhere between being totally irrelevant and semantically   */
  /*    distinct; for this reason, the Unicode consortium decided to       */
  /*    introduce Ideographic Variation Sequences (IVS), consisting of a   */
  /*    Unicode base character and one of 240 variant selectors            */
  /*    (U+E0100-U+E01EF), instead of further extending the already huge   */
  /*    code range for CJK characters.                                     */
  /*                                                                       */
  /*    An IVS is registered and unique; for further details please refer  */
  /*    to Unicode Technical Report #37, the Ideographic Variation         */
  /*    Database.  To date (October 2007), the character with the most     */
  /*    variants is U+908A, having 8~such IVS.                             */
  /*                                                                       */
  /*    Adobe and MS decided to support IVS with a new cmap subtable       */
  /*    (format~14).  It is an odd subtable because it is not a mapping of */
  /*    input code points to glyphs, but contains lists of all variants    */
  /*    supported by the font.                                             */
  /*                                                                       */
  /*    A variant may be either `default' or `non-default'.  A default     */
  /*    variant is the one you will get for that code point if you look it */
  /*    up in the standard Unicode cmap.  A non-default variant is a       */
  /*    different glyph.                                                   */
  /*                                                                       */
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Face_GetCharVariantIndex                                        */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Return the glyph index of a given character code as modified by    */
  /*    the variation selector.                                            */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face ::                                                            */
  /*      A handle to the source face object.                              */
  /*                                                                       */
  /*    charcode ::                                                        */
  /*      The character code point in Unicode.                             */
  /*                                                                       */
  /*    variantSelector ::                                                 */
  /*      The Unicode code point of the variation selector.                */
  /*                                                                       */
  /* <Return>                                                              */
  /*    The glyph index.  0~means either `undefined character code', or    */
  /*    `undefined selector code', or `no variation selector cmap          */
  /*    subtable', or `current CharMap is not Unicode'.                    */
  /*                                                                       */
  /* <Note>                                                                */
  /*    If you use FreeType to manipulate the contents of font files       */
  /*    directly, be aware that the glyph index returned by this function  */
  /*    doesn't always correspond to the internal indices used within      */
  /*    the file.  This is done to ensure that value~0 always corresponds  */
  /*    to the `missing glyph'.                                            */
  /*                                                                       */
  /*    This function is only meaningful if                                */
  /*      a) the font has a variation selector cmap sub table,             */
  /*    and                                                                */
  /*      b) the current charmap has a Unicode encoding.                   */
  /*                                                                       */
  /* <Since>                                                               */
  /*    2.3.6                                                              */
  /*                                                                       */
  FT_EXPORT( FT_UInt )
  FT_Face_GetCharVariantIndex( FT_Face   face,
                               FT_ULong  charcode,
                               FT_ULong  variantSelector );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Face_GetCharVariantIsDefault                                    */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Check whether this variant of this Unicode character is the one to */
  /*    be found in the `cmap'.                                            */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face ::                                                            */
  /*      A handle to the source face object.                              */
  /*                                                                       */
  /*    charcode ::                                                        */
  /*      The character codepoint in Unicode.                              */
  /*                                                                       */
  /*    variantSelector ::                                                 */
  /*      The Unicode codepoint of the variation selector.                 */
  /*                                                                       */
  /* <Return>                                                              */
  /*    1~if found in the standard (Unicode) cmap, 0~if found in the       */
  /*    variation selector cmap, or -1 if it is not a variant.             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    This function is only meaningful if the font has a variation       */
  /*    selector cmap subtable.                                            */
  /*                                                                       */
  /* <Since>                                                               */
  /*    2.3.6                                                              */
  /*                                                                       */
  FT_EXPORT( FT_Int )
  FT_Face_GetCharVariantIsDefault( FT_Face   face,
                                   FT_ULong  charcode,
                                   FT_ULong  variantSelector );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Face_GetVariantSelectors                                        */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Return a zero-terminated list of Unicode variant selectors found   */
  /*    in the font.                                                       */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face ::                                                            */
  /*      A handle to the source face object.                              */
  /*                                                                       */
  /* <Return>                                                              */
  /*    A pointer to an array of selector code points, or NULL if there is */
  /*    no valid variant selector cmap subtable.                           */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The last item in the array is~0; the array is owned by the         */
  /*    @FT_Face object but can be overwritten or released on the next     */
  /*    call to a FreeType function.                                       */
  /*                                                                       */
  /* <Since>                                                               */
  /*    2.3.6                                                              */
  /*                                                                       */
  FT_EXPORT( FT_UInt32* )
  FT_Face_GetVariantSelectors( FT_Face  face );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Face_GetVariantsOfChar                                          */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Return a zero-terminated list of Unicode variant selectors found   */
  /*    for the specified character code.                                  */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face ::                                                            */
  /*      A handle to the source face object.                              */
  /*                                                                       */
  /*    charcode ::                                                        */
  /*      The character codepoint in Unicode.                              */
  /*                                                                       */
  /* <Return>                                                              */
  /*    A pointer to an array of variant selector code points which are    */
  /*    active for the given character, or NULL if the corresponding list  */
  /*    is empty.                                                          */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The last item in the array is~0; the array is owned by the         */
  /*    @FT_Face object but can be overwritten or released on the next     */
  /*    call to a FreeType function.                                       */
  /*                                                                       */
  /* <Since>                                                               */
  /*    2.3.6                                                              */
  /*                                                                       */
  FT_EXPORT( FT_UInt32* )
  FT_Face_GetVariantsOfChar( FT_Face   face,
                             FT_ULong  charcode );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Face_GetCharsOfVariant                                          */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Return a zero-terminated list of Unicode character codes found for */
  /*    the specified variant selector.                                    */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face ::                                                            */
  /*      A handle to the source face object.                              */
  /*                                                                       */
  /*    variantSelector ::                                                 */
  /*      The variant selector code point in Unicode.                      */
  /*                                                                       */
  /* <Return>                                                              */
  /*    A list of all the code points which are specified by this selector */
  /*    (both default and non-default codes are returned) or NULL if there */
  /*    is no valid cmap or the variant selector is invalid.               */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The last item in the array is~0; the array is owned by the         */
  /*    @FT_Face object but can be overwritten or released on the next     */
  /*    call to a FreeType function.                                       */
  /*                                                                       */
  /* <Since>                                                               */
  /*    2.3.6                                                              */
  /*                                                                       */
  FT_EXPORT( FT_UInt32* )
  FT_Face_GetCharsOfVariant( FT_Face   face,
                             FT_ULong  variantSelector );


  /*************************************************************************/
  /*                                                                       */
  /* <Section>                                                             */
  /*    computations                                                       */
  /*                                                                       */
  /* <Title>                                                               */
  /*    Computations                                                       */
  /*                                                                       */
  /* <Abstract>                                                            */
  /*    Crunching fixed numbers and vectors.                               */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This section contains various functions used to perform            */
  /*    computations on 16.16 fixed-float numbers or 2d vectors.           */
  /*                                                                       */
  /* <Order>                                                               */
  /*    FT_MulDiv                                                          */
  /*    FT_MulFix                                                          */
  /*    FT_DivFix                                                          */
  /*    FT_RoundFix                                                        */
  /*    FT_CeilFix                                                         */
  /*    FT_FloorFix                                                        */
  /*    FT_Vector_Transform                                                */
  /*    FT_Matrix_Multiply                                                 */
  /*    FT_Matrix_Invert                                                   */
  /*                                                                       */
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_MulDiv                                                          */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A very simple function used to perform the computation `(a*b)/c'   */
  /*    with maximal accuracy (it uses a 64-bit intermediate integer       */
  /*    whenever necessary).                                               */
  /*                                                                       */
  /*    This function isn't necessarily as fast as some processor specific */
  /*    operations, but is at least completely portable.                   */
  /*                                                                       */
  /* <Input>                                                               */
  /*    a :: The first multiplier.                                         */
  /*    b :: The second multiplier.                                        */
  /*    c :: The divisor.                                                  */
  /*                                                                       */
  /* <Return>                                                              */
  /*    The result of `(a*b)/c'.  This function never traps when trying to */
  /*    divide by zero; it simply returns `MaxInt' or `MinInt' depending   */
  /*    on the signs of `a' and `b'.                                       */
  /*                                                                       */
  FT_EXPORT( FT_Long )
  FT_MulDiv( FT_Long  a,
             FT_Long  b,
             FT_Long  c );


  /* */

  /* The following #if 0 ... #endif is for the documentation formatter, */
  /* hiding the internal `FT_MULFIX_INLINED' macro.                     */

#if 0
  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_MulFix                                                          */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A very simple function used to perform the computation             */
  /*    `(a*b)/0x10000' with maximal accuracy.  Most of the time this is   */
  /*    used to multiply a given value by a 16.16 fixed float factor.      */
  /*                                                                       */
  /* <Input>                                                               */
  /*    a :: The first multiplier.                                         */
  /*    b :: The second multiplier.  Use a 16.16 factor here whenever      */
  /*         possible (see note below).                                    */
  /*                                                                       */
  /* <Return>                                                              */
  /*    The result of `(a*b)/0x10000'.                                     */
  /*                                                                       */
  /* <Note>                                                                */
  /*    This function has been optimized for the case where the absolute   */
  /*    value of `a' is less than 2048, and `b' is a 16.16 scaling factor. */
  /*    As this happens mainly when scaling from notional units to         */
  /*    fractional pixels in FreeType, it resulted in noticeable speed     */
  /*    improvements between versions 2.x and 1.x.                         */
  /*                                                                       */
  /*    As a conclusion, always try to place a 16.16 factor as the         */
  /*    _second_ argument of this function; this can make a great          */
  /*    difference.                                                        */
  /*                                                                       */
  FT_EXPORT( FT_Long )
  FT_MulFix( FT_Long  a,
             FT_Long  b );

  /* */
#endif

#ifdef FT_MULFIX_INLINED
#define FT_MulFix( a, b )  FT_MULFIX_INLINED( a, b )
#else
  FT_EXPORT( FT_Long )
  FT_MulFix( FT_Long  a,
             FT_Long  b );
#endif


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_DivFix                                                          */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A very simple function used to perform the computation             */
  /*    `(a*0x10000)/b' with maximal accuracy.  Most of the time, this is  */
  /*    used to divide a given value by a 16.16 fixed float factor.        */
  /*                                                                       */
  /* <Input>                                                               */
  /*    a :: The first multiplier.                                         */
  /*    b :: The second multiplier.  Use a 16.16 factor here whenever      */
  /*         possible (see note below).                                    */
  /*                                                                       */
  /* <Return>                                                              */
  /*    The result of `(a*0x10000)/b'.                                     */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The optimization for FT_DivFix() is simple: If (a~<<~16) fits in   */
  /*    32~bits, then the division is computed directly.  Otherwise, we    */
  /*    use a specialized version of @FT_MulDiv.                           */
  /*                                                                       */
  FT_EXPORT( FT_Long )
  FT_DivFix( FT_Long  a,
             FT_Long  b );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_RoundFix                                                        */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A very simple function used to round a 16.16 fixed number.         */
  /*                                                                       */
  /* <Input>                                                               */
  /*    a :: The number to be rounded.                                     */
  /*                                                                       */
  /* <Return>                                                              */
  /*    The result of `(a + 0x8000) & -0x10000'.                           */
  /*                                                                       */
  FT_EXPORT( FT_Fixed )
  FT_RoundFix( FT_Fixed  a );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_CeilFix                                                         */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A very simple function used to compute the ceiling function of a   */
  /*    16.16 fixed number.                                                */
  /*                                                                       */
  /* <Input>                                                               */
  /*    a :: The number for which the ceiling function is to be computed.  */
  /*                                                                       */
  /* <Return>                                                              */
  /*    The result of `(a + 0x10000 - 1) & -0x10000'.                      */
  /*                                                                       */
  FT_EXPORT( FT_Fixed )
  FT_CeilFix( FT_Fixed  a );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_FloorFix                                                        */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A very simple function used to compute the floor function of a     */
  /*    16.16 fixed number.                                                */
  /*                                                                       */
  /* <Input>                                                               */
  /*    a :: The number for which the floor function is to be computed.    */
  /*                                                                       */
  /* <Return>                                                              */
  /*    The result of `a & -0x10000'.                                      */
  /*                                                                       */
  FT_EXPORT( FT_Fixed )
  FT_FloorFix( FT_Fixed  a );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Vector_Transform                                                */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Transform a single vector through a 2x2 matrix.                    */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    vector :: The target vector to transform.                          */
  /*                                                                       */
  /* <Input>                                                               */
  /*    matrix :: A pointer to the source 2x2 matrix.                      */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The result is undefined if either `vector' or `matrix' is invalid. */
  /*                                                                       */
  FT_EXPORT( void )
  FT_Vector_Transform( FT_Vector*        vec,
                       const FT_Matrix*  matrix );


  /*************************************************************************/
  /*                                                                       */
  /* <Section>                                                             */
  /*    version                                                            */
  /*                                                                       */
  /* <Title>                                                               */
  /*    FreeType Version                                                   */
  /*                                                                       */
  /* <Abstract>                                                            */
  /*    Functions and macros related to FreeType versions.                 */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Note that those functions and macros are of limited use because    */
  /*    even a new release of FreeType with only documentation changes     */
  /*    increases the version number.                                      */
  /*                                                                       */
  /*************************************************************************/


  /*************************************************************************
   *
   * @enum:
   *   FREETYPE_XXX
   *
   * @description:
   *   These three macros identify the FreeType source code version.
   *   Use @FT_Library_Version to access them at runtime.
   *
   * @values:
   *   FREETYPE_MAJOR :: The major version number.
   *   FREETYPE_MINOR :: The minor version number.
   *   FREETYPE_PATCH :: The patch level.
   *
   * @note:
   *   The version number of FreeType if built as a dynamic link library
   *   with the `libtool' package is _not_ controlled by these three
   *   macros.
   *
   */
#define FREETYPE_MAJOR  2
#define FREETYPE_MINOR  4
#define FREETYPE_PATCH  3


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Library_Version                                                 */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Return the version of the FreeType library being used.  This is    */
  /*    useful when dynamically linking to the library, since one cannot   */
  /*    use the macros @FREETYPE_MAJOR, @FREETYPE_MINOR, and               */
  /*    @FREETYPE_PATCH.                                                   */
  /*                                                                       */
  /* <Input>                                                               */
  /*    library :: A source library handle.                                */
  /*                                                                       */
  /* <Output>                                                              */
  /*    amajor  :: The major version number.                               */
  /*                                                                       */
  /*    aminor  :: The minor version number.                               */
  /*                                                                       */
  /*    apatch  :: The patch version number.                               */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The reason why this function takes a `library' argument is because */
  /*    certain programs implement library initialization in a custom way  */
  /*    that doesn't use @FT_Init_FreeType.                                */
  /*                                                                       */
  /*    In such cases, the library version might not be available before   */
  /*    the library object has been created.                               */
  /*                                                                       */
  FT_EXPORT( void )
  FT_Library_Version( FT_Library   library,
                      FT_Int      *amajor,
                      FT_Int      *aminor,
                      FT_Int      *apatch );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Face_CheckTrueTypePatents                                       */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Parse all bytecode instructions of a TrueType font file to check   */
  /*    whether any of the patented opcodes are used.  This is only useful */
  /*    if you want to be able to use the unpatented hinter with           */
  /*    fonts that do *not* use these opcodes.                             */
  /*                                                                       */
  /*    Note that this function parses *all* glyph instructions in the     */
  /*    font file, which may be slow.                                      */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face :: A face handle.                                             */
  /*                                                                       */
  /* <Return>                                                              */
  /*    1~if this is a TrueType font that uses one of the patented         */
  /*    opcodes, 0~otherwise.                                              */
  /*                                                                       */
  /* <Note>                                                                */
  /*    Since May 2010, TrueType hinting is no longer patented.            */
  /*                                                                       */
  /* <Since>                                                               */
  /*    2.3.5                                                              */
  /*                                                                       */
  FT_EXPORT( FT_Bool )
  FT_Face_CheckTrueTypePatents( FT_Face  face );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Face_SetUnpatentedHinting                                       */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Enable or disable the unpatented hinter for a given face.          */
  /*    Only enable it if you have determined that the face doesn't        */
  /*    use any patented opcodes (see @FT_Face_CheckTrueTypePatents).      */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face  :: A face handle.                                            */
  /*                                                                       */
  /*    value :: New boolean setting.                                      */
  /*                                                                       */
  /* <Return>                                                              */
  /*    The old setting value.  This will always be false if this is not   */
  /*    an SFNT font, or if the unpatented hinter is not compiled in this  */
  /*    instance of the library.                                           */
  /*                                                                       */
  /* <Note>                                                                */
  /*    Since May 2010, TrueType hinting is no longer patented.            */
  /*                                                                       */
  /* <Since>                                                               */
  /*    2.3.5                                                              */
  /*                                                                       */
  FT_EXPORT( FT_Bool )
  FT_Face_SetUnpatentedHinting( FT_Face  face,
                                FT_Bool  value );

  /* */


FT_END_HEADER

#endif /* __FREETYPE_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  ftadvanc.h                                                             */
/*                                                                         */
/*    Quick computation of advance widths (specification only).            */
/*                                                                         */
/*  Copyright 2008 by                                                      */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __FTADVANC_H__
#define __FTADVANC_H__


#include <ft2build.h>
#include FT_FREETYPE_H

#ifdef FREETYPE_H
#error "freetype.h of FreeType 1 has been loaded!"
#error "Please fix the directory search order for header files"
#error "so that freetype.h of FreeType 2 is found first."
#endif


FT_BEGIN_HEADER


  /**************************************************************************
   *
   * @section:
   *   quick_advance
   *
   * @title:
   *   Quick retrieval of advance values
   *
   * @abstract:
   *   Retrieve horizontal and vertical advance values without processing
   *   glyph outlines, if possible.
   *
   * @description:
   *   This section contains functions to quickly extract advance values
   *   without handling glyph outlines, if possible.
   */


  /*************************************************************************/
  /*                                                                       */
  /* <Const>                                                               */
  /*    FT_ADVANCE_FLAG_FAST_ONLY                                          */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A bit-flag to be OR-ed with the `flags' parameter of the           */
  /*    @FT_Get_Advance and @FT_Get_Advances functions.                    */
  /*                                                                       */
  /*    If set, it indicates that you want these functions to fail if the  */
  /*    corresponding hinting mode or font driver doesn't allow for very   */
  /*    quick advance computation.                                         */
  /*                                                                       */
  /*    Typically, glyphs which are either unscaled, unhinted, bitmapped,  */
  /*    or light-hinted can have their advance width computed very         */
  /*    quickly.                                                           */
  /*                                                                       */
  /*    Normal and bytecode hinted modes, which require loading, scaling,  */
  /*    and hinting of the glyph outline, are extremely slow by            */
  /*    comparison.                                                        */
  /*                                                                       */
#define FT_ADVANCE_FLAG_FAST_ONLY  0x20000000UL


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Get_Advance                                                     */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Retrieve the advance value of a given glyph outline in an          */
  /*    @FT_Face.  By default, the unhinted advance is returned in font    */
  /*    units.                                                             */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face       :: The source @FT_Face handle.                          */
  /*                                                                       */
  /*    gindex     :: The glyph index.                                     */
  /*                                                                       */
  /*    load_flags :: A set of bit flags similar to those used when        */
  /*                  calling @FT_Load_Glyph, used to determine what kind  */
  /*                  of advances you need.                                */
  /* <Output>                                                              */
  /*    padvance :: The advance value, in either font units or 16.16       */
  /*                format.                                                */
  /*                                                                       */
  /*                If @FT_LOAD_VERTICAL_LAYOUT is set, this is the        */
  /*                vertical advance corresponding to a vertical layout.   */
  /*                Otherwise, it is the horizontal advance in a           */
  /*                horizontal layout.                                     */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0 means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    This function may fail if you use @FT_ADVANCE_FLAG_FAST_ONLY and   */
  /*    if the corresponding font backend doesn't have a quick way to      */
  /*    retrieve the advances.                                             */
  /*                                                                       */
  /*    A scaled advance is returned in 16.16 format but isn't transformed */
  /*    by the affine transformation specified by @FT_Set_Transform.       */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Get_Advance( FT_Face    face,
                  FT_UInt    gindex,
                  FT_Int32   load_flags,
                  FT_Fixed  *padvance );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Get_Advances                                                    */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Retrieve the advance values of several glyph outlines in an        */
  /*    @FT_Face.  By default, the unhinted advances are returned in font  */
  /*    units.                                                             */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face        :: The source @FT_Face handle.                         */
  /*                                                                       */
  /*    start       :: The first glyph index.                              */
  /*                                                                       */
  /*    count       :: The number of advance values you want to retrieve.  */
  /*                                                                       */
  /*    load_flags  :: A set of bit flags similar to those used when       */
  /*                   calling @FT_Load_Glyph.                             */
  /*                                                                       */
  /* <Output>                                                              */
  /*    padvance :: The advances, in either font units or 16.16 format.    */
  /*                This array must contain at least `count' elements.     */
  /*                                                                       */
  /*                If @FT_LOAD_VERTICAL_LAYOUT is set, these are the      */
  /*                vertical advances corresponding to a vertical layout.  */
  /*                Otherwise, they are the horizontal advances in a       */
  /*                horizontal layout.                                     */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0 means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    This function may fail if you use @FT_ADVANCE_FLAG_FAST_ONLY and   */
  /*    if the corresponding font backend doesn't have a quick way to      */
  /*    retrieve the advances.                                             */
  /*                                                                       */
  /*    Scaled advances are returned in 16.16 format but aren't            */
  /*    transformed by the affine transformation specified by              */
  /*    @FT_Set_Transform.                                                 */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Get_Advances( FT_Face    face,
                   FT_UInt    start,
                   FT_UInt    count,
                   FT_Int32   load_flags,
                   FT_Fixed  *padvances );

/* */


FT_END_HEADER

#endif /* __FTADVANC_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  ftbbox.h                                                               */
/*                                                                         */
/*    FreeType exact bbox computation (specification).                     */
/*                                                                         */
/*  Copyright 1996-2001, 2003, 2007 by                                     */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* This component has a _single_ role: to compute exact outline bounding */
  /* boxes.                                                                */
  /*                                                                       */
  /* It is separated from the rest of the engine for various technical     */
  /* reasons.  It may well be integrated in `ftoutln' later.               */
  /*                                                                       */
  /*************************************************************************/


#ifndef __FTBBOX_H__
#define __FTBBOX_H__


#include <ft2build.h>
#include FT_FREETYPE_H

#ifdef FREETYPE_H
#error "freetype.h of FreeType 1 has been loaded!"
#error "Please fix the directory search order for header files"
#error "so that freetype.h of FreeType 2 is found first."
#endif


FT_BEGIN_HEADER


  /*************************************************************************/
  /*                                                                       */
  /* <Section>                                                             */
  /*    outline_processing                                                 */
  /*                                                                       */
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Outline_Get_BBox                                                */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Compute the exact bounding box of an outline.  This is slower      */
  /*    than computing the control box.  However, it uses an advanced      */
  /*    algorithm which returns _very_ quickly when the two boxes          */
  /*    coincide.  Otherwise, the outline BÃ©zier arcs are traversed to     */
  /*    extract their extrema.                                             */
  /*                                                                       */
  /* <Input>                                                               */
  /*    outline :: A pointer to the source outline.                        */
  /*                                                                       */
  /* <Output>                                                              */
  /*    abbox   :: The outline's exact bounding box.                       */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Outline_Get_BBox( FT_Outline*  outline,
                       FT_BBox     *abbox );


  /* */


FT_END_HEADER

#endif /* __FTBBOX_H__ */


/* END */


/* Local Variables: */
/* coding: utf-8    */
/* End:             */
/***************************************************************************/
/*                                                                         */
/*  ftbdf.h                                                                */
/*                                                                         */
/*    FreeType API for accessing BDF-specific strings (specification).     */
/*                                                                         */
/*  Copyright 2002, 2003, 2004, 2006, 2009 by                              */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __FTBDF_H__
#define __FTBDF_H__

#include <ft2build.h>
#include FT_FREETYPE_H

#ifdef FREETYPE_H
#error "freetype.h of FreeType 1 has been loaded!"
#error "Please fix the directory search order for header files"
#error "so that freetype.h of FreeType 2 is found first."
#endif


FT_BEGIN_HEADER


  /*************************************************************************/
  /*                                                                       */
  /* <Section>                                                             */
  /*    bdf_fonts                                                          */
  /*                                                                       */
  /* <Title>                                                               */
  /*    BDF and PCF Files                                                  */
  /*                                                                       */
  /* <Abstract>                                                            */
  /*    BDF and PCF specific API.                                          */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This section contains the declaration of functions specific to BDF */
  /*    and PCF fonts.                                                     */
  /*                                                                       */
  /*************************************************************************/


  /**********************************************************************
   *
   * @enum:
   *    FT_PropertyType
   *
   * @description:
   *    A list of BDF property types.
   *
   * @values:
   *    BDF_PROPERTY_TYPE_NONE ::
   *      Value~0 is used to indicate a missing property.
   *
   *    BDF_PROPERTY_TYPE_ATOM ::
   *      Property is a string atom.
   *
   *    BDF_PROPERTY_TYPE_INTEGER ::
   *      Property is a 32-bit signed integer.
   *
   *    BDF_PROPERTY_TYPE_CARDINAL ::
   *      Property is a 32-bit unsigned integer.
   */
  typedef enum  BDF_PropertyType_
  {
    BDF_PROPERTY_TYPE_NONE     = 0,
    BDF_PROPERTY_TYPE_ATOM     = 1,
    BDF_PROPERTY_TYPE_INTEGER  = 2,
    BDF_PROPERTY_TYPE_CARDINAL = 3

  } BDF_PropertyType;


  /**********************************************************************
   *
   * @type:
   *    BDF_Property
   *
   * @description:
   *    A handle to a @BDF_PropertyRec structure to model a given
   *    BDF/PCF property.
   */
  typedef struct BDF_PropertyRec_*  BDF_Property;


 /**********************************************************************
  *
  * @struct:
  *    BDF_PropertyRec
  *
  * @description:
  *    This structure models a given BDF/PCF property.
  *
  * @fields:
  *    type ::
  *      The property type.
  *
  *    u.atom ::
  *      The atom string, if type is @BDF_PROPERTY_TYPE_ATOM.
  *
  *    u.integer ::
  *      A signed integer, if type is @BDF_PROPERTY_TYPE_INTEGER.
  *
  *    u.cardinal ::
  *      An unsigned integer, if type is @BDF_PROPERTY_TYPE_CARDINAL.
  */
  typedef struct  BDF_PropertyRec_
  {
    BDF_PropertyType  type;
    union {
      const char*     atom;
      FT_Int32        integer;
      FT_UInt32       cardinal;

    } u;

  } BDF_PropertyRec;


 /**********************************************************************
  *
  * @function:
  *    FT_Get_BDF_Charset_ID
  *
  * @description:
  *    Retrieve a BDF font character set identity, according to
  *    the BDF specification.
  *
  * @input:
  *    face ::
  *       A handle to the input face.
  *
  * @output:
  *    acharset_encoding ::
  *       Charset encoding, as a C~string, owned by the face.
  *
  *    acharset_registry ::
  *       Charset registry, as a C~string, owned by the face.
  *
  * @return:
  *   FreeType error code.  0~means success.
  *
  * @note:
  *   This function only works with BDF faces, returning an error otherwise.
  */
  FT_EXPORT( FT_Error )
  FT_Get_BDF_Charset_ID( FT_Face       face,
                         const char*  *acharset_encoding,
                         const char*  *acharset_registry );


 /**********************************************************************
  *
  * @function:
  *    FT_Get_BDF_Property
  *
  * @description:
  *    Retrieve a BDF property from a BDF or PCF font file.
  *
  * @input:
  *    face :: A handle to the input face.
  *
  *    name :: The property name.
  *
  * @output:
  *    aproperty :: The property.
  *
  * @return:
  *   FreeType error code.  0~means success.
  *
  * @note:
  *   This function works with BDF _and_ PCF fonts.  It returns an error
  *   otherwise.  It also returns an error if the property is not in the
  *   font.
  *
  *   A `property' is a either key-value pair within the STARTPROPERTIES
  *   ... ENDPROPERTIES block of a BDF font or a key-value pair from the
  *   `info->props' array within a `FontRec' structure of a PCF font.
  *
  *   Integer properties are always stored as `signed' within PCF fonts;
  *   consequently, @BDF_PROPERTY_TYPE_CARDINAL is a possible return value
  *   for BDF fonts only.
  *
  *   In case of error, `aproperty->type' is always set to
  *   @BDF_PROPERTY_TYPE_NONE.
  */
  FT_EXPORT( FT_Error )
  FT_Get_BDF_Property( FT_Face           face,
                       const char*       prop_name,
                       BDF_PropertyRec  *aproperty );

 /* */

FT_END_HEADER

#endif /* __FTBDF_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  ftbitmap.h                                                             */
/*                                                                         */
/*    FreeType utility functions for bitmaps (specification).              */
/*                                                                         */
/*  Copyright 2004, 2005, 2006, 2008 by                                    */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __FTBITMAP_H__
#define __FTBITMAP_H__


#include <ft2build.h>
#include FT_FREETYPE_H

#ifdef FREETYPE_H
#error "freetype.h of FreeType 1 has been loaded!"
#error "Please fix the directory search order for header files"
#error "so that freetype.h of FreeType 2 is found first."
#endif


FT_BEGIN_HEADER


  /*************************************************************************/
  /*                                                                       */
  /* <Section>                                                             */
  /*    bitmap_handling                                                    */
  /*                                                                       */
  /* <Title>                                                               */
  /*    Bitmap Handling                                                    */
  /*                                                                       */
  /* <Abstract>                                                            */
  /*    Handling FT_Bitmap objects.                                        */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This section contains functions for converting FT_Bitmap objects.  */
  /*                                                                       */
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Bitmap_New                                                      */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Initialize a pointer to an @FT_Bitmap structure.                   */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    abitmap :: A pointer to the bitmap structure.                      */
  /*                                                                       */
  FT_EXPORT( void )
  FT_Bitmap_New( FT_Bitmap  *abitmap );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Bitmap_Copy                                                     */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Copy a bitmap into another one.                                    */
  /*                                                                       */
  /* <Input>                                                               */
  /*    library :: A handle to a library object.                           */
  /*                                                                       */
  /*    source  :: A handle to the source bitmap.                          */
  /*                                                                       */
  /* <Output>                                                              */
  /*    target  :: A handle to the target bitmap.                          */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Bitmap_Copy( FT_Library        library,
                  const FT_Bitmap  *source,
                  FT_Bitmap        *target);


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Bitmap_Embolden                                                 */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Embolden a bitmap.  The new bitmap will be about `xStrength'       */
  /*    pixels wider and `yStrength' pixels higher.  The left and bottom   */
  /*    borders are kept unchanged.                                        */
  /*                                                                       */
  /* <Input>                                                               */
  /*    library   :: A handle to a library object.                         */
  /*                                                                       */
  /*    xStrength :: How strong the glyph is emboldened horizontally.      */
  /*                 Expressed in 26.6 pixel format.                       */
  /*                                                                       */
  /*    yStrength :: How strong the glyph is emboldened vertically.        */
  /*                 Expressed in 26.6 pixel format.                       */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    bitmap    :: A handle to the target bitmap.                        */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The current implementation restricts `xStrength' to be less than   */
  /*    or equal to~8 if bitmap is of pixel_mode @FT_PIXEL_MODE_MONO.      */
  /*                                                                       */
  /*    If you want to embolden the bitmap owned by a @FT_GlyphSlotRec,    */
  /*    you should call @FT_GlyphSlot_Own_Bitmap on the slot first.        */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Bitmap_Embolden( FT_Library  library,
                      FT_Bitmap*  bitmap,
                      FT_Pos      xStrength,
                      FT_Pos      yStrength );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Bitmap_Convert                                                  */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Convert a bitmap object with depth 1bpp, 2bpp, 4bpp, or 8bpp to a  */
  /*    bitmap object with depth 8bpp, making the number of used bytes per */
  /*    line (a.k.a. the `pitch') a multiple of `alignment'.               */
  /*                                                                       */
  /* <Input>                                                               */
  /*    library   :: A handle to a library object.                         */
  /*                                                                       */
  /*    source    :: The source bitmap.                                    */
  /*                                                                       */
  /*    alignment :: The pitch of the bitmap is a multiple of this         */
  /*                 parameter.  Common values are 1, 2, or 4.             */
  /*                                                                       */
  /* <Output>                                                              */
  /*    target    :: The target bitmap.                                    */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    It is possible to call @FT_Bitmap_Convert multiple times without   */
  /*    calling @FT_Bitmap_Done (the memory is simply reallocated).        */
  /*                                                                       */
  /*    Use @FT_Bitmap_Done to finally remove the bitmap object.           */
  /*                                                                       */
  /*    The `library' argument is taken to have access to FreeType's       */
  /*    memory handling functions.                                         */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Bitmap_Convert( FT_Library        library,
                     const FT_Bitmap  *source,
                     FT_Bitmap        *target,
                     FT_Int            alignment );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_GlyphSlot_Own_Bitmap                                            */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Make sure that a glyph slot owns `slot->bitmap'.                   */
  /*                                                                       */
  /* <Input>                                                               */
  /*    slot :: The glyph slot.                                            */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    This function is to be used in combination with                    */
  /*    @FT_Bitmap_Embolden.                                               */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_GlyphSlot_Own_Bitmap( FT_GlyphSlot  slot );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Bitmap_Done                                                     */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Destroy a bitmap object created with @FT_Bitmap_New.               */
  /*                                                                       */
  /* <Input>                                                               */
  /*    library :: A handle to a library object.                           */
  /*                                                                       */
  /*    bitmap  :: The bitmap object to be freed.                          */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The `library' argument is taken to have access to FreeType's       */
  /*    memory handling functions.                                         */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Bitmap_Done( FT_Library  library,
                  FT_Bitmap  *bitmap );


  /* */


FT_END_HEADER

#endif /* __FTBITMAP_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  ftcache.h                                                              */
/*                                                                         */
/*    FreeType Cache subsystem (specification).                            */
/*                                                                         */
/*  Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2010 by */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __FTCACHE_H__
#define __FTCACHE_H__


#include <ft2build.h>
#include FT_GLYPH_H


FT_BEGIN_HEADER


  /*************************************************************************
   *
   * <Section>
   *    cache_subsystem
   *
   * <Title>
   *    Cache Sub-System
   *
   * <Abstract>
   *    How to cache face, size, and glyph data with FreeType~2.
   *
   * <Description>
   *   This section describes the FreeType~2 cache sub-system, which is used
   *   to limit the number of concurrently opened @FT_Face and @FT_Size
   *   objects, as well as caching information like character maps and glyph
   *   images while limiting their maximum memory usage.
   *
   *   Note that all types and functions begin with the `FTC_' prefix.
   *
   *   The cache is highly portable and thus doesn't know anything about the
   *   fonts installed on your system, or how to access them.  This implies
   *   the following scheme:
   *
   *   First, available or installed font faces are uniquely identified by
   *   @FTC_FaceID values, provided to the cache by the client.  Note that
   *   the cache only stores and compares these values, and doesn't try to
   *   interpret them in any way.
   *
   *   Second, the cache calls, only when needed, a client-provided function
   *   to convert an @FTC_FaceID into a new @FT_Face object.  The latter is
   *   then completely managed by the cache, including its termination
   *   through @FT_Done_Face.  To monitor termination of face objects, the
   *   finalizer callback in the `generic' field of the @FT_Face object can
   *   be used, which might also be used to store the @FTC_FaceID of the
   *   face.
   *
   *   Clients are free to map face IDs to anything else.  The most simple
   *   usage is to associate them to a (pathname,face_index) pair that is
   *   used to call @FT_New_Face.  However, more complex schemes are also
   *   possible.
   *
   *   Note that for the cache to work correctly, the face ID values must be
   *   *persistent*, which means that the contents they point to should not
   *   change at runtime, or that their value should not become invalid.
   *
   *   If this is unavoidable (e.g., when a font is uninstalled at runtime),
   *   you should call @FTC_Manager_RemoveFaceID as soon as possible, to let
   *   the cache get rid of any references to the old @FTC_FaceID it may
   *   keep internally.  Failure to do so will lead to incorrect behaviour
   *   or even crashes.
   *
   *   To use the cache, start with calling @FTC_Manager_New to create a new
   *   @FTC_Manager object, which models a single cache instance.  You can
   *   then look up @FT_Face and @FT_Size objects with
   *   @FTC_Manager_LookupFace and @FTC_Manager_LookupSize, respectively.
   *
   *   If you want to use the charmap caching, call @FTC_CMapCache_New, then
   *   later use @FTC_CMapCache_Lookup to perform the equivalent of
   *   @FT_Get_Char_Index, only much faster.
   *
   *   If you want to use the @FT_Glyph caching, call @FTC_ImageCache, then
   *   later use @FTC_ImageCache_Lookup to retrieve the corresponding
   *   @FT_Glyph objects from the cache.
   *
   *   If you need lots of small bitmaps, it is much more memory efficient
   *   to call @FTC_SBitCache_New followed by @FTC_SBitCache_Lookup.  This
   *   returns @FTC_SBitRec structures, which are used to store small
   *   bitmaps directly.  (A small bitmap is one whose metrics and
   *   dimensions all fit into 8-bit integers).
   *
   *   We hope to also provide a kerning cache in the near future.
   *
   *
   * <Order>
   *   FTC_Manager
   *   FTC_FaceID
   *   FTC_Face_Requester
   *
   *   FTC_Manager_New
   *   FTC_Manager_Reset
   *   FTC_Manager_Done
   *   FTC_Manager_LookupFace
   *   FTC_Manager_LookupSize
   *   FTC_Manager_RemoveFaceID
   *
   *   FTC_Node
   *   FTC_Node_Unref
   *
   *   FTC_ImageCache
   *   FTC_ImageCache_New
   *   FTC_ImageCache_Lookup
   *
   *   FTC_SBit
   *   FTC_SBitCache
   *   FTC_SBitCache_New
   *   FTC_SBitCache_Lookup
   *
   *   FTC_CMapCache
   *   FTC_CMapCache_New
   *   FTC_CMapCache_Lookup
   *
   *************************************************************************/


  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****                    BASIC TYPE DEFINITIONS                     *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/


  /*************************************************************************
   *
   * @type: FTC_FaceID
   *
   * @description:
   *   An opaque pointer type that is used to identity face objects.  The
   *   contents of such objects is application-dependent.
   *
   *   These pointers are typically used to point to a user-defined
   *   structure containing a font file path, and face index.
   *
   * @note:
   *   Never use NULL as a valid @FTC_FaceID.
   *
   *   Face IDs are passed by the client to the cache manager, which calls,
   *   when needed, the @FTC_Face_Requester to translate them into new
   *   @FT_Face objects.
   *
   *   If the content of a given face ID changes at runtime, or if the value
   *   becomes invalid (e.g., when uninstalling a font), you should
   *   immediately call @FTC_Manager_RemoveFaceID before any other cache
   *   function.
   *
   *   Failure to do so will result in incorrect behaviour or even
   *   memory leaks and crashes.
   */
  typedef FT_Pointer  FTC_FaceID;


  /************************************************************************
   *
   * @functype:
   *   FTC_Face_Requester
   *
   * @description:
   *   A callback function provided by client applications.  It is used by
   *   the cache manager to translate a given @FTC_FaceID into a new valid
   *   @FT_Face object, on demand.
   *
   * <Input>
   *   face_id ::
   *     The face ID to resolve.
   *
   *   library ::
   *     A handle to a FreeType library object.
   *
   *   req_data ::
   *     Application-provided request data (see note below).
   *
   * <Output>
   *   aface ::
   *     A new @FT_Face handle.
   *
   * <Return>
   *   FreeType error code.  0~means success.
   *
   * <Note>
   *   The third parameter `req_data' is the same as the one passed by the
   *   client when @FTC_Manager_New is called.
   *
   *   The face requester should not perform funny things on the returned
   *   face object, like creating a new @FT_Size for it, or setting a
   *   transformation through @FT_Set_Transform!
   */
  typedef FT_Error
  (*FTC_Face_Requester)( FTC_FaceID  face_id,
                         FT_Library  library,
                         FT_Pointer  request_data,
                         FT_Face*    aface );

 /* */

#define FT_POINTER_TO_ULONG( p )  ( (FT_ULong)(FT_Pointer)(p) )

#define FTC_FACE_ID_HASH( i )                                \
          ((FT_UInt32)(( FT_POINTER_TO_ULONG( i ) >> 3 ) ^   \
                       ( FT_POINTER_TO_ULONG( i ) << 7 ) ) )


  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****                      CACHE MANAGER OBJECT                     *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FTC_Manager                                                        */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This object corresponds to one instance of the cache-subsystem.    */
  /*    It is used to cache one or more @FT_Face objects, along with       */
  /*    corresponding @FT_Size objects.                                    */
  /*                                                                       */
  /*    The manager intentionally limits the total number of opened        */
  /*    @FT_Face and @FT_Size objects to control memory usage.  See the    */
  /*    `max_faces' and `max_sizes' parameters of @FTC_Manager_New.        */
  /*                                                                       */
  /*    The manager is also used to cache `nodes' of various types while   */
  /*    limiting their total memory usage.                                 */
  /*                                                                       */
  /*    All limitations are enforced by keeping lists of managed objects   */
  /*    in most-recently-used order, and flushing old nodes to make room   */
  /*    for new ones.                                                      */
  /*                                                                       */
  typedef struct FTC_ManagerRec_*  FTC_Manager;


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FTC_Node                                                           */
  /*                                                                       */
  /* <Description>                                                         */
  /*    An opaque handle to a cache node object.  Each cache node is       */
  /*    reference-counted.  A node with a count of~0 might be flushed      */
  /*    out of a full cache whenever a lookup request is performed.        */
  /*                                                                       */
  /*    If you look up nodes, you have the ability to `acquire' them,      */
  /*    i.e., to increment their reference count.  This will prevent the   */
  /*    node from being flushed out of the cache until you explicitly      */
  /*    `release' it (see @FTC_Node_Unref).                                */
  /*                                                                       */
  /*    See also @FTC_SBitCache_Lookup and @FTC_ImageCache_Lookup.         */
  /*                                                                       */
  typedef struct FTC_NodeRec_*  FTC_Node;


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FTC_Manager_New                                                    */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Create a new cache manager.                                        */
  /*                                                                       */
  /* <Input>                                                               */
  /*    library   :: The parent FreeType library handle to use.            */
  /*                                                                       */
  /*    max_faces :: Maximum number of opened @FT_Face objects managed by  */
  /*                 this cache instance.  Use~0 for defaults.             */
  /*                                                                       */
  /*    max_sizes :: Maximum number of opened @FT_Size objects managed by  */
  /*                 this cache instance.  Use~0 for defaults.             */
  /*                                                                       */
  /*    max_bytes :: Maximum number of bytes to use for cached data nodes. */
  /*                 Use~0 for defaults.  Note that this value does not    */
  /*                 account for managed @FT_Face and @FT_Size objects.    */
  /*                                                                       */
  /*    requester :: An application-provided callback used to translate    */
  /*                 face IDs into real @FT_Face objects.                  */
  /*                                                                       */
  /*    req_data  :: A generic pointer that is passed to the requester     */
  /*                 each time it is called (see @FTC_Face_Requester).     */
  /*                                                                       */
  /* <Output>                                                              */
  /*    amanager  :: A handle to a new manager object.  0~in case of       */
  /*                 failure.                                              */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FTC_Manager_New( FT_Library          library,
                   FT_UInt             max_faces,
                   FT_UInt             max_sizes,
                   FT_ULong            max_bytes,
                   FTC_Face_Requester  requester,
                   FT_Pointer          req_data,
                   FTC_Manager        *amanager );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FTC_Manager_Reset                                                  */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Empty a given cache manager.  This simply gets rid of all the      */
  /*    currently cached @FT_Face and @FT_Size objects within the manager. */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    manager :: A handle to the manager.                                */
  /*                                                                       */
  FT_EXPORT( void )
  FTC_Manager_Reset( FTC_Manager  manager );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FTC_Manager_Done                                                   */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Destroy a given manager after emptying it.                         */
  /*                                                                       */
  /* <Input>                                                               */
  /*    manager :: A handle to the target cache manager object.            */
  /*                                                                       */
  FT_EXPORT( void )
  FTC_Manager_Done( FTC_Manager  manager );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FTC_Manager_LookupFace                                             */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Retrieve the @FT_Face object that corresponds to a given face ID   */
  /*    through a cache manager.                                           */
  /*                                                                       */
  /* <Input>                                                               */
  /*    manager :: A handle to the cache manager.                          */
  /*                                                                       */
  /*    face_id :: The ID of the face object.                              */
  /*                                                                       */
  /* <Output>                                                              */
  /*    aface   :: A handle to the face object.                            */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The returned @FT_Face object is always owned by the manager.  You  */
  /*    should never try to discard it yourself.                           */
  /*                                                                       */
  /*    The @FT_Face object doesn't necessarily have a current size object */
  /*    (i.e., face->size can be 0).  If you need a specific `font size',  */
  /*    use @FTC_Manager_LookupSize instead.                               */
  /*                                                                       */
  /*    Never change the face's transformation matrix (i.e., never call    */
  /*    the @FT_Set_Transform function) on a returned face!  If you need   */
  /*    to transform glyphs, do it yourself after glyph loading.           */
  /*                                                                       */
  /*    When you perform a lookup, out-of-memory errors are detected       */
  /*    _within_ the lookup and force incremental flushes of the cache     */
  /*    until enough memory is released for the lookup to succeed.         */
  /*                                                                       */
  /*    If a lookup fails with `FT_Err_Out_Of_Memory' the cache has        */
  /*    already been completely flushed, and still no memory was available */
  /*    for the operation.                                                 */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FTC_Manager_LookupFace( FTC_Manager  manager,
                          FTC_FaceID   face_id,
                          FT_Face     *aface );


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FTC_ScalerRec                                                      */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A structure used to describe a given character size in either      */
  /*    pixels or points to the cache manager.  See                        */
  /*    @FTC_Manager_LookupSize.                                           */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    face_id :: The source face ID.                                     */
  /*                                                                       */
  /*    width   :: The character width.                                    */
  /*                                                                       */
  /*    height  :: The character height.                                   */
  /*                                                                       */
  /*    pixel   :: A Boolean.  If 1, the `width' and `height' fields are   */
  /*               interpreted as integer pixel character sizes.           */
  /*               Otherwise, they are expressed as 1/64th of points.      */
  /*                                                                       */
  /*    x_res   :: Only used when `pixel' is value~0 to indicate the       */
  /*               horizontal resolution in dpi.                           */
  /*                                                                       */
  /*    y_res   :: Only used when `pixel' is value~0 to indicate the       */
  /*               vertical resolution in dpi.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    This type is mainly used to retrieve @FT_Size objects through the  */
  /*    cache manager.                                                     */
  /*                                                                       */
  typedef struct  FTC_ScalerRec_
  {
    FTC_FaceID  face_id;
    FT_UInt     width;
    FT_UInt     height;
    FT_Int      pixel;
    FT_UInt     x_res;
    FT_UInt     y_res;

  } FTC_ScalerRec;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FTC_Scaler                                                         */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A handle to an @FTC_ScalerRec structure.                           */
  /*                                                                       */
  typedef struct FTC_ScalerRec_*  FTC_Scaler;


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FTC_Manager_LookupSize                                             */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Retrieve the @FT_Size object that corresponds to a given           */
  /*    @FTC_ScalerRec pointer through a cache manager.                    */
  /*                                                                       */
  /* <Input>                                                               */
  /*    manager :: A handle to the cache manager.                          */
  /*                                                                       */
  /*    scaler  :: A scaler handle.                                        */
  /*                                                                       */
  /* <Output>                                                              */
  /*    asize   :: A handle to the size object.                            */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The returned @FT_Size object is always owned by the manager.  You  */
  /*    should never try to discard it by yourself.                        */
  /*                                                                       */
  /*    You can access the parent @FT_Face object simply as `size->face'   */
  /*    if you need it.  Note that this object is also owned by the        */
  /*    manager.                                                           */
  /*                                                                       */
  /* <Note>                                                                */
  /*    When you perform a lookup, out-of-memory errors are detected       */
  /*    _within_ the lookup and force incremental flushes of the cache     */
  /*    until enough memory is released for the lookup to succeed.         */
  /*                                                                       */
  /*    If a lookup fails with `FT_Err_Out_Of_Memory' the cache has        */
  /*    already been completely flushed, and still no memory is available  */
  /*    for the operation.                                                 */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FTC_Manager_LookupSize( FTC_Manager  manager,
                          FTC_Scaler   scaler,
                          FT_Size     *asize );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FTC_Node_Unref                                                     */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Decrement a cache node's internal reference count.  When the count */
  /*    reaches 0, it is not destroyed but becomes eligible for subsequent */
  /*    cache flushes.                                                     */
  /*                                                                       */
  /* <Input>                                                               */
  /*    node    :: The cache node handle.                                  */
  /*                                                                       */
  /*    manager :: The cache manager handle.                               */
  /*                                                                       */
  FT_EXPORT( void )
  FTC_Node_Unref( FTC_Node     node,
                  FTC_Manager  manager );


  /*************************************************************************
   *
   * @function:
   *   FTC_Manager_RemoveFaceID
   *
   * @description:
   *   A special function used to indicate to the cache manager that
   *   a given @FTC_FaceID is no longer valid, either because its
   *   content changed, or because it was deallocated or uninstalled.
   *
   * @input:
   *   manager ::
   *     The cache manager handle.
   *
   *   face_id ::
   *     The @FTC_FaceID to be removed.
   *
   * @note:
   *   This function flushes all nodes from the cache corresponding to this
   *   `face_id', with the exception of nodes with a non-null reference
   *   count.
   *
   *   Such nodes are however modified internally so as to never appear
   *   in later lookups with the same `face_id' value, and to be immediately
   *   destroyed when released by all their users.
   *
   */
  FT_EXPORT( void )
  FTC_Manager_RemoveFaceID( FTC_Manager  manager,
                            FTC_FaceID   face_id );


  /*************************************************************************/
  /*                                                                       */
  /* <Section>                                                             */
  /*    cache_subsystem                                                    */
  /*                                                                       */
  /*************************************************************************/

  /*************************************************************************
   *
   * @type:
   *   FTC_CMapCache
   *
   * @description:
   *   An opaque handle used to model a charmap cache.  This cache is to
   *   hold character codes -> glyph indices mappings.
   *
   */
  typedef struct FTC_CMapCacheRec_*  FTC_CMapCache;


  /*************************************************************************
   *
   * @function:
   *   FTC_CMapCache_New
   *
   * @description:
   *   Create a new charmap cache.
   *
   * @input:
   *   manager ::
   *     A handle to the cache manager.
   *
   * @output:
   *   acache ::
   *     A new cache handle.  NULL in case of error.
   *
   * @return:
   *   FreeType error code.  0~means success.
   *
   * @note:
   *   Like all other caches, this one will be destroyed with the cache
   *   manager.
   *
   */
  FT_EXPORT( FT_Error )
  FTC_CMapCache_New( FTC_Manager     manager,
                     FTC_CMapCache  *acache );


  /************************************************************************
   *
   * @function:
   *   FTC_CMapCache_Lookup
   *
   * @description:
   *   Translate a character code into a glyph index, using the charmap
   *   cache.
   *
   * @input:
   *   cache ::
   *     A charmap cache handle.
   *
   *   face_id ::
   *     The source face ID.
   *
   *   cmap_index ::
   *     The index of the charmap in the source face.  Any negative value
   *     means to use the cache @FT_Face's default charmap.
   *
   *   char_code ::
   *     The character code (in the corresponding charmap).
   *
   * @return:
   *    Glyph index.  0~means `no glyph'.
   *
   */
  FT_EXPORT( FT_UInt )
  FTC_CMapCache_Lookup( FTC_CMapCache  cache,
                        FTC_FaceID     face_id,
                        FT_Int         cmap_index,
                        FT_UInt32      char_code );


  /*************************************************************************/
  /*                                                                       */
  /* <Section>                                                             */
  /*    cache_subsystem                                                    */
  /*                                                                       */
  /*************************************************************************/


  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****                       IMAGE CACHE OBJECT                      *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/


  /*************************************************************************
   *
   * @struct:
   *   FTC_ImageTypeRec
   *
   * @description:
   *   A structure used to model the type of images in a glyph cache.
   *
   * @fields:
   *   face_id ::
   *     The face ID.
   *
   *   width ::
   *     The width in pixels.
   *
   *   height ::
   *     The height in pixels.
   *
   *   flags ::
   *     The load flags, as in @FT_Load_Glyph.
   *
   */
  typedef struct  FTC_ImageTypeRec_
  {
    FTC_FaceID  face_id;
    FT_Int      width;
    FT_Int      height;
    FT_Int32    flags;

  } FTC_ImageTypeRec;


  /*************************************************************************
   *
   * @type:
   *   FTC_ImageType
   *
   * @description:
   *   A handle to an @FTC_ImageTypeRec structure.
   *
   */
  typedef struct FTC_ImageTypeRec_*  FTC_ImageType;


  /* */


#define FTC_IMAGE_TYPE_COMPARE( d1, d2 )      \
          ( (d1)->face_id == (d2)->face_id && \
            (d1)->width   == (d2)->width   && \
            (d1)->flags   == (d2)->flags   )

#define FTC_IMAGE_TYPE_HASH( d )                          \
          (FT_UFast)( FTC_FACE_ID_HASH( (d)->face_id )  ^ \
                      ( (d)->width << 8 ) ^ (d)->height ^ \
                      ( (d)->flags << 4 )               )


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FTC_ImageCache                                                     */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A handle to an glyph image cache object.  They are designed to     */
  /*    hold many distinct glyph images while not exceeding a certain      */
  /*    memory threshold.                                                  */
  /*                                                                       */
  typedef struct FTC_ImageCacheRec_*  FTC_ImageCache;


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FTC_ImageCache_New                                                 */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Create a new glyph image cache.                                    */
  /*                                                                       */
  /* <Input>                                                               */
  /*    manager :: The parent manager for the image cache.                 */
  /*                                                                       */
  /* <Output>                                                              */
  /*    acache  :: A handle to the new glyph image cache object.           */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FTC_ImageCache_New( FTC_Manager      manager,
                      FTC_ImageCache  *acache );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FTC_ImageCache_Lookup                                              */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Retrieve a given glyph image from a glyph image cache.             */
  /*                                                                       */
  /* <Input>                                                               */
  /*    cache  :: A handle to the source glyph image cache.                */
  /*                                                                       */
  /*    type   :: A pointer to a glyph image type descriptor.              */
  /*                                                                       */
  /*    gindex :: The glyph index to retrieve.                             */
  /*                                                                       */
  /* <Output>                                                              */
  /*    aglyph :: The corresponding @FT_Glyph object.  0~in case of        */
  /*              failure.                                                 */
  /*                                                                       */
  /*    anode  :: Used to return the address of of the corresponding cache */
  /*              node after incrementing its reference count (see note    */
  /*              below).                                                  */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The returned glyph is owned and managed by the glyph image cache.  */
  /*    Never try to transform or discard it manually!  You can however    */
  /*    create a copy with @FT_Glyph_Copy and modify the new one.          */
  /*                                                                       */
  /*    If `anode' is _not_ NULL, it receives the address of the cache     */
  /*    node containing the glyph image, after increasing its reference    */
  /*    count.  This ensures that the node (as well as the @FT_Glyph) will */
  /*    always be kept in the cache until you call @FTC_Node_Unref to      */
  /*    `release' it.                                                      */
  /*                                                                       */
  /*    If `anode' is NULL, the cache node is left unchanged, which means  */
  /*    that the @FT_Glyph could be flushed out of the cache on the next   */
  /*    call to one of the caching sub-system APIs.  Don't assume that it  */
  /*    is persistent!                                                     */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FTC_ImageCache_Lookup( FTC_ImageCache  cache,
                         FTC_ImageType   type,
                         FT_UInt         gindex,
                         FT_Glyph       *aglyph,
                         FTC_Node       *anode );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FTC_ImageCache_LookupScaler                                        */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A variant of @FTC_ImageCache_Lookup that uses an @FTC_ScalerRec    */
  /*    to specify the face ID and its size.                               */
  /*                                                                       */
  /* <Input>                                                               */
  /*    cache      :: A handle to the source glyph image cache.            */
  /*                                                                       */
  /*    scaler     :: A pointer to a scaler descriptor.                    */
  /*                                                                       */
  /*    load_flags :: The corresponding load flags.                        */
  /*                                                                       */
  /*    gindex     :: The glyph index to retrieve.                         */
  /*                                                                       */
  /* <Output>                                                              */
  /*    aglyph     :: The corresponding @FT_Glyph object.  0~in case of    */
  /*                  failure.                                             */
  /*                                                                       */
  /*    anode      :: Used to return the address of of the corresponding   */
  /*                  cache node after incrementing its reference count    */
  /*                  (see note below).                                    */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The returned glyph is owned and managed by the glyph image cache.  */
  /*    Never try to transform or discard it manually!  You can however    */
  /*    create a copy with @FT_Glyph_Copy and modify the new one.          */
  /*                                                                       */
  /*    If `anode' is _not_ NULL, it receives the address of the cache     */
  /*    node containing the glyph image, after increasing its reference    */
  /*    count.  This ensures that the node (as well as the @FT_Glyph) will */
  /*    always be kept in the cache until you call @FTC_Node_Unref to      */
  /*    `release' it.                                                      */
  /*                                                                       */
  /*    If `anode' is NULL, the cache node is left unchanged, which means  */
  /*    that the @FT_Glyph could be flushed out of the cache on the next   */
  /*    call to one of the caching sub-system APIs.  Don't assume that it  */
  /*    is persistent!                                                     */
  /*                                                                       */
  /*    Calls to @FT_Set_Char_Size and friends have no effect on cached    */
  /*    glyphs; you should always use the FreeType cache API instead.      */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FTC_ImageCache_LookupScaler( FTC_ImageCache  cache,
                               FTC_Scaler      scaler,
                               FT_ULong        load_flags,
                               FT_UInt         gindex,
                               FT_Glyph       *aglyph,
                               FTC_Node       *anode );


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FTC_SBit                                                           */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A handle to a small bitmap descriptor.  See the @FTC_SBitRec       */
  /*    structure for details.                                             */
  /*                                                                       */
  typedef struct FTC_SBitRec_*  FTC_SBit;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FTC_SBitRec                                                        */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A very compact structure used to describe a small glyph bitmap.    */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    width     :: The bitmap width in pixels.                           */
  /*                                                                       */
  /*    height    :: The bitmap height in pixels.                          */
  /*                                                                       */
  /*    left      :: The horizontal distance from the pen position to the  */
  /*                 left bitmap border (a.k.a. `left side bearing', or    */
  /*                 `lsb').                                               */
  /*                                                                       */
  /*    top       :: The vertical distance from the pen position (on the   */
  /*                 baseline) to the upper bitmap border (a.k.a. `top     */
  /*                 side bearing').  The distance is positive for upwards */
  /*                 y~coordinates.                                        */
  /*                                                                       */
  /*    format    :: The format of the glyph bitmap (monochrome or gray).  */
  /*                                                                       */
  /*    max_grays :: Maximum gray level value (in the range 1 to~255).     */
  /*                                                                       */
  /*    pitch     :: The number of bytes per bitmap line.  May be positive */
  /*                 or negative.                                          */
  /*                                                                       */
  /*    xadvance  :: The horizontal advance width in pixels.               */
  /*                                                                       */
  /*    yadvance  :: The vertical advance height in pixels.                */
  /*                                                                       */
  /*    buffer    :: A pointer to the bitmap pixels.                       */
  /*                                                                       */
  typedef struct  FTC_SBitRec_
  {
    FT_Byte   width;
    FT_Byte   height;
    FT_Char   left;
    FT_Char   top;

    FT_Byte   format;
    FT_Byte   max_grays;
    FT_Short  pitch;
    FT_Char   xadvance;
    FT_Char   yadvance;

    FT_Byte*  buffer;

  } FTC_SBitRec;


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FTC_SBitCache                                                      */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A handle to a small bitmap cache.  These are special cache objects */
  /*    used to store small glyph bitmaps (and anti-aliased pixmaps) in a  */
  /*    much more efficient way than the traditional glyph image cache     */
  /*    implemented by @FTC_ImageCache.                                    */
  /*                                                                       */
  typedef struct FTC_SBitCacheRec_*  FTC_SBitCache;


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FTC_SBitCache_New                                                  */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Create a new cache to store small glyph bitmaps.                   */
  /*                                                                       */
  /* <Input>                                                               */
  /*    manager :: A handle to the source cache manager.                   */
  /*                                                                       */
  /* <Output>                                                              */
  /*    acache  :: A handle to the new sbit cache.  NULL in case of error. */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FTC_SBitCache_New( FTC_Manager     manager,
                     FTC_SBitCache  *acache );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FTC_SBitCache_Lookup                                               */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Look up a given small glyph bitmap in a given sbit cache and       */
  /*    `lock' it to prevent its flushing from the cache until needed.     */
  /*                                                                       */
  /* <Input>                                                               */
  /*    cache  :: A handle to the source sbit cache.                       */
  /*                                                                       */
  /*    type   :: A pointer to the glyph image type descriptor.            */
  /*                                                                       */
  /*    gindex :: The glyph index.                                         */
  /*                                                                       */
  /* <Output>                                                              */
  /*    sbit   :: A handle to a small bitmap descriptor.                   */
  /*                                                                       */
  /*    anode  :: Used to return the address of of the corresponding cache */
  /*              node after incrementing its reference count (see note    */
  /*              below).                                                  */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The small bitmap descriptor and its bit buffer are owned by the    */
  /*    cache and should never be freed by the application.  They might    */
  /*    as well disappear from memory on the next cache lookup, so don't   */
  /*    treat them as persistent data.                                     */
  /*                                                                       */
  /*    The descriptor's `buffer' field is set to~0 to indicate a missing  */
  /*    glyph bitmap.                                                      */
  /*                                                                       */
  /*    If `anode' is _not_ NULL, it receives the address of the cache     */
  /*    node containing the bitmap, after increasing its reference count.  */
  /*    This ensures that the node (as well as the image) will always be   */
  /*    kept in the cache until you call @FTC_Node_Unref to `release' it.  */
  /*                                                                       */
  /*    If `anode' is NULL, the cache node is left unchanged, which means  */
  /*    that the bitmap could be flushed out of the cache on the next      */
  /*    call to one of the caching sub-system APIs.  Don't assume that it  */
  /*    is persistent!                                                     */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FTC_SBitCache_Lookup( FTC_SBitCache    cache,
                        FTC_ImageType    type,
                        FT_UInt          gindex,
                        FTC_SBit        *sbit,
                        FTC_Node        *anode );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FTC_SBitCache_LookupScaler                                         */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A variant of @FTC_SBitCache_Lookup that uses an @FTC_ScalerRec     */
  /*    to specify the face ID and its size.                               */
  /*                                                                       */
  /* <Input>                                                               */
  /*    cache      :: A handle to the source sbit cache.                   */
  /*                                                                       */
  /*    scaler     :: A pointer to the scaler descriptor.                  */
  /*                                                                       */
  /*    load_flags :: The corresponding load flags.                        */
  /*                                                                       */
  /*    gindex     :: The glyph index.                                     */
  /*                                                                       */
  /* <Output>                                                              */
  /*    sbit       :: A handle to a small bitmap descriptor.               */
  /*                                                                       */
  /*    anode      :: Used to return the address of of the corresponding   */
  /*                  cache node after incrementing its reference count    */
  /*                  (see note below).                                    */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The small bitmap descriptor and its bit buffer are owned by the    */
  /*    cache and should never be freed by the application.  They might    */
  /*    as well disappear from memory on the next cache lookup, so don't   */
  /*    treat them as persistent data.                                     */
  /*                                                                       */
  /*    The descriptor's `buffer' field is set to~0 to indicate a missing  */
  /*    glyph bitmap.                                                      */
  /*                                                                       */
  /*    If `anode' is _not_ NULL, it receives the address of the cache     */
  /*    node containing the bitmap, after increasing its reference count.  */
  /*    This ensures that the node (as well as the image) will always be   */
  /*    kept in the cache until you call @FTC_Node_Unref to `release' it.  */
  /*                                                                       */
  /*    If `anode' is NULL, the cache node is left unchanged, which means  */
  /*    that the bitmap could be flushed out of the cache on the next      */
  /*    call to one of the caching sub-system APIs.  Don't assume that it  */
  /*    is persistent!                                                     */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FTC_SBitCache_LookupScaler( FTC_SBitCache  cache,
                              FTC_Scaler     scaler,
                              FT_ULong       load_flags,
                              FT_UInt        gindex,
                              FTC_SBit      *sbit,
                              FTC_Node      *anode );


 /* */

#ifdef FT_CONFIG_OPTION_OLD_INTERNALS

  /*@***********************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FTC_FontRec                                                        */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A simple structure used to describe a given `font' to the cache    */
  /*    manager.  Note that a `font' is the combination of a given face    */
  /*    with a given character size.                                       */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    face_id    :: The ID of the face to use.                           */
  /*                                                                       */
  /*    pix_width  :: The character width in integer pixels.               */
  /*                                                                       */
  /*    pix_height :: The character height in integer pixels.              */
  /*                                                                       */
  typedef struct  FTC_FontRec_
  {
    FTC_FaceID  face_id;
    FT_UShort   pix_width;
    FT_UShort   pix_height;

  } FTC_FontRec;


  /* */


#define FTC_FONT_COMPARE( f1, f2 )                  \
          ( (f1)->face_id    == (f2)->face_id    && \
            (f1)->pix_width  == (f2)->pix_width  && \
            (f1)->pix_height == (f2)->pix_height )

#define FTC_FONT_HASH( f )                              \
          (FT_UInt32)( FTC_FACE_ID_HASH((f)->face_id) ^ \
                       ((f)->pix_width << 8)          ^ \
                       ((f)->pix_height)              )

  typedef FTC_FontRec*  FTC_Font;


  FT_EXPORT( FT_Error )
  FTC_Manager_Lookup_Face( FTC_Manager  manager,
                           FTC_FaceID   face_id,
                           FT_Face     *aface );

  FT_EXPORT( FT_Error )
  FTC_Manager_Lookup_Size( FTC_Manager  manager,
                           FTC_Font     font,
                           FT_Face     *aface,
                           FT_Size     *asize );

#endif /* FT_CONFIG_OPTION_OLD_INTERNALS */


 /* */

FT_END_HEADER

#endif /* __FTCACHE_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/* This file defines the structure of the FreeType reference.              */
/* It is used by the python script which generates the HTML files.         */
/*                                                                         */
/***************************************************************************/


/***************************************************************************/
/*                                                                         */
/* <Chapter>                                                               */
/*    general_remarks                                                      */
/*                                                                         */
/* <Title>                                                                 */
/*    General Remarks                                                      */
/*                                                                         */
/* <Sections>                                                              */
/*    user_allocation                                                      */
/*                                                                         */
/***************************************************************************/


/***************************************************************************/
/*                                                                         */
/* <Chapter>                                                               */
/*    core_api                                                             */
/*                                                                         */
/* <Title>                                                                 */
/*    Core API                                                             */
/*                                                                         */
/* <Sections>                                                              */
/*    version                                                              */
/*    basic_types                                                          */
/*    base_interface                                                       */
/*    glyph_variants                                                       */
/*    glyph_management                                                     */
/*    mac_specific                                                         */
/*    sizes_management                                                     */
/*    header_file_macros                                                   */
/*                                                                         */
/***************************************************************************/


/***************************************************************************/
/*                                                                         */
/* <Chapter>                                                               */
/*    format_specific                                                      */
/*                                                                         */
/* <Title>                                                                 */
/*    Format-Specific API                                                  */
/*                                                                         */
/* <Sections>                                                              */
/*    multiple_masters                                                     */
/*    truetype_tables                                                      */
/*    type1_tables                                                         */
/*    sfnt_names                                                           */
/*    bdf_fonts                                                            */
/*    cid_fonts                                                            */
/*    pfr_fonts                                                            */
/*    winfnt_fonts                                                         */
/*    font_formats                                                         */
/*    gasp_table                                                           */
/*                                                                         */
/***************************************************************************/


/***************************************************************************/
/*                                                                         */
/* <Chapter>                                                               */
/*    cache_subsystem                                                      */
/*                                                                         */
/* <Title>                                                                 */
/*    Cache Sub-System                                                     */
/*                                                                         */
/* <Sections>                                                              */
/*    cache_subsystem                                                      */
/*                                                                         */
/***************************************************************************/


/***************************************************************************/
/*                                                                         */
/* <Chapter>                                                               */
/*    support_api                                                          */
/*                                                                         */
/* <Title>                                                                 */
/*    Support API                                                          */
/*                                                                         */
/* <Sections>                                                              */
/*    computations                                                         */
/*    list_processing                                                      */
/*    outline_processing                                                   */
/*    quick_advance                                                        */
/*    bitmap_handling                                                      */
/*    raster                                                               */
/*    glyph_stroker                                                        */
/*    system_interface                                                     */
/*    module_management                                                    */
/*    gzip                                                                 */
/*    lzw                                                                  */
/*    lcd_filtering                                                        */
/*                                                                         */
/***************************************************************************/
/***************************************************************************/
/*                                                                         */
/*  ftcid.h                                                                */
/*                                                                         */
/*    FreeType API for accessing CID font information (specification).     */
/*                                                                         */
/*  Copyright 2007, 2009 by Dereg Clegg, Michael Toftdal.                  */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __FTCID_H__
#define __FTCID_H__

#include <ft2build.h>
#include FT_FREETYPE_H

#ifdef FREETYPE_H
#error "freetype.h of FreeType 1 has been loaded!"
#error "Please fix the directory search order for header files"
#error "so that freetype.h of FreeType 2 is found first."
#endif


FT_BEGIN_HEADER


  /*************************************************************************/
  /*                                                                       */
  /* <Section>                                                             */
  /*    cid_fonts                                                          */
  /*                                                                       */
  /* <Title>                                                               */
  /*    CID Fonts                                                          */
  /*                                                                       */
  /* <Abstract>                                                            */
  /*    CID-keyed font specific API.                                       */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This section contains the declaration of CID-keyed font specific   */
  /*    functions.                                                         */
  /*                                                                       */
  /*************************************************************************/


  /**********************************************************************
   *
   * @function:
   *    FT_Get_CID_Registry_Ordering_Supplement
   *
   * @description:
   *    Retrieve the Registry/Ordering/Supplement triple (also known as the
   *    "R/O/S") from a CID-keyed font.
   *
   * @input:
   *    face ::
   *       A handle to the input face.
   *
   * @output:
   *    registry ::
   *       The registry, as a C~string, owned by the face.
   *
   *    ordering ::
   *       The ordering, as a C~string, owned by the face.
   *
   *    supplement ::
   *       The supplement.
   *
   * @return:
   *    FreeType error code.  0~means success.
   *
   * @note:
   *    This function only works with CID faces, returning an error
   *    otherwise.
   *
   * @since:
   *    2.3.6
   */
  FT_EXPORT( FT_Error )
  FT_Get_CID_Registry_Ordering_Supplement( FT_Face       face,
                                           const char*  *registry,
                                           const char*  *ordering,
                                           FT_Int       *supplement);


  /**********************************************************************
   *
   * @function:
   *    FT_Get_CID_Is_Internally_CID_Keyed
   *
   * @description:
   *    Retrieve the type of the input face, CID keyed or not.  In
   *    constrast to the @FT_IS_CID_KEYED macro this function returns
   *    successfully also for CID-keyed fonts in an SNFT wrapper.
   *
   * @input:
   *    face ::
   *       A handle to the input face.
   *
   * @output:
   *    is_cid ::
   *       The type of the face as an @FT_Bool.
   *
   * @return:
   *    FreeType error code.  0~means success.
   *
   * @note:
   *    This function only works with CID faces and OpenType fonts,
   *    returning an error otherwise.
   *
   * @since:
   *    2.3.9
   */
  FT_EXPORT( FT_Error )
  FT_Get_CID_Is_Internally_CID_Keyed( FT_Face   face,
                                      FT_Bool  *is_cid );


  /**********************************************************************
   *
   * @function:
   *    FT_Get_CID_From_Glyph_Index
   *
   * @description:
   *    Retrieve the CID of the input glyph index.
   *
   * @input:
   *    face ::
   *       A handle to the input face.
   *
   *    glyph_index ::
   *       The input glyph index.
   *
   * @output:
   *    cid ::
   *       The CID as an @FT_UInt.
   *
   * @return:
   *    FreeType error code.  0~means success.
   *
   * @note:
   *    This function only works with CID faces and OpenType fonts,
   *    returning an error otherwise.
   *
   * @since:
   *    2.3.9
   */
  FT_EXPORT( FT_Error )
  FT_Get_CID_From_Glyph_Index( FT_Face   face,
                               FT_UInt   glyph_index,
                               FT_UInt  *cid );

 /* */

FT_END_HEADER

#endif /* __FTCID_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  fterrdef.h                                                             */
/*                                                                         */
/*    FreeType error codes (specification).                                */
/*                                                                         */
/*  Copyright 2002, 2004, 2006, 2007, 2010 by                              */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


  /*******************************************************************/
  /*******************************************************************/
  /*****                                                         *****/
  /*****                LIST OF ERROR CODES/MESSAGES             *****/
  /*****                                                         *****/
  /*******************************************************************/
  /*******************************************************************/


  /* You need to define both FT_ERRORDEF_ and FT_NOERRORDEF_ before */
  /* including this file.                                           */


  /* generic errors */

  FT_NOERRORDEF_( Ok,                                        0x00, \
                  "no error" )

  FT_ERRORDEF_( Cannot_Open_Resource,                        0x01, \
                "cannot open resource" )
  FT_ERRORDEF_( Unknown_File_Format,                         0x02, \
                "unknown file format" )
  FT_ERRORDEF_( Invalid_File_Format,                         0x03, \
                "broken file" )
  FT_ERRORDEF_( Invalid_Version,                             0x04, \
                "invalid FreeType version" )
  FT_ERRORDEF_( Lower_Module_Version,                        0x05, \
                "module version is too low" )
  FT_ERRORDEF_( Invalid_Argument,                            0x06, \
                "invalid argument" )
  FT_ERRORDEF_( Unimplemented_Feature,                       0x07, \
                "unimplemented feature" )
  FT_ERRORDEF_( Invalid_Table,                               0x08, \
                "broken table" )
  FT_ERRORDEF_( Invalid_Offset,                              0x09, \
                "broken offset within table" )
  FT_ERRORDEF_( Array_Too_Large,                             0x0A, \
                "array allocation size too large" )

  /* glyph/character errors */

  FT_ERRORDEF_( Invalid_Glyph_Index,                         0x10, \
                "invalid glyph index" )
  FT_ERRORDEF_( Invalid_Character_Code,                      0x11, \
                "invalid character code" )
  FT_ERRORDEF_( Invalid_Glyph_Format,                        0x12, \
                "unsupported glyph image format" )
  FT_ERRORDEF_( Cannot_Render_Glyph,                         0x13, \
                "cannot render this glyph format" )
  FT_ERRORDEF_( Invalid_Outline,                             0x14, \
                "invalid outline" )
  FT_ERRORDEF_( Invalid_Composite,                           0x15, \
                "invalid composite glyph" )
  FT_ERRORDEF_( Too_Many_Hints,                              0x16, \
                "too many hints" )
  FT_ERRORDEF_( Invalid_Pixel_Size,                          0x17, \
                "invalid pixel size" )

  /* handle errors */

  FT_ERRORDEF_( Invalid_Handle,                              0x20, \
                "invalid object handle" )
  FT_ERRORDEF_( Invalid_Library_Handle,                      0x21, \
                "invalid library handle" )
  FT_ERRORDEF_( Invalid_Driver_Handle,                       0x22, \
                "invalid module handle" )
  FT_ERRORDEF_( Invalid_Face_Handle,                         0x23, \
                "invalid face handle" )
  FT_ERRORDEF_( Invalid_Size_Handle,                         0x24, \
                "invalid size handle" )
  FT_ERRORDEF_( Invalid_Slot_Handle,                         0x25, \
                "invalid glyph slot handle" )
  FT_ERRORDEF_( Invalid_CharMap_Handle,                      0x26, \
                "invalid charmap handle" )
  FT_ERRORDEF_( Invalid_Cache_Handle,                        0x27, \
                "invalid cache manager handle" )
  FT_ERRORDEF_( Invalid_Stream_Handle,                       0x28, \
                "invalid stream handle" )

  /* driver errors */

  FT_ERRORDEF_( Too_Many_Drivers,                            0x30, \
                "too many modules" )
  FT_ERRORDEF_( Too_Many_Extensions,                         0x31, \
                "too many extensions" )

  /* memory errors */

  FT_ERRORDEF_( Out_Of_Memory,                               0x40, \
                "out of memory" )
  FT_ERRORDEF_( Unlisted_Object,                             0x41, \
                "unlisted object" )

  /* stream errors */

  FT_ERRORDEF_( Cannot_Open_Stream,                          0x51, \
                "cannot open stream" )
  FT_ERRORDEF_( Invalid_Stream_Seek,                         0x52, \
                "invalid stream seek" )
  FT_ERRORDEF_( Invalid_Stream_Skip,                         0x53, \
                "invalid stream skip" )
  FT_ERRORDEF_( Invalid_Stream_Read,                         0x54, \
                "invalid stream read" )
  FT_ERRORDEF_( Invalid_Stream_Operation,                    0x55, \
                "invalid stream operation" )
  FT_ERRORDEF_( Invalid_Frame_Operation,                     0x56, \
                "invalid frame operation" )
  FT_ERRORDEF_( Nested_Frame_Access,                         0x57, \
                "nested frame access" )
  FT_ERRORDEF_( Invalid_Frame_Read,                          0x58, \
                "invalid frame read" )

  /* raster errors */

  FT_ERRORDEF_( Raster_Uninitialized,                        0x60, \
                "raster uninitialized" )
  FT_ERRORDEF_( Raster_Corrupted,                            0x61, \
                "raster corrupted" )
  FT_ERRORDEF_( Raster_Overflow,                             0x62, \
                "raster overflow" )
  FT_ERRORDEF_( Raster_Negative_Height,                      0x63, \
                "negative height while rastering" )

  /* cache errors */

  FT_ERRORDEF_( Too_Many_Caches,                             0x70, \
                "too many registered caches" )

  /* TrueType and SFNT errors */

  FT_ERRORDEF_( Invalid_Opcode,                              0x80, \
                "invalid opcode" )
  FT_ERRORDEF_( Too_Few_Arguments,                           0x81, \
                "too few arguments" )
  FT_ERRORDEF_( Stack_Overflow,                              0x82, \
                "stack overflow" )
  FT_ERRORDEF_( Code_Overflow,                               0x83, \
                "code overflow" )
  FT_ERRORDEF_( Bad_Argument,                                0x84, \
                "bad argument" )
  FT_ERRORDEF_( Divide_By_Zero,                              0x85, \
                "division by zero" )
  FT_ERRORDEF_( Invalid_Reference,                           0x86, \
                "invalid reference" )
  FT_ERRORDEF_( Debug_OpCode,                                0x87, \
                "found debug opcode" )
  FT_ERRORDEF_( ENDF_In_Exec_Stream,                         0x88, \
                "found ENDF opcode in execution stream" )
  FT_ERRORDEF_( Nested_DEFS,                                 0x89, \
                "nested DEFS" )
  FT_ERRORDEF_( Invalid_CodeRange,                           0x8A, \
                "invalid code range" )
  FT_ERRORDEF_( Execution_Too_Long,                          0x8B, \
                "execution context too long" )
  FT_ERRORDEF_( Too_Many_Function_Defs,                      0x8C, \
                "too many function definitions" )
  FT_ERRORDEF_( Too_Many_Instruction_Defs,                   0x8D, \
                "too many instruction definitions" )
  FT_ERRORDEF_( Table_Missing,                               0x8E, \
                "SFNT font table missing" )
  FT_ERRORDEF_( Horiz_Header_Missing,                        0x8F, \
                "horizontal header (hhea) table missing" )
  FT_ERRORDEF_( Locations_Missing,                           0x90, \
                "locations (loca) table missing" )
  FT_ERRORDEF_( Name_Table_Missing,                          0x91, \
                "name table missing" )
  FT_ERRORDEF_( CMap_Table_Missing,                          0x92, \
                "character map (cmap) table missing" )
  FT_ERRORDEF_( Hmtx_Table_Missing,                          0x93, \
                "horizontal metrics (hmtx) table missing" )
  FT_ERRORDEF_( Post_Table_Missing,                          0x94, \
                "PostScript (post) table missing" )
  FT_ERRORDEF_( Invalid_Horiz_Metrics,                       0x95, \
                "invalid horizontal metrics" )
  FT_ERRORDEF_( Invalid_CharMap_Format,                      0x96, \
                "invalid character map (cmap) format" )
  FT_ERRORDEF_( Invalid_PPem,                                0x97, \
                "invalid ppem value" )
  FT_ERRORDEF_( Invalid_Vert_Metrics,                        0x98, \
                "invalid vertical metrics" )
  FT_ERRORDEF_( Could_Not_Find_Context,                      0x99, \
                "could not find context" )
  FT_ERRORDEF_( Invalid_Post_Table_Format,                   0x9A, \
                "invalid PostScript (post) table format" )
  FT_ERRORDEF_( Invalid_Post_Table,                          0x9B, \
                "invalid PostScript (post) table" )

  /* CFF, CID, and Type 1 errors */

  FT_ERRORDEF_( Syntax_Error,                                0xA0, \
                "opcode syntax error" )
  FT_ERRORDEF_( Stack_Underflow,                             0xA1, \
                "argument stack underflow" )
  FT_ERRORDEF_( Ignore,                                      0xA2, \
                "ignore" )
  FT_ERRORDEF_( No_Unicode_Glyph_Name,                       0xA3, \
                "no Unicode glyph name found" )


  /* BDF errors */

  FT_ERRORDEF_( Missing_Startfont_Field,                     0xB0, \
                "`STARTFONT' field missing" )
  FT_ERRORDEF_( Missing_Font_Field,                          0xB1, \
                "`FONT' field missing" )
  FT_ERRORDEF_( Missing_Size_Field,                          0xB2, \
                "`SIZE' field missing" )
  FT_ERRORDEF_( Missing_Fontboundingbox_Field,               0xB3, \
                "`FONTBOUNDINGBOX' field missing" )
  FT_ERRORDEF_( Missing_Chars_Field,                         0xB4, \
                "`CHARS' field missing" )
  FT_ERRORDEF_( Missing_Startchar_Field,                     0xB5, \
                "`STARTCHAR' field missing" )
  FT_ERRORDEF_( Missing_Encoding_Field,                      0xB6, \
                "`ENCODING' field missing" )
  FT_ERRORDEF_( Missing_Bbx_Field,                           0xB7, \
                "`BBX' field missing" )
  FT_ERRORDEF_( Bbx_Too_Big,                                 0xB8, \
                "`BBX' too big" )
  FT_ERRORDEF_( Corrupted_Font_Header,                       0xB9, \
                "Font header corrupted or missing fields" )
  FT_ERRORDEF_( Corrupted_Font_Glyphs,                       0xBA, \
                "Font glyphs corrupted or missing fields" )


/* END */
/***************************************************************************/
/*                                                                         */
/*  fterrors.h                                                             */
/*                                                                         */
/*    FreeType error code handling (specification).                        */
/*                                                                         */
/*  Copyright 1996-2001, 2002, 2004, 2007 by                               */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* This special header file is used to define the handling of FT2        */
  /* enumeration constants.  It can also be used to generate error message */
  /* strings with a small macro trick explained below.                     */
  /*                                                                       */
  /* I - Error Formats                                                     */
  /* -----------------                                                     */
  /*                                                                       */
  /*   The configuration macro FT_CONFIG_OPTION_USE_MODULE_ERRORS can be   */
  /*   defined in ftoption.h in order to make the higher byte indicate     */
  /*   the module where the error has happened (this is not compatible     */
  /*   with standard builds of FreeType 2).  You can then use the macro    */
  /*   FT_ERROR_BASE macro to extract the generic error code from an       */
  /*   FT_Error value.                                                     */
  /*                                                                       */
  /*                                                                       */
  /* II - Error Message strings                                            */
  /* --------------------------                                            */
  /*                                                                       */
  /*   The error definitions below are made through special macros that    */
  /*   allow client applications to build a table of error message strings */
  /*   if they need it.  The strings are not included in a normal build of */
  /*   FreeType 2 to save space (most client applications do not use       */
  /*   them).                                                              */
  /*                                                                       */
  /*   To do so, you have to define the following macros before including  */
  /*   this file:                                                          */
  /*                                                                       */
  /*   FT_ERROR_START_LIST ::                                              */
  /*     This macro is called before anything else to define the start of  */
  /*     the error list.  It is followed by several FT_ERROR_DEF calls     */
  /*     (see below).                                                      */
  /*                                                                       */
  /*   FT_ERROR_DEF( e, v, s ) ::                                          */
  /*     This macro is called to define one single error.                  */
  /*     `e' is the error code identifier (e.g. FT_Err_Invalid_Argument).  */
  /*     `v' is the error numerical value.                                 */
  /*     `s' is the corresponding error string.                            */
  /*                                                                       */
  /*   FT_ERROR_END_LIST ::                                                */
  /*     This macro ends the list.                                         */
  /*                                                                       */
  /*   Additionally, you have to undefine __FTERRORS_H__ before #including */
  /*   this file.                                                          */
  /*                                                                       */
  /*   Here is a simple example:                                           */
  /*                                                                       */
  /*     {                                                                 */
  /*       #undef __FTERRORS_H__                                           */
  /*       #define FT_ERRORDEF( e, v, s )  { e, s },                       */
  /*       #define FT_ERROR_START_LIST     {                               */
  /*       #define FT_ERROR_END_LIST       { 0, 0 } };                     */
  /*                                                                       */
  /*       const struct                                                    */
  /*       {                                                               */
  /*         int          err_code;                                        */
  /*         const char*  err_msg;                                         */
  /*       } ft_errors[] =                                                 */
  /*                                                                       */
  /*       #include FT_ERRORS_H                                            */
  /*     }                                                                 */
  /*                                                                       */
  /*************************************************************************/


#ifndef __FTERRORS_H__
#define __FTERRORS_H__


  /* include module base error codes */
#include FT_MODULE_ERRORS_H


  /*******************************************************************/
  /*******************************************************************/
  /*****                                                         *****/
  /*****                       SETUP MACROS                      *****/
  /*****                                                         *****/
  /*******************************************************************/
  /*******************************************************************/


#undef  FT_NEED_EXTERN_C

#undef  FT_ERR_XCAT
#undef  FT_ERR_CAT

#define FT_ERR_XCAT( x, y )  x ## y
#define FT_ERR_CAT( x, y )   FT_ERR_XCAT( x, y )


  /* FT_ERR_PREFIX is used as a prefix for error identifiers. */
  /* By default, we use `FT_Err_'.                            */
  /*                                                          */
#ifndef FT_ERR_PREFIX
#define FT_ERR_PREFIX  FT_Err_
#endif


  /* FT_ERR_BASE is used as the base for module-specific errors. */
  /*                                                             */
#ifdef FT_CONFIG_OPTION_USE_MODULE_ERRORS

#ifndef FT_ERR_BASE
#define FT_ERR_BASE  FT_Mod_Err_Base
#endif

#else

#undef FT_ERR_BASE
#define FT_ERR_BASE  0

#endif /* FT_CONFIG_OPTION_USE_MODULE_ERRORS */


  /* If FT_ERRORDEF is not defined, we need to define a simple */
  /* enumeration type.                                         */
  /*                                                           */
#ifndef FT_ERRORDEF

#define FT_ERRORDEF( e, v, s )  e = v,
#define FT_ERROR_START_LIST     enum {
#define FT_ERROR_END_LIST       FT_ERR_CAT( FT_ERR_PREFIX, Max ) };

#ifdef __cplusplus
#define FT_NEED_EXTERN_C
  extern "C" {
#endif

#endif /* !FT_ERRORDEF */


  /* this macro is used to define an error */
#define FT_ERRORDEF_( e, v, s )   \
          FT_ERRORDEF( FT_ERR_CAT( FT_ERR_PREFIX, e ), v + FT_ERR_BASE, s )

  /* this is only used for <module>_Err_Ok, which must be 0! */
#define FT_NOERRORDEF_( e, v, s ) \
          FT_ERRORDEF( FT_ERR_CAT( FT_ERR_PREFIX, e ), v, s )


#ifdef FT_ERROR_START_LIST
  FT_ERROR_START_LIST
#endif


  /* now include the error codes */
#include FT_ERROR_DEFINITIONS_H


#ifdef FT_ERROR_END_LIST
  FT_ERROR_END_LIST
#endif


  /*******************************************************************/
  /*******************************************************************/
  /*****                                                         *****/
  /*****                      SIMPLE CLEANUP                     *****/
  /*****                                                         *****/
  /*******************************************************************/
  /*******************************************************************/

#ifdef FT_NEED_EXTERN_C
  }
#endif

#undef FT_ERROR_START_LIST
#undef FT_ERROR_END_LIST

#undef FT_ERRORDEF
#undef FT_ERRORDEF_
#undef FT_NOERRORDEF_

#undef FT_NEED_EXTERN_C
#undef FT_ERR_CONCAT
#undef FT_ERR_BASE

  /* FT_KEEP_ERR_PREFIX is needed for ftvalid.h */
#ifndef FT_KEEP_ERR_PREFIX
#undef FT_ERR_PREFIX
#endif

#endif /* __FTERRORS_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  ftgasp.h                                                               */
/*                                                                         */
/*    Access of TrueType's `gasp' table (specification).                   */
/*                                                                         */
/*  Copyright 2007, 2008 by                                                */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef _FT_GASP_H_
#define _FT_GASP_H_

#include <ft2build.h>
#include FT_FREETYPE_H

#ifdef FREETYPE_H
#error "freetype.h of FreeType 1 has been loaded!"
#error "Please fix the directory search order for header files"
#error "so that freetype.h of FreeType 2 is found first."
#endif


  /***************************************************************************
   *
   * @section:
   *   gasp_table
   *
   * @title:
   *   Gasp Table
   *
   * @abstract:
   *   Retrieving TrueType `gasp' table entries.
   *
   * @description:
   *   The function @FT_Get_Gasp can be used to query a TrueType or OpenType
   *   font for specific entries in its `gasp' table, if any.  This is
   *   mainly useful when implementing native TrueType hinting with the
   *   bytecode interpreter to duplicate the Windows text rendering results.
   */

  /*************************************************************************
   *
   * @enum:
   *   FT_GASP_XXX
   *
   * @description:
   *   A list of values and/or bit-flags returned by the @FT_Get_Gasp
   *   function.
   *
   * @values:
   *   FT_GASP_NO_TABLE ::
   *     This special value means that there is no GASP table in this face.
   *     It is up to the client to decide what to do.
   *
   *   FT_GASP_DO_GRIDFIT ::
   *     Grid-fitting and hinting should be performed at the specified ppem.
   *     This *really* means TrueType bytecode interpretation.
   *
   *   FT_GASP_DO_GRAY ::
   *     Anti-aliased rendering should be performed at the specified ppem.
   *
   *   FT_GASP_SYMMETRIC_SMOOTHING ::
   *     Smoothing along multiple axes must be used with ClearType.
   *
   *   FT_GASP_SYMMETRIC_GRIDFIT ::
   *     Grid-fitting must be used with ClearType's symmetric smoothing.
   *
   * @note:
   *   `ClearType' is Microsoft's implementation of LCD rendering, partly
   *   protected by patents.
   *
   * @since:
   *   2.3.0
   */
#define FT_GASP_NO_TABLE               -1
#define FT_GASP_DO_GRIDFIT           0x01
#define FT_GASP_DO_GRAY              0x02
#define FT_GASP_SYMMETRIC_SMOOTHING  0x08
#define FT_GASP_SYMMETRIC_GRIDFIT    0x10


  /*************************************************************************
   *
   * @func:
   *   FT_Get_Gasp
   *
   * @description:
   *   Read the `gasp' table from a TrueType or OpenType font file and
   *   return the entry corresponding to a given character pixel size.
   *
   * @input:
   *   face :: The source face handle.
   *   ppem :: The vertical character pixel size.
   *
   * @return:
   *   Bit flags (see @FT_GASP_XXX), or @FT_GASP_NO_TABLE if there is no
   *   `gasp' table in the face.
   *
   * @since:
   *   2.3.0
   */
  FT_EXPORT( FT_Int )
  FT_Get_Gasp( FT_Face  face,
               FT_UInt  ppem );

/* */

#endif /* _FT_GASP_H_ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  ftglyph.h                                                              */
/*                                                                         */
/*    FreeType convenience functions to handle glyphs (specification).     */
/*                                                                         */
/*  Copyright 1996-2001, 2002, 2003, 2006, 2008, 2009 by                   */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* This file contains the definition of several convenience functions    */
  /* that can be used by client applications to easily retrieve glyph      */
  /* bitmaps and outlines from a given face.                               */
  /*                                                                       */
  /* These functions should be optional if you are writing a font server   */
  /* or text layout engine on top of FreeType.  However, they are pretty   */
  /* handy for many other simple uses of the library.                      */
  /*                                                                       */
  /*************************************************************************/


#ifndef __FTGLYPH_H__
#define __FTGLYPH_H__


#include <ft2build.h>
#include FT_FREETYPE_H

#ifdef FREETYPE_H
#error "freetype.h of FreeType 1 has been loaded!"
#error "Please fix the directory search order for header files"
#error "so that freetype.h of FreeType 2 is found first."
#endif


FT_BEGIN_HEADER


  /*************************************************************************/
  /*                                                                       */
  /* <Section>                                                             */
  /*    glyph_management                                                   */
  /*                                                                       */
  /* <Title>                                                               */
  /*    Glyph Management                                                   */
  /*                                                                       */
  /* <Abstract>                                                            */
  /*    Generic interface to manage individual glyph data.                 */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This section contains definitions used to manage glyph data        */
  /*    through generic FT_Glyph objects.  Each of them can contain a      */
  /*    bitmap, a vector outline, or even images in other formats.         */
  /*                                                                       */
  /*************************************************************************/


  /* forward declaration to a private type */
  typedef struct FT_Glyph_Class_  FT_Glyph_Class;


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FT_Glyph                                                           */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Handle to an object used to model generic glyph images.  It is a   */
  /*    pointer to the @FT_GlyphRec structure and can contain a glyph      */
  /*    bitmap or pointer.                                                 */
  /*                                                                       */
  /* <Note>                                                                */
  /*    Glyph objects are not owned by the library.  You must thus release */
  /*    them manually (through @FT_Done_Glyph) _before_ calling            */
  /*    @FT_Done_FreeType.                                                 */
  /*                                                                       */
  typedef struct FT_GlyphRec_*  FT_Glyph;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_GlyphRec                                                        */
  /*                                                                       */
  /* <Description>                                                         */
  /*    The root glyph structure contains a given glyph image plus its     */
  /*    advance width in 16.16 fixed float format.                         */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    library :: A handle to the FreeType library object.                */
  /*                                                                       */
  /*    clazz   :: A pointer to the glyph's class.  Private.               */
  /*                                                                       */
  /*    format  :: The format of the glyph's image.                        */
  /*                                                                       */
  /*    advance :: A 16.16 vector that gives the glyph's advance width.    */
  /*                                                                       */
  typedef struct  FT_GlyphRec_
  {
    FT_Library             library;
    const FT_Glyph_Class*  clazz;
    FT_Glyph_Format        format;
    FT_Vector              advance;

  } FT_GlyphRec;


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FT_BitmapGlyph                                                     */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A handle to an object used to model a bitmap glyph image.  This is */
  /*    a sub-class of @FT_Glyph, and a pointer to @FT_BitmapGlyphRec.     */
  /*                                                                       */
  typedef struct FT_BitmapGlyphRec_*  FT_BitmapGlyph;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_BitmapGlyphRec                                                  */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A structure used for bitmap glyph images.  This really is a        */
  /*    `sub-class' of @FT_GlyphRec.                                       */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    root   :: The root @FT_Glyph fields.                               */
  /*                                                                       */
  /*    left   :: The left-side bearing, i.e., the horizontal distance     */
  /*              from the current pen position to the left border of the  */
  /*              glyph bitmap.                                            */
  /*                                                                       */
  /*    top    :: The top-side bearing, i.e., the vertical distance from   */
  /*              the current pen position to the top border of the glyph  */
  /*              bitmap.  This distance is positive for upwards~y!        */
  /*                                                                       */
  /*    bitmap :: A descriptor for the bitmap.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    You can typecast an @FT_Glyph to @FT_BitmapGlyph if you have       */
  /*    `glyph->format == FT_GLYPH_FORMAT_BITMAP'.  This lets you access   */
  /*    the bitmap's contents easily.                                      */
  /*                                                                       */
  /*    The corresponding pixel buffer is always owned by @FT_BitmapGlyph  */
  /*    and is thus created and destroyed with it.                         */
  /*                                                                       */
  typedef struct  FT_BitmapGlyphRec_
  {
    FT_GlyphRec  root;
    FT_Int       left;
    FT_Int       top;
    FT_Bitmap    bitmap;

  } FT_BitmapGlyphRec;


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FT_OutlineGlyph                                                    */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A handle to an object used to model an outline glyph image.  This  */
  /*    is a sub-class of @FT_Glyph, and a pointer to @FT_OutlineGlyphRec. */
  /*                                                                       */
  typedef struct FT_OutlineGlyphRec_*  FT_OutlineGlyph;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_OutlineGlyphRec                                                 */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A structure used for outline (vectorial) glyph images.  This       */
  /*    really is a `sub-class' of @FT_GlyphRec.                           */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    root    :: The root @FT_Glyph fields.                              */
  /*                                                                       */
  /*    outline :: A descriptor for the outline.                           */
  /*                                                                       */
  /* <Note>                                                                */
  /*    You can typecast an @FT_Glyph to @FT_OutlineGlyph if you have      */
  /*    `glyph->format == FT_GLYPH_FORMAT_OUTLINE'.  This lets you access  */
  /*    the outline's content easily.                                      */
  /*                                                                       */
  /*    As the outline is extracted from a glyph slot, its coordinates are */
  /*    expressed normally in 26.6 pixels, unless the flag                 */
  /*    @FT_LOAD_NO_SCALE was used in @FT_Load_Glyph() or @FT_Load_Char(). */
  /*                                                                       */
  /*    The outline's tables are always owned by the object and are        */
  /*    destroyed with it.                                                 */
  /*                                                                       */
  typedef struct  FT_OutlineGlyphRec_
  {
    FT_GlyphRec  root;
    FT_Outline   outline;

  } FT_OutlineGlyphRec;


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Get_Glyph                                                       */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A function used to extract a glyph image from a slot.  Note that   */
  /*    the created @FT_Glyph object must be released with @FT_Done_Glyph. */
  /*                                                                       */
  /* <Input>                                                               */
  /*    slot   :: A handle to the source glyph slot.                       */
  /*                                                                       */
  /* <Output>                                                              */
  /*    aglyph :: A handle to the glyph object.                            */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Get_Glyph( FT_GlyphSlot  slot,
                FT_Glyph     *aglyph );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Glyph_Copy                                                      */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A function used to copy a glyph image.  Note that the created      */
  /*    @FT_Glyph object must be released with @FT_Done_Glyph.             */
  /*                                                                       */
  /* <Input>                                                               */
  /*    source :: A handle to the source glyph object.                     */
  /*                                                                       */
  /* <Output>                                                              */
  /*    target :: A handle to the target glyph object.  0~in case of       */
  /*              error.                                                   */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Glyph_Copy( FT_Glyph   source,
                 FT_Glyph  *target );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Glyph_Transform                                                 */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Transform a glyph image if its format is scalable.                 */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    glyph  :: A handle to the target glyph object.                     */
  /*                                                                       */
  /* <Input>                                                               */
  /*    matrix :: A pointer to a 2x2 matrix to apply.                      */
  /*                                                                       */
  /*    delta  :: A pointer to a 2d vector to apply.  Coordinates are      */
  /*              expressed in 1/64th of a pixel.                          */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code (if not 0, the glyph format is not scalable).  */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The 2x2 transformation matrix is also applied to the glyph's       */
  /*    advance vector.                                                    */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Glyph_Transform( FT_Glyph    glyph,
                      FT_Matrix*  matrix,
                      FT_Vector*  delta );


  /*************************************************************************/
  /*                                                                       */
  /* <Enum>                                                                */
  /*    FT_Glyph_BBox_Mode                                                 */
  /*                                                                       */
  /* <Description>                                                         */
  /*    The mode how the values of @FT_Glyph_Get_CBox are returned.        */
  /*                                                                       */
  /* <Values>                                                              */
  /*    FT_GLYPH_BBOX_UNSCALED ::                                          */
  /*      Return unscaled font units.                                      */
  /*                                                                       */
  /*    FT_GLYPH_BBOX_SUBPIXELS ::                                         */
  /*      Return unfitted 26.6 coordinates.                                */
  /*                                                                       */
  /*    FT_GLYPH_BBOX_GRIDFIT ::                                           */
  /*      Return grid-fitted 26.6 coordinates.                             */
  /*                                                                       */
  /*    FT_GLYPH_BBOX_TRUNCATE ::                                          */
  /*      Return coordinates in integer pixels.                            */
  /*                                                                       */
  /*    FT_GLYPH_BBOX_PIXELS ::                                            */
  /*      Return grid-fitted pixel coordinates.                            */
  /*                                                                       */
  typedef enum  FT_Glyph_BBox_Mode_
  {
    FT_GLYPH_BBOX_UNSCALED  = 0,
    FT_GLYPH_BBOX_SUBPIXELS = 0,
    FT_GLYPH_BBOX_GRIDFIT   = 1,
    FT_GLYPH_BBOX_TRUNCATE  = 2,
    FT_GLYPH_BBOX_PIXELS    = 3

  } FT_Glyph_BBox_Mode;


  /*************************************************************************/
  /*                                                                       */
  /* <Enum>                                                                */
  /*    ft_glyph_bbox_xxx                                                  */
  /*                                                                       */
  /* <Description>                                                         */
  /*    These constants are deprecated.  Use the corresponding             */
  /*    @FT_Glyph_BBox_Mode values instead.                                */
  /*                                                                       */
  /* <Values>                                                              */
  /*   ft_glyph_bbox_unscaled  :: See @FT_GLYPH_BBOX_UNSCALED.             */
  /*   ft_glyph_bbox_subpixels :: See @FT_GLYPH_BBOX_SUBPIXELS.            */
  /*   ft_glyph_bbox_gridfit   :: See @FT_GLYPH_BBOX_GRIDFIT.              */
  /*   ft_glyph_bbox_truncate  :: See @FT_GLYPH_BBOX_TRUNCATE.             */
  /*   ft_glyph_bbox_pixels    :: See @FT_GLYPH_BBOX_PIXELS.               */
  /*                                                                       */
#define ft_glyph_bbox_unscaled   FT_GLYPH_BBOX_UNSCALED
#define ft_glyph_bbox_subpixels  FT_GLYPH_BBOX_SUBPIXELS
#define ft_glyph_bbox_gridfit    FT_GLYPH_BBOX_GRIDFIT
#define ft_glyph_bbox_truncate   FT_GLYPH_BBOX_TRUNCATE
#define ft_glyph_bbox_pixels     FT_GLYPH_BBOX_PIXELS


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Glyph_Get_CBox                                                  */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Return a glyph's `control box'.  The control box encloses all the  */
  /*    outline's points, including BÃ©zier control points.  Though it      */
  /*    coincides with the exact bounding box for most glyphs, it can be   */
  /*    slightly larger in some situations (like when rotating an outline  */
  /*    which contains BÃ©zier outside arcs).                               */
  /*                                                                       */
  /*    Computing the control box is very fast, while getting the bounding */
  /*    box can take much more time as it needs to walk over all segments  */
  /*    and arcs in the outline.  To get the latter, you can use the       */
  /*    `ftbbox' component which is dedicated to this single task.         */
  /*                                                                       */
  /* <Input>                                                               */
  /*    glyph :: A handle to the source glyph object.                      */
  /*                                                                       */
  /*    mode  :: The mode which indicates how to interpret the returned    */
  /*             bounding box values.                                      */
  /*                                                                       */
  /* <Output>                                                              */
  /*    acbox :: The glyph coordinate bounding box.  Coordinates are       */
  /*             expressed in 1/64th of pixels if it is grid-fitted.       */
  /*                                                                       */
  /* <Note>                                                                */
  /*    Coordinates are relative to the glyph origin, using the y~upwards  */
  /*    convention.                                                        */
  /*                                                                       */
  /*    If the glyph has been loaded with @FT_LOAD_NO_SCALE, `bbox_mode'   */
  /*    must be set to @FT_GLYPH_BBOX_UNSCALED to get unscaled font        */
  /*    units in 26.6 pixel format.  The value @FT_GLYPH_BBOX_SUBPIXELS    */
  /*    is another name for this constant.                                 */
  /*                                                                       */
  /*    Note that the maximum coordinates are exclusive, which means that  */
  /*    one can compute the width and height of the glyph image (be it in  */
  /*    integer or 26.6 pixels) as:                                        */
  /*                                                                       */
  /*    {                                                                  */
  /*      width  = bbox.xMax - bbox.xMin;                                  */
  /*      height = bbox.yMax - bbox.yMin;                                  */
  /*    }                                                                  */
  /*                                                                       */
  /*    Note also that for 26.6 coordinates, if `bbox_mode' is set to      */
  /*    @FT_GLYPH_BBOX_GRIDFIT, the coordinates will also be grid-fitted,  */
  /*    which corresponds to:                                              */
  /*                                                                       */
  /*    {                                                                  */
  /*      bbox.xMin = FLOOR(bbox.xMin);                                    */
  /*      bbox.yMin = FLOOR(bbox.yMin);                                    */
  /*      bbox.xMax = CEILING(bbox.xMax);                                  */
  /*      bbox.yMax = CEILING(bbox.yMax);                                  */
  /*    }                                                                  */
  /*                                                                       */
  /*    To get the bbox in pixel coordinates, set `bbox_mode' to           */
  /*    @FT_GLYPH_BBOX_TRUNCATE.                                           */
  /*                                                                       */
  /*    To get the bbox in grid-fitted pixel coordinates, set `bbox_mode'  */
  /*    to @FT_GLYPH_BBOX_PIXELS.                                          */
  /*                                                                       */
  FT_EXPORT( void )
  FT_Glyph_Get_CBox( FT_Glyph  glyph,
                     FT_UInt   bbox_mode,
                     FT_BBox  *acbox );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Glyph_To_Bitmap                                                 */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Convert a given glyph object to a bitmap glyph object.             */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    the_glyph   :: A pointer to a handle to the target glyph.          */
  /*                                                                       */
  /* <Input>                                                               */
  /*    render_mode :: An enumeration that describes how the data is       */
  /*                   rendered.                                           */
  /*                                                                       */
  /*    origin      :: A pointer to a vector used to translate the glyph   */
  /*                   image before rendering.  Can be~0 (if no            */
  /*                   translation).  The origin is expressed in           */
  /*                   26.6 pixels.                                        */
  /*                                                                       */
  /*    destroy     :: A boolean that indicates that the original glyph    */
  /*                   image should be destroyed by this function.  It is  */
  /*                   never destroyed in case of error.                   */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    This function does nothing if the glyph format isn't scalable.     */
  /*                                                                       */
  /*    The glyph image is translated with the `origin' vector before      */
  /*    rendering.                                                         */
  /*                                                                       */
  /*    The first parameter is a pointer to an @FT_Glyph handle, that will */
  /*    be _replaced_ by this function (with newly allocated data).        */
  /*    Typically, you would use (omitting error handling):                */
  /*                                                                       */
  /*                                                                       */
  /*      {                                                                */
  /*        FT_Glyph        glyph;                                         */
  /*        FT_BitmapGlyph  glyph_bitmap;                                  */
  /*                                                                       */
  /*                                                                       */
  /*        // load glyph                                                  */
  /*        error = FT_Load_Char( face, glyph_index, FT_LOAD_DEFAUT );     */
  /*                                                                       */
  /*        // extract glyph image                                         */
  /*        error = FT_Get_Glyph( face->glyph, &glyph );                   */
  /*                                                                       */
  /*        // convert to a bitmap (default render mode + destroying old)  */
  /*        if ( glyph->format != FT_GLYPH_FORMAT_BITMAP )                 */
  /*        {                                                              */
  /*          error = FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_NORMAL,   */
  /*                                      0, 1 );                          */
  /*          if ( error ) // `glyph' unchanged                            */
  /*            ...                                                        */
  /*        }                                                              */
  /*                                                                       */
  /*        // access bitmap content by typecasting                        */
  /*        glyph_bitmap = (FT_BitmapGlyph)glyph;                          */
  /*                                                                       */
  /*        // do funny stuff with it, like blitting/drawing               */
  /*        ...                                                            */
  /*                                                                       */
  /*        // discard glyph image (bitmap or not)                         */
  /*        FT_Done_Glyph( glyph );                                        */
  /*      }                                                                */
  /*                                                                       */
  /*                                                                       */
  /*    Here another example, again without error handling:                */
  /*                                                                       */
  /*                                                                       */
  /*      {                                                                */
  /*        FT_Glyph  glyphs[MAX_GLYPHS]                                   */
  /*                                                                       */
  /*                                                                       */
  /*        ...                                                            */
  /*                                                                       */
  /*        for ( idx = 0; i < MAX_GLYPHS; i++ )                           */
  /*          error = FT_Load_Glyph( face, idx, FT_LOAD_DEFAULT ) ||       */
  /*                  FT_Get_Glyph ( face->glyph, &glyph[idx] );           */
  /*                                                                       */
  /*        ...                                                            */
  /*                                                                       */
  /*        for ( idx = 0; i < MAX_GLYPHS; i++ )                           */
  /*        {                                                              */
  /*          FT_Glyph  bitmap = glyphs[idx];                              */
  /*                                                                       */
  /*                                                                       */
  /*          ...                                                          */
  /*                                                                       */
  /*          // after this call, `bitmap' no longer points into           */
  /*          // the `glyphs' array (and the old value isn't destroyed)    */
  /*          FT_Glyph_To_Bitmap( &bitmap, FT_RENDER_MODE_MONO, 0, 0 );    */
  /*                                                                       */
  /*          ...                                                          */
  /*                                                                       */
  /*          FT_Done_Glyph( bitmap );                                     */
  /*        }                                                              */
  /*                                                                       */
  /*        ...                                                            */
  /*                                                                       */
  /*        for ( idx = 0; i < MAX_GLYPHS; i++ )                           */
  /*          FT_Done_Glyph( glyphs[idx] );                                */
  /*      }                                                                */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Glyph_To_Bitmap( FT_Glyph*       the_glyph,
                      FT_Render_Mode  render_mode,
                      FT_Vector*      origin,
                      FT_Bool         destroy );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Done_Glyph                                                      */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Destroy a given glyph.                                             */
  /*                                                                       */
  /* <Input>                                                               */
  /*    glyph :: A handle to the target glyph object.                      */
  /*                                                                       */
  FT_EXPORT( void )
  FT_Done_Glyph( FT_Glyph  glyph );

  /* */


  /* other helpful functions */

  /*************************************************************************/
  /*                                                                       */
  /* <Section>                                                             */
  /*    computations                                                       */
  /*                                                                       */
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Matrix_Multiply                                                 */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Perform the matrix operation `b = a*b'.                            */
  /*                                                                       */
  /* <Input>                                                               */
  /*    a :: A pointer to matrix `a'.                                      */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    b :: A pointer to matrix `b'.                                      */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The result is undefined if either `a' or `b' is zero.              */
  /*                                                                       */
  FT_EXPORT( void )
  FT_Matrix_Multiply( const FT_Matrix*  a,
                      FT_Matrix*        b );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Matrix_Invert                                                   */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Invert a 2x2 matrix.  Return an error if it can't be inverted.     */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    matrix :: A pointer to the target matrix.  Remains untouched in    */
  /*              case of error.                                           */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Matrix_Invert( FT_Matrix*  matrix );


  /* */


FT_END_HEADER

#endif /* __FTGLYPH_H__ */


/* END */


/* Local Variables: */
/* coding: utf-8    */
/* End:             */
/***************************************************************************/
/*                                                                         */
/*  ftgxval.h                                                              */
/*                                                                         */
/*    FreeType API for validating TrueTypeGX/AAT tables (specification).   */
/*                                                                         */
/*  Copyright 2004, 2005, 2006 by                                          */
/*  Masatake YAMATO, Redhat K.K,                                           */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/

/***************************************************************************/
/*                                                                         */
/* gxvalid is derived from both gxlayout module and otvalid module.        */
/* Development of gxlayout is supported by the Information-technology      */
/* Promotion Agency(IPA), Japan.                                           */
/*                                                                         */
/***************************************************************************/


#ifndef __FTGXVAL_H__
#define __FTGXVAL_H__

#include <ft2build.h>
#include FT_FREETYPE_H

#ifdef FREETYPE_H
#error "freetype.h of FreeType 1 has been loaded!"
#error "Please fix the directory search order for header files"
#error "so that freetype.h of FreeType 2 is found first."
#endif


FT_BEGIN_HEADER


  /*************************************************************************/
  /*                                                                       */
  /* <Section>                                                             */
  /*    gx_validation                                                      */
  /*                                                                       */
  /* <Title>                                                               */
  /*    TrueTypeGX/AAT Validation                                          */
  /*                                                                       */
  /* <Abstract>                                                            */
  /*    An API to validate TrueTypeGX/AAT tables.                          */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This section contains the declaration of functions to validate     */
  /*    some TrueTypeGX tables (feat, mort, morx, bsln, just, kern, opbd,  */
  /*    trak, prop, lcar).                                                 */
  /*                                                                       */
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /*                                                                       */
  /* Warning: Use FT_VALIDATE_XXX to validate a table.                     */
  /*          Following definitions are for gxvalid developers.            */
  /*                                                                       */
  /*                                                                       */
  /*************************************************************************/

#define FT_VALIDATE_feat_INDEX     0
#define FT_VALIDATE_mort_INDEX     1
#define FT_VALIDATE_morx_INDEX     2
#define FT_VALIDATE_bsln_INDEX     3
#define FT_VALIDATE_just_INDEX     4
#define FT_VALIDATE_kern_INDEX     5
#define FT_VALIDATE_opbd_INDEX     6
#define FT_VALIDATE_trak_INDEX     7
#define FT_VALIDATE_prop_INDEX     8
#define FT_VALIDATE_lcar_INDEX     9
#define FT_VALIDATE_GX_LAST_INDEX  FT_VALIDATE_lcar_INDEX


  /*************************************************************************
   *
   * @macro:
   *   FT_VALIDATE_GX_LENGTH
   *
   * @description:
   *   The number of tables checked in this module.  Use it as a parameter
   *   for the `table-length' argument of function @FT_TrueTypeGX_Validate.
   */
#define FT_VALIDATE_GX_LENGTH     (FT_VALIDATE_GX_LAST_INDEX + 1)

  /* */

  /* Up to 0x1000 is used by otvalid.
     Ox2xxx is reserved for feature OT extension. */
#define FT_VALIDATE_GX_START 0x4000
#define FT_VALIDATE_GX_BITFIELD( tag )                  \
  ( FT_VALIDATE_GX_START << FT_VALIDATE_##tag##_INDEX )


 /**********************************************************************
  *
  * @enum:
  *    FT_VALIDATE_GXXXX
  *
  * @description:
  *    A list of bit-field constants used with @FT_TrueTypeGX_Validate to
  *    indicate which TrueTypeGX/AAT Type tables should be validated.
  *
  * @values:
  *    FT_VALIDATE_feat ::
  *      Validate `feat' table.
  *
  *    FT_VALIDATE_mort ::
  *      Validate `mort' table.
  *
  *    FT_VALIDATE_morx ::
  *      Validate `morx' table.
  *
  *    FT_VALIDATE_bsln ::
  *      Validate `bsln' table.
  *
  *    FT_VALIDATE_just ::
  *      Validate `just' table.
  *
  *    FT_VALIDATE_kern ::
  *      Validate `kern' table.
  *
  *    FT_VALIDATE_opbd ::
  *      Validate `opbd' table.
  *
  *    FT_VALIDATE_trak ::
  *      Validate `trak' table.
  *
  *    FT_VALIDATE_prop ::
  *      Validate `prop' table.
  *
  *    FT_VALIDATE_lcar ::
  *      Validate `lcar' table.
  *
  *    FT_VALIDATE_GX ::
  *      Validate all TrueTypeGX tables (feat, mort, morx, bsln, just, kern,
  *      opbd, trak, prop and lcar).
  *
  */

#define FT_VALIDATE_feat  FT_VALIDATE_GX_BITFIELD( feat )
#define FT_VALIDATE_mort  FT_VALIDATE_GX_BITFIELD( mort )
#define FT_VALIDATE_morx  FT_VALIDATE_GX_BITFIELD( morx )
#define FT_VALIDATE_bsln  FT_VALIDATE_GX_BITFIELD( bsln )
#define FT_VALIDATE_just  FT_VALIDATE_GX_BITFIELD( just )
#define FT_VALIDATE_kern  FT_VALIDATE_GX_BITFIELD( kern )
#define FT_VALIDATE_opbd  FT_VALIDATE_GX_BITFIELD( opbd )
#define FT_VALIDATE_trak  FT_VALIDATE_GX_BITFIELD( trak )
#define FT_VALIDATE_prop  FT_VALIDATE_GX_BITFIELD( prop )
#define FT_VALIDATE_lcar  FT_VALIDATE_GX_BITFIELD( lcar )

#define FT_VALIDATE_GX  ( FT_VALIDATE_feat | \
                          FT_VALIDATE_mort | \
                          FT_VALIDATE_morx | \
                          FT_VALIDATE_bsln | \
                          FT_VALIDATE_just | \
                          FT_VALIDATE_kern | \
                          FT_VALIDATE_opbd | \
                          FT_VALIDATE_trak | \
                          FT_VALIDATE_prop | \
                          FT_VALIDATE_lcar )


  /* */

 /**********************************************************************
  *
  * @function:
  *    FT_TrueTypeGX_Validate
  *
  * @description:
  *    Validate various TrueTypeGX tables to assure that all offsets and
  *    indices are valid.  The idea is that a higher-level library which
  *    actually does the text layout can access those tables without
  *    error checking (which can be quite time consuming).
  *
  * @input:
  *    face ::
  *       A handle to the input face.
  *
  *    validation_flags ::
  *       A bit field which specifies the tables to be validated.  See
  *       @FT_VALIDATE_GXXXX for possible values.
  *
  *    table_length ::
  *       The size of the `tables' array.  Normally, @FT_VALIDATE_GX_LENGTH
  *       should be passed.
  *
  * @output:
  *    tables ::
  *       The array where all validated sfnt tables are stored.
  *       The array itself must be allocated by a client.
  *
  * @return:
  *   FreeType error code.  0~means success.
  *
  * @note:
  *   This function only works with TrueTypeGX fonts, returning an error
  *   otherwise.
  *
  *   After use, the application should deallocate the buffers pointed to by
  *   each `tables' element, by calling @FT_TrueTypeGX_Free.  A NULL value
  *   indicates that the table either doesn't exist in the font, the
  *   application hasn't asked for validation, or the validator doesn't have
  *   the ability to validate the sfnt table.
  */
  FT_EXPORT( FT_Error )
  FT_TrueTypeGX_Validate( FT_Face   face,
                          FT_UInt   validation_flags,
                          FT_Bytes  tables[FT_VALIDATE_GX_LENGTH],
                          FT_UInt   table_length );


  /* */

 /**********************************************************************
  *
  * @function:
  *    FT_TrueTypeGX_Free
  *
  * @description:
  *    Free the buffer allocated by TrueTypeGX validator.
  *
  * @input:
  *    face ::
  *       A handle to the input face.
  *
  *    table ::
  *       The pointer to the buffer allocated by
  *       @FT_TrueTypeGX_Validate.
  *
  * @note:
  *   This function must be used to free the buffer allocated by
  *   @FT_TrueTypeGX_Validate only.
  */
  FT_EXPORT( void )
  FT_TrueTypeGX_Free( FT_Face   face,
                      FT_Bytes  table );


  /* */

 /**********************************************************************
  *
  * @enum:
  *    FT_VALIDATE_CKERNXXX
  *
  * @description:
  *    A list of bit-field constants used with @FT_ClassicKern_Validate
  *    to indicate the classic kern dialect or dialects.  If the selected
  *    type doesn't fit, @FT_ClassicKern_Validate regards the table as
  *    invalid.
  *
  * @values:
  *    FT_VALIDATE_MS ::
  *      Handle the `kern' table as a classic Microsoft kern table.
  *
  *    FT_VALIDATE_APPLE ::
  *      Handle the `kern' table as a classic Apple kern table.
  *
  *    FT_VALIDATE_CKERN ::
  *      Handle the `kern' as either classic Apple or Microsoft kern table.
  */
#define FT_VALIDATE_MS     ( FT_VALIDATE_GX_START << 0 )
#define FT_VALIDATE_APPLE  ( FT_VALIDATE_GX_START << 1 )

#define FT_VALIDATE_CKERN  ( FT_VALIDATE_MS | FT_VALIDATE_APPLE )


  /* */

 /**********************************************************************
  *
  * @function:
  *    FT_ClassicKern_Validate
  *
  * @description:
  *    Validate classic (16-bit format) kern table to assure that the offsets
  *    and indices are valid.  The idea is that a higher-level library which
  *    actually does the text layout can access those tables without error
  *    checking (which can be quite time consuming).
  *
  *    The `kern' table validator in @FT_TrueTypeGX_Validate deals with both
  *    the new 32-bit format and the classic 16-bit format, while
  *    FT_ClassicKern_Validate only supports the classic 16-bit format.
  *
  * @input:
  *    face ::
  *       A handle to the input face.
  *
  *    validation_flags ::
  *       A bit field which specifies the dialect to be validated.  See
  *       @FT_VALIDATE_CKERNXXX for possible values.
  *
  * @output:
  *    ckern_table ::
  *       A pointer to the kern table.
  *
  * @return:
  *   FreeType error code.  0~means success.
  *
  * @note:
  *   After use, the application should deallocate the buffers pointed to by
  *   `ckern_table', by calling @FT_ClassicKern_Free.  A NULL value
  *   indicates that the table doesn't exist in the font.
  */
  FT_EXPORT( FT_Error )
  FT_ClassicKern_Validate( FT_Face    face,
                           FT_UInt    validation_flags,
                           FT_Bytes  *ckern_table );


  /* */

 /**********************************************************************
  *
  * @function:
  *    FT_ClassicKern_Free
  *
  * @description:
  *    Free the buffer allocated by classic Kern validator.
  *
  * @input:
  *    face ::
  *       A handle to the input face.
  *
  *    table ::
  *       The pointer to the buffer that is allocated by
  *       @FT_ClassicKern_Validate.
  *
  * @note:
  *   This function must be used to free the buffer allocated by
  *   @FT_ClassicKern_Validate only.
  */
  FT_EXPORT( void )
  FT_ClassicKern_Free( FT_Face   face,
                       FT_Bytes  table );


 /* */


FT_END_HEADER

#endif /* __FTGXVAL_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  ftgzip.h                                                               */
/*                                                                         */
/*    Gzip-compressed stream support.                                      */
/*                                                                         */
/*  Copyright 2002, 2003, 2004, 2006 by                                    */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __FTGZIP_H__
#define __FTGZIP_H__

#include <ft2build.h>
#include FT_FREETYPE_H

#ifdef FREETYPE_H
#error "freetype.h of FreeType 1 has been loaded!"
#error "Please fix the directory search order for header files"
#error "so that freetype.h of FreeType 2 is found first."
#endif


FT_BEGIN_HEADER

  /*************************************************************************/
  /*                                                                       */
  /* <Section>                                                             */
  /*    gzip                                                               */
  /*                                                                       */
  /* <Title>                                                               */
  /*    GZIP Streams                                                       */
  /*                                                                       */
  /* <Abstract>                                                            */
  /*    Using gzip-compressed font files.                                  */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This section contains the declaration of Gzip-specific functions.  */
  /*                                                                       */
  /*************************************************************************/


 /************************************************************************
  *
  * @function:
  *   FT_Stream_OpenGzip
  *
  * @description:
  *   Open a new stream to parse gzip-compressed font files.  This is
  *   mainly used to support the compressed `*.pcf.gz' fonts that come
  *   with XFree86.
  *
  * @input:
  *   stream ::
  *     The target embedding stream.
  *
  *   source ::
  *     The source stream.
  *
  * @return:
  *   FreeType error code.  0~means success.
  *
  * @note:
  *   The source stream must be opened _before_ calling this function.
  *
  *   Calling the internal function `FT_Stream_Close' on the new stream will
  *   *not* call `FT_Stream_Close' on the source stream.  None of the stream
  *   objects will be released to the heap.
  *
  *   The stream implementation is very basic and resets the decompression
  *   process each time seeking backwards is needed within the stream.
  *
  *   In certain builds of the library, gzip compression recognition is
  *   automatically handled when calling @FT_New_Face or @FT_Open_Face.
  *   This means that if no font driver is capable of handling the raw
  *   compressed file, the library will try to open a gzipped stream from
  *   it and re-open the face with it.
  *
  *   This function may return `FT_Err_Unimplemented_Feature' if your build
  *   of FreeType was not compiled with zlib support.
  */
  FT_EXPORT( FT_Error )
  FT_Stream_OpenGzip( FT_Stream  stream,
                      FT_Stream  source );

 /* */


FT_END_HEADER

#endif /* __FTGZIP_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  ftimage.h                                                              */
/*                                                                         */
/*    FreeType glyph image formats and default raster interface            */
/*    (specification).                                                     */
/*                                                                         */
/*  Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,   */
/*            2010 by                                                      */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/

  /*************************************************************************/
  /*                                                                       */
  /* Note: A `raster' is simply a scan-line converter, used to render      */
  /*       FT_Outlines into FT_Bitmaps.                                    */
  /*                                                                       */
  /*************************************************************************/


#ifndef __FTIMAGE_H__
#define __FTIMAGE_H__


  /* _STANDALONE_ is from ftgrays.c */
#ifndef _STANDALONE_
#include <ft2build.h>
#endif


FT_BEGIN_HEADER


  /*************************************************************************/
  /*                                                                       */
  /* <Section>                                                             */
  /*    basic_types                                                        */
  /*                                                                       */
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FT_Pos                                                             */
  /*                                                                       */
  /* <Description>                                                         */
  /*    The type FT_Pos is used to store vectorial coordinates.  Depending */
  /*    on the context, these can represent distances in integer font      */
  /*    units, or 16.16, or 26.6 fixed float pixel coordinates.            */
  /*                                                                       */
  typedef signed long  FT_Pos;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_Vector                                                          */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A simple structure used to store a 2D vector; coordinates are of   */
  /*    the FT_Pos type.                                                   */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    x :: The horizontal coordinate.                                    */
  /*    y :: The vertical coordinate.                                      */
  /*                                                                       */
  typedef struct  FT_Vector_
  {
    FT_Pos  x;
    FT_Pos  y;

  } FT_Vector;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_BBox                                                            */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A structure used to hold an outline's bounding box, i.e., the      */
  /*    coordinates of its extrema in the horizontal and vertical          */
  /*    directions.                                                        */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    xMin :: The horizontal minimum (left-most).                        */
  /*                                                                       */
  /*    yMin :: The vertical minimum (bottom-most).                        */
  /*                                                                       */
  /*    xMax :: The horizontal maximum (right-most).                       */
  /*                                                                       */
  /*    yMax :: The vertical maximum (top-most).                           */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The bounding box is specified with the coordinates of the lower    */
  /*    left and the upper right corner.  In PostScript, those values are  */
  /*    often called (llx,lly) and (urx,ury), respectively.                */
  /*                                                                       */
  /*    If `yMin' is negative, this value gives the glyph's descender.     */
  /*    Otherwise, the glyph doesn't descend below the baseline.           */
  /*    Similarly, if `ymax' is positive, this value gives the glyph's     */
  /*    ascender.                                                          */
  /*                                                                       */
  /*    `xMin' gives the horizontal distance from the glyph's origin to    */
  /*    the left edge of the glyph's bounding box.  If `xMin' is negative, */
  /*    the glyph extends to the left of the origin.                       */
  /*                                                                       */
  typedef struct  FT_BBox_
  {
    FT_Pos  xMin, yMin;
    FT_Pos  xMax, yMax;

  } FT_BBox;


  /*************************************************************************/
  /*                                                                       */
  /* <Enum>                                                                */
  /*    FT_Pixel_Mode                                                      */
  /*                                                                       */
  /* <Description>                                                         */
  /*    An enumeration type used to describe the format of pixels in a     */
  /*    given bitmap.  Note that additional formats may be added in the    */
  /*    future.                                                            */
  /*                                                                       */
  /* <Values>                                                              */
  /*    FT_PIXEL_MODE_NONE ::                                              */
  /*      Value~0 is reserved.                                             */
  /*                                                                       */
  /*    FT_PIXEL_MODE_MONO ::                                              */
  /*      A monochrome bitmap, using 1~bit per pixel.  Note that pixels    */
  /*      are stored in most-significant order (MSB), which means that     */
  /*      the left-most pixel in a byte has value 128.                     */
  /*                                                                       */
  /*    FT_PIXEL_MODE_GRAY ::                                              */
  /*      An 8-bit bitmap, generally used to represent anti-aliased glyph  */
  /*      images.  Each pixel is stored in one byte.  Note that the number */
  /*      of `gray' levels is stored in the `num_grays' field of the       */
  /*      @FT_Bitmap structure (it generally is 256).                      */
  /*                                                                       */
  /*    FT_PIXEL_MODE_GRAY2 ::                                             */
  /*      A 2-bit per pixel bitmap, used to represent embedded             */
  /*      anti-aliased bitmaps in font files according to the OpenType     */
  /*      specification.  We haven't found a single font using this        */
  /*      format, however.                                                 */
  /*                                                                       */
  /*    FT_PIXEL_MODE_GRAY4 ::                                             */
  /*      A 4-bit per pixel bitmap, representing embedded anti-aliased     */
  /*      bitmaps in font files according to the OpenType specification.   */
  /*      We haven't found a single font using this format, however.       */
  /*                                                                       */
  /*    FT_PIXEL_MODE_LCD ::                                               */
  /*      An 8-bit bitmap, representing RGB or BGR decimated glyph images  */
  /*      used for display on LCD displays; the bitmap is three times      */
  /*      wider than the original glyph image.  See also                   */
  /*      @FT_RENDER_MODE_LCD.                                             */
  /*                                                                       */
  /*    FT_PIXEL_MODE_LCD_V ::                                             */
  /*      An 8-bit bitmap, representing RGB or BGR decimated glyph images  */
  /*      used for display on rotated LCD displays; the bitmap is three    */
  /*      times taller than the original glyph image.  See also            */
  /*      @FT_RENDER_MODE_LCD_V.                                           */
  /*                                                                       */
  typedef enum  FT_Pixel_Mode_
  {
    FT_PIXEL_MODE_NONE = 0,
    FT_PIXEL_MODE_MONO,
    FT_PIXEL_MODE_GRAY,
    FT_PIXEL_MODE_GRAY2,
    FT_PIXEL_MODE_GRAY4,
    FT_PIXEL_MODE_LCD,
    FT_PIXEL_MODE_LCD_V,

    FT_PIXEL_MODE_MAX      /* do not remove */

  } FT_Pixel_Mode;


  /*************************************************************************/
  /*                                                                       */
  /* <Enum>                                                                */
  /*    ft_pixel_mode_xxx                                                  */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A list of deprecated constants.  Use the corresponding             */
  /*    @FT_Pixel_Mode values instead.                                     */
  /*                                                                       */
  /* <Values>                                                              */
  /*    ft_pixel_mode_none  :: See @FT_PIXEL_MODE_NONE.                    */
  /*    ft_pixel_mode_mono  :: See @FT_PIXEL_MODE_MONO.                    */
  /*    ft_pixel_mode_grays :: See @FT_PIXEL_MODE_GRAY.                    */
  /*    ft_pixel_mode_pal2  :: See @FT_PIXEL_MODE_GRAY2.                   */
  /*    ft_pixel_mode_pal4  :: See @FT_PIXEL_MODE_GRAY4.                   */
  /*                                                                       */
#define ft_pixel_mode_none   FT_PIXEL_MODE_NONE
#define ft_pixel_mode_mono   FT_PIXEL_MODE_MONO
#define ft_pixel_mode_grays  FT_PIXEL_MODE_GRAY
#define ft_pixel_mode_pal2   FT_PIXEL_MODE_GRAY2
#define ft_pixel_mode_pal4   FT_PIXEL_MODE_GRAY4

 /* */

#if 0

  /*************************************************************************/
  /*                                                                       */
  /* <Enum>                                                                */
  /*    FT_Palette_Mode                                                    */
  /*                                                                       */
  /* <Description>                                                         */
  /*    THIS TYPE IS DEPRECATED.  DO NOT USE IT!                           */
  /*                                                                       */
  /*    An enumeration type to describe the format of a bitmap palette,    */
  /*    used with ft_pixel_mode_pal4 and ft_pixel_mode_pal8.               */
  /*                                                                       */
  /* <Values>                                                              */
  /*    ft_palette_mode_rgb  :: The palette is an array of 3-byte RGB      */
  /*                            records.                                   */
  /*                                                                       */
  /*    ft_palette_mode_rgba :: The palette is an array of 4-byte RGBA     */
  /*                            records.                                   */
  /*                                                                       */
  /* <Note>                                                                */
  /*    As ft_pixel_mode_pal2, pal4 and pal8 are currently unused by       */
  /*    FreeType, these types are not handled by the library itself.       */
  /*                                                                       */
  typedef enum  FT_Palette_Mode_
  {
    ft_palette_mode_rgb = 0,
    ft_palette_mode_rgba,

    ft_palette_mode_max   /* do not remove */

  } FT_Palette_Mode;

  /* */

#endif


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_Bitmap                                                          */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A structure used to describe a bitmap or pixmap to the raster.     */
  /*    Note that we now manage pixmaps of various depths through the      */
  /*    `pixel_mode' field.                                                */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    rows         :: The number of bitmap rows.                         */
  /*                                                                       */
  /*    width        :: The number of pixels in bitmap row.                */
  /*                                                                       */
  /*    pitch        :: The pitch's absolute value is the number of bytes  */
  /*                    taken by one bitmap row, including padding.        */
  /*                    However, the pitch is positive when the bitmap has */
  /*                    a `down' flow, and negative when it has an `up'    */
  /*                    flow.  In all cases, the pitch is an offset to add */
  /*                    to a bitmap pointer in order to go down one row.   */
  /*                                                                       */
  /*                    Note that `padding' means the alignment of a       */
  /*                    bitmap to a byte border, and FreeType functions    */
  /*                    normally align to the smallest possible integer    */
  /*                    value.                                             */
  /*                                                                       */
  /*                    For the B/W rasterizer, `pitch' is always an even  */
  /*                    number.                                            */
  /*                                                                       */
  /*                    To change the pitch of a bitmap (say, to make it a */
  /*                    multiple of 4), use @FT_Bitmap_Convert.            */
  /*                    Alternatively, you might use callback functions to */
  /*                    directly render to the application's surface; see  */
  /*                    the file `example2.cpp' in the tutorial for a      */
  /*                    demonstration.                                     */
  /*                                                                       */
  /*    buffer       :: A typeless pointer to the bitmap buffer.  This     */
  /*                    value should be aligned on 32-bit boundaries in    */
  /*                    most cases.                                        */
  /*                                                                       */
  /*    num_grays    :: This field is only used with                       */
  /*                    @FT_PIXEL_MODE_GRAY; it gives the number of gray   */
  /*                    levels used in the bitmap.                         */
  /*                                                                       */
  /*    pixel_mode   :: The pixel mode, i.e., how pixel bits are stored.   */
  /*                    See @FT_Pixel_Mode for possible values.            */
  /*                                                                       */
  /*    palette_mode :: This field is intended for paletted pixel modes;   */
  /*                    it indicates how the palette is stored.  Not       */
  /*                    used currently.                                    */
  /*                                                                       */
  /*    palette      :: A typeless pointer to the bitmap palette; this     */
  /*                    field is intended for paletted pixel modes.  Not   */
  /*                    used currently.                                    */
  /*                                                                       */
  /* <Note>                                                                */
  /*   For now, the only pixel modes supported by FreeType are mono and    */
  /*   grays.  However, drivers might be added in the future to support    */
  /*   more `colorful' options.                                            */
  /*                                                                       */
  typedef struct  FT_Bitmap_
  {
    int             rows;
    int             width;
    int             pitch;
    unsigned char*  buffer;
    short           num_grays;
    char            pixel_mode;
    char            palette_mode;
    void*           palette;

  } FT_Bitmap;


  /*************************************************************************/
  /*                                                                       */
  /* <Section>                                                             */
  /*    outline_processing                                                 */
  /*                                                                       */
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_Outline                                                         */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This structure is used to describe an outline to the scan-line     */
  /*    converter.                                                         */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    n_contours :: The number of contours in the outline.               */
  /*                                                                       */
  /*    n_points   :: The number of points in the outline.                 */
  /*                                                                       */
  /*    points     :: A pointer to an array of `n_points' @FT_Vector       */
  /*                  elements, giving the outline's point coordinates.    */
  /*                                                                       */
  /*    tags       :: A pointer to an array of `n_points' chars, giving    */
  /*                  each outline point's type.                           */
  /*                                                                       */
  /*                  If bit~0 is unset, the point is `off' the curve,     */
  /*                  i.e., a BÃ©zier control point, while it is `on' if    */
  /*                  set.                                                 */
  /*                                                                       */
  /*                  Bit~1 is meaningful for `off' points only.  If set,  */
  /*                  it indicates a third-order BÃ©zier arc control point; */
  /*                  and a second-order control point if unset.           */
  /*                                                                       */
  /*                  If bit~2 is set, bits 5-7 contain the drop-out mode  */
  /*                  (as defined in the OpenType specification; the value */
  /*                  is the same as the argument to the SCANMODE          */
  /*                  instruction).                                        */
  /*                                                                       */
  /*                  Bits 3 and~4 are reserved for internal purposes.     */
  /*                                                                       */
  /*    contours   :: An array of `n_contours' shorts, giving the end      */
  /*                  point of each contour within the outline.  For       */
  /*                  example, the first contour is defined by the points  */
  /*                  `0' to `contours[0]', the second one is defined by   */
  /*                  the points `contours[0]+1' to `contours[1]', etc.    */
  /*                                                                       */
  /*    flags      :: A set of bit flags used to characterize the outline  */
  /*                  and give hints to the scan-converter and hinter on   */
  /*                  how to convert/grid-fit it.  See @FT_OUTLINE_FLAGS.  */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The B/W rasterizer only checks bit~2 in the `tags' array for the   */
  /*    first point of each contour.  The drop-out mode as given with      */
  /*    @FT_OUTLINE_IGNORE_DROPOUTS, @FT_OUTLINE_SMART_DROPOUTS, and       */
  /*    @FT_OUTLINE_INCLUDE_STUBS in `flags' is then overridden.           */
  /*                                                                       */
  typedef struct  FT_Outline_
  {
    short       n_contours;      /* number of contours in glyph        */
    short       n_points;        /* number of points in the glyph      */

    FT_Vector*  points;          /* the outline's points               */
    char*       tags;            /* the points flags                   */
    short*      contours;        /* the contour end points             */

    int         flags;           /* outline masks                      */

  } FT_Outline;

  /* Following limits must be consistent with */
  /* FT_Outline.{n_contours,n_points}         */
#define FT_OUTLINE_CONTOURS_MAX  SHRT_MAX
#define FT_OUTLINE_POINTS_MAX    SHRT_MAX


  /*************************************************************************/
  /*                                                                       */
  /* <Enum>                                                                */
  /*    FT_OUTLINE_FLAGS                                                   */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A list of bit-field constants use for the flags in an outline's    */
  /*    `flags' field.                                                     */
  /*                                                                       */
  /* <Values>                                                              */
  /*    FT_OUTLINE_NONE ::                                                 */
  /*      Value~0 is reserved.                                             */
  /*                                                                       */
  /*    FT_OUTLINE_OWNER ::                                                */
  /*      If set, this flag indicates that the outline's field arrays      */
  /*      (i.e., `points', `flags', and `contours') are `owned' by the     */
  /*      outline object, and should thus be freed when it is destroyed.   */
  /*                                                                       */
  /*    FT_OUTLINE_EVEN_ODD_FILL ::                                        */
  /*      By default, outlines are filled using the non-zero winding rule. */
  /*      If set to 1, the outline will be filled using the even-odd fill  */
  /*      rule (only works with the smooth rasterizer).                    */
  /*                                                                       */
  /*    FT_OUTLINE_REVERSE_FILL ::                                         */
  /*      By default, outside contours of an outline are oriented in       */
  /*      clock-wise direction, as defined in the TrueType specification.  */
  /*      This flag is set if the outline uses the opposite direction      */
  /*      (typically for Type~1 fonts).  This flag is ignored by the scan  */
  /*      converter.                                                       */
  /*                                                                       */
  /*    FT_OUTLINE_IGNORE_DROPOUTS ::                                      */
  /*      By default, the scan converter will try to detect drop-outs in   */
  /*      an outline and correct the glyph bitmap to ensure consistent     */
  /*      shape continuity.  If set, this flag hints the scan-line         */
  /*      converter to ignore such cases.  See below for more information. */
  /*                                                                       */
  /*    FT_OUTLINE_SMART_DROPOUTS ::                                       */
  /*      Select smart dropout control.  If unset, use simple dropout      */
  /*      control.  Ignored if @FT_OUTLINE_IGNORE_DROPOUTS is set.  See    */
  /*      below for more information.                                      */
  /*                                                                       */
  /*    FT_OUTLINE_INCLUDE_STUBS ::                                        */
  /*      If set, turn pixels on for `stubs', otherwise exclude them.      */
  /*      Ignored if @FT_OUTLINE_IGNORE_DROPOUTS is set.  See below for    */
  /*      more information.                                                */
  /*                                                                       */
  /*    FT_OUTLINE_HIGH_PRECISION ::                                       */
  /*      This flag indicates that the scan-line converter should try to   */
  /*      convert this outline to bitmaps with the highest possible        */
  /*      quality.  It is typically set for small character sizes.  Note   */
  /*      that this is only a hint that might be completely ignored by a   */
  /*      given scan-converter.                                            */
  /*                                                                       */
  /*    FT_OUTLINE_SINGLE_PASS ::                                          */
  /*      This flag is set to force a given scan-converter to only use a   */
  /*      single pass over the outline to render a bitmap glyph image.     */
  /*      Normally, it is set for very large character sizes.  It is only  */
  /*      a hint that might be completely ignored by a given               */
  /*      scan-converter.                                                  */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The flags @FT_OUTLINE_IGNORE_DROPOUTS, @FT_OUTLINE_SMART_DROPOUTS, */
  /*    and @FT_OUTLINE_INCLUDE_STUBS are ignored by the smooth            */
  /*    rasterizer.                                                        */
  /*                                                                       */
  /*    There exists a second mechanism to pass the drop-out mode to the   */
  /*    B/W rasterizer; see the `tags' field in @FT_Outline.               */
  /*                                                                       */
  /*    Please refer to the description of the `SCANTYPE' instruction in   */
  /*    the OpenType specification (in file `ttinst1.doc') how simple      */
  /*    drop-outs, smart drop-outs, and stubs are defined.                 */
  /*                                                                       */
#define FT_OUTLINE_NONE             0x0
#define FT_OUTLINE_OWNER            0x1
#define FT_OUTLINE_EVEN_ODD_FILL    0x2
#define FT_OUTLINE_REVERSE_FILL     0x4
#define FT_OUTLINE_IGNORE_DROPOUTS  0x8
#define FT_OUTLINE_SMART_DROPOUTS   0x10
#define FT_OUTLINE_INCLUDE_STUBS    0x20

#define FT_OUTLINE_HIGH_PRECISION   0x100
#define FT_OUTLINE_SINGLE_PASS      0x200


 /*************************************************************************
  *
  * @enum:
  *   ft_outline_flags
  *
  * @description:
  *   These constants are deprecated.  Please use the corresponding
  *   @FT_OUTLINE_FLAGS values.
  *
  * @values:
  *   ft_outline_none            :: See @FT_OUTLINE_NONE.
  *   ft_outline_owner           :: See @FT_OUTLINE_OWNER.
  *   ft_outline_even_odd_fill   :: See @FT_OUTLINE_EVEN_ODD_FILL.
  *   ft_outline_reverse_fill    :: See @FT_OUTLINE_REVERSE_FILL.
  *   ft_outline_ignore_dropouts :: See @FT_OUTLINE_IGNORE_DROPOUTS.
  *   ft_outline_high_precision  :: See @FT_OUTLINE_HIGH_PRECISION.
  *   ft_outline_single_pass     :: See @FT_OUTLINE_SINGLE_PASS.
  */
#define ft_outline_none             FT_OUTLINE_NONE
#define ft_outline_owner            FT_OUTLINE_OWNER
#define ft_outline_even_odd_fill    FT_OUTLINE_EVEN_ODD_FILL
#define ft_outline_reverse_fill     FT_OUTLINE_REVERSE_FILL
#define ft_outline_ignore_dropouts  FT_OUTLINE_IGNORE_DROPOUTS
#define ft_outline_high_precision   FT_OUTLINE_HIGH_PRECISION
#define ft_outline_single_pass      FT_OUTLINE_SINGLE_PASS

  /* */

#define FT_CURVE_TAG( flag )  ( flag & 3 )

#define FT_CURVE_TAG_ON            1
#define FT_CURVE_TAG_CONIC         0
#define FT_CURVE_TAG_CUBIC         2

#define FT_CURVE_TAG_HAS_SCANMODE  4

#define FT_CURVE_TAG_TOUCH_X       8  /* reserved for the TrueType hinter */
#define FT_CURVE_TAG_TOUCH_Y      16  /* reserved for the TrueType hinter */

#define FT_CURVE_TAG_TOUCH_BOTH    ( FT_CURVE_TAG_TOUCH_X | \
                                     FT_CURVE_TAG_TOUCH_Y )

#define FT_Curve_Tag_On       FT_CURVE_TAG_ON
#define FT_Curve_Tag_Conic    FT_CURVE_TAG_CONIC
#define FT_Curve_Tag_Cubic    FT_CURVE_TAG_CUBIC
#define FT_Curve_Tag_Touch_X  FT_CURVE_TAG_TOUCH_X
#define FT_Curve_Tag_Touch_Y  FT_CURVE_TAG_TOUCH_Y


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    FT_Outline_MoveToFunc                                              */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A function pointer type used to describe the signature of a `move  */
  /*    to' function during outline walking/decomposition.                 */
  /*                                                                       */
  /*    A `move to' is emitted to start a new contour in an outline.       */
  /*                                                                       */
  /* <Input>                                                               */
  /*    to   :: A pointer to the target point of the `move to'.            */
  /*                                                                       */
  /*    user :: A typeless pointer which is passed from the caller of the  */
  /*            decomposition function.                                    */
  /*                                                                       */
  /* <Return>                                                              */
  /*    Error code.  0~means success.                                      */
  /*                                                                       */
  typedef int
  (*FT_Outline_MoveToFunc)( const FT_Vector*  to,
                            void*             user );

#define FT_Outline_MoveTo_Func  FT_Outline_MoveToFunc


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    FT_Outline_LineToFunc                                              */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A function pointer type used to describe the signature of a `line  */
  /*    to' function during outline walking/decomposition.                 */
  /*                                                                       */
  /*    A `line to' is emitted to indicate a segment in the outline.       */
  /*                                                                       */
  /* <Input>                                                               */
  /*    to   :: A pointer to the target point of the `line to'.            */
  /*                                                                       */
  /*    user :: A typeless pointer which is passed from the caller of the  */
  /*            decomposition function.                                    */
  /*                                                                       */
  /* <Return>                                                              */
  /*    Error code.  0~means success.                                      */
  /*                                                                       */
  typedef int
  (*FT_Outline_LineToFunc)( const FT_Vector*  to,
                            void*             user );

#define FT_Outline_LineTo_Func  FT_Outline_LineToFunc


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    FT_Outline_ConicToFunc                                             */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A function pointer type used to describe the signature of a `conic */
  /*    to' function during outline walking or decomposition.              */
  /*                                                                       */
  /*    A `conic to' is emitted to indicate a second-order BÃ©zier arc in   */
  /*    the outline.                                                       */
  /*                                                                       */
  /* <Input>                                                               */
  /*    control :: An intermediate control point between the last position */
  /*               and the new target in `to'.                             */
  /*                                                                       */
  /*    to      :: A pointer to the target end point of the conic arc.     */
  /*                                                                       */
  /*    user    :: A typeless pointer which is passed from the caller of   */
  /*               the decomposition function.                             */
  /*                                                                       */
  /* <Return>                                                              */
  /*    Error code.  0~means success.                                      */
  /*                                                                       */
  typedef int
  (*FT_Outline_ConicToFunc)( const FT_Vector*  control,
                             const FT_Vector*  to,
                             void*             user );

#define FT_Outline_ConicTo_Func  FT_Outline_ConicToFunc


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    FT_Outline_CubicToFunc                                             */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A function pointer type used to describe the signature of a `cubic */
  /*    to' function during outline walking or decomposition.              */
  /*                                                                       */
  /*    A `cubic to' is emitted to indicate a third-order BÃ©zier arc.      */
  /*                                                                       */
  /* <Input>                                                               */
  /*    control1 :: A pointer to the first BÃ©zier control point.           */
  /*                                                                       */
  /*    control2 :: A pointer to the second BÃ©zier control point.          */
  /*                                                                       */
  /*    to       :: A pointer to the target end point.                     */
  /*                                                                       */
  /*    user     :: A typeless pointer which is passed from the caller of  */
  /*                the decomposition function.                            */
  /*                                                                       */
  /* <Return>                                                              */
  /*    Error code.  0~means success.                                      */
  /*                                                                       */
  typedef int
  (*FT_Outline_CubicToFunc)( const FT_Vector*  control1,
                             const FT_Vector*  control2,
                             const FT_Vector*  to,
                             void*             user );

#define FT_Outline_CubicTo_Func  FT_Outline_CubicToFunc


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_Outline_Funcs                                                   */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A structure to hold various function pointers used during outline  */
  /*    decomposition in order to emit segments, conic, and cubic BÃ©ziers. */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    move_to  :: The `move to' emitter.                                 */
  /*                                                                       */
  /*    line_to  :: The segment emitter.                                   */
  /*                                                                       */
  /*    conic_to :: The second-order BÃ©zier arc emitter.                   */
  /*                                                                       */
  /*    cubic_to :: The third-order BÃ©zier arc emitter.                    */
  /*                                                                       */
  /*    shift    :: The shift that is applied to coordinates before they   */
  /*                are sent to the emitter.                               */
  /*                                                                       */
  /*    delta    :: The delta that is applied to coordinates before they   */
  /*                are sent to the emitter, but after the shift.          */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The point coordinates sent to the emitters are the transformed     */
  /*    version of the original coordinates (this is important for high    */
  /*    accuracy during scan-conversion).  The transformation is simple:   */
  /*                                                                       */
  /*    {                                                                  */
  /*      x' = (x << shift) - delta                                        */
  /*      y' = (x << shift) - delta                                        */
  /*    }                                                                  */
  /*                                                                       */
  /*    Set the values of `shift' and `delta' to~0 to get the original     */
  /*    point coordinates.                                                 */
  /*                                                                       */
  typedef struct  FT_Outline_Funcs_
  {
    FT_Outline_MoveToFunc   move_to;
    FT_Outline_LineToFunc   line_to;
    FT_Outline_ConicToFunc  conic_to;
    FT_Outline_CubicToFunc  cubic_to;

    int                     shift;
    FT_Pos                  delta;

  } FT_Outline_Funcs;


  /*************************************************************************/
  /*                                                                       */
  /* <Section>                                                             */
  /*    basic_types                                                        */
  /*                                                                       */
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* <Macro>                                                               */
  /*    FT_IMAGE_TAG                                                       */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This macro converts four-letter tags to an unsigned long type.     */
  /*                                                                       */
  /* <Note>                                                                */
  /*    Since many 16-bit compilers don't like 32-bit enumerations, you    */
  /*    should redefine this macro in case of problems to something like   */
  /*    this:                                                              */
  /*                                                                       */
  /*    {                                                                  */
  /*      #define FT_IMAGE_TAG( value, _x1, _x2, _x3, _x4 )  value         */
  /*    }                                                                  */
  /*                                                                       */
  /*    to get a simple enumeration without assigning special numbers.     */
  /*                                                                       */
#ifndef FT_IMAGE_TAG
#define FT_IMAGE_TAG( value, _x1, _x2, _x3, _x4 )  \
          value = ( ( (unsigned long)_x1 << 24 ) | \
                    ( (unsigned long)_x2 << 16 ) | \
                    ( (unsigned long)_x3 << 8  ) | \
                      (unsigned long)_x4         )
#endif /* FT_IMAGE_TAG */


  /*************************************************************************/
  /*                                                                       */
  /* <Enum>                                                                */
  /*    FT_Glyph_Format                                                    */
  /*                                                                       */
  /* <Description>                                                         */
  /*    An enumeration type used to describe the format of a given glyph   */
  /*    image.  Note that this version of FreeType only supports two image */
  /*    formats, even though future font drivers will be able to register  */
  /*    their own format.                                                  */
  /*                                                                       */
  /* <Values>                                                              */
  /*    FT_GLYPH_FORMAT_NONE ::                                            */
  /*      The value~0 is reserved.                                         */
  /*                                                                       */
  /*    FT_GLYPH_FORMAT_COMPOSITE ::                                       */
  /*      The glyph image is a composite of several other images.  This    */
  /*      format is _only_ used with @FT_LOAD_NO_RECURSE, and is used to   */
  /*      report compound glyphs (like accented characters).               */
  /*                                                                       */
  /*    FT_GLYPH_FORMAT_BITMAP ::                                          */
  /*      The glyph image is a bitmap, and can be described as an          */
  /*      @FT_Bitmap.  You generally need to access the `bitmap' field of  */
  /*      the @FT_GlyphSlotRec structure to read it.                       */
  /*                                                                       */
  /*    FT_GLYPH_FORMAT_OUTLINE ::                                         */
  /*      The glyph image is a vectorial outline made of line segments     */
  /*      and BÃ©zier arcs; it can be described as an @FT_Outline; you      */
  /*      generally want to access the `outline' field of the              */
  /*      @FT_GlyphSlotRec structure to read it.                           */
  /*                                                                       */
  /*    FT_GLYPH_FORMAT_PLOTTER ::                                         */
  /*      The glyph image is a vectorial path with no inside and outside   */
  /*      contours.  Some Type~1 fonts, like those in the Hershey family,  */
  /*      contain glyphs in this format.  These are described as           */
  /*      @FT_Outline, but FreeType isn't currently capable of rendering   */
  /*      them correctly.                                                  */
  /*                                                                       */
  typedef enum  FT_Glyph_Format_
  {
    FT_IMAGE_TAG( FT_GLYPH_FORMAT_NONE, 0, 0, 0, 0 ),

    FT_IMAGE_TAG( FT_GLYPH_FORMAT_COMPOSITE, 'c', 'o', 'm', 'p' ),
    FT_IMAGE_TAG( FT_GLYPH_FORMAT_BITMAP,    'b', 'i', 't', 's' ),
    FT_IMAGE_TAG( FT_GLYPH_FORMAT_OUTLINE,   'o', 'u', 't', 'l' ),
    FT_IMAGE_TAG( FT_GLYPH_FORMAT_PLOTTER,   'p', 'l', 'o', 't' )

  } FT_Glyph_Format;


  /*************************************************************************/
  /*                                                                       */
  /* <Enum>                                                                */
  /*    ft_glyph_format_xxx                                                */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A list of deprecated constants.  Use the corresponding             */
  /*    @FT_Glyph_Format values instead.                                   */
  /*                                                                       */
  /* <Values>                                                              */
  /*    ft_glyph_format_none      :: See @FT_GLYPH_FORMAT_NONE.            */
  /*    ft_glyph_format_composite :: See @FT_GLYPH_FORMAT_COMPOSITE.       */
  /*    ft_glyph_format_bitmap    :: See @FT_GLYPH_FORMAT_BITMAP.          */
  /*    ft_glyph_format_outline   :: See @FT_GLYPH_FORMAT_OUTLINE.         */
  /*    ft_glyph_format_plotter   :: See @FT_GLYPH_FORMAT_PLOTTER.         */
  /*                                                                       */
#define ft_glyph_format_none       FT_GLYPH_FORMAT_NONE
#define ft_glyph_format_composite  FT_GLYPH_FORMAT_COMPOSITE
#define ft_glyph_format_bitmap     FT_GLYPH_FORMAT_BITMAP
#define ft_glyph_format_outline    FT_GLYPH_FORMAT_OUTLINE
#define ft_glyph_format_plotter    FT_GLYPH_FORMAT_PLOTTER


  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****            R A S T E R   D E F I N I T I O N S                *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* A raster is a scan converter, in charge of rendering an outline into  */
  /* a a bitmap.  This section contains the public API for rasters.        */
  /*                                                                       */
  /* Note that in FreeType 2, all rasters are now encapsulated within      */
  /* specific modules called `renderers'.  See `freetype/ftrender.h' for   */
  /* more details on renderers.                                            */
  /*                                                                       */
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* <Section>                                                             */
  /*    raster                                                             */
  /*                                                                       */
  /* <Title>                                                               */
  /*    Scanline Converter                                                 */
  /*                                                                       */
  /* <Abstract>                                                            */
  /*    How vectorial outlines are converted into bitmaps and pixmaps.     */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This section contains technical definitions.                       */
  /*                                                                       */
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FT_Raster                                                          */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A handle (pointer) to a raster object.  Each object can be used    */
  /*    independently to convert an outline into a bitmap or pixmap.       */
  /*                                                                       */
  typedef struct FT_RasterRec_*  FT_Raster;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_Span                                                            */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A structure used to model a single span of gray (or black) pixels  */
  /*    when rendering a monochrome or anti-aliased bitmap.                */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    x        :: The span's horizontal start position.                  */
  /*                                                                       */
  /*    len      :: The span's length in pixels.                           */
  /*                                                                       */
  /*    coverage :: The span color/coverage, ranging from 0 (background)   */
  /*                to 255 (foreground).  Only used for anti-aliased       */
  /*                rendering.                                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    This structure is used by the span drawing callback type named     */
  /*    @FT_SpanFunc which takes the y~coordinate of the span as a         */
  /*    a parameter.                                                       */
  /*                                                                       */
  /*    The coverage value is always between 0 and 255.  If you want less  */
  /*    gray values, the callback function has to reduce them.             */
  /*                                                                       */
  typedef struct  FT_Span_
  {
    short           x;
    unsigned short  len;
    unsigned char   coverage;

  } FT_Span;


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    FT_SpanFunc                                                        */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A function used as a call-back by the anti-aliased renderer in     */
  /*    order to let client applications draw themselves the gray pixel    */
  /*    spans on each scan line.                                           */
  /*                                                                       */
  /* <Input>                                                               */
  /*    y     :: The scanline's y~coordinate.                              */
  /*                                                                       */
  /*    count :: The number of spans to draw on this scanline.             */
  /*                                                                       */
  /*    spans :: A table of `count' spans to draw on the scanline.         */
  /*                                                                       */
  /*    user  :: User-supplied data that is passed to the callback.        */
  /*                                                                       */
  /* <Note>                                                                */
  /*    This callback allows client applications to directly render the    */
  /*    gray spans of the anti-aliased bitmap to any kind of surfaces.     */
  /*                                                                       */
  /*    This can be used to write anti-aliased outlines directly to a      */
  /*    given background bitmap, and even perform translucency.            */
  /*                                                                       */
  /*    Note that the `count' field cannot be greater than a fixed value   */
  /*    defined by the `FT_MAX_GRAY_SPANS' configuration macro in          */
  /*    `ftoption.h'.  By default, this value is set to~32, which means    */
  /*    that if there are more than 32~spans on a given scanline, the      */
  /*    callback is called several times with the same `y' parameter in    */
  /*    order to draw all callbacks.                                       */
  /*                                                                       */
  /*    Otherwise, the callback is only called once per scan-line, and     */
  /*    only for those scanlines that do have `gray' pixels on them.       */
  /*                                                                       */
  typedef void
  (*FT_SpanFunc)( int             y,
                  int             count,
                  const FT_Span*  spans,
                  void*           user );

#define FT_Raster_Span_Func  FT_SpanFunc


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    FT_Raster_BitTest_Func                                             */
  /*                                                                       */
  /* <Description>                                                         */
  /*    THIS TYPE IS DEPRECATED.  DO NOT USE IT.                           */
  /*                                                                       */
  /*    A function used as a call-back by the monochrome scan-converter    */
  /*    to test whether a given target pixel is already set to the drawing */
  /*    `color'.  These tests are crucial to implement drop-out control    */
  /*    per-se the TrueType spec.                                          */
  /*                                                                       */
  /* <Input>                                                               */
  /*    y     :: The pixel's y~coordinate.                                 */
  /*                                                                       */
  /*    x     :: The pixel's x~coordinate.                                 */
  /*                                                                       */
  /*    user  :: User-supplied data that is passed to the callback.        */
  /*                                                                       */
  /* <Return>                                                              */
  /*   1~if the pixel is `set', 0~otherwise.                               */
  /*                                                                       */
  typedef int
  (*FT_Raster_BitTest_Func)( int    y,
                             int    x,
                             void*  user );


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    FT_Raster_BitSet_Func                                              */
  /*                                                                       */
  /* <Description>                                                         */
  /*    THIS TYPE IS DEPRECATED.  DO NOT USE IT.                           */
  /*                                                                       */
  /*    A function used as a call-back by the monochrome scan-converter    */
  /*    to set an individual target pixel.  This is crucial to implement   */
  /*    drop-out control according to the TrueType specification.          */
  /*                                                                       */
  /* <Input>                                                               */
  /*    y     :: The pixel's y~coordinate.                                 */
  /*                                                                       */
  /*    x     :: The pixel's x~coordinate.                                 */
  /*                                                                       */
  /*    user  :: User-supplied data that is passed to the callback.        */
  /*                                                                       */
  /* <Return>                                                              */
  /*    1~if the pixel is `set', 0~otherwise.                              */
  /*                                                                       */
  typedef void
  (*FT_Raster_BitSet_Func)( int    y,
                            int    x,
                            void*  user );


  /*************************************************************************/
  /*                                                                       */
  /* <Enum>                                                                */
  /*    FT_RASTER_FLAG_XXX                                                 */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A list of bit flag constants as used in the `flags' field of a     */
  /*    @FT_Raster_Params structure.                                       */
  /*                                                                       */
  /* <Values>                                                              */
  /*    FT_RASTER_FLAG_DEFAULT :: This value is 0.                         */
  /*                                                                       */
  /*    FT_RASTER_FLAG_AA      :: This flag is set to indicate that an     */
  /*                              anti-aliased glyph image should be       */
  /*                              generated.  Otherwise, it will be        */
  /*                              monochrome (1-bit).                      */
  /*                                                                       */
  /*    FT_RASTER_FLAG_DIRECT  :: This flag is set to indicate direct      */
  /*                              rendering.  In this mode, client         */
  /*                              applications must provide their own span */
  /*                              callback.  This lets them directly       */
  /*                              draw or compose over an existing bitmap. */
  /*                              If this bit is not set, the target       */
  /*                              pixmap's buffer _must_ be zeroed before  */
  /*                              rendering.                               */
  /*                                                                       */
  /*                              Note that for now, direct rendering is   */
  /*                              only possible with anti-aliased glyphs.  */
  /*                                                                       */
  /*    FT_RASTER_FLAG_CLIP    :: This flag is only used in direct         */
  /*                              rendering mode.  If set, the output will */
  /*                              be clipped to a box specified in the     */
  /*                              `clip_box' field of the                  */
  /*                              @FT_Raster_Params structure.             */
  /*                                                                       */
  /*                              Note that by default, the glyph bitmap   */
  /*                              is clipped to the target pixmap, except  */
  /*                              in direct rendering mode where all spans */
  /*                              are generated if no clipping box is set. */
  /*                                                                       */
#define FT_RASTER_FLAG_DEFAULT  0x0
#define FT_RASTER_FLAG_AA       0x1
#define FT_RASTER_FLAG_DIRECT   0x2
#define FT_RASTER_FLAG_CLIP     0x4

  /* deprecated */
#define ft_raster_flag_default  FT_RASTER_FLAG_DEFAULT
#define ft_raster_flag_aa       FT_RASTER_FLAG_AA
#define ft_raster_flag_direct   FT_RASTER_FLAG_DIRECT
#define ft_raster_flag_clip     FT_RASTER_FLAG_CLIP


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_Raster_Params                                                   */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A structure to hold the arguments used by a raster's render        */
  /*    function.                                                          */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    target      :: The target bitmap.                                  */
  /*                                                                       */
  /*    source      :: A pointer to the source glyph image (e.g., an       */
  /*                   @FT_Outline).                                       */
  /*                                                                       */
  /*    flags       :: The rendering flags.                                */
  /*                                                                       */
  /*    gray_spans  :: The gray span drawing callback.                     */
  /*                                                                       */
  /*    black_spans :: The black span drawing callback.  UNIMPLEMENTED!    */
  /*                                                                       */
  /*    bit_test    :: The bit test callback.  UNIMPLEMENTED!              */
  /*                                                                       */
  /*    bit_set     :: The bit set callback.  UNIMPLEMENTED!               */
  /*                                                                       */
  /*    user        :: User-supplied data that is passed to each drawing   */
  /*                   callback.                                           */
  /*                                                                       */
  /*    clip_box    :: An optional clipping box.  It is only used in       */
  /*                   direct rendering mode.  Note that coordinates here  */
  /*                   should be expressed in _integer_ pixels (and not in */
  /*                   26.6 fixed-point units).                            */
  /*                                                                       */
  /* <Note>                                                                */
  /*    An anti-aliased glyph bitmap is drawn if the @FT_RASTER_FLAG_AA    */
  /*    bit flag is set in the `flags' field, otherwise a monochrome       */
  /*    bitmap is generated.                                               */
  /*                                                                       */
  /*    If the @FT_RASTER_FLAG_DIRECT bit flag is set in `flags', the      */
  /*    raster will call the `gray_spans' callback to draw gray pixel      */
  /*    spans, in the case of an aa glyph bitmap, it will call             */
  /*    `black_spans', and `bit_test' and `bit_set' in the case of a       */
  /*    monochrome bitmap.  This allows direct composition over a          */
  /*    pre-existing bitmap through user-provided callbacks to perform the */
  /*    span drawing/composition.                                          */
  /*                                                                       */
  /*    Note that the `bit_test' and `bit_set' callbacks are required when */
  /*    rendering a monochrome bitmap, as they are crucial to implement    */
  /*    correct drop-out control as defined in the TrueType specification. */
  /*                                                                       */
  typedef struct  FT_Raster_Params_
  {
    const FT_Bitmap*        target;
    const void*             source;
    int                     flags;
    FT_SpanFunc             gray_spans;
    FT_SpanFunc             black_spans;  /* doesn't work! */
    FT_Raster_BitTest_Func  bit_test;     /* doesn't work! */
    FT_Raster_BitSet_Func   bit_set;      /* doesn't work! */
    void*                   user;
    FT_BBox                 clip_box;

  } FT_Raster_Params;


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    FT_Raster_NewFunc                                                  */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A function used to create a new raster object.                     */
  /*                                                                       */
  /* <Input>                                                               */
  /*    memory :: A handle to the memory allocator.                        */
  /*                                                                       */
  /* <Output>                                                              */
  /*    raster :: A handle to the new raster object.                       */
  /*                                                                       */
  /* <Return>                                                              */
  /*    Error code.  0~means success.                                      */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The `memory' parameter is a typeless pointer in order to avoid     */
  /*    un-wanted dependencies on the rest of the FreeType code.  In       */
  /*    practice, it is an @FT_Memory object, i.e., a handle to the        */
  /*    standard FreeType memory allocator.  However, this field can be    */
  /*    completely ignored by a given raster implementation.               */
  /*                                                                       */
  typedef int
  (*FT_Raster_NewFunc)( void*       memory,
                        FT_Raster*  raster );

#define FT_Raster_New_Func  FT_Raster_NewFunc


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    FT_Raster_DoneFunc                                                 */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A function used to destroy a given raster object.                  */
  /*                                                                       */
  /* <Input>                                                               */
  /*    raster :: A handle to the raster object.                           */
  /*                                                                       */
  typedef void
  (*FT_Raster_DoneFunc)( FT_Raster  raster );

#define FT_Raster_Done_Func  FT_Raster_DoneFunc


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    FT_Raster_ResetFunc                                                */
  /*                                                                       */
  /* <Description>                                                         */
  /*    FreeType provides an area of memory called the `render pool',      */
  /*    available to all registered rasters.  This pool can be freely used */
  /*    during a given scan-conversion but is shared by all rasters.  Its  */
  /*    content is thus transient.                                         */
  /*                                                                       */
  /*    This function is called each time the render pool changes, or just */
  /*    after a new raster object is created.                              */
  /*                                                                       */
  /* <Input>                                                               */
  /*    raster    :: A handle to the new raster object.                    */
  /*                                                                       */
  /*    pool_base :: The address in memory of the render pool.             */
  /*                                                                       */
  /*    pool_size :: The size in bytes of the render pool.                 */
  /*                                                                       */
  /* <Note>                                                                */
  /*    Rasters can ignore the render pool and rely on dynamic memory      */
  /*    allocation if they want to (a handle to the memory allocator is    */
  /*    passed to the raster constructor).  However, this is not           */
  /*    recommended for efficiency purposes.                               */
  /*                                                                       */
  typedef void
  (*FT_Raster_ResetFunc)( FT_Raster       raster,
                          unsigned char*  pool_base,
                          unsigned long   pool_size );

#define FT_Raster_Reset_Func  FT_Raster_ResetFunc


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    FT_Raster_SetModeFunc                                              */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This function is a generic facility to change modes or attributes  */
  /*    in a given raster.  This can be used for debugging purposes, or    */
  /*    simply to allow implementation-specific `features' in a given      */
  /*    raster module.                                                     */
  /*                                                                       */
  /* <Input>                                                               */
  /*    raster :: A handle to the new raster object.                       */
  /*                                                                       */
  /*    mode   :: A 4-byte tag used to name the mode or property.          */
  /*                                                                       */
  /*    args   :: A pointer to the new mode/property to use.               */
  /*                                                                       */
  typedef int
  (*FT_Raster_SetModeFunc)( FT_Raster      raster,
                            unsigned long  mode,
                            void*          args );

#define FT_Raster_Set_Mode_Func  FT_Raster_SetModeFunc


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    FT_Raster_RenderFunc                                               */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Invoke a given raster to scan-convert a given glyph image into a   */
  /*    target bitmap.                                                     */
  /*                                                                       */
  /* <Input>                                                               */
  /*    raster :: A handle to the raster object.                           */
  /*                                                                       */
  /*    params :: A pointer to an @FT_Raster_Params structure used to      */
  /*              store the rendering parameters.                          */
  /*                                                                       */
  /* <Return>                                                              */
  /*    Error code.  0~means success.                                      */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The exact format of the source image depends on the raster's glyph */
  /*    format defined in its @FT_Raster_Funcs structure.  It can be an    */
  /*    @FT_Outline or anything else in order to support a large array of  */
  /*    glyph formats.                                                     */
  /*                                                                       */
  /*    Note also that the render function can fail and return a           */
  /*    `FT_Err_Unimplemented_Feature' error code if the raster used does  */
  /*    not support direct composition.                                    */
  /*                                                                       */
  /*    XXX: For now, the standard raster doesn't support direct           */
  /*         composition but this should change for the final release (see */
  /*         the files `demos/src/ftgrays.c' and `demos/src/ftgrays2.c'    */
  /*         for examples of distinct implementations which support direct */
  /*         composition).                                                 */
  /*                                                                       */
  typedef int
  (*FT_Raster_RenderFunc)( FT_Raster                raster,
                           const FT_Raster_Params*  params );

#define FT_Raster_Render_Func  FT_Raster_RenderFunc


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_Raster_Funcs                                                    */
  /*                                                                       */
  /* <Description>                                                         */
  /*   A structure used to describe a given raster class to the library.   */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    glyph_format  :: The supported glyph format for this raster.       */
  /*                                                                       */
  /*    raster_new    :: The raster constructor.                           */
  /*                                                                       */
  /*    raster_reset  :: Used to reset the render pool within the raster.  */
  /*                                                                       */
  /*    raster_render :: A function to render a glyph into a given bitmap. */
  /*                                                                       */
  /*    raster_done   :: The raster destructor.                            */
  /*                                                                       */
  typedef struct  FT_Raster_Funcs_
  {
    FT_Glyph_Format        glyph_format;
    FT_Raster_NewFunc      raster_new;
    FT_Raster_ResetFunc    raster_reset;
    FT_Raster_SetModeFunc  raster_set_mode;
    FT_Raster_RenderFunc   raster_render;
    FT_Raster_DoneFunc     raster_done;

  } FT_Raster_Funcs;


  /* */


FT_END_HEADER

#endif /* __FTIMAGE_H__ */


/* END */


/* Local Variables: */
/* coding: utf-8    */
/* End:             */
/***************************************************************************/
/*                                                                         */
/*  ftincrem.h                                                             */
/*                                                                         */
/*    FreeType incremental loading (specification).                        */
/*                                                                         */
/*  Copyright 2002, 2003, 2006, 2007, 2008, 2010 by                        */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __FTINCREM_H__
#define __FTINCREM_H__

#include <ft2build.h>
#include FT_FREETYPE_H

#ifdef FREETYPE_H
#error "freetype.h of FreeType 1 has been loaded!"
#error "Please fix the directory search order for header files"
#error "so that freetype.h of FreeType 2 is found first."
#endif


FT_BEGIN_HEADER

  /***************************************************************************
   *
   * @section:
   *    incremental
   *
   * @title:
   *    Incremental Loading
   *
   * @abstract:
   *    Custom Glyph Loading.
   *
   * @description:
   *   This section contains various functions used to perform so-called
   *   `incremental' glyph loading.  This is a mode where all glyphs loaded
   *   from a given @FT_Face are provided by the client application,
   *
   *   Apart from that, all other tables are loaded normally from the font
   *   file.  This mode is useful when FreeType is used within another
   *   engine, e.g., a PostScript Imaging Processor.
   *
   *   To enable this mode, you must use @FT_Open_Face, passing an
   *   @FT_Parameter with the @FT_PARAM_TAG_INCREMENTAL tag and an
   *   @FT_Incremental_Interface value.  See the comments for
   *   @FT_Incremental_InterfaceRec for an example.
   *
   */


  /***************************************************************************
   *
   * @type:
   *   FT_Incremental
   *
   * @description:
   *   An opaque type describing a user-provided object used to implement
   *   `incremental' glyph loading within FreeType.  This is used to support
   *   embedded fonts in certain environments (e.g., PostScript interpreters),
   *   where the glyph data isn't in the font file, or must be overridden by
   *   different values.
   *
   * @note:
   *   It is up to client applications to create and implement @FT_Incremental
   *   objects, as long as they provide implementations for the methods
   *   @FT_Incremental_GetGlyphDataFunc, @FT_Incremental_FreeGlyphDataFunc
   *   and @FT_Incremental_GetGlyphMetricsFunc.
   *
   *   See the description of @FT_Incremental_InterfaceRec to understand how
   *   to use incremental objects with FreeType.
   *
   */
  typedef struct FT_IncrementalRec_*  FT_Incremental;


  /***************************************************************************
   *
   * @struct:
   *   FT_Incremental_MetricsRec
   *
   * @description:
   *   A small structure used to contain the basic glyph metrics returned
   *   by the @FT_Incremental_GetGlyphMetricsFunc method.
   *
   * @fields:
   *   bearing_x ::
   *     Left bearing, in font units.
   *
   *   bearing_y ::
   *     Top bearing, in font units.
   *
   *   advance ::
   *     Horizontal component of glyph advance, in font units.
   *
   *   advance_v ::
   *     Vertical component of glyph advance, in font units.
   *
   * @note:
   *   These correspond to horizontal or vertical metrics depending on the
   *   value of the `vertical' argument to the function
   *   @FT_Incremental_GetGlyphMetricsFunc.
   *
   */
  typedef struct  FT_Incremental_MetricsRec_
  {
    FT_Long  bearing_x;
    FT_Long  bearing_y;
    FT_Long  advance;
    FT_Long  advance_v;     /* since 2.3.12 */

  } FT_Incremental_MetricsRec;


  /***************************************************************************
   *
   * @struct:
   *   FT_Incremental_Metrics
   *
   * @description:
   *   A handle to an @FT_Incremental_MetricsRec structure.
   *
   */
   typedef struct FT_Incremental_MetricsRec_*  FT_Incremental_Metrics;


  /***************************************************************************
   *
   * @type:
   *   FT_Incremental_GetGlyphDataFunc
   *
   * @description:
   *   A function called by FreeType to access a given glyph's data bytes
   *   during @FT_Load_Glyph or @FT_Load_Char if incremental loading is
   *   enabled.
   *
   *   Note that the format of the glyph's data bytes depends on the font
   *   file format.  For TrueType, it must correspond to the raw bytes within
   *   the `glyf' table.  For PostScript formats, it must correspond to the
   *   *unencrypted* charstring bytes, without any `lenIV' header.  It is
   *   undefined for any other format.
   *
   * @input:
   *   incremental ::
   *     Handle to an opaque @FT_Incremental handle provided by the client
   *     application.
   *
   *   glyph_index ::
   *     Index of relevant glyph.
   *
   * @output:
   *   adata ::
   *     A structure describing the returned glyph data bytes (which will be
   *     accessed as a read-only byte block).
   *
   * @return:
   *   FreeType error code.  0~means success.
   *
   * @note:
   *   If this function returns successfully the method
   *   @FT_Incremental_FreeGlyphDataFunc will be called later to release
   *   the data bytes.
   *
   *   Nested calls to @FT_Incremental_GetGlyphDataFunc can happen for
   *   compound glyphs.
   *
   */
  typedef FT_Error
  (*FT_Incremental_GetGlyphDataFunc)( FT_Incremental  incremental,
                                      FT_UInt         glyph_index,
                                      FT_Data*        adata );


  /***************************************************************************
   *
   * @type:
   *   FT_Incremental_FreeGlyphDataFunc
   *
   * @description:
   *   A function used to release the glyph data bytes returned by a
   *   successful call to @FT_Incremental_GetGlyphDataFunc.
   *
   * @input:
   *   incremental ::
   *     A handle to an opaque @FT_Incremental handle provided by the client
   *     application.
   *
   *   data ::
   *     A structure describing the glyph data bytes (which will be accessed
   *     as a read-only byte block).
   *
   */
  typedef void
  (*FT_Incremental_FreeGlyphDataFunc)( FT_Incremental  incremental,
                                       FT_Data*        data );


  /***************************************************************************
   *
   * @type:
   *   FT_Incremental_GetGlyphMetricsFunc
   *
   * @description:
   *   A function used to retrieve the basic metrics of a given glyph index
   *   before accessing its data.  This is necessary because, in certain
   *   formats like TrueType, the metrics are stored in a different place from
   *   the glyph images proper.
   *
   * @input:
   *   incremental ::
   *     A handle to an opaque @FT_Incremental handle provided by the client
   *     application.
   *
   *   glyph_index ::
   *     Index of relevant glyph.
   *
   *   vertical ::
   *     If true, return vertical metrics.
   *
   *   ametrics ::
   *     This parameter is used for both input and output.
   *     The original glyph metrics, if any, in font units.  If metrics are
   *     not available all the values must be set to zero.
   *
   * @output:
   *   ametrics ::
   *     The replacement glyph metrics in font units.
   *
   */
  typedef FT_Error
  (*FT_Incremental_GetGlyphMetricsFunc)
                      ( FT_Incremental              incremental,
                        FT_UInt                     glyph_index,
                        FT_Bool                     vertical,
                        FT_Incremental_MetricsRec  *ametrics );


  /**************************************************************************
   *
   * @struct:
   *   FT_Incremental_FuncsRec
   *
   * @description:
   *   A table of functions for accessing fonts that load data
   *   incrementally.  Used in @FT_Incremental_InterfaceRec.
   *
   * @fields:
   *   get_glyph_data ::
   *     The function to get glyph data.  Must not be null.
   *
   *   free_glyph_data ::
   *     The function to release glyph data.  Must not be null.
   *
   *   get_glyph_metrics ::
   *     The function to get glyph metrics.  May be null if the font does
   *     not provide overriding glyph metrics.
   *
   */
  typedef struct  FT_Incremental_FuncsRec_
  {
    FT_Incremental_GetGlyphDataFunc     get_glyph_data;
    FT_Incremental_FreeGlyphDataFunc    free_glyph_data;
    FT_Incremental_GetGlyphMetricsFunc  get_glyph_metrics;

  } FT_Incremental_FuncsRec;


  /***************************************************************************
   *
   * @struct:
   *   FT_Incremental_InterfaceRec
   *
   * @description:
   *   A structure to be used with @FT_Open_Face to indicate that the user
   *   wants to support incremental glyph loading.  You should use it with
   *   @FT_PARAM_TAG_INCREMENTAL as in the following example:
   *
   *     {
   *       FT_Incremental_InterfaceRec  inc_int;
   *       FT_Parameter                 parameter;
   *       FT_Open_Args                 open_args;
   *
   *
   *       // set up incremental descriptor
   *       inc_int.funcs  = my_funcs;
   *       inc_int.object = my_object;
   *
   *       // set up optional parameter
   *       parameter.tag  = FT_PARAM_TAG_INCREMENTAL;
   *       parameter.data = &inc_int;
   *
   *       // set up FT_Open_Args structure
   *       open_args.flags      = FT_OPEN_PATHNAME | FT_OPEN_PARAMS;
   *       open_args.pathname   = my_font_pathname;
   *       open_args.num_params = 1;
   *       open_args.params     = &parameter; // we use one optional argument
   *
   *       // open the font
   *       error = FT_Open_Face( library, &open_args, index, &face );
   *       ...
   *     }
   *
   */
  typedef struct  FT_Incremental_InterfaceRec_
  {
    const FT_Incremental_FuncsRec*  funcs;
    FT_Incremental                  object;

  } FT_Incremental_InterfaceRec;


  /***************************************************************************
   *
   * @type:
   *   FT_Incremental_Interface
   *
   * @description:
   *   A pointer to an @FT_Incremental_InterfaceRec structure.
   *
   */
  typedef FT_Incremental_InterfaceRec*   FT_Incremental_Interface;


  /***************************************************************************
   *
   * @constant:
   *   FT_PARAM_TAG_INCREMENTAL
   *
   * @description:
   *   A constant used as the tag of @FT_Parameter structures to indicate
   *   an incremental loading object to be used by FreeType.
   *
   */
#define FT_PARAM_TAG_INCREMENTAL  FT_MAKE_TAG( 'i', 'n', 'c', 'r' )

  /* */

FT_END_HEADER

#endif /* __FTINCREM_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  ftlcdfil.h                                                             */
/*                                                                         */
/*    FreeType API for color filtering of subpixel bitmap glyphs           */
/*    (specification).                                                     */
/*                                                                         */
/*  Copyright 2006, 2007, 2008, 2010 by                                    */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __FT_LCD_FILTER_H__
#define __FT_LCD_FILTER_H__

#include <ft2build.h>
#include FT_FREETYPE_H

#ifdef FREETYPE_H
#error "freetype.h of FreeType 1 has been loaded!"
#error "Please fix the directory search order for header files"
#error "so that freetype.h of FreeType 2 is found first."
#endif


FT_BEGIN_HEADER

  /***************************************************************************
   *
   * @section:
   *   lcd_filtering
   *
   * @title:
   *   LCD Filtering
   *
   * @abstract:
   *   Reduce color fringes of LCD-optimized bitmaps.
   *
   * @description:
   *   The @FT_Library_SetLcdFilter API can be used to specify a low-pass
   *   filter which is then applied to LCD-optimized bitmaps generated
   *   through @FT_Render_Glyph.  This is useful to reduce color fringes
   *   which would occur with unfiltered rendering.
   *
   *   Note that no filter is active by default, and that this function is
   *   *not* implemented in default builds of the library.  You need to
   *   #define FT_CONFIG_OPTION_SUBPIXEL_RENDERING in your `ftoption.h' file
   *   in order to activate it.
   */


  /****************************************************************************
   *
   * @enum:
   *   FT_LcdFilter
   *
   * @description:
   *   A list of values to identify various types of LCD filters.
   *
   * @values:
   *   FT_LCD_FILTER_NONE ::
   *     Do not perform filtering.  When used with subpixel rendering, this
   *     results in sometimes severe color fringes.
   *
   *   FT_LCD_FILTER_DEFAULT ::
   *     The default filter reduces color fringes considerably, at the cost
   *     of a slight blurriness in the output.
   *
   *   FT_LCD_FILTER_LIGHT ::
   *     The light filter is a variant that produces less blurriness at the
   *     cost of slightly more color fringes than the default one.  It might
   *     be better, depending on taste, your monitor, or your personal vision.
   *
   *   FT_LCD_FILTER_LEGACY ::
   *     This filter corresponds to the original libXft color filter.  It
   *     provides high contrast output but can exhibit really bad color
   *     fringes if glyphs are not extremely well hinted to the pixel grid.
   *     In other words, it only works well if the TrueType bytecode
   *     interpreter is enabled *and* high-quality hinted fonts are used.
   *
   *     This filter is only provided for comparison purposes, and might be
   *     disabled or stay unsupported in the future.
   *
   * @since:
   *   2.3.0
   */
  typedef enum  FT_LcdFilter_
  {
    FT_LCD_FILTER_NONE    = 0,
    FT_LCD_FILTER_DEFAULT = 1,
    FT_LCD_FILTER_LIGHT   = 2,
    FT_LCD_FILTER_LEGACY  = 16,

    FT_LCD_FILTER_MAX   /* do not remove */

  } FT_LcdFilter;


  /**************************************************************************
   *
   * @func:
   *   FT_Library_SetLcdFilter
   *
   * @description:
   *   This function is used to apply color filtering to LCD decimated
   *   bitmaps, like the ones used when calling @FT_Render_Glyph with
   *   @FT_RENDER_MODE_LCD or @FT_RENDER_MODE_LCD_V.
   *
   * @input:
   *   library ::
   *     A handle to the target library instance.
   *
   *   filter ::
   *     The filter type.
   *
   *     You can use @FT_LCD_FILTER_NONE here to disable this feature, or
   *     @FT_LCD_FILTER_DEFAULT to use a default filter that should work
   *     well on most LCD screens.
   *
   * @return:
   *   FreeType error code.  0~means success.
   *
   * @note:
   *   This feature is always disabled by default.  Clients must make an
   *   explicit call to this function with a `filter' value other than
   *   @FT_LCD_FILTER_NONE in order to enable it.
   *
   *   Due to *PATENTS* covering subpixel rendering, this function doesn't
   *   do anything except returning `FT_Err_Unimplemented_Feature' if the
   *   configuration macro FT_CONFIG_OPTION_SUBPIXEL_RENDERING is not
   *   defined in your build of the library, which should correspond to all
   *   default builds of FreeType.
   *
   *   The filter affects glyph bitmaps rendered through @FT_Render_Glyph,
   *   @FT_Outline_Get_Bitmap, @FT_Load_Glyph, and @FT_Load_Char.
   *
   *   It does _not_ affect the output of @FT_Outline_Render and
   *   @FT_Outline_Get_Bitmap.
   *
   *   If this feature is activated, the dimensions of LCD glyph bitmaps are
   *   either larger or taller than the dimensions of the corresponding
   *   outline with regards to the pixel grid.  For example, for
   *   @FT_RENDER_MODE_LCD, the filter adds up to 3~pixels to the left, and
   *   up to 3~pixels to the right.
   *
   *   The bitmap offset values are adjusted correctly, so clients shouldn't
   *   need to modify their layout and glyph positioning code when enabling
   *   the filter.
   *
   * @since:
   *   2.3.0
   */
  FT_EXPORT( FT_Error )
  FT_Library_SetLcdFilter( FT_Library    library,
                           FT_LcdFilter  filter );


  /**************************************************************************
   *
   * @func:
   *   FT_Library_SetLcdFilterWeights
   *
   * @description:
   *   Use this function to override the filter weights selected by
   *   @FT_Library_SetLcdFilter.  By default, FreeType uses the quintuple
   *   (0x00, 0x55, 0x56, 0x55, 0x00) for FT_LCD_FILTER_LIGHT, and (0x10,
   *   0x40, 0x70, 0x40, 0x10) for FT_LCD_FILTER_DEFAULT and
   *   FT_LCD_FILTER_LEGACY.
   *
   * @input:
   *   library ::
   *     A handle to the target library instance.
   *
   *   weights ::
   *     A pointer to an array; the function copies the first five bytes and
   *     uses them to specify the filter weights.
   *
   * @return:
   *   FreeType error code.  0~means success.
   *
   * @note:
   *   Due to *PATENTS* covering subpixel rendering, this function doesn't
   *   do anything except returning `FT_Err_Unimplemented_Feature' if the
   *   configuration macro FT_CONFIG_OPTION_SUBPIXEL_RENDERING is not
   *   defined in your build of the library, which should correspond to all
   *   default builds of FreeType.
   *
   *   This function must be called after @FT_Library_SetLcdFilter to have
   *   any effect.
   *
   * @since:
   *   2.4.0
   */
  FT_EXPORT( FT_Error )
  FT_Library_SetLcdFilterWeights( FT_Library      library,
                                  unsigned char  *weights );

  /* */


FT_END_HEADER

#endif /* __FT_LCD_FILTER_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  ftlist.h                                                               */
/*                                                                         */
/*    Generic list support for FreeType (specification).                   */
/*                                                                         */
/*  Copyright 1996-2001, 2003, 2007, 2010 by                               */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /*  This file implements functions relative to list processing.  Its     */
  /*  data structures are defined in `freetype.h'.                         */
  /*                                                                       */
  /*************************************************************************/


#ifndef __FTLIST_H__
#define __FTLIST_H__


#include <ft2build.h>
#include FT_FREETYPE_H

#ifdef FREETYPE_H
#error "freetype.h of FreeType 1 has been loaded!"
#error "Please fix the directory search order for header files"
#error "so that freetype.h of FreeType 2 is found first."
#endif


FT_BEGIN_HEADER


  /*************************************************************************/
  /*                                                                       */
  /* <Section>                                                             */
  /*    list_processing                                                    */
  /*                                                                       */
  /* <Title>                                                               */
  /*    List Processing                                                    */
  /*                                                                       */
  /* <Abstract>                                                            */
  /*    Simple management of lists.                                        */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This section contains various definitions related to list          */
  /*    processing using doubly-linked nodes.                              */
  /*                                                                       */
  /* <Order>                                                               */
  /*    FT_List                                                            */
  /*    FT_ListNode                                                        */
  /*    FT_ListRec                                                         */
  /*    FT_ListNodeRec                                                     */
  /*                                                                       */
  /*    FT_List_Add                                                        */
  /*    FT_List_Insert                                                     */
  /*    FT_List_Find                                                       */
  /*    FT_List_Remove                                                     */
  /*    FT_List_Up                                                         */
  /*    FT_List_Iterate                                                    */
  /*    FT_List_Iterator                                                   */
  /*    FT_List_Finalize                                                   */
  /*    FT_List_Destructor                                                 */
  /*                                                                       */
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_List_Find                                                       */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Find the list node for a given listed object.                      */
  /*                                                                       */
  /* <Input>                                                               */
  /*    list :: A pointer to the parent list.                              */
  /*    data :: The address of the listed object.                          */
  /*                                                                       */
  /* <Return>                                                              */
  /*    List node.  NULL if it wasn't found.                               */
  /*                                                                       */
  FT_EXPORT( FT_ListNode )
  FT_List_Find( FT_List  list,
                void*    data );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_List_Add                                                        */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Append an element to the end of a list.                            */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    list :: A pointer to the parent list.                              */
  /*    node :: The node to append.                                        */
  /*                                                                       */
  FT_EXPORT( void )
  FT_List_Add( FT_List      list,
               FT_ListNode  node );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_List_Insert                                                     */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Insert an element at the head of a list.                           */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    list :: A pointer to parent list.                                  */
  /*    node :: The node to insert.                                        */
  /*                                                                       */
  FT_EXPORT( void )
  FT_List_Insert( FT_List      list,
                  FT_ListNode  node );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_List_Remove                                                     */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Remove a node from a list.  This function doesn't check whether    */
  /*    the node is in the list!                                           */
  /*                                                                       */
  /* <Input>                                                               */
  /*    node :: The node to remove.                                        */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    list :: A pointer to the parent list.                              */
  /*                                                                       */
  FT_EXPORT( void )
  FT_List_Remove( FT_List      list,
                  FT_ListNode  node );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_List_Up                                                         */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Move a node to the head/top of a list.  Used to maintain LRU       */
  /*    lists.                                                             */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    list :: A pointer to the parent list.                              */
  /*    node :: The node to move.                                          */
  /*                                                                       */
  FT_EXPORT( void )
  FT_List_Up( FT_List      list,
              FT_ListNode  node );


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    FT_List_Iterator                                                   */
  /*                                                                       */
  /* <Description>                                                         */
  /*    An FT_List iterator function which is called during a list parse   */
  /*    by @FT_List_Iterate.                                               */
  /*                                                                       */
  /* <Input>                                                               */
  /*    node :: The current iteration list node.                           */
  /*                                                                       */
  /*    user :: A typeless pointer passed to @FT_List_Iterate.             */
  /*            Can be used to point to the iteration's state.             */
  /*                                                                       */
  typedef FT_Error
  (*FT_List_Iterator)( FT_ListNode  node,
                       void*        user );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_List_Iterate                                                    */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Parse a list and calls a given iterator function on each element.  */
  /*    Note that parsing is stopped as soon as one of the iterator calls  */
  /*    returns a non-zero value.                                          */
  /*                                                                       */
  /* <Input>                                                               */
  /*    list     :: A handle to the list.                                  */
  /*    iterator :: An iterator function, called on each node of the list. */
  /*    user     :: A user-supplied field which is passed as the second    */
  /*                argument to the iterator.                              */
  /*                                                                       */
  /* <Return>                                                              */
  /*    The result (a FreeType error code) of the last iterator call.      */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_List_Iterate( FT_List           list,
                   FT_List_Iterator  iterator,
                   void*             user );


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    FT_List_Destructor                                                 */
  /*                                                                       */
  /* <Description>                                                         */
  /*    An @FT_List iterator function which is called during a list        */
  /*    finalization by @FT_List_Finalize to destroy all elements in a     */
  /*    given list.                                                        */
  /*                                                                       */
  /* <Input>                                                               */
  /*    system :: The current system object.                               */
  /*                                                                       */
  /*    data   :: The current object to destroy.                           */
  /*                                                                       */
  /*    user   :: A typeless pointer passed to @FT_List_Iterate.  It can   */
  /*              be used to point to the iteration's state.               */
  /*                                                                       */
  typedef void
  (*FT_List_Destructor)( FT_Memory  memory,
                         void*      data,
                         void*      user );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_List_Finalize                                                   */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Destroy all elements in the list as well as the list itself.       */
  /*                                                                       */
  /* <Input>                                                               */
  /*    list    :: A handle to the list.                                   */
  /*                                                                       */
  /*    destroy :: A list destructor that will be applied to each element  */
  /*               of the list.                                            */
  /*                                                                       */
  /*    memory  :: The current memory object which handles deallocation.   */
  /*                                                                       */
  /*    user    :: A user-supplied field which is passed as the last       */
  /*               argument to the destructor.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    This function expects that all nodes added by @FT_List_Add or      */
  /*    @FT_List_Insert have been dynamically allocated.                   */
  /*                                                                       */
  FT_EXPORT( void )
  FT_List_Finalize( FT_List             list,
                    FT_List_Destructor  destroy,
                    FT_Memory           memory,
                    void*               user );


  /* */


FT_END_HEADER

#endif /* __FTLIST_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  ftlzw.h                                                                */
/*                                                                         */
/*    LZW-compressed stream support.                                       */
/*                                                                         */
/*  Copyright 2004, 2006 by                                                */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __FTLZW_H__
#define __FTLZW_H__

#include <ft2build.h>
#include FT_FREETYPE_H

#ifdef FREETYPE_H
#error "freetype.h of FreeType 1 has been loaded!"
#error "Please fix the directory search order for header files"
#error "so that freetype.h of FreeType 2 is found first."
#endif


FT_BEGIN_HEADER

  /*************************************************************************/
  /*                                                                       */
  /* <Section>                                                             */
  /*    lzw                                                                */
  /*                                                                       */
  /* <Title>                                                               */
  /*    LZW Streams                                                        */
  /*                                                                       */
  /* <Abstract>                                                            */
  /*    Using LZW-compressed font files.                                   */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This section contains the declaration of LZW-specific functions.   */
  /*                                                                       */
  /*************************************************************************/

 /************************************************************************
  *
  * @function:
  *   FT_Stream_OpenLZW
  *
  * @description:
  *   Open a new stream to parse LZW-compressed font files.  This is
  *   mainly used to support the compressed `*.pcf.Z' fonts that come
  *   with XFree86.
  *
  * @input:
  *   stream :: The target embedding stream.
  *
  *   source :: The source stream.
  *
  * @return:
  *   FreeType error code.  0~means success.
  *
  * @note:
  *   The source stream must be opened _before_ calling this function.
  *
  *   Calling the internal function `FT_Stream_Close' on the new stream will
  *   *not* call `FT_Stream_Close' on the source stream.  None of the stream
  *   objects will be released to the heap.
  *
  *   The stream implementation is very basic and resets the decompression
  *   process each time seeking backwards is needed within the stream
  *
  *   In certain builds of the library, LZW compression recognition is
  *   automatically handled when calling @FT_New_Face or @FT_Open_Face.
  *   This means that if no font driver is capable of handling the raw
  *   compressed file, the library will try to open a LZW stream from it
  *   and re-open the face with it.
  *
  *   This function may return `FT_Err_Unimplemented_Feature' if your build
  *   of FreeType was not compiled with LZW support.
  */
  FT_EXPORT( FT_Error )
  FT_Stream_OpenLZW( FT_Stream  stream,
                     FT_Stream  source );

 /* */


FT_END_HEADER

#endif /* __FTLZW_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  ftmac.h                                                                */
/*                                                                         */
/*    Additional Mac-specific API.                                         */
/*                                                                         */
/*  Copyright 1996-2001, 2004, 2006, 2007 by                               */
/*  Just van Rossum, David Turner, Robert Wilhelm, and Werner Lemberg.     */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


/***************************************************************************/
/*                                                                         */
/* NOTE: Include this file after <freetype/freetype.h> and after any       */
/*       Mac-specific headers (because this header uses Mac types such as  */
/*       Handle, FSSpec, FSRef, etc.)                                      */
/*                                                                         */
/***************************************************************************/


#ifndef __FTMAC_H__
#define __FTMAC_H__


#include <ft2build.h>


FT_BEGIN_HEADER


/* gcc-3.4.1 and later can warn about functions tagged as deprecated */
#ifndef FT_DEPRECATED_ATTRIBUTE
#if defined(__GNUC__)                                               && \
    ((__GNUC__ >= 4) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1)))
#define FT_DEPRECATED_ATTRIBUTE  __attribute__((deprecated))
#else
#define FT_DEPRECATED_ATTRIBUTE
#endif
#endif


  /*************************************************************************/
  /*                                                                       */
  /* <Section>                                                             */
  /*    mac_specific                                                       */
  /*                                                                       */
  /* <Title>                                                               */
  /*    Mac Specific Interface                                             */
  /*                                                                       */
  /* <Abstract>                                                            */
  /*    Only available on the Macintosh.                                   */
  /*                                                                       */
  /* <Description>                                                         */
  /*    The following definitions are only available if FreeType is        */
  /*    compiled on a Macintosh.                                           */
  /*                                                                       */
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_New_Face_From_FOND                                              */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Create a new face object from a FOND resource.                     */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    library    :: A handle to the library resource.                    */
  /*                                                                       */
  /* <Input>                                                               */
  /*    fond       :: A FOND resource.                                     */
  /*                                                                       */
  /*    face_index :: Only supported for the -1 `sanity check' special     */
  /*                  case.                                                */
  /*                                                                       */
  /* <Output>                                                              */
  /*    aface      :: A handle to a new face object.                       */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  /* <Notes>                                                               */
  /*    This function can be used to create @FT_Face objects from fonts    */
  /*    that are installed in the system as follows.                       */
  /*                                                                       */
  /*    {                                                                  */
  /*      fond = GetResource( 'FOND', fontName );                          */
  /*      error = FT_New_Face_From_FOND( library, fond, 0, &face );        */
  /*    }                                                                  */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_New_Face_From_FOND( FT_Library  library,
                         Handle      fond,
                         FT_Long     face_index,
                         FT_Face    *aface )
                       FT_DEPRECATED_ATTRIBUTE;


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_GetFile_From_Mac_Name                                           */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Return an FSSpec for the disk file containing the named font.      */
  /*                                                                       */
  /* <Input>                                                               */
  /*    fontName   :: Mac OS name of the font (e.g., Times New Roman       */
  /*                  Bold).                                               */
  /*                                                                       */
  /* <Output>                                                              */
  /*    pathSpec   :: FSSpec to the file.  For passing to                  */
  /*                  @FT_New_Face_From_FSSpec.                            */
  /*                                                                       */
  /*    face_index :: Index of the face.  For passing to                   */
  /*                  @FT_New_Face_From_FSSpec.                            */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_GetFile_From_Mac_Name( const char*  fontName,
                            FSSpec*      pathSpec,
                            FT_Long*     face_index )
                          FT_DEPRECATED_ATTRIBUTE;


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_GetFile_From_Mac_ATS_Name                                       */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Return an FSSpec for the disk file containing the named font.      */
  /*                                                                       */
  /* <Input>                                                               */
  /*    fontName   :: Mac OS name of the font in ATS framework.            */
  /*                                                                       */
  /* <Output>                                                              */
  /*    pathSpec   :: FSSpec to the file. For passing to                   */
  /*                  @FT_New_Face_From_FSSpec.                            */
  /*                                                                       */
  /*    face_index :: Index of the face. For passing to                    */
  /*                  @FT_New_Face_From_FSSpec.                            */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_GetFile_From_Mac_ATS_Name( const char*  fontName,
                                FSSpec*      pathSpec,
                                FT_Long*     face_index )
                              FT_DEPRECATED_ATTRIBUTE;


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_GetFilePath_From_Mac_ATS_Name                                   */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Return a pathname of the disk file and face index for given font   */
  /*    name which is handled by ATS framework.                            */
  /*                                                                       */
  /* <Input>                                                               */
  /*    fontName    :: Mac OS name of the font in ATS framework.           */
  /*                                                                       */
  /* <Output>                                                              */
  /*    path        :: Buffer to store pathname of the file.  For passing  */
  /*                   to @FT_New_Face.  The client must allocate this     */
  /*                   buffer before calling this function.                */
  /*                                                                       */
  /*    maxPathSize :: Lengths of the buffer `path' that client allocated. */
  /*                                                                       */
  /*    face_index  :: Index of the face.  For passing to @FT_New_Face.    */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_GetFilePath_From_Mac_ATS_Name( const char*  fontName,
                                    UInt8*       path,
                                    UInt32       maxPathSize,
                                    FT_Long*     face_index )
                                  FT_DEPRECATED_ATTRIBUTE;


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_New_Face_From_FSSpec                                            */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Create a new face object from a given resource and typeface index  */
  /*    using an FSSpec to the font file.                                  */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    library    :: A handle to the library resource.                    */
  /*                                                                       */
  /* <Input>                                                               */
  /*    spec       :: FSSpec to the font file.                             */
  /*                                                                       */
  /*    face_index :: The index of the face within the resource.  The      */
  /*                  first face has index~0.                              */
  /* <Output>                                                              */
  /*    aface      :: A handle to a new face object.                       */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    @FT_New_Face_From_FSSpec is identical to @FT_New_Face except       */
  /*    it accepts an FSSpec instead of a path.                            */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_New_Face_From_FSSpec( FT_Library     library,
                           const FSSpec  *spec,
                           FT_Long        face_index,
                           FT_Face       *aface )
                         FT_DEPRECATED_ATTRIBUTE;


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_New_Face_From_FSRef                                             */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Create a new face object from a given resource and typeface index  */
  /*    using an FSRef to the font file.                                   */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    library    :: A handle to the library resource.                    */
  /*                                                                       */
  /* <Input>                                                               */
  /*    spec       :: FSRef to the font file.                              */
  /*                                                                       */
  /*    face_index :: The index of the face within the resource.  The      */
  /*                  first face has index~0.                              */
  /* <Output>                                                              */
  /*    aface      :: A handle to a new face object.                       */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    @FT_New_Face_From_FSRef is identical to @FT_New_Face except        */
  /*    it accepts an FSRef instead of a path.                             */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_New_Face_From_FSRef( FT_Library    library,
                          const FSRef  *ref,
                          FT_Long       face_index,
                          FT_Face      *aface )
                        FT_DEPRECATED_ATTRIBUTE;

  /* */


FT_END_HEADER


#endif /* __FTMAC_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  ftmm.h                                                                 */
/*                                                                         */
/*    FreeType Multiple Master font interface (specification).             */
/*                                                                         */
/*  Copyright 1996-2001, 2003, 2004, 2006, 2009 by                         */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __FTMM_H__
#define __FTMM_H__


#include <ft2build.h>
#include FT_TYPE1_TABLES_H


FT_BEGIN_HEADER


  /*************************************************************************/
  /*                                                                       */
  /* <Section>                                                             */
  /*    multiple_masters                                                   */
  /*                                                                       */
  /* <Title>                                                               */
  /*    Multiple Masters                                                   */
  /*                                                                       */
  /* <Abstract>                                                            */
  /*    How to manage Multiple Masters fonts.                              */
  /*                                                                       */
  /* <Description>                                                         */
  /*    The following types and functions are used to manage Multiple      */
  /*    Master fonts, i.e., the selection of specific design instances by  */
  /*    setting design axis coordinates.                                   */
  /*                                                                       */
  /*    George Williams has extended this interface to make it work with   */
  /*    both Type~1 Multiple Masters fonts and GX distortable (var)        */
  /*    fonts.  Some of these routines only work with MM fonts, others     */
  /*    will work with both types.  They are similar enough that a         */
  /*    consistent interface makes sense.                                  */
  /*                                                                       */
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_MM_Axis                                                         */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A simple structure used to model a given axis in design space for  */
  /*    Multiple Masters fonts.                                            */
  /*                                                                       */
  /*    This structure can't be used for GX var fonts.                     */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    name    :: The axis's name.                                        */
  /*                                                                       */
  /*    minimum :: The axis's minimum design coordinate.                   */
  /*                                                                       */
  /*    maximum :: The axis's maximum design coordinate.                   */
  /*                                                                       */
  typedef struct  FT_MM_Axis_
  {
    FT_String*  name;
    FT_Long     minimum;
    FT_Long     maximum;

  } FT_MM_Axis;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_Multi_Master                                                    */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A structure used to model the axes and space of a Multiple Masters */
  /*    font.                                                              */
  /*                                                                       */
  /*    This structure can't be used for GX var fonts.                     */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    num_axis    :: Number of axes.  Cannot exceed~4.                   */
  /*                                                                       */
  /*    num_designs :: Number of designs; should be normally 2^num_axis    */
  /*                   even though the Type~1 specification strangely      */
  /*                   allows for intermediate designs to be present. This */
  /*                   number cannot exceed~16.                            */
  /*                                                                       */
  /*    axis        :: A table of axis descriptors.                        */
  /*                                                                       */
  typedef struct  FT_Multi_Master_
  {
    FT_UInt     num_axis;
    FT_UInt     num_designs;
    FT_MM_Axis  axis[T1_MAX_MM_AXIS];

  } FT_Multi_Master;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_Var_Axis                                                        */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A simple structure used to model a given axis in design space for  */
  /*    Multiple Masters and GX var fonts.                                 */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    name    :: The axis's name.                                        */
  /*               Not always meaningful for GX.                           */
  /*                                                                       */
  /*    minimum :: The axis's minimum design coordinate.                   */
  /*                                                                       */
  /*    def     :: The axis's default design coordinate.                   */
  /*               FreeType computes meaningful default values for MM; it  */
  /*               is then an integer value, not in 16.16 format.          */
  /*                                                                       */
  /*    maximum :: The axis's maximum design coordinate.                   */
  /*                                                                       */
  /*    tag     :: The axis's tag (the GX equivalent to `name').           */
  /*               FreeType provides default values for MM if possible.    */
  /*                                                                       */
  /*    strid   :: The entry in `name' table (another GX version of        */
  /*               `name').                                                */
  /*               Not meaningful for MM.                                  */
  /*                                                                       */
  typedef struct  FT_Var_Axis_
  {
    FT_String*  name;

    FT_Fixed    minimum;
    FT_Fixed    def;
    FT_Fixed    maximum;

    FT_ULong    tag;
    FT_UInt     strid;

  } FT_Var_Axis;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_Var_Named_Style                                                 */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A simple structure used to model a named style in a GX var font.   */
  /*                                                                       */
  /*    This structure can't be used for MM fonts.                         */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    coords :: The design coordinates for this style.                   */
  /*              This is an array with one entry for each axis.           */
  /*                                                                       */
  /*    strid  :: The entry in `name' table identifying this style.        */
  /*                                                                       */
  typedef struct  FT_Var_Named_Style_
  {
    FT_Fixed*  coords;
    FT_UInt    strid;

  } FT_Var_Named_Style;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_MM_Var                                                          */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A structure used to model the axes and space of a Multiple Masters */
  /*    or GX var distortable font.                                        */
  /*                                                                       */
  /*    Some fields are specific to one format and not to the other.       */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    num_axis        :: The number of axes.  The maximum value is~4 for */
  /*                       MM; no limit in GX.                             */
  /*                                                                       */
  /*    num_designs     :: The number of designs; should be normally       */
  /*                       2^num_axis for MM fonts.  Not meaningful for GX */
  /*                       (where every glyph could have a different       */
  /*                       number of designs).                             */
  /*                                                                       */
  /*    num_namedstyles :: The number of named styles; only meaningful for */
  /*                       GX which allows certain design coordinates to   */
  /*                       have a string ID (in the `name' table)          */
  /*                       associated with them.  The font can tell the    */
  /*                       user that, for example, Weight=1.5 is `Bold'.   */
  /*                                                                       */
  /*    axis            :: A table of axis descriptors.                    */
  /*                       GX fonts contain slightly more data than MM.    */
  /*                                                                       */
  /*    namedstyles     :: A table of named styles.                        */
  /*                       Only meaningful with GX.                        */
  /*                                                                       */
  typedef struct  FT_MM_Var_
  {
    FT_UInt              num_axis;
    FT_UInt              num_designs;
    FT_UInt              num_namedstyles;
    FT_Var_Axis*         axis;
    FT_Var_Named_Style*  namedstyle;

  } FT_MM_Var;


  /* */


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Get_Multi_Master                                                */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Retrieve the Multiple Master descriptor of a given font.           */
  /*                                                                       */
  /*    This function can't be used with GX fonts.                         */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face    :: A handle to the source face.                            */
  /*                                                                       */
  /* <Output>                                                              */
  /*    amaster :: The Multiple Masters descriptor.                        */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Get_Multi_Master( FT_Face           face,
                       FT_Multi_Master  *amaster );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Get_MM_Var                                                      */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Retrieve the Multiple Master/GX var descriptor of a given font.    */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face    :: A handle to the source face.                            */
  /*                                                                       */
  /* <Output>                                                              */
  /*    amaster :: The Multiple Masters/GX var descriptor.                 */
  /*               Allocates a data structure, which the user must free    */
  /*               (a single call to FT_FREE will do it).                  */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Get_MM_Var( FT_Face      face,
                 FT_MM_Var*  *amaster );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Set_MM_Design_Coordinates                                       */
  /*                                                                       */
  /* <Description>                                                         */
  /*    For Multiple Masters fonts, choose an interpolated font design     */
  /*    through design coordinates.                                        */
  /*                                                                       */
  /*    This function can't be used with GX fonts.                         */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    face       :: A handle to the source face.                         */
  /*                                                                       */
  /* <Input>                                                               */
  /*    num_coords :: The number of design coordinates (must be equal to   */
  /*                  the number of axes in the font).                     */
  /*                                                                       */
  /*    coords     :: An array of design coordinates.                      */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Set_MM_Design_Coordinates( FT_Face   face,
                                FT_UInt   num_coords,
                                FT_Long*  coords );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Set_Var_Design_Coordinates                                      */
  /*                                                                       */
  /* <Description>                                                         */
  /*    For Multiple Master or GX Var fonts, choose an interpolated font   */
  /*    design through design coordinates.                                 */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    face       :: A handle to the source face.                         */
  /*                                                                       */
  /* <Input>                                                               */
  /*    num_coords :: The number of design coordinates (must be equal to   */
  /*                  the number of axes in the font).                     */
  /*                                                                       */
  /*    coords     :: An array of design coordinates.                      */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Set_Var_Design_Coordinates( FT_Face    face,
                                 FT_UInt    num_coords,
                                 FT_Fixed*  coords );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Set_MM_Blend_Coordinates                                        */
  /*                                                                       */
  /* <Description>                                                         */
  /*    For Multiple Masters and GX var fonts, choose an interpolated font */
  /*    design through normalized blend coordinates.                       */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    face       :: A handle to the source face.                         */
  /*                                                                       */
  /* <Input>                                                               */
  /*    num_coords :: The number of design coordinates (must be equal to   */
  /*                  the number of axes in the font).                     */
  /*                                                                       */
  /*    coords     :: The design coordinates array (each element must be   */
  /*                  between 0 and 1.0).                                  */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Set_MM_Blend_Coordinates( FT_Face    face,
                               FT_UInt    num_coords,
                               FT_Fixed*  coords );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Set_Var_Blend_Coordinates                                       */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This is another name of @FT_Set_MM_Blend_Coordinates.              */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Set_Var_Blend_Coordinates( FT_Face    face,
                                FT_UInt    num_coords,
                                FT_Fixed*  coords );


  /* */


FT_END_HEADER

#endif /* __FTMM_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  ftmodapi.h                                                             */
/*                                                                         */
/*    FreeType modules public interface (specification).                   */
/*                                                                         */
/*  Copyright 1996-2001, 2002, 2003, 2006, 2008, 2009, 2010 by             */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __FTMODAPI_H__
#define __FTMODAPI_H__


#include <ft2build.h>
#include FT_FREETYPE_H

#ifdef FREETYPE_H
#error "freetype.h of FreeType 1 has been loaded!"
#error "Please fix the directory search order for header files"
#error "so that freetype.h of FreeType 2 is found first."
#endif


FT_BEGIN_HEADER


  /*************************************************************************/
  /*                                                                       */
  /* <Section>                                                             */
  /*    module_management                                                  */
  /*                                                                       */
  /* <Title>                                                               */
  /*    Module Management                                                  */
  /*                                                                       */
  /* <Abstract>                                                            */
  /*    How to add, upgrade, and remove modules from FreeType.             */
  /*                                                                       */
  /* <Description>                                                         */
  /*    The definitions below are used to manage modules within FreeType.  */
  /*    Modules can be added, upgraded, and removed at runtime.            */
  /*                                                                       */
  /*************************************************************************/


  /* module bit flags */
#define FT_MODULE_FONT_DRIVER         1  /* this module is a font driver  */
#define FT_MODULE_RENDERER            2  /* this module is a renderer     */
#define FT_MODULE_HINTER              4  /* this module is a glyph hinter */
#define FT_MODULE_STYLER              8  /* this module is a styler       */

#define FT_MODULE_DRIVER_SCALABLE     0x100   /* the driver supports      */
                                              /* scalable fonts           */
#define FT_MODULE_DRIVER_NO_OUTLINES  0x200   /* the driver does not      */
                                              /* support vector outlines  */
#define FT_MODULE_DRIVER_HAS_HINTER   0x400   /* the driver provides its  */
                                              /* own hinter               */


  /* deprecated values */
#define ft_module_font_driver         FT_MODULE_FONT_DRIVER
#define ft_module_renderer            FT_MODULE_RENDERER
#define ft_module_hinter              FT_MODULE_HINTER
#define ft_module_styler              FT_MODULE_STYLER

#define ft_module_driver_scalable     FT_MODULE_DRIVER_SCALABLE
#define ft_module_driver_no_outlines  FT_MODULE_DRIVER_NO_OUTLINES
#define ft_module_driver_has_hinter   FT_MODULE_DRIVER_HAS_HINTER


  typedef FT_Pointer  FT_Module_Interface;


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    FT_Module_Constructor                                              */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A function used to initialize (not create) a new module object.    */
  /*                                                                       */
  /* <Input>                                                               */
  /*    module :: The module to initialize.                                */
  /*                                                                       */
  typedef FT_Error
  (*FT_Module_Constructor)( FT_Module  module );


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    FT_Module_Destructor                                               */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A function used to finalize (not destroy) a given module object.   */
  /*                                                                       */
  /* <Input>                                                               */
  /*    module :: The module to finalize.                                  */
  /*                                                                       */
  typedef void
  (*FT_Module_Destructor)( FT_Module  module );


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    FT_Module_Requester                                                */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A function used to query a given module for a specific interface.  */
  /*                                                                       */
  /* <Input>                                                               */
  /*    module :: The module to finalize.                                  */
  /*                                                                       */
  /*    name ::   The name of the interface in the module.                 */
  /*                                                                       */
  typedef FT_Module_Interface
  (*FT_Module_Requester)( FT_Module    module,
                          const char*  name );


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_Module_Class                                                    */
  /*                                                                       */
  /* <Description>                                                         */
  /*    The module class descriptor.                                       */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    module_flags    :: Bit flags describing the module.                */
  /*                                                                       */
  /*    module_size     :: The size of one module object/instance in       */
  /*                       bytes.                                          */
  /*                                                                       */
  /*    module_name     :: The name of the module.                         */
  /*                                                                       */
  /*    module_version  :: The version, as a 16.16 fixed number            */
  /*                       (major.minor).                                  */
  /*                                                                       */
  /*    module_requires :: The version of FreeType this module requires,   */
  /*                       as a 16.16 fixed number (major.minor).  Starts  */
  /*                       at version 2.0, i.e., 0x20000.                  */
  /*                                                                       */
  /*    module_init     :: The initializing function.                      */
  /*                                                                       */
  /*    module_done     :: The finalizing function.                        */
  /*                                                                       */
  /*    get_interface   :: The interface requesting function.              */
  /*                                                                       */
  typedef struct  FT_Module_Class_
  {
    FT_ULong               module_flags;
    FT_Long                module_size;
    const FT_String*       module_name;
    FT_Fixed               module_version;
    FT_Fixed               module_requires;

    const void*            module_interface;

    FT_Module_Constructor  module_init;
    FT_Module_Destructor   module_done;
    FT_Module_Requester    get_interface;

  } FT_Module_Class;


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Add_Module                                                      */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Add a new module to a given library instance.                      */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    library :: A handle to the library object.                         */
  /*                                                                       */
  /* <Input>                                                               */
  /*    clazz   :: A pointer to class descriptor for the module.           */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    An error will be returned if a module already exists by that name, */
  /*    or if the module requires a version of FreeType that is too great. */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Add_Module( FT_Library              library,
                 const FT_Module_Class*  clazz );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Get_Module                                                      */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Find a module by its name.                                         */
  /*                                                                       */
  /* <Input>                                                               */
  /*    library     :: A handle to the library object.                     */
  /*                                                                       */
  /*    module_name :: The module's name (as an ASCII string).             */
  /*                                                                       */
  /* <Return>                                                              */
  /*    A module handle.  0~if none was found.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    FreeType's internal modules aren't documented very well, and you   */
  /*    should look up the source code for details.                        */
  /*                                                                       */
  FT_EXPORT( FT_Module )
  FT_Get_Module( FT_Library   library,
                 const char*  module_name );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Remove_Module                                                   */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Remove a given module from a library instance.                     */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    library :: A handle to a library object.                           */
  /*                                                                       */
  /* <Input>                                                               */
  /*    module  :: A handle to a module object.                            */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The module object is destroyed by the function in case of success. */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Remove_Module( FT_Library  library,
                    FT_Module   module );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Reference_Library                                               */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A counter gets initialized to~1 at the time an @FT_Library         */
  /*    structure is created.  This function increments the counter.       */
  /*    @FT_Done_Library then only destroys a library if the counter is~1, */
  /*    otherwise it simply decrements the counter.                        */
  /*                                                                       */
  /*    This function helps in managing life-cycles of structures which    */
  /*    reference @FT_Library objects.                                     */
  /*                                                                       */
  /* <Input>                                                               */
  /*    library :: A handle to a target library object.                    */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  /* <Since>                                                               */
  /*    2.4.2                                                              */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Reference_Library( FT_Library  library );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_New_Library                                                     */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This function is used to create a new FreeType library instance    */
  /*    from a given memory object.  It is thus possible to use libraries  */
  /*    with distinct memory allocators within the same program.           */
  /*                                                                       */
  /*    Normally, you would call this function (followed by a call to      */
  /*    @FT_Add_Default_Modules or a series of calls to @FT_Add_Module)    */
  /*    instead of @FT_Init_FreeType to initialize the FreeType library.   */
  /*                                                                       */
  /*    Don't use @FT_Done_FreeType but @FT_Done_Library to destroy a      */
  /*    library instance.                                                  */
  /*                                                                       */
  /* <Input>                                                               */
  /*    memory   :: A handle to the original memory object.                */
  /*                                                                       */
  /* <Output>                                                              */
  /*    alibrary :: A pointer to handle of a new library object.           */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    See the discussion of reference counters in the description of     */
  /*    @FT_Reference_Library.                                             */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_New_Library( FT_Memory    memory,
                  FT_Library  *alibrary );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Done_Library                                                    */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Discard a given library object.  This closes all drivers and       */
  /*    discards all resource objects.                                     */
  /*                                                                       */
  /* <Input>                                                               */
  /*    library :: A handle to the target library.                         */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    See the discussion of reference counters in the description of     */
  /*    @FT_Reference_Library.                                             */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Done_Library( FT_Library  library );

/* */

  typedef void
  (*FT_DebugHook_Func)( void*  arg );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Set_Debug_Hook                                                  */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Set a debug hook function for debugging the interpreter of a font  */
  /*    format.                                                            */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    library    :: A handle to the library object.                      */
  /*                                                                       */
  /* <Input>                                                               */
  /*    hook_index :: The index of the debug hook.  You should use the     */
  /*                  values defined in `ftobjs.h', e.g.,                  */
  /*                  `FT_DEBUG_HOOK_TRUETYPE'.                            */
  /*                                                                       */
  /*    debug_hook :: The function used to debug the interpreter.          */
  /*                                                                       */
  /* <Note>                                                                */
  /*    Currently, four debug hook slots are available, but only two (for  */
  /*    the TrueType and the Type~1 interpreter) are defined.              */
  /*                                                                       */
  /*    Since the internal headers of FreeType are no longer installed,    */
  /*    the symbol `FT_DEBUG_HOOK_TRUETYPE' isn't available publicly.      */
  /*    This is a bug and will be fixed in a forthcoming release.          */
  /*                                                                       */
  FT_EXPORT( void )
  FT_Set_Debug_Hook( FT_Library         library,
                     FT_UInt            hook_index,
                     FT_DebugHook_Func  debug_hook );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Add_Default_Modules                                             */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Add the set of default drivers to a given library object.          */
  /*    This is only useful when you create a library object with          */
  /*    @FT_New_Library (usually to plug a custom memory manager).         */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    library :: A handle to a new library object.                       */
  /*                                                                       */
  FT_EXPORT( void )
  FT_Add_Default_Modules( FT_Library  library );



  /**************************************************************************
   *
   * @section:
   *   truetype_engine
   *
   * @title:
   *   The TrueType Engine
   *
   * @abstract:
   *   TrueType bytecode support.
   *
   * @description:
   *   This section contains a function used to query the level of TrueType
   *   bytecode support compiled in this version of the library.
   *
   */


  /**************************************************************************
   *
   *  @enum:
   *     FT_TrueTypeEngineType
   *
   *  @description:
   *     A list of values describing which kind of TrueType bytecode
   *     engine is implemented in a given FT_Library instance.  It is used
   *     by the @FT_Get_TrueType_Engine_Type function.
   *
   *  @values:
   *     FT_TRUETYPE_ENGINE_TYPE_NONE ::
   *       The library doesn't implement any kind of bytecode interpreter.
   *
   *     FT_TRUETYPE_ENGINE_TYPE_UNPATENTED ::
   *       The library implements a bytecode interpreter that doesn't
   *       support the patented operations of the TrueType virtual machine.
   *
   *       Its main use is to load certain Asian fonts which position and
   *       scale glyph components with bytecode instructions.  It produces
   *       bad output for most other fonts.
   *
   *    FT_TRUETYPE_ENGINE_TYPE_PATENTED ::
   *       The library implements a bytecode interpreter that covers
   *       the full instruction set of the TrueType virtual machine (this
   *       was governed by patents until May 2010, hence the name).
   *
   *  @since:
   *       2.2
   *
   */
  typedef enum  FT_TrueTypeEngineType_
  {
    FT_TRUETYPE_ENGINE_TYPE_NONE = 0,
    FT_TRUETYPE_ENGINE_TYPE_UNPATENTED,
    FT_TRUETYPE_ENGINE_TYPE_PATENTED

  } FT_TrueTypeEngineType;


  /**************************************************************************
   *
   *  @func:
   *     FT_Get_TrueType_Engine_Type
   *
   *  @description:
   *     Return an @FT_TrueTypeEngineType value to indicate which level of
   *     the TrueType virtual machine a given library instance supports.
   *
   *  @input:
   *     library ::
   *       A library instance.
   *
   *  @return:
   *     A value indicating which level is supported.
   *
   *  @since:
   *     2.2
   *
   */
  FT_EXPORT( FT_TrueTypeEngineType )
  FT_Get_TrueType_Engine_Type( FT_Library  library );


  /* */


FT_END_HEADER

#endif /* __FTMODAPI_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  ftmoderr.h                                                             */
/*                                                                         */
/*    FreeType module error offsets (specification).                       */
/*                                                                         */
/*  Copyright 2001, 2002, 2003, 2004, 2005 by                              */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* This file is used to define the FreeType module error offsets.        */
  /*                                                                       */
  /* The lower byte gives the error code, the higher byte gives the        */
  /* module.  The base module has error offset 0.  For example, the error  */
  /* `FT_Err_Invalid_File_Format' has value 0x003, the error               */
  /* `TT_Err_Invalid_File_Format' has value 0x1103, the error              */
  /* `T1_Err_Invalid_File_Format' has value 0x1203, etc.                   */
  /*                                                                       */
  /* Undefine the macro FT_CONFIG_OPTION_USE_MODULE_ERRORS in ftoption.h   */
  /* to make the higher byte always zero (disabling the module error       */
  /* mechanism).                                                           */
  /*                                                                       */
  /* It can also be used to create a module error message table easily     */
  /* with something like                                                   */
  /*                                                                       */
  /*   {                                                                   */
  /*     #undef __FTMODERR_H__                                             */
  /*     #define FT_MODERRDEF( e, v, s )  { FT_Mod_Err_ ## e, s },         */
  /*     #define FT_MODERR_START_LIST     {                                */
  /*     #define FT_MODERR_END_LIST       { 0, 0 } };                      */
  /*                                                                       */
  /*     const struct                                                      */
  /*     {                                                                 */
  /*       int          mod_err_offset;                                    */
  /*       const char*  mod_err_msg                                        */
  /*     } ft_mod_errors[] =                                               */
  /*                                                                       */
  /*     #include FT_MODULE_ERRORS_H                                       */
  /*   }                                                                   */
  /*                                                                       */
  /* To use such a table, all errors must be ANDed with 0xFF00 to remove   */
  /* the error code.                                                       */
  /*                                                                       */
  /*************************************************************************/


#ifndef __FTMODERR_H__
#define __FTMODERR_H__


  /*******************************************************************/
  /*******************************************************************/
  /*****                                                         *****/
  /*****                       SETUP MACROS                      *****/
  /*****                                                         *****/
  /*******************************************************************/
  /*******************************************************************/


#undef  FT_NEED_EXTERN_C

#ifndef FT_MODERRDEF

#ifdef FT_CONFIG_OPTION_USE_MODULE_ERRORS
#define FT_MODERRDEF( e, v, s )  FT_Mod_Err_ ## e = v,
#else
#define FT_MODERRDEF( e, v, s )  FT_Mod_Err_ ## e = 0,
#endif

#define FT_MODERR_START_LIST  enum {
#define FT_MODERR_END_LIST    FT_Mod_Err_Max };

#ifdef __cplusplus
#define FT_NEED_EXTERN_C
  extern "C" {
#endif

#endif /* !FT_MODERRDEF */


  /*******************************************************************/
  /*******************************************************************/
  /*****                                                         *****/
  /*****               LIST MODULE ERROR BASES                   *****/
  /*****                                                         *****/
  /*******************************************************************/
  /*******************************************************************/


#ifdef FT_MODERR_START_LIST
  FT_MODERR_START_LIST
#endif


  FT_MODERRDEF( Base,      0x000, "base module" )
  FT_MODERRDEF( Autofit,   0x100, "autofitter module" )
  FT_MODERRDEF( BDF,       0x200, "BDF module" )
  FT_MODERRDEF( Cache,     0x300, "cache module" )
  FT_MODERRDEF( CFF,       0x400, "CFF module" )
  FT_MODERRDEF( CID,       0x500, "CID module" )
  FT_MODERRDEF( Gzip,      0x600, "Gzip module" )
  FT_MODERRDEF( LZW,       0x700, "LZW module" )
  FT_MODERRDEF( OTvalid,   0x800, "OpenType validation module" )
  FT_MODERRDEF( PCF,       0x900, "PCF module" )
  FT_MODERRDEF( PFR,       0xA00, "PFR module" )
  FT_MODERRDEF( PSaux,     0xB00, "PS auxiliary module" )
  FT_MODERRDEF( PShinter,  0xC00, "PS hinter module" )
  FT_MODERRDEF( PSnames,   0xD00, "PS names module" )
  FT_MODERRDEF( Raster,    0xE00, "raster module" )
  FT_MODERRDEF( SFNT,      0xF00, "SFNT module" )
  FT_MODERRDEF( Smooth,   0x1000, "smooth raster module" )
  FT_MODERRDEF( TrueType, 0x1100, "TrueType module" )
  FT_MODERRDEF( Type1,    0x1200, "Type 1 module" )
  FT_MODERRDEF( Type42,   0x1300, "Type 42 module" )
  FT_MODERRDEF( Winfonts, 0x1400, "Windows FON/FNT module" )


#ifdef FT_MODERR_END_LIST
  FT_MODERR_END_LIST
#endif


  /*******************************************************************/
  /*******************************************************************/
  /*****                                                         *****/
  /*****                      CLEANUP                            *****/
  /*****                                                         *****/
  /*******************************************************************/
  /*******************************************************************/


#ifdef FT_NEED_EXTERN_C
  }
#endif

#undef FT_MODERR_START_LIST
#undef FT_MODERR_END_LIST
#undef FT_MODERRDEF
#undef FT_NEED_EXTERN_C


#endif /* __FTMODERR_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  ftotval.h                                                              */
/*                                                                         */
/*    FreeType API for validating OpenType tables (specification).         */
/*                                                                         */
/*  Copyright 2004, 2005, 2006, 2007 by                                    */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


/***************************************************************************/
/*                                                                         */
/*                                                                         */
/* Warning: This module might be moved to a different library in the       */
/*          future to avoid a tight dependency between FreeType and the    */
/*          OpenType specification.                                        */
/*                                                                         */
/*                                                                         */
/***************************************************************************/


#ifndef __FTOTVAL_H__
#define __FTOTVAL_H__

#include <ft2build.h>
#include FT_FREETYPE_H

#ifdef FREETYPE_H
#error "freetype.h of FreeType 1 has been loaded!"
#error "Please fix the directory search order for header files"
#error "so that freetype.h of FreeType 2 is found first."
#endif


FT_BEGIN_HEADER


  /*************************************************************************/
  /*                                                                       */
  /* <Section>                                                             */
  /*    ot_validation                                                      */
  /*                                                                       */
  /* <Title>                                                               */
  /*    OpenType Validation                                                */
  /*                                                                       */
  /* <Abstract>                                                            */
  /*    An API to validate OpenType tables.                                */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This section contains the declaration of functions to validate     */
  /*    some OpenType tables (BASE, GDEF, GPOS, GSUB, JSTF, MATH).         */
  /*                                                                       */
  /*************************************************************************/


 /**********************************************************************
  *
  * @enum:
  *    FT_VALIDATE_OTXXX
  *
  * @description:
  *    A list of bit-field constants used with @FT_OpenType_Validate to
  *    indicate which OpenType tables should be validated.
  *
  * @values:
  *    FT_VALIDATE_BASE ::
  *      Validate BASE table.
  *
  *    FT_VALIDATE_GDEF ::
  *      Validate GDEF table.
  *
  *    FT_VALIDATE_GPOS ::
  *      Validate GPOS table.
  *
  *    FT_VALIDATE_GSUB ::
  *      Validate GSUB table.
  *
  *    FT_VALIDATE_JSTF ::
  *      Validate JSTF table.
  *
  *    FT_VALIDATE_MATH ::
  *      Validate MATH table.
  *
  *    FT_VALIDATE_OT ::
  *      Validate all OpenType tables (BASE, GDEF, GPOS, GSUB, JSTF, MATH).
  *
  */
#define FT_VALIDATE_BASE  0x0100
#define FT_VALIDATE_GDEF  0x0200
#define FT_VALIDATE_GPOS  0x0400
#define FT_VALIDATE_GSUB  0x0800
#define FT_VALIDATE_JSTF  0x1000
#define FT_VALIDATE_MATH  0x2000

#define FT_VALIDATE_OT  FT_VALIDATE_BASE | \
                        FT_VALIDATE_GDEF | \
                        FT_VALIDATE_GPOS | \
                        FT_VALIDATE_GSUB | \
                        FT_VALIDATE_JSTF | \
                        FT_VALIDATE_MATH

  /* */

 /**********************************************************************
  *
  * @function:
  *    FT_OpenType_Validate
  *
  * @description:
  *    Validate various OpenType tables to assure that all offsets and
  *    indices are valid.  The idea is that a higher-level library which
  *    actually does the text layout can access those tables without
  *    error checking (which can be quite time consuming).
  *
  * @input:
  *    face ::
  *       A handle to the input face.
  *
  *    validation_flags ::
  *       A bit field which specifies the tables to be validated.  See
  *       @FT_VALIDATE_OTXXX for possible values.
  *
  * @output:
  *    BASE_table ::
  *       A pointer to the BASE table.
  *
  *    GDEF_table ::
  *       A pointer to the GDEF table.
  *
  *    GPOS_table ::
  *       A pointer to the GPOS table.
  *
  *    GSUB_table ::
  *       A pointer to the GSUB table.
  *
  *    JSTF_table ::
  *       A pointer to the JSTF table.
  *
  * @return:
  *   FreeType error code.  0~means success.
  *
  * @note:
  *   This function only works with OpenType fonts, returning an error
  *   otherwise.
  *
  *   After use, the application should deallocate the five tables with
  *   @FT_OpenType_Free.  A NULL value indicates that the table either
  *   doesn't exist in the font, or the application hasn't asked for
  *   validation.
  */
  FT_EXPORT( FT_Error )
  FT_OpenType_Validate( FT_Face    face,
                        FT_UInt    validation_flags,
                        FT_Bytes  *BASE_table,
                        FT_Bytes  *GDEF_table,
                        FT_Bytes  *GPOS_table,
                        FT_Bytes  *GSUB_table,
                        FT_Bytes  *JSTF_table );

  /* */

 /**********************************************************************
  *
  * @function:
  *    FT_OpenType_Free
  *
  * @description:
  *    Free the buffer allocated by OpenType validator.
  *
  * @input:
  *    face ::
  *       A handle to the input face.
  *
  *    table ::
  *       The pointer to the buffer that is allocated by
  *       @FT_OpenType_Validate.
  *
  * @note:
  *   This function must be used to free the buffer allocated by
  *   @FT_OpenType_Validate only.
  */
  FT_EXPORT( void )
  FT_OpenType_Free( FT_Face   face,
                    FT_Bytes  table );


 /* */


FT_END_HEADER

#endif /* __FTOTVAL_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  ftoutln.h                                                              */
/*                                                                         */
/*    Support for the FT_Outline type used to store glyph shapes of        */
/*    most scalable font formats (specification).                          */
/*                                                                         */
/*  Copyright 1996-2001, 2002, 2003, 2005, 2006, 2007, 2008, 2009, 2010 by */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __FTOUTLN_H__
#define __FTOUTLN_H__


#include <ft2build.h>
#include FT_FREETYPE_H

#ifdef FREETYPE_H
#error "freetype.h of FreeType 1 has been loaded!"
#error "Please fix the directory search order for header files"
#error "so that freetype.h of FreeType 2 is found first."
#endif


FT_BEGIN_HEADER


  /*************************************************************************/
  /*                                                                       */
  /* <Section>                                                             */
  /*    outline_processing                                                 */
  /*                                                                       */
  /* <Title>                                                               */
  /*    Outline Processing                                                 */
  /*                                                                       */
  /* <Abstract>                                                            */
  /*    Functions to create, transform, and render vectorial glyph images. */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This section contains routines used to create and destroy scalable */
  /*    glyph images known as `outlines'.  These can also be measured,     */
  /*    transformed, and converted into bitmaps and pixmaps.               */
  /*                                                                       */
  /* <Order>                                                               */
  /*    FT_Outline                                                         */
  /*    FT_OUTLINE_FLAGS                                                   */
  /*    FT_Outline_New                                                     */
  /*    FT_Outline_Done                                                    */
  /*    FT_Outline_Copy                                                    */
  /*    FT_Outline_Translate                                               */
  /*    FT_Outline_Transform                                               */
  /*    FT_Outline_Embolden                                                */
  /*    FT_Outline_Reverse                                                 */
  /*    FT_Outline_Check                                                   */
  /*                                                                       */
  /*    FT_Outline_Get_CBox                                                */
  /*    FT_Outline_Get_BBox                                                */
  /*                                                                       */
  /*    FT_Outline_Get_Bitmap                                              */
  /*    FT_Outline_Render                                                  */
  /*                                                                       */
  /*    FT_Outline_Decompose                                               */
  /*    FT_Outline_Funcs                                                   */
  /*    FT_Outline_MoveTo_Func                                             */
  /*    FT_Outline_LineTo_Func                                             */
  /*    FT_Outline_ConicTo_Func                                            */
  /*    FT_Outline_CubicTo_Func                                            */
  /*                                                                       */
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Outline_Decompose                                               */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Walk over an outline's structure to decompose it into individual   */
  /*    segments and BÃ©zier arcs.  This function also emits `move to'      */
  /*    operations to indicate the start of new contours in the outline.   */
  /*                                                                       */
  /* <Input>                                                               */
  /*    outline        :: A pointer to the source target.                  */
  /*                                                                       */
  /*    func_interface :: A table of `emitters', i.e., function pointers   */
  /*                      called during decomposition to indicate path     */
  /*                      operations.                                      */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    user           :: A typeless pointer which is passed to each       */
  /*                      emitter during the decomposition.  It can be     */
  /*                      used to store the state during the               */
  /*                      decomposition.                                   */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Outline_Decompose( FT_Outline*              outline,
                        const FT_Outline_Funcs*  func_interface,
                        void*                    user );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Outline_New                                                     */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Create a new outline of a given size.                              */
  /*                                                                       */
  /* <Input>                                                               */
  /*    library     :: A handle to the library object from where the       */
  /*                   outline is allocated.  Note however that the new    */
  /*                   outline will *not* necessarily be *freed*, when     */
  /*                   destroying the library, by @FT_Done_FreeType.       */
  /*                                                                       */
  /*    numPoints   :: The maximal number of points within the outline.    */
  /*                                                                       */
  /*    numContours :: The maximal number of contours within the outline.  */
  /*                                                                       */
  /* <Output>                                                              */
  /*    anoutline   :: A handle to the new outline.                        */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The reason why this function takes a `library' parameter is simply */
  /*    to use the library's memory allocator.                             */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Outline_New( FT_Library   library,
                  FT_UInt      numPoints,
                  FT_Int       numContours,
                  FT_Outline  *anoutline );


  FT_EXPORT( FT_Error )
  FT_Outline_New_Internal( FT_Memory    memory,
                           FT_UInt      numPoints,
                           FT_Int       numContours,
                           FT_Outline  *anoutline );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Outline_Done                                                    */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Destroy an outline created with @FT_Outline_New.                   */
  /*                                                                       */
  /* <Input>                                                               */
  /*    library :: A handle of the library object used to allocate the     */
  /*               outline.                                                */
  /*                                                                       */
  /*    outline :: A pointer to the outline object to be discarded.        */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    If the outline's `owner' field is not set, only the outline        */
  /*    descriptor will be released.                                       */
  /*                                                                       */
  /*    The reason why this function takes an `library' parameter is       */
  /*    simply to use ft_mem_free().                                       */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Outline_Done( FT_Library   library,
                   FT_Outline*  outline );


  FT_EXPORT( FT_Error )
  FT_Outline_Done_Internal( FT_Memory    memory,
                            FT_Outline*  outline );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Outline_Check                                                   */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Check the contents of an outline descriptor.                       */
  /*                                                                       */
  /* <Input>                                                               */
  /*    outline :: A handle to a source outline.                           */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Outline_Check( FT_Outline*  outline );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Outline_Get_CBox                                                */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Return an outline's `control box'.  The control box encloses all   */
  /*    the outline's points, including BÃ©zier control points.  Though it  */
  /*    coincides with the exact bounding box for most glyphs, it can be   */
  /*    slightly larger in some situations (like when rotating an outline  */
  /*    which contains BÃ©zier outside arcs).                               */
  /*                                                                       */
  /*    Computing the control box is very fast, while getting the bounding */
  /*    box can take much more time as it needs to walk over all segments  */
  /*    and arcs in the outline.  To get the latter, you can use the       */
  /*    `ftbbox' component which is dedicated to this single task.         */
  /*                                                                       */
  /* <Input>                                                               */
  /*    outline :: A pointer to the source outline descriptor.             */
  /*                                                                       */
  /* <Output>                                                              */
  /*    acbox   :: The outline's control box.                              */
  /*                                                                       */
  FT_EXPORT( void )
  FT_Outline_Get_CBox( const FT_Outline*  outline,
                       FT_BBox           *acbox );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Outline_Translate                                               */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Apply a simple translation to the points of an outline.            */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    outline :: A pointer to the target outline descriptor.             */
  /*                                                                       */
  /* <Input>                                                               */
  /*    xOffset :: The horizontal offset.                                  */
  /*                                                                       */
  /*    yOffset :: The vertical offset.                                    */
  /*                                                                       */
  FT_EXPORT( void )
  FT_Outline_Translate( const FT_Outline*  outline,
                        FT_Pos             xOffset,
                        FT_Pos             yOffset );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Outline_Copy                                                    */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Copy an outline into another one.  Both objects must have the      */
  /*    same sizes (number of points & number of contours) when this       */
  /*    function is called.                                                */
  /*                                                                       */
  /* <Input>                                                               */
  /*    source :: A handle to the source outline.                          */
  /*                                                                       */
  /* <Output>                                                              */
  /*    target :: A handle to the target outline.                          */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Outline_Copy( const FT_Outline*  source,
                   FT_Outline        *target );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Outline_Transform                                               */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Apply a simple 2x2 matrix to all of an outline's points.  Useful   */
  /*    for applying rotations, slanting, flipping, etc.                   */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    outline :: A pointer to the target outline descriptor.             */
  /*                                                                       */
  /* <Input>                                                               */
  /*    matrix  :: A pointer to the transformation matrix.                 */
  /*                                                                       */
  /* <Note>                                                                */
  /*    You can use @FT_Outline_Translate if you need to translate the     */
  /*    outline's points.                                                  */
  /*                                                                       */
  FT_EXPORT( void )
  FT_Outline_Transform( const FT_Outline*  outline,
                        const FT_Matrix*   matrix );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Outline_Embolden                                                */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Embolden an outline.  The new outline will be at most 4~times      */
  /*    `strength' pixels wider and higher.  You may think of the left and */
  /*    bottom borders as unchanged.                                       */
  /*                                                                       */
  /*    Negative `strength' values to reduce the outline thickness are     */
  /*    possible also.                                                     */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    outline  :: A handle to the target outline.                        */
  /*                                                                       */
  /* <Input>                                                               */
  /*    strength :: How strong the glyph is emboldened.  Expressed in      */
  /*                26.6 pixel format.                                     */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The used algorithm to increase or decrease the thickness of the    */
  /*    glyph doesn't change the number of points; this means that certain */
  /*    situations like acute angles or intersections are sometimes        */
  /*    handled incorrectly.                                               */
  /*                                                                       */
  /*    If you need `better' metrics values you should call                */
  /*    @FT_Outline_Get_CBox ot @FT_Outline_Get_BBox.                      */
  /*                                                                       */
  /*    Example call:                                                      */
  /*                                                                       */
  /*    {                                                                  */
  /*      FT_Load_Glyph( face, index, FT_LOAD_DEFAULT );                   */
  /*      if ( face->slot->format == FT_GLYPH_FORMAT_OUTLINE )             */
  /*        FT_Outline_Embolden( &face->slot->outline, strength );         */
  /*    }                                                                  */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Outline_Embolden( FT_Outline*  outline,
                       FT_Pos       strength );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Outline_Reverse                                                 */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Reverse the drawing direction of an outline.  This is used to      */
  /*    ensure consistent fill conventions for mirrored glyphs.            */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    outline :: A pointer to the target outline descriptor.             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    This function toggles the bit flag @FT_OUTLINE_REVERSE_FILL in     */
  /*    the outline's `flags' field.                                       */
  /*                                                                       */
  /*    It shouldn't be used by a normal client application, unless it     */
  /*    knows what it is doing.                                            */
  /*                                                                       */
  FT_EXPORT( void )
  FT_Outline_Reverse( FT_Outline*  outline );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Outline_Get_Bitmap                                              */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Render an outline within a bitmap.  The outline's image is simply  */
  /*    OR-ed to the target bitmap.                                        */
  /*                                                                       */
  /* <Input>                                                               */
  /*    library :: A handle to a FreeType library object.                  */
  /*                                                                       */
  /*    outline :: A pointer to the source outline descriptor.             */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    abitmap :: A pointer to the target bitmap descriptor.              */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    This function does NOT CREATE the bitmap, it only renders an       */
  /*    outline image within the one you pass to it!  Consequently, the    */
  /*    various fields in `abitmap' should be set accordingly.             */
  /*                                                                       */
  /*    It will use the raster corresponding to the default glyph format.  */
  /*                                                                       */
  /*    The value of the `num_grays' field in `abitmap' is ignored.  If    */
  /*    you select the gray-level rasterizer, and you want less than 256   */
  /*    gray levels, you have to use @FT_Outline_Render directly.          */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Outline_Get_Bitmap( FT_Library        library,
                         FT_Outline*       outline,
                         const FT_Bitmap  *abitmap );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Outline_Render                                                  */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Render an outline within a bitmap using the current scan-convert.  */
  /*    This function uses an @FT_Raster_Params structure as an argument,  */
  /*    allowing advanced features like direct composition, translucency,  */
  /*    etc.                                                               */
  /*                                                                       */
  /* <Input>                                                               */
  /*    library :: A handle to a FreeType library object.                  */
  /*                                                                       */
  /*    outline :: A pointer to the source outline descriptor.             */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    params  :: A pointer to an @FT_Raster_Params structure used to     */
  /*               describe the rendering operation.                       */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    You should know what you are doing and how @FT_Raster_Params works */
  /*    to use this function.                                              */
  /*                                                                       */
  /*    The field `params.source' will be set to `outline' before the scan */
  /*    converter is called, which means that the value you give to it is  */
  /*    actually ignored.                                                  */
  /*                                                                       */
  /*    The gray-level rasterizer always uses 256 gray levels.  If you     */
  /*    want less gray levels, you have to provide your own span callback. */
  /*    See the @FT_RASTER_FLAG_DIRECT value of the `flags' field in the   */
  /*    @FT_Raster_Params structure for more details.                      */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Outline_Render( FT_Library         library,
                     FT_Outline*        outline,
                     FT_Raster_Params*  params );


 /**************************************************************************
  *
  * @enum:
  *   FT_Orientation
  *
  * @description:
  *   A list of values used to describe an outline's contour orientation.
  *
  *   The TrueType and PostScript specifications use different conventions
  *   to determine whether outline contours should be filled or unfilled.
  *
  * @values:
  *   FT_ORIENTATION_TRUETYPE ::
  *     According to the TrueType specification, clockwise contours must
  *     be filled, and counter-clockwise ones must be unfilled.
  *
  *   FT_ORIENTATION_POSTSCRIPT ::
  *     According to the PostScript specification, counter-clockwise contours
  *     must be filled, and clockwise ones must be unfilled.
  *
  *   FT_ORIENTATION_FILL_RIGHT ::
  *     This is identical to @FT_ORIENTATION_TRUETYPE, but is used to
  *     remember that in TrueType, everything that is to the right of
  *     the drawing direction of a contour must be filled.
  *
  *   FT_ORIENTATION_FILL_LEFT ::
  *     This is identical to @FT_ORIENTATION_POSTSCRIPT, but is used to
  *     remember that in PostScript, everything that is to the left of
  *     the drawing direction of a contour must be filled.
  *
  *   FT_ORIENTATION_NONE ::
  *     The orientation cannot be determined.  That is, different parts of
  *     the glyph have different orientation.
  *
  */
  typedef enum  FT_Orientation_
  {
    FT_ORIENTATION_TRUETYPE   = 0,
    FT_ORIENTATION_POSTSCRIPT = 1,
    FT_ORIENTATION_FILL_RIGHT = FT_ORIENTATION_TRUETYPE,
    FT_ORIENTATION_FILL_LEFT  = FT_ORIENTATION_POSTSCRIPT,
    FT_ORIENTATION_NONE

  } FT_Orientation;


 /**************************************************************************
  *
  * @function:
  *   FT_Outline_Get_Orientation
  *
  * @description:
  *   This function analyzes a glyph outline and tries to compute its
  *   fill orientation (see @FT_Orientation).  This is done by computing
  *   the direction of each global horizontal and/or vertical extrema
  *   within the outline.
  *
  *   Note that this will return @FT_ORIENTATION_TRUETYPE for empty
  *   outlines.
  *
  * @input:
  *   outline ::
  *     A handle to the source outline.
  *
  * @return:
  *   The orientation.
  *
  */
  FT_EXPORT( FT_Orientation )
  FT_Outline_Get_Orientation( FT_Outline*  outline );


  /* */


FT_END_HEADER

#endif /* __FTOUTLN_H__ */


/* END */


/* Local Variables: */
/* coding: utf-8    */
/* End:             */
/***************************************************************************/
/*                                                                         */
/*  ftpfr.h                                                                */
/*                                                                         */
/*    FreeType API for accessing PFR-specific data (specification only).   */
/*                                                                         */
/*  Copyright 2002, 2003, 2004, 2006, 2008, 2009 by                        */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __FTPFR_H__
#define __FTPFR_H__

#include <ft2build.h>
#include FT_FREETYPE_H

#ifdef FREETYPE_H
#error "freetype.h of FreeType 1 has been loaded!"
#error "Please fix the directory search order for header files"
#error "so that freetype.h of FreeType 2 is found first."
#endif


FT_BEGIN_HEADER


  /*************************************************************************/
  /*                                                                       */
  /* <Section>                                                             */
  /*    pfr_fonts                                                          */
  /*                                                                       */
  /* <Title>                                                               */
  /*    PFR Fonts                                                          */
  /*                                                                       */
  /* <Abstract>                                                            */
  /*    PFR/TrueDoc specific API.                                          */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This section contains the declaration of PFR-specific functions.   */
  /*                                                                       */
  /*************************************************************************/


 /**********************************************************************
  *
  * @function:
  *    FT_Get_PFR_Metrics
  *
  * @description:
  *    Return the outline and metrics resolutions of a given PFR face.
  *
  * @input:
  *    face :: Handle to the input face.  It can be a non-PFR face.
  *
  * @output:
  *    aoutline_resolution ::
  *      Outline resolution.  This is equivalent to `face->units_per_EM'
  *      for non-PFR fonts.  Optional (parameter can be NULL).
  *
  *    ametrics_resolution ::
  *      Metrics resolution.  This is equivalent to `outline_resolution'
  *      for non-PFR fonts.  Optional (parameter can be NULL).
  *
  *    ametrics_x_scale ::
  *      A 16.16 fixed-point number used to scale distance expressed
  *      in metrics units to device sub-pixels.  This is equivalent to
  *      `face->size->x_scale', but for metrics only.  Optional (parameter
  *      can be NULL).
  *
  *    ametrics_y_scale ::
  *      Same as `ametrics_x_scale' but for the vertical direction.
  *      optional (parameter can be NULL).
  *
  * @return:
  *    FreeType error code.  0~means success.
  *
  * @note:
  *   If the input face is not a PFR, this function will return an error.
  *   However, in all cases, it will return valid values.
  */
  FT_EXPORT( FT_Error )
  FT_Get_PFR_Metrics( FT_Face    face,
                      FT_UInt   *aoutline_resolution,
                      FT_UInt   *ametrics_resolution,
                      FT_Fixed  *ametrics_x_scale,
                      FT_Fixed  *ametrics_y_scale );


 /**********************************************************************
  *
  * @function:
  *    FT_Get_PFR_Kerning
  *
  * @description:
  *    Return the kerning pair corresponding to two glyphs in a PFR face.
  *    The distance is expressed in metrics units, unlike the result of
  *    @FT_Get_Kerning.
  *
  * @input:
  *    face  :: A handle to the input face.
  *
  *    left  :: Index of the left glyph.
  *
  *    right :: Index of the right glyph.
  *
  * @output:
  *    avector :: A kerning vector.
  *
  * @return:
  *    FreeType error code.  0~means success.
  *
  * @note:
  *    This function always return distances in original PFR metrics
  *    units.  This is unlike @FT_Get_Kerning with the @FT_KERNING_UNSCALED
  *    mode, which always returns distances converted to outline units.
  *
  *    You can use the value of the `x_scale' and `y_scale' parameters
  *    returned by @FT_Get_PFR_Metrics to scale these to device sub-pixels.
  */
  FT_EXPORT( FT_Error )
  FT_Get_PFR_Kerning( FT_Face     face,
                      FT_UInt     left,
                      FT_UInt     right,
                      FT_Vector  *avector );


 /**********************************************************************
  *
  * @function:
  *    FT_Get_PFR_Advance
  *
  * @description:
  *    Return a given glyph advance, expressed in original metrics units,
  *    from a PFR font.
  *
  * @input:
  *    face   :: A handle to the input face.
  *
  *    gindex :: The glyph index.
  *
  * @output:
  *    aadvance :: The glyph advance in metrics units.
  *
  * @return:
  *    FreeType error code.  0~means success.
  *
  * @note:
  *    You can use the `x_scale' or `y_scale' results of @FT_Get_PFR_Metrics
  *    to convert the advance to device sub-pixels (i.e., 1/64th of pixels).
  */
  FT_EXPORT( FT_Error )
  FT_Get_PFR_Advance( FT_Face   face,
                      FT_UInt   gindex,
                      FT_Pos   *aadvance );

 /* */


FT_END_HEADER

#endif /* __FTPFR_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  ftrender.h                                                             */
/*                                                                         */
/*    FreeType renderer modules public interface (specification).          */
/*                                                                         */
/*  Copyright 1996-2001, 2005, 2006, 2010 by                               */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __FTRENDER_H__
#define __FTRENDER_H__


#include <ft2build.h>
#include FT_MODULE_H
#include FT_GLYPH_H


FT_BEGIN_HEADER


  /*************************************************************************/
  /*                                                                       */
  /* <Section>                                                             */
  /*    module_management                                                  */
  /*                                                                       */
  /*************************************************************************/


  /* create a new glyph object */
  typedef FT_Error
  (*FT_Glyph_InitFunc)( FT_Glyph      glyph,
                        FT_GlyphSlot  slot );

  /* destroys a given glyph object */
  typedef void
  (*FT_Glyph_DoneFunc)( FT_Glyph  glyph );

  typedef void
  (*FT_Glyph_TransformFunc)( FT_Glyph          glyph,
                             const FT_Matrix*  matrix,
                             const FT_Vector*  delta );

  typedef void
  (*FT_Glyph_GetBBoxFunc)( FT_Glyph  glyph,
                           FT_BBox*  abbox );

  typedef FT_Error
  (*FT_Glyph_CopyFunc)( FT_Glyph   source,
                        FT_Glyph   target );

  typedef FT_Error
  (*FT_Glyph_PrepareFunc)( FT_Glyph      glyph,
                           FT_GlyphSlot  slot );

/* deprecated */
#define FT_Glyph_Init_Func       FT_Glyph_InitFunc
#define FT_Glyph_Done_Func       FT_Glyph_DoneFunc
#define FT_Glyph_Transform_Func  FT_Glyph_TransformFunc
#define FT_Glyph_BBox_Func       FT_Glyph_GetBBoxFunc
#define FT_Glyph_Copy_Func       FT_Glyph_CopyFunc
#define FT_Glyph_Prepare_Func    FT_Glyph_PrepareFunc


  struct  FT_Glyph_Class_
  {
    FT_Long                 glyph_size;
    FT_Glyph_Format         glyph_format;
    FT_Glyph_InitFunc       glyph_init;
    FT_Glyph_DoneFunc       glyph_done;
    FT_Glyph_CopyFunc       glyph_copy;
    FT_Glyph_TransformFunc  glyph_transform;
    FT_Glyph_GetBBoxFunc    glyph_bbox;
    FT_Glyph_PrepareFunc    glyph_prepare;
  };


  typedef FT_Error
  (*FT_Renderer_RenderFunc)( FT_Renderer       renderer,
                             FT_GlyphSlot      slot,
                             FT_UInt           mode,
                             const FT_Vector*  origin );

  typedef FT_Error
  (*FT_Renderer_TransformFunc)( FT_Renderer       renderer,
                                FT_GlyphSlot      slot,
                                const FT_Matrix*  matrix,
                                const FT_Vector*  delta );


  typedef void
  (*FT_Renderer_GetCBoxFunc)( FT_Renderer   renderer,
                              FT_GlyphSlot  slot,
                              FT_BBox*      cbox );


  typedef FT_Error
  (*FT_Renderer_SetModeFunc)( FT_Renderer  renderer,
                              FT_ULong     mode_tag,
                              FT_Pointer   mode_ptr );

/* deprecated identifiers */
#define FTRenderer_render  FT_Renderer_RenderFunc
#define FTRenderer_transform  FT_Renderer_TransformFunc
#define FTRenderer_getCBox  FT_Renderer_GetCBoxFunc
#define FTRenderer_setMode  FT_Renderer_SetModeFunc


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_Renderer_Class                                                  */
  /*                                                                       */
  /* <Description>                                                         */
  /*    The renderer module class descriptor.                              */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    root            :: The root @FT_Module_Class fields.               */
  /*                                                                       */
  /*    glyph_format    :: The glyph image format this renderer handles.   */
  /*                                                                       */
  /*    render_glyph    :: A method used to render the image that is in a  */
  /*                       given glyph slot into a bitmap.                 */
  /*                                                                       */
  /*    transform_glyph :: A method used to transform the image that is in */
  /*                       a given glyph slot.                             */
  /*                                                                       */
  /*    get_glyph_cbox  :: A method used to access the glyph's cbox.       */
  /*                                                                       */
  /*    set_mode        :: A method used to pass additional parameters.    */
  /*                                                                       */
  /*    raster_class    :: For @FT_GLYPH_FORMAT_OUTLINE renderers only.    */
  /*                       This is a pointer to its raster's class.        */
  /*                                                                       */
  typedef struct  FT_Renderer_Class_
  {
    FT_Module_Class            root;

    FT_Glyph_Format            glyph_format;

    FT_Renderer_RenderFunc     render_glyph;
    FT_Renderer_TransformFunc  transform_glyph;
    FT_Renderer_GetCBoxFunc    get_glyph_cbox;
    FT_Renderer_SetModeFunc    set_mode;

    FT_Raster_Funcs*           raster_class;

  } FT_Renderer_Class;


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Get_Renderer                                                    */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Retrieve the current renderer for a given glyph format.            */
  /*                                                                       */
  /* <Input>                                                               */
  /*    library :: A handle to the library object.                         */
  /*                                                                       */
  /*    format  :: The glyph format.                                       */
  /*                                                                       */
  /* <Return>                                                              */
  /*    A renderer handle.  0~if none found.                               */
  /*                                                                       */
  /* <Note>                                                                */
  /*    An error will be returned if a module already exists by that name, */
  /*    or if the module requires a version of FreeType that is too great. */
  /*                                                                       */
  /*    To add a new renderer, simply use @FT_Add_Module.  To retrieve a   */
  /*    renderer by its name, use @FT_Get_Module.                          */
  /*                                                                       */
  FT_EXPORT( FT_Renderer )
  FT_Get_Renderer( FT_Library       library,
                   FT_Glyph_Format  format );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Set_Renderer                                                    */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Set the current renderer to use, and set additional mode.          */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    library    :: A handle to the library object.                      */
  /*                                                                       */
  /* <Input>                                                               */
  /*    renderer   :: A handle to the renderer object.                     */
  /*                                                                       */
  /*    num_params :: The number of additional parameters.                 */
  /*                                                                       */
  /*    parameters :: Additional parameters.                               */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    In case of success, the renderer will be used to convert glyph     */
  /*    images in the renderer's known format into bitmaps.                */
  /*                                                                       */
  /*    This doesn't change the current renderer for other formats.        */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Set_Renderer( FT_Library     library,
                   FT_Renderer    renderer,
                   FT_UInt        num_params,
                   FT_Parameter*  parameters );


  /* */


FT_END_HEADER

#endif /* __FTRENDER_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  ftsizes.h                                                              */
/*                                                                         */
/*    FreeType size objects management (specification).                    */
/*                                                                         */
/*  Copyright 1996-2001, 2003, 2004, 2006, 2009 by                         */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* Typical application would normally not need to use these functions.   */
  /* However, they have been placed in a public API for the rare cases     */
  /* where they are needed.                                                */
  /*                                                                       */
  /*************************************************************************/


#ifndef __FTSIZES_H__
#define __FTSIZES_H__


#include <ft2build.h>
#include FT_FREETYPE_H

#ifdef FREETYPE_H
#error "freetype.h of FreeType 1 has been loaded!"
#error "Please fix the directory search order for header files"
#error "so that freetype.h of FreeType 2 is found first."
#endif


FT_BEGIN_HEADER


  /*************************************************************************/
  /*                                                                       */
  /* <Section>                                                             */
  /*    sizes_management                                                   */
  /*                                                                       */
  /* <Title>                                                               */
  /*    Size Management                                                    */
  /*                                                                       */
  /* <Abstract>                                                            */
  /*    Managing multiple sizes per face.                                  */
  /*                                                                       */
  /* <Description>                                                         */
  /*    When creating a new face object (e.g., with @FT_New_Face), an      */
  /*    @FT_Size object is automatically created and used to store all     */
  /*    pixel-size dependent information, available in the `face->size'    */
  /*    field.                                                             */
  /*                                                                       */
  /*    It is however possible to create more sizes for a given face,      */
  /*    mostly in order to manage several character pixel sizes of the     */
  /*    same font family and style.  See @FT_New_Size and @FT_Done_Size.   */
  /*                                                                       */
  /*    Note that @FT_Set_Pixel_Sizes and @FT_Set_Char_Size only           */
  /*    modify the contents of the current `active' size; you thus need    */
  /*    to use @FT_Activate_Size to change it.                             */
  /*                                                                       */
  /*    99% of applications won't need the functions provided here,        */
  /*    especially if they use the caching sub-system, so be cautious      */
  /*    when using these.                                                  */
  /*                                                                       */
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_New_Size                                                        */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Create a new size object from a given face object.                 */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face :: A handle to a parent face object.                          */
  /*                                                                       */
  /* <Output>                                                              */
  /*    asize :: A handle to a new size object.                            */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    You need to call @FT_Activate_Size in order to select the new size */
  /*    for upcoming calls to @FT_Set_Pixel_Sizes, @FT_Set_Char_Size,      */
  /*    @FT_Load_Glyph, @FT_Load_Char, etc.                                */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_New_Size( FT_Face   face,
               FT_Size*  size );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Done_Size                                                       */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Discard a given size object.  Note that @FT_Done_Face              */
  /*    automatically discards all size objects allocated with             */
  /*    @FT_New_Size.                                                      */
  /*                                                                       */
  /* <Input>                                                               */
  /*    size :: A handle to a target size object.                          */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Done_Size( FT_Size  size );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Activate_Size                                                   */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Even though it is possible to create several size objects for a    */
  /*    given face (see @FT_New_Size for details), functions like          */
  /*    @FT_Load_Glyph or @FT_Load_Char only use the one which has been    */
  /*    activated last to determine the `current character pixel size'.    */
  /*                                                                       */
  /*    This function can be used to `activate' a previously created size  */
  /*    object.                                                            */
  /*                                                                       */
  /* <Input>                                                               */
  /*    size :: A handle to a target size object.                          */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    If `face' is the size's parent face object, this function changes  */
  /*    the value of `face->size' to the input size handle.                */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Activate_Size( FT_Size  size );

  /* */


FT_END_HEADER

#endif /* __FTSIZES_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  ftsnames.h                                                             */
/*                                                                         */
/*    Simple interface to access SFNT name tables (which are used          */
/*    to hold font names, copyright info, notices, etc.) (specification).  */
/*                                                                         */
/*    This is _not_ used to retrieve glyph names!                          */
/*                                                                         */
/*  Copyright 1996-2001, 2002, 2003, 2006, 2009, 2010 by                   */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __FT_SFNT_NAMES_H__
#define __FT_SFNT_NAMES_H__


#include <ft2build.h>
#include FT_FREETYPE_H

#ifdef FREETYPE_H
#error "freetype.h of FreeType 1 has been loaded!"
#error "Please fix the directory search order for header files"
#error "so that freetype.h of FreeType 2 is found first."
#endif


FT_BEGIN_HEADER


  /*************************************************************************/
  /*                                                                       */
  /* <Section>                                                             */
  /*    sfnt_names                                                         */
  /*                                                                       */
  /* <Title>                                                               */
  /*    SFNT Names                                                         */
  /*                                                                       */
  /* <Abstract>                                                            */
  /*    Access the names embedded in TrueType and OpenType files.          */
  /*                                                                       */
  /* <Description>                                                         */
  /*    The TrueType and OpenType specifications allow the inclusion of    */
  /*    a special `names table' in font files.  This table contains        */
  /*    textual (and internationalized) information regarding the font,    */
  /*    like family name, copyright, version, etc.                         */
  /*                                                                       */
  /*    The definitions below are used to access them if available.        */
  /*                                                                       */
  /*    Note that this has nothing to do with glyph names!                 */
  /*                                                                       */
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_SfntName                                                        */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A structure used to model an SFNT `name' table entry.              */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    platform_id :: The platform ID for `string'.                       */
  /*                                                                       */
  /*    encoding_id :: The encoding ID for `string'.                       */
  /*                                                                       */
  /*    language_id :: The language ID for `string'.                       */
  /*                                                                       */
  /*    name_id     :: An identifier for `string'.                         */
  /*                                                                       */
  /*    string      :: The `name' string.  Note that its format differs    */
  /*                   depending on the (platform,encoding) pair.  It can  */
  /*                   be a Pascal String, a UTF-16 one, etc.              */
  /*                                                                       */
  /*                   Generally speaking, the string is not               */
  /*                   zero-terminated.  Please refer to the TrueType      */
  /*                   specification for details.                          */
  /*                                                                       */
  /*    string_len  :: The length of `string' in bytes.                    */
  /*                                                                       */
  /* <Note>                                                                */
  /*    Possible values for `platform_id', `encoding_id', `language_id',   */
  /*    and `name_id' are given in the file `ttnameid.h'.  For details     */
  /*    please refer to the TrueType or OpenType specification.            */
  /*                                                                       */
  /*    See also @TT_PLATFORM_XXX, @TT_APPLE_ID_XXX, @TT_MAC_ID_XXX,       */
  /*    @TT_ISO_ID_XXX, and @TT_MS_ID_XXX.                                 */
  /*                                                                       */
  typedef struct  FT_SfntName_
  {
    FT_UShort  platform_id;
    FT_UShort  encoding_id;
    FT_UShort  language_id;
    FT_UShort  name_id;

    FT_Byte*   string;      /* this string is *not* null-terminated! */
    FT_UInt    string_len;  /* in bytes */

  } FT_SfntName;


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Get_Sfnt_Name_Count                                             */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Retrieve the number of name strings in the SFNT `name' table.      */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face :: A handle to the source face.                               */
  /*                                                                       */
  /* <Return>                                                              */
  /*    The number of strings in the `name' table.                         */
  /*                                                                       */
  FT_EXPORT( FT_UInt )
  FT_Get_Sfnt_Name_Count( FT_Face  face );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Get_Sfnt_Name                                                   */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Retrieve a string of the SFNT `name' table for a given index.      */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face  :: A handle to the source face.                              */
  /*                                                                       */
  /*    idx   :: The index of the `name' string.                           */
  /*                                                                       */
  /* <Output>                                                              */
  /*    aname :: The indexed @FT_SfntName structure.                       */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0~means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The `string' array returned in the `aname' structure is not        */
  /*    null-terminated.  The application should deallocate it if it is no */
  /*    longer in use.                                                     */
  /*                                                                       */
  /*    Use @FT_Get_Sfnt_Name_Count to get the total number of available   */
  /*    `name' table entries, then do a loop until you get the right       */
  /*    platform, encoding, and name ID.                                   */
  /*                                                                       */
  FT_EXPORT( FT_Error )
  FT_Get_Sfnt_Name( FT_Face       face,
                    FT_UInt       idx,
                    FT_SfntName  *aname );


  /***************************************************************************
   *
   * @constant:
   *   FT_PARAM_TAG_IGNORE_PREFERRED_FAMILY
   *
   * @description:
   *   A constant used as the tag of @FT_Parameter structures to make
   *   FT_Open_Face() ignore preferred family subfamily names in `name'
   *   table since OpenType version 1.4.  For backwards compatibility with
   *   legacy systems which has 4-face-per-family restriction.
   *
   */
#define FT_PARAM_TAG_IGNORE_PREFERRED_FAMILY  FT_MAKE_TAG( 'i', 'g', 'p', 'f' )


  /***************************************************************************
   *
   * @constant:
   *   FT_PARAM_TAG_IGNORE_PREFERRED_SUBFAMILY
   *
   * @description:
   *   A constant used as the tag of @FT_Parameter structures to make
   *   FT_Open_Face() ignore preferred subfamily names in `name' table since
   *   OpenType version 1.4.  For backwards compatibility with legacy
   *   systems which has 4-face-per-family restriction.
   *
   */
#define FT_PARAM_TAG_IGNORE_PREFERRED_SUBFAMILY  FT_MAKE_TAG( 'i', 'g', 'p', 's' )

  /* */


FT_END_HEADER

#endif /* __FT_SFNT_NAMES_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  ftstroke.h                                                             */
/*                                                                         */
/*    FreeType path stroker (specification).                               */
/*                                                                         */
/*  Copyright 2002, 2003, 2004, 2005, 2006, 2008, 2009 by                  */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __FT_STROKE_H__
#define __FT_STROKE_H__

#include <ft2build.h>
#include FT_OUTLINE_H
#include FT_GLYPH_H


FT_BEGIN_HEADER


 /************************************************************************
  *
  * @section:
  *    glyph_stroker
  *
  * @title:
  *    Glyph Stroker
  *
  * @abstract:
  *    Generating bordered and stroked glyphs.
  *
  * @description:
  *    This component generates stroked outlines of a given vectorial
  *    glyph.  It also allows you to retrieve the `outside' and/or the
  *    `inside' borders of the stroke.
  *
  *    This can be useful to generate `bordered' glyph, i.e., glyphs
  *    displayed with a coloured (and anti-aliased) border around their
  *    shape.
  */


 /**************************************************************
  *
  * @type:
  *   FT_Stroker
  *
  * @description:
  *   Opaque handler to a path stroker object.
  */
  typedef struct FT_StrokerRec_*  FT_Stroker;


  /**************************************************************
   *
   * @enum:
   *   FT_Stroker_LineJoin
   *
   * @description:
   *   These values determine how two joining lines are rendered
   *   in a stroker.
   *
   * @values:
   *   FT_STROKER_LINEJOIN_ROUND ::
   *     Used to render rounded line joins.  Circular arcs are used
   *     to join two lines smoothly.
   *
   *   FT_STROKER_LINEJOIN_BEVEL ::
   *     Used to render beveled line joins; i.e., the two joining lines
   *     are extended until they intersect.
   *
   *   FT_STROKER_LINEJOIN_MITER ::
   *     Same as beveled rendering, except that an additional line
   *     break is added if the angle between the two joining lines
   *     is too closed (this is useful to avoid unpleasant spikes
   *     in beveled rendering).
   */
  typedef enum  FT_Stroker_LineJoin_
  {
    FT_STROKER_LINEJOIN_ROUND = 0,
    FT_STROKER_LINEJOIN_BEVEL,
    FT_STROKER_LINEJOIN_MITER

  } FT_Stroker_LineJoin;


  /**************************************************************
   *
   * @enum:
   *   FT_Stroker_LineCap
   *
   * @description:
   *   These values determine how the end of opened sub-paths are
   *   rendered in a stroke.
   *
   * @values:
   *   FT_STROKER_LINECAP_BUTT ::
   *     The end of lines is rendered as a full stop on the last
   *     point itself.
   *
   *   FT_STROKER_LINECAP_ROUND ::
   *     The end of lines is rendered as a half-circle around the
   *     last point.
   *
   *   FT_STROKER_LINECAP_SQUARE ::
   *     The end of lines is rendered as a square around the
   *     last point.
   */
  typedef enum  FT_Stroker_LineCap_
  {
    FT_STROKER_LINECAP_BUTT = 0,
    FT_STROKER_LINECAP_ROUND,
    FT_STROKER_LINECAP_SQUARE

  } FT_Stroker_LineCap;


  /**************************************************************
   *
   * @enum:
   *   FT_StrokerBorder
   *
   * @description:
   *   These values are used to select a given stroke border
   *   in @FT_Stroker_GetBorderCounts and @FT_Stroker_ExportBorder.
   *
   * @values:
   *   FT_STROKER_BORDER_LEFT ::
   *     Select the left border, relative to the drawing direction.
   *
   *   FT_STROKER_BORDER_RIGHT ::
   *     Select the right border, relative to the drawing direction.
   *
   * @note:
   *   Applications are generally interested in the `inside' and `outside'
   *   borders.  However, there is no direct mapping between these and the
   *   `left' and `right' ones, since this really depends on the glyph's
   *   drawing orientation, which varies between font formats.
   *
   *   You can however use @FT_Outline_GetInsideBorder and
   *   @FT_Outline_GetOutsideBorder to get these.
   */
  typedef enum  FT_StrokerBorder_
  {
    FT_STROKER_BORDER_LEFT = 0,
    FT_STROKER_BORDER_RIGHT

  } FT_StrokerBorder;


  /**************************************************************
   *
   * @function:
   *   FT_Outline_GetInsideBorder
   *
   * @description:
   *   Retrieve the @FT_StrokerBorder value corresponding to the
   *   `inside' borders of a given outline.
   *
   * @input:
   *   outline ::
   *     The source outline handle.
   *
   * @return:
   *   The border index.  @FT_STROKER_BORDER_RIGHT for empty or invalid
   *   outlines.
   */
  FT_EXPORT( FT_StrokerBorder )
  FT_Outline_GetInsideBorder( FT_Outline*  outline );


  /**************************************************************
   *
   * @function:
   *   FT_Outline_GetOutsideBorder
   *
   * @description:
   *   Retrieve the @FT_StrokerBorder value corresponding to the
   *   `outside' borders of a given outline.
   *
   * @input:
   *   outline ::
   *     The source outline handle.
   *
   * @return:
   *   The border index.  @FT_STROKER_BORDER_LEFT for empty or invalid
   *   outlines.
   */
  FT_EXPORT( FT_StrokerBorder )
  FT_Outline_GetOutsideBorder( FT_Outline*  outline );


  /**************************************************************
   *
   * @function:
   *   FT_Stroker_New
   *
   * @description:
   *   Create a new stroker object.
   *
   * @input:
   *   library ::
   *     FreeType library handle.
   *
   * @output:
   *   astroker ::
   *     A new stroker object handle.  NULL in case of error.
   *
   * @return:
   *    FreeType error code.  0~means success.
   */
  FT_EXPORT( FT_Error )
  FT_Stroker_New( FT_Library   library,
                  FT_Stroker  *astroker );


  /**************************************************************
   *
   * @function:
   *   FT_Stroker_Set
   *
   * @description:
   *   Reset a stroker object's attributes.
   *
   * @input:
   *   stroker ::
   *     The target stroker handle.
   *
   *   radius ::
   *     The border radius.
   *
   *   line_cap ::
   *     The line cap style.
   *
   *   line_join ::
   *     The line join style.
   *
   *   miter_limit ::
   *     The miter limit for the FT_STROKER_LINEJOIN_MITER style,
   *     expressed as 16.16 fixed point value.
   *
   * @note:
   *   The radius is expressed in the same units as the outline
   *   coordinates.
   */
  FT_EXPORT( void )
  FT_Stroker_Set( FT_Stroker           stroker,
                  FT_Fixed             radius,
                  FT_Stroker_LineCap   line_cap,
                  FT_Stroker_LineJoin  line_join,
                  FT_Fixed             miter_limit );


  /**************************************************************
   *
   * @function:
   *   FT_Stroker_Rewind
   *
   * @description:
   *   Reset a stroker object without changing its attributes.
   *   You should call this function before beginning a new
   *   series of calls to @FT_Stroker_BeginSubPath or
   *   @FT_Stroker_EndSubPath.
   *
   * @input:
   *   stroker ::
   *     The target stroker handle.
   */
  FT_EXPORT( void )
  FT_Stroker_Rewind( FT_Stroker  stroker );


  /**************************************************************
   *
   * @function:
   *   FT_Stroker_ParseOutline
   *
   * @description:
   *   A convenience function used to parse a whole outline with
   *   the stroker.  The resulting outline(s) can be retrieved
   *   later by functions like @FT_Stroker_GetCounts and @FT_Stroker_Export.
   *
   * @input:
   *   stroker ::
   *     The target stroker handle.
   *
   *   outline ::
   *     The source outline.
   *
   *   opened ::
   *     A boolean.  If~1, the outline is treated as an open path instead
   *     of a closed one.
   *
   * @return:
   *   FreeType error code.  0~means success.
   *
   * @note:
   *   If `opened' is~0 (the default), the outline is treated as a closed
   *   path, and the stroker generates two distinct `border' outlines.
   *
   *   If `opened' is~1, the outline is processed as an open path, and the
   *   stroker generates a single `stroke' outline.
   *
   *   This function calls @FT_Stroker_Rewind automatically.
   */
  FT_EXPORT( FT_Error )
  FT_Stroker_ParseOutline( FT_Stroker   stroker,
                           FT_Outline*  outline,
                           FT_Bool      opened );


  /**************************************************************
   *
   * @function:
   *   FT_Stroker_BeginSubPath
   *
   * @description:
   *   Start a new sub-path in the stroker.
   *
   * @input:
   *   stroker ::
   *     The target stroker handle.
   *
   *   to ::
   *     A pointer to the start vector.
   *
   *   open ::
   *     A boolean.  If~1, the sub-path is treated as an open one.
   *
   * @return:
   *   FreeType error code.  0~means success.
   *
   * @note:
   *   This function is useful when you need to stroke a path that is
   *   not stored as an @FT_Outline object.
   */
  FT_EXPORT( FT_Error )
  FT_Stroker_BeginSubPath( FT_Stroker  stroker,
                           FT_Vector*  to,
                           FT_Bool     open );


  /**************************************************************
   *
   * @function:
   *   FT_Stroker_EndSubPath
   *
   * @description:
   *   Close the current sub-path in the stroker.
   *
   * @input:
   *   stroker ::
   *     The target stroker handle.
   *
   * @return:
   *   FreeType error code.  0~means success.
   *
   * @note:
   *   You should call this function after @FT_Stroker_BeginSubPath.
   *   If the subpath was not `opened', this function `draws' a
   *   single line segment to the start position when needed.
   */
  FT_EXPORT( FT_Error )
  FT_Stroker_EndSubPath( FT_Stroker  stroker );


  /**************************************************************
   *
   * @function:
   *   FT_Stroker_LineTo
   *
   * @description:
   *   `Draw' a single line segment in the stroker's current sub-path,
   *   from the last position.
   *
   * @input:
   *   stroker ::
   *     The target stroker handle.
   *
   *   to ::
   *     A pointer to the destination point.
   *
   * @return:
   *   FreeType error code.  0~means success.
   *
   * @note:
   *   You should call this function between @FT_Stroker_BeginSubPath and
   *   @FT_Stroker_EndSubPath.
   */
  FT_EXPORT( FT_Error )
  FT_Stroker_LineTo( FT_Stroker  stroker,
                     FT_Vector*  to );


  /**************************************************************
   *
   * @function:
   *   FT_Stroker_ConicTo
   *
   * @description:
   *   `Draw' a single quadratic BÃ©zier in the stroker's current sub-path,
   *   from the last position.
   *
   * @input:
   *   stroker ::
   *     The target stroker handle.
   *
   *   control ::
   *     A pointer to a BÃ©zier control point.
   *
   *   to ::
   *     A pointer to the destination point.
   *
   * @return:
   *   FreeType error code.  0~means success.
   *
   * @note:
   *   You should call this function between @FT_Stroker_BeginSubPath and
   *   @FT_Stroker_EndSubPath.
   */
  FT_EXPORT( FT_Error )
  FT_Stroker_ConicTo( FT_Stroker  stroker,
                      FT_Vector*  control,
                      FT_Vector*  to );


  /**************************************************************
   *
   * @function:
   *   FT_Stroker_CubicTo
   *
   * @description:
   *   `Draw' a single cubic BÃ©zier in the stroker's current sub-path,
   *   from the last position.
   *
   * @input:
   *   stroker ::
   *     The target stroker handle.
   *
   *   control1 ::
   *     A pointer to the first BÃ©zier control point.
   *
   *   control2 ::
   *     A pointer to second BÃ©zier control point.
   *
   *   to ::
   *     A pointer to the destination point.
   *
   * @return:
   *   FreeType error code.  0~means success.
   *
   * @note:
   *   You should call this function between @FT_Stroker_BeginSubPath and
   *   @FT_Stroker_EndSubPath.
   */
  FT_EXPORT( FT_Error )
  FT_Stroker_CubicTo( FT_Stroker  stroker,
                      FT_Vector*  control1,
                      FT_Vector*  control2,
                      FT_Vector*  to );


  /**************************************************************
   *
   * @function:
   *   FT_Stroker_GetBorderCounts
   *
   * @description:
   *   Call this function once you have finished parsing your paths
   *   with the stroker.  It returns the number of points and
   *   contours necessary to export one of the `border' or `stroke'
   *   outlines generated by the stroker.
   *
   * @input:
   *   stroker ::
   *     The target stroker handle.
   *
   *   border ::
   *     The border index.
   *
   * @output:
   *   anum_points ::
   *     The number of points.
   *
   *   anum_contours ::
   *     The number of contours.
   *
   * @return:
   *   FreeType error code.  0~means success.
   *
   * @note:
   *   When an outline, or a sub-path, is `closed', the stroker generates
   *   two independent `border' outlines, named `left' and `right'.
   *
   *   When the outline, or a sub-path, is `opened', the stroker merges
   *   the `border' outlines with caps.  The `left' border receives all
   *   points, while the `right' border becomes empty.
   *
   *   Use the function @FT_Stroker_GetCounts instead if you want to
   *   retrieve the counts associated to both borders.
   */
  FT_EXPORT( FT_Error )
  FT_Stroker_GetBorderCounts( FT_Stroker        stroker,
                              FT_StrokerBorder  border,
                              FT_UInt          *anum_points,
                              FT_UInt          *anum_contours );


  /**************************************************************
   *
   * @function:
   *   FT_Stroker_ExportBorder
   *
   * @description:
   *   Call this function after @FT_Stroker_GetBorderCounts to
   *   export the corresponding border to your own @FT_Outline
   *   structure.
   *
   *   Note that this function appends the border points and
   *   contours to your outline, but does not try to resize its
   *   arrays.
   *
   * @input:
   *   stroker ::
   *     The target stroker handle.
   *
   *   border ::
   *     The border index.
   *
   *   outline ::
   *     The target outline handle.
   *
   * @note:
   *   Always call this function after @FT_Stroker_GetBorderCounts to
   *   get sure that there is enough room in your @FT_Outline object to
   *   receive all new data.
   *
   *   When an outline, or a sub-path, is `closed', the stroker generates
   *   two independent `border' outlines, named `left' and `right'
   *
   *   When the outline, or a sub-path, is `opened', the stroker merges
   *   the `border' outlines with caps. The `left' border receives all
   *   points, while the `right' border becomes empty.
   *
   *   Use the function @FT_Stroker_Export instead if you want to
   *   retrieve all borders at once.
   */
  FT_EXPORT( void )
  FT_Stroker_ExportBorder( FT_Stroker        stroker,
                           FT_StrokerBorder  border,
                           FT_Outline*       outline );


  /**************************************************************
   *
   * @function:
   *   FT_Stroker_GetCounts
   *
   * @description:
   *   Call this function once you have finished parsing your paths
   *   with the stroker.  It returns the number of points and
   *   contours necessary to export all points/borders from the stroked
   *   outline/path.
   *
   * @input:
   *   stroker ::
   *     The target stroker handle.
   *
   * @output:
   *   anum_points ::
   *     The number of points.
   *
   *   anum_contours ::
   *     The number of contours.
   *
   * @return:
   *   FreeType error code.  0~means success.
   */
  FT_EXPORT( FT_Error )
  FT_Stroker_GetCounts( FT_Stroker  stroker,
                        FT_UInt    *anum_points,
                        FT_UInt    *anum_contours );


  /**************************************************************
   *
   * @function:
   *   FT_Stroker_Export
   *
   * @description:
   *   Call this function after @FT_Stroker_GetBorderCounts to
   *   export all borders to your own @FT_Outline structure.
   *
   *   Note that this function appends the border points and
   *   contours to your outline, but does not try to resize its
   *   arrays.
   *
   * @input:
   *   stroker ::
   *     The target stroker handle.
   *
   *   outline ::
   *     The target outline handle.
   */
  FT_EXPORT( void )
  FT_Stroker_Export( FT_Stroker   stroker,
                     FT_Outline*  outline );


  /**************************************************************
   *
   * @function:
   *   FT_Stroker_Done
   *
   * @description:
   *   Destroy a stroker object.
   *
   * @input:
   *   stroker ::
   *     A stroker handle.  Can be NULL.
   */
  FT_EXPORT( void )
  FT_Stroker_Done( FT_Stroker  stroker );


  /**************************************************************
   *
   * @function:
   *   FT_Glyph_Stroke
   *
   * @description:
   *   Stroke a given outline glyph object with a given stroker.
   *
   * @inout:
   *   pglyph ::
   *     Source glyph handle on input, new glyph handle on output.
   *
   * @input:
   *   stroker ::
   *     A stroker handle.
   *
   *   destroy ::
   *     A Boolean.  If~1, the source glyph object is destroyed
   *     on success.
   *
   * @return:
   *    FreeType error code.  0~means success.
   *
   * @note:
   *   The source glyph is untouched in case of error.
   */
  FT_EXPORT( FT_Error )
  FT_Glyph_Stroke( FT_Glyph    *pglyph,
                   FT_Stroker   stroker,
                   FT_Bool      destroy );


  /**************************************************************
   *
   * @function:
   *   FT_Glyph_StrokeBorder
   *
   * @description:
   *   Stroke a given outline glyph object with a given stroker, but
   *   only return either its inside or outside border.
   *
   * @inout:
   *   pglyph ::
   *     Source glyph handle on input, new glyph handle on output.
   *
   * @input:
   *   stroker ::
   *     A stroker handle.
   *
   *   inside ::
   *     A Boolean.  If~1, return the inside border, otherwise
   *     the outside border.
   *
   *   destroy ::
   *     A Boolean.  If~1, the source glyph object is destroyed
   *     on success.
   *
   * @return:
   *    FreeType error code.  0~means success.
   *
   * @note:
   *   The source glyph is untouched in case of error.
   */
  FT_EXPORT( FT_Error )
  FT_Glyph_StrokeBorder( FT_Glyph    *pglyph,
                         FT_Stroker   stroker,
                         FT_Bool      inside,
                         FT_Bool      destroy );

 /* */

FT_END_HEADER

#endif /* __FT_STROKE_H__ */


/* END */


/* Local Variables: */
/* coding: utf-8    */
/* End:             */
/***************************************************************************/
/*                                                                         */
/*  ftsynth.h                                                              */
/*                                                                         */
/*    FreeType synthesizing code for emboldening and slanting              */
/*    (specification).                                                     */
/*                                                                         */
/*  Copyright 2000-2001, 2003, 2006, 2008 by                               */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /*********                                                       *********/
  /*********        WARNING, THIS IS ALPHA CODE!  THIS API         *********/
  /*********    IS DUE TO CHANGE UNTIL STRICTLY NOTIFIED BY THE    *********/
  /*********            FREETYPE DEVELOPMENT TEAM                  *********/
  /*********                                                       *********/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/


  /* Main reason for not lifting the functions in this module to a  */
  /* `standard' API is that the used parameters for emboldening and */
  /* slanting are not configurable.  Consider the functions as a    */
  /* code resource which should be copied into the application and  */
  /* adapted to the particular needs.                               */


#ifndef __FTSYNTH_H__
#define __FTSYNTH_H__


#include <ft2build.h>
#include FT_FREETYPE_H

#ifdef FREETYPE_H
#error "freetype.h of FreeType 1 has been loaded!"
#error "Please fix the directory search order for header files"
#error "so that freetype.h of FreeType 2 is found first."
#endif


FT_BEGIN_HEADER

  /* Embolden a glyph by a `reasonable' value (which is highly a matter of */
  /* taste).  This function is actually a convenience function, providing  */
  /* a wrapper for @FT_Outline_Embolden and @FT_Bitmap_Embolden.           */
  /*                                                                       */
  /* For emboldened outlines the metrics are estimates only; if you need   */
  /* precise values you should call @FT_Outline_Get_CBox.                  */
  FT_EXPORT( void )
  FT_GlyphSlot_Embolden( FT_GlyphSlot  slot );

  /* Slant an outline glyph to the right by about 12 degrees. */
  FT_EXPORT( void )
  FT_GlyphSlot_Oblique( FT_GlyphSlot  slot );

  /* */

FT_END_HEADER

#endif /* __FTSYNTH_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  ftsystem.h                                                             */
/*                                                                         */
/*    FreeType low-level system interface definition (specification).      */
/*                                                                         */
/*  Copyright 1996-2001, 2002, 2005, 2010 by                               */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __FTSYSTEM_H__
#define __FTSYSTEM_H__


#include <ft2build.h>


FT_BEGIN_HEADER


  /*************************************************************************/
  /*                                                                       */
  /* <Section>                                                             */
  /*   system_interface                                                    */
  /*                                                                       */
  /* <Title>                                                               */
  /*   System Interface                                                    */
  /*                                                                       */
  /* <Abstract>                                                            */
  /*   How FreeType manages memory and i/o.                                */
  /*                                                                       */
  /* <Description>                                                         */
  /*   This section contains various definitions related to memory         */
  /*   management and i/o access.  You need to understand this             */
  /*   information if you want to use a custom memory manager or you own   */
  /*   i/o streams.                                                        */
  /*                                                                       */
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /*                  M E M O R Y   M A N A G E M E N T                    */
  /*                                                                       */
  /*************************************************************************/


  /*************************************************************************
   *
   * @type:
   *   FT_Memory
   *
   * @description:
   *   A handle to a given memory manager object, defined with an
   *   @FT_MemoryRec structure.
   *
   */
  typedef struct FT_MemoryRec_*  FT_Memory;


  /*************************************************************************
   *
   * @functype:
   *   FT_Alloc_Func
   *
   * @description:
   *   A function used to allocate `size' bytes from `memory'.
   *
   * @input:
   *   memory ::
   *     A handle to the source memory manager.
   *
   *   size ::
   *     The size in bytes to allocate.
   *
   * @return:
   *   Address of new memory block.  0~in case of failure.
   *
   */
  typedef void*
  (*FT_Alloc_Func)( FT_Memory  memory,
                    long       size );


  /*************************************************************************
   *
   * @functype:
   *   FT_Free_Func
   *
   * @description:
   *   A function used to release a given block of memory.
   *
   * @input:
   *   memory ::
   *     A handle to the source memory manager.
   *
   *   block ::
   *     The address of the target memory block.
   *
   */
  typedef void
  (*FT_Free_Func)( FT_Memory  memory,
                   void*      block );


  /*************************************************************************
   *
   * @functype:
   *   FT_Realloc_Func
   *
   * @description:
   *   A function used to re-allocate a given block of memory.
   *
   * @input:
   *   memory ::
   *     A handle to the source memory manager.
   *
   *   cur_size ::
   *     The block's current size in bytes.
   *
   *   new_size ::
   *     The block's requested new size.
   *
   *   block ::
   *     The block's current address.
   *
   * @return:
   *   New block address.  0~in case of memory shortage.
   *
   * @note:
   *   In case of error, the old block must still be available.
   *
   */
  typedef void*
  (*FT_Realloc_Func)( FT_Memory  memory,
                      long       cur_size,
                      long       new_size,
                      void*      block );


  /*************************************************************************
   *
   * @struct:
   *   FT_MemoryRec
   *
   * @description:
   *   A structure used to describe a given memory manager to FreeType~2.
   *
   * @fields:
   *   user ::
   *     A generic typeless pointer for user data.
   *
   *   alloc ::
   *     A pointer type to an allocation function.
   *
   *   free ::
   *     A pointer type to an memory freeing function.
   *
   *   realloc ::
   *     A pointer type to a reallocation function.
   *
   */
  struct  FT_MemoryRec_
  {
    void*            user;
    FT_Alloc_Func    alloc;
    FT_Free_Func     free;
    FT_Realloc_Func  realloc;
  };


  /*************************************************************************/
  /*                                                                       */
  /*                       I / O   M A N A G E M E N T                     */
  /*                                                                       */
  /*************************************************************************/


  /*************************************************************************
   *
   * @type:
   *   FT_Stream
   *
   * @description:
   *   A handle to an input stream.
   *
   */
  typedef struct FT_StreamRec_*  FT_Stream;


  /*************************************************************************
   *
   * @struct:
   *   FT_StreamDesc
   *
   * @description:
   *   A union type used to store either a long or a pointer.  This is used
   *   to store a file descriptor or a `FILE*' in an input stream.
   *
   */
  typedef union  FT_StreamDesc_
  {
    long   value;
    void*  pointer;

  } FT_StreamDesc;


  /*************************************************************************
   *
   * @functype:
   *   FT_Stream_IoFunc
   *
   * @description:
   *   A function used to seek and read data from a given input stream.
   *
   * @input:
   *   stream ::
   *     A handle to the source stream.
   *
   *   offset ::
   *     The offset of read in stream (always from start).
   *
   *   buffer ::
   *     The address of the read buffer.
   *
   *   count ::
   *     The number of bytes to read from the stream.
   *
   * @return:
   *   The number of bytes effectively read by the stream.
   *
   * @note:
   *   This function might be called to perform a seek or skip operation
   *   with a `count' of~0.  A non-zero return value then indicates an
   *   error.
   *
   */
  typedef unsigned long
  (*FT_Stream_IoFunc)( FT_Stream       stream,
                       unsigned long   offset,
                       unsigned char*  buffer,
                       unsigned long   count );


  /*************************************************************************
   *
   * @functype:
   *   FT_Stream_CloseFunc
   *
   * @description:
   *   A function used to close a given input stream.
   *
   * @input:
   *  stream ::
   *     A handle to the target stream.
   *
   */
  typedef void
  (*FT_Stream_CloseFunc)( FT_Stream  stream );


  /*************************************************************************
   *
   * @struct:
   *   FT_StreamRec
   *
   * @description:
   *   A structure used to describe an input stream.
   *
   * @input:
   *   base ::
   *     For memory-based streams, this is the address of the first stream
   *     byte in memory.  This field should always be set to NULL for
   *     disk-based streams.
   *
   *   size ::
   *     The stream size in bytes.
   *
   *   pos ::
   *     The current position within the stream.
   *
   *   descriptor ::
   *     This field is a union that can hold an integer or a pointer.  It is
   *     used by stream implementations to store file descriptors or `FILE*'
   *     pointers.
   *
   *   pathname ::
   *     This field is completely ignored by FreeType.  However, it is often
   *     useful during debugging to use it to store the stream's filename
   *     (where available).
   *
   *   read ::
   *     The stream's input function.
   *
   *   close ::
   *     The stream's close function.
   *
   *   memory ::
   *     The memory manager to use to preload frames.  This is set
   *     internally by FreeType and shouldn't be touched by stream
   *     implementations.
   *
   *   cursor ::
   *     This field is set and used internally by FreeType when parsing
   *     frames.
   *
   *   limit ::
   *     This field is set and used internally by FreeType when parsing
   *     frames.
   *
   */
  typedef struct  FT_StreamRec_
  {
    unsigned char*       base;
    unsigned long        size;
    unsigned long        pos;

    FT_StreamDesc        descriptor;
    FT_StreamDesc        pathname;
    FT_Stream_IoFunc     read;
    FT_Stream_CloseFunc  close;

    FT_Memory            memory;
    unsigned char*       cursor;
    unsigned char*       limit;

  } FT_StreamRec;


  /* */


FT_END_HEADER

#endif /* __FTSYSTEM_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  fttrigon.h                                                             */
/*                                                                         */
/*    FreeType trigonometric functions (specification).                    */
/*                                                                         */
/*  Copyright 2001, 2003, 2005, 2007 by                                    */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __FTTRIGON_H__
#define __FTTRIGON_H__

#include FT_FREETYPE_H

#ifdef FREETYPE_H
#error "freetype.h of FreeType 1 has been loaded!"
#error "Please fix the directory search order for header files"
#error "so that freetype.h of FreeType 2 is found first."
#endif


FT_BEGIN_HEADER


  /*************************************************************************/
  /*                                                                       */
  /* <Section>                                                             */
  /*   computations                                                        */
  /*                                                                       */
  /*************************************************************************/


  /*************************************************************************
   *
   * @type:
   *   FT_Angle
   *
   * @description:
   *   This type is used to model angle values in FreeType.  Note that the
   *   angle is a 16.16 fixed float value expressed in degrees.
   *
   */
  typedef FT_Fixed  FT_Angle;


  /*************************************************************************
   *
   * @macro:
   *   FT_ANGLE_PI
   *
   * @description:
   *   The angle pi expressed in @FT_Angle units.
   *
   */
#define FT_ANGLE_PI  ( 180L << 16 )


  /*************************************************************************
   *
   * @macro:
   *   FT_ANGLE_2PI
   *
   * @description:
   *   The angle 2*pi expressed in @FT_Angle units.
   *
   */
#define FT_ANGLE_2PI  ( FT_ANGLE_PI * 2 )


  /*************************************************************************
   *
   * @macro:
   *   FT_ANGLE_PI2
   *
   * @description:
   *   The angle pi/2 expressed in @FT_Angle units.
   *
   */
#define FT_ANGLE_PI2  ( FT_ANGLE_PI / 2 )


  /*************************************************************************
   *
   * @macro:
   *   FT_ANGLE_PI4
   *
   * @description:
   *   The angle pi/4 expressed in @FT_Angle units.
   *
   */
#define FT_ANGLE_PI4  ( FT_ANGLE_PI / 4 )


  /*************************************************************************
   *
   * @function:
   *   FT_Sin
   *
   * @description:
   *   Return the sinus of a given angle in fixed point format.
   *
   * @input:
   *   angle ::
   *     The input angle.
   *
   * @return:
   *   The sinus value.
   *
   * @note:
   *   If you need both the sinus and cosinus for a given angle, use the
   *   function @FT_Vector_Unit.
   *
   */
  FT_EXPORT( FT_Fixed )
  FT_Sin( FT_Angle  angle );


  /*************************************************************************
   *
   * @function:
   *   FT_Cos
   *
   * @description:
   *   Return the cosinus of a given angle in fixed point format.
   *
   * @input:
   *   angle ::
   *     The input angle.
   *
   * @return:
   *   The cosinus value.
   *
   * @note:
   *   If you need both the sinus and cosinus for a given angle, use the
   *   function @FT_Vector_Unit.
   *
   */
  FT_EXPORT( FT_Fixed )
  FT_Cos( FT_Angle  angle );


  /*************************************************************************
   *
   * @function:
   *   FT_Tan
   *
   * @description:
   *   Return the tangent of a given angle in fixed point format.
   *
   * @input:
   *   angle ::
   *     The input angle.
   *
   * @return:
   *   The tangent value.
   *
   */
  FT_EXPORT( FT_Fixed )
  FT_Tan( FT_Angle  angle );


  /*************************************************************************
   *
   * @function:
   *   FT_Atan2
   *
   * @description:
   *   Return the arc-tangent corresponding to a given vector (x,y) in
   *   the 2d plane.
   *
   * @input:
   *   x ::
   *     The horizontal vector coordinate.
   *
   *   y ::
   *     The vertical vector coordinate.
   *
   * @return:
   *   The arc-tangent value (i.e. angle).
   *
   */
  FT_EXPORT( FT_Angle )
  FT_Atan2( FT_Fixed  x,
            FT_Fixed  y );


  /*************************************************************************
   *
   * @function:
   *   FT_Angle_Diff
   *
   * @description:
   *   Return the difference between two angles.  The result is always
   *   constrained to the ]-PI..PI] interval.
   *
   * @input:
   *   angle1 ::
   *     First angle.
   *
   *   angle2 ::
   *     Second angle.
   *
   * @return:
   *   Constrained value of `value2-value1'.
   *
   */
  FT_EXPORT( FT_Angle )
  FT_Angle_Diff( FT_Angle  angle1,
                 FT_Angle  angle2 );


  /*************************************************************************
   *
   * @function:
   *   FT_Vector_Unit
   *
   * @description:
   *   Return the unit vector corresponding to a given angle.  After the
   *   call, the value of `vec.x' will be `sin(angle)', and the value of
   *   `vec.y' will be `cos(angle)'.
   *
   *   This function is useful to retrieve both the sinus and cosinus of a
   *   given angle quickly.
   *
   * @output:
   *   vec ::
   *     The address of target vector.
   *
   * @input:
   *   angle ::
   *     The address of angle.
   *
   */
  FT_EXPORT( void )
  FT_Vector_Unit( FT_Vector*  vec,
                  FT_Angle    angle );


  /*************************************************************************
   *
   * @function:
   *   FT_Vector_Rotate
   *
   * @description:
   *   Rotate a vector by a given angle.
   *
   * @inout:
   *   vec ::
   *     The address of target vector.
   *
   * @input:
   *   angle ::
   *     The address of angle.
   *
   */
  FT_EXPORT( void )
  FT_Vector_Rotate( FT_Vector*  vec,
                    FT_Angle    angle );


  /*************************************************************************
   *
   * @function:
   *   FT_Vector_Length
   *
   * @description:
   *   Return the length of a given vector.
   *
   * @input:
   *   vec ::
   *     The address of target vector.
   *
   * @return:
   *   The vector length, expressed in the same units that the original
   *   vector coordinates.
   *
   */
  FT_EXPORT( FT_Fixed )
  FT_Vector_Length( FT_Vector*  vec );


  /*************************************************************************
   *
   * @function:
   *   FT_Vector_Polarize
   *
   * @description:
   *   Compute both the length and angle of a given vector.
   *
   * @input:
   *   vec ::
   *     The address of source vector.
   *
   * @output:
   *   length ::
   *     The vector length.
   *
   *   angle ::
   *     The vector angle.
   *
   */
  FT_EXPORT( void )
  FT_Vector_Polarize( FT_Vector*  vec,
                      FT_Fixed   *length,
                      FT_Angle   *angle );


  /*************************************************************************
   *
   * @function:
   *   FT_Vector_From_Polar
   *
   * @description:
   *   Compute vector coordinates from a length and angle.
   *
   * @output:
   *   vec ::
   *     The address of source vector.
   *
   * @input:
   *   length ::
   *     The vector length.
   *
   *   angle ::
   *     The vector angle.
   *
   */
  FT_EXPORT( void )
  FT_Vector_From_Polar( FT_Vector*  vec,
                        FT_Fixed    length,
                        FT_Angle    angle );

  /* */


FT_END_HEADER

#endif /* __FTTRIGON_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  fttypes.h                                                              */
/*                                                                         */
/*    FreeType simple types definitions (specification only).              */
/*                                                                         */
/*  Copyright 1996-2001, 2002, 2004, 2006, 2007, 2008 by                   */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __FTTYPES_H__
#define __FTTYPES_H__


#include <ft2build.h>
#include FT_CONFIG_CONFIG_H
#include FT_SYSTEM_H
#include FT_IMAGE_H

#include <stddef.h>


FT_BEGIN_HEADER


  /*************************************************************************/
  /*                                                                       */
  /* <Section>                                                             */
  /*    basic_types                                                        */
  /*                                                                       */
  /* <Title>                                                               */
  /*    Basic Data Types                                                   */
  /*                                                                       */
  /* <Abstract>                                                            */
  /*    The basic data types defined by the library.                       */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This section contains the basic data types defined by FreeType~2,  */
  /*    ranging from simple scalar types to bitmap descriptors.  More      */
  /*    font-specific structures are defined in a different section.       */
  /*                                                                       */
  /* <Order>                                                               */
  /*    FT_Byte                                                            */
  /*    FT_Bytes                                                           */
  /*    FT_Char                                                            */
  /*    FT_Int                                                             */
  /*    FT_UInt                                                            */
  /*    FT_Int16                                                           */
  /*    FT_UInt16                                                          */
  /*    FT_Int32                                                           */
  /*    FT_UInt32                                                          */
  /*    FT_Short                                                           */
  /*    FT_UShort                                                          */
  /*    FT_Long                                                            */
  /*    FT_ULong                                                           */
  /*    FT_Bool                                                            */
  /*    FT_Offset                                                          */
  /*    FT_PtrDist                                                         */
  /*    FT_String                                                          */
  /*    FT_Tag                                                             */
  /*    FT_Error                                                           */
  /*    FT_Fixed                                                           */
  /*    FT_Pointer                                                         */
  /*    FT_Pos                                                             */
  /*    FT_Vector                                                          */
  /*    FT_BBox                                                            */
  /*    FT_Matrix                                                          */
  /*    FT_FWord                                                           */
  /*    FT_UFWord                                                          */
  /*    FT_F2Dot14                                                         */
  /*    FT_UnitVector                                                      */
  /*    FT_F26Dot6                                                         */
  /*                                                                       */
  /*                                                                       */
  /*    FT_Generic                                                         */
  /*    FT_Generic_Finalizer                                               */
  /*                                                                       */
  /*    FT_Bitmap                                                          */
  /*    FT_Pixel_Mode                                                      */
  /*    FT_Palette_Mode                                                    */
  /*    FT_Glyph_Format                                                    */
  /*    FT_IMAGE_TAG                                                       */
  /*                                                                       */
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FT_Bool                                                            */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A typedef of unsigned char, used for simple booleans.  As usual,   */
  /*    values 1 and~0 represent true and false, respectively.             */
  /*                                                                       */
  typedef unsigned char  FT_Bool;


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FT_FWord                                                           */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A signed 16-bit integer used to store a distance in original font  */
  /*    units.                                                             */
  /*                                                                       */
  typedef signed short  FT_FWord;   /* distance in FUnits */


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FT_UFWord                                                          */
  /*                                                                       */
  /* <Description>                                                         */
  /*    An unsigned 16-bit integer used to store a distance in original    */
  /*    font units.                                                        */
  /*                                                                       */
  typedef unsigned short  FT_UFWord;  /* unsigned distance */


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FT_Char                                                            */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A simple typedef for the _signed_ char type.                       */
  /*                                                                       */
  typedef signed char  FT_Char;


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FT_Byte                                                            */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A simple typedef for the _unsigned_ char type.                     */
  /*                                                                       */
  typedef unsigned char  FT_Byte;


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FT_Bytes                                                           */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A typedef for constant memory areas.                               */
  /*                                                                       */
  typedef const FT_Byte*  FT_Bytes;


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FT_Tag                                                             */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A typedef for 32-bit tags (as used in the SFNT format).            */
  /*                                                                       */
  typedef FT_UInt32  FT_Tag;


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FT_String                                                          */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A simple typedef for the char type, usually used for strings.      */
  /*                                                                       */
  typedef char  FT_String;


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FT_Short                                                           */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A typedef for signed short.                                        */
  /*                                                                       */
  typedef signed short  FT_Short;


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FT_UShort                                                          */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A typedef for unsigned short.                                      */
  /*                                                                       */
  typedef unsigned short  FT_UShort;


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FT_Int                                                             */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A typedef for the int type.                                        */
  /*                                                                       */
  typedef signed int  FT_Int;


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FT_UInt                                                            */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A typedef for the unsigned int type.                               */
  /*                                                                       */
  typedef unsigned int  FT_UInt;


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FT_Long                                                            */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A typedef for signed long.                                         */
  /*                                                                       */
  typedef signed long  FT_Long;


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FT_ULong                                                           */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A typedef for unsigned long.                                       */
  /*                                                                       */
  typedef unsigned long  FT_ULong;


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FT_F2Dot14                                                         */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A signed 2.14 fixed float type used for unit vectors.              */
  /*                                                                       */
  typedef signed short  FT_F2Dot14;


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FT_F26Dot6                                                         */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A signed 26.6 fixed float type used for vectorial pixel            */
  /*    coordinates.                                                       */
  /*                                                                       */
  typedef signed long  FT_F26Dot6;


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FT_Fixed                                                           */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This type is used to store 16.16 fixed float values, like scaling  */
  /*    values or matrix coefficients.                                     */
  /*                                                                       */
  typedef signed long  FT_Fixed;


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FT_Error                                                           */
  /*                                                                       */
  /* <Description>                                                         */
  /*    The FreeType error code type.  A value of~0 is always interpreted  */
  /*    as a successful operation.                                         */
  /*                                                                       */
  typedef int  FT_Error;


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FT_Pointer                                                         */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A simple typedef for a typeless pointer.                           */
  /*                                                                       */
  typedef void*  FT_Pointer;


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FT_Offset                                                          */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This is equivalent to the ANSI~C `size_t' type, i.e., the largest  */
  /*    _unsigned_ integer type used to express a file size or position,   */
  /*    or a memory block size.                                            */
  /*                                                                       */
  typedef size_t  FT_Offset;


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FT_PtrDist                                                         */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This is equivalent to the ANSI~C `ptrdiff_t' type, i.e., the       */
  /*    largest _signed_ integer type used to express the distance         */
  /*    between two pointers.                                              */
  /*                                                                       */
  typedef ft_ptrdiff_t  FT_PtrDist;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_UnitVector                                                      */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A simple structure used to store a 2D vector unit vector.  Uses    */
  /*    FT_F2Dot14 types.                                                  */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    x :: Horizontal coordinate.                                        */
  /*                                                                       */
  /*    y :: Vertical coordinate.                                          */
  /*                                                                       */
  typedef struct  FT_UnitVector_
  {
    FT_F2Dot14  x;
    FT_F2Dot14  y;

  } FT_UnitVector;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_Matrix                                                          */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A simple structure used to store a 2x2 matrix.  Coefficients are   */
  /*    in 16.16 fixed float format.  The computation performed is:        */
  /*                                                                       */
  /*       {                                                               */
  /*          x' = x*xx + y*xy                                             */
  /*          y' = x*yx + y*yy                                             */
  /*       }                                                               */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    xx :: Matrix coefficient.                                          */
  /*                                                                       */
  /*    xy :: Matrix coefficient.                                          */
  /*                                                                       */
  /*    yx :: Matrix coefficient.                                          */
  /*                                                                       */
  /*    yy :: Matrix coefficient.                                          */
  /*                                                                       */
  typedef struct  FT_Matrix_
  {
    FT_Fixed  xx, xy;
    FT_Fixed  yx, yy;

  } FT_Matrix;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_Data                                                            */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Read-only binary data represented as a pointer and a length.       */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    pointer :: The data.                                               */
  /*                                                                       */
  /*    length  :: The length of the data in bytes.                        */
  /*                                                                       */
  typedef struct  FT_Data_
  {
    const FT_Byte*  pointer;
    FT_Int          length;

  } FT_Data;


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    FT_Generic_Finalizer                                               */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Describe a function used to destroy the `client' data of any       */
  /*    FreeType object.  See the description of the @FT_Generic type for  */
  /*    details of usage.                                                  */
  /*                                                                       */
  /* <Input>                                                               */
  /*    The address of the FreeType object which is under finalization.    */
  /*    Its client data is accessed through its `generic' field.           */
  /*                                                                       */
  typedef void  (*FT_Generic_Finalizer)(void*  object);


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_Generic                                                         */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Client applications often need to associate their own data to a    */
  /*    variety of FreeType core objects.  For example, a text layout API  */
  /*    might want to associate a glyph cache to a given size object.      */
  /*                                                                       */
  /*    Most FreeType object contains a `generic' field, of type           */
  /*    FT_Generic, which usage is left to client applications and font    */
  /*    servers.                                                           */
  /*                                                                       */
  /*    It can be used to store a pointer to client-specific data, as well */
  /*    as the address of a `finalizer' function, which will be called by  */
  /*    FreeType when the object is destroyed (for example, the previous   */
  /*    client example would put the address of the glyph cache destructor */
  /*    in the `finalizer' field).                                         */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    data      :: A typeless pointer to any client-specified data. This */
  /*                 field is completely ignored by the FreeType library.  */
  /*                                                                       */
  /*    finalizer :: A pointer to a `generic finalizer' function, which    */
  /*                 will be called when the object is destroyed.  If this */
  /*                 field is set to NULL, no code will be called.         */
  /*                                                                       */
  typedef struct  FT_Generic_
  {
    void*                 data;
    FT_Generic_Finalizer  finalizer;

  } FT_Generic;


  /*************************************************************************/
  /*                                                                       */
  /* <Macro>                                                               */
  /*    FT_MAKE_TAG                                                        */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This macro converts four-letter tags which are used to label       */
  /*    TrueType tables into an unsigned long to be used within FreeType.  */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The produced values *must* be 32-bit integers.  Don't redefine     */
  /*    this macro.                                                        */
  /*                                                                       */
#define FT_MAKE_TAG( _x1, _x2, _x3, _x4 ) \
          (FT_Tag)                        \
          ( ( (FT_ULong)_x1 << 24 ) |     \
            ( (FT_ULong)_x2 << 16 ) |     \
            ( (FT_ULong)_x3 <<  8 ) |     \
              (FT_ULong)_x4         )


  /*************************************************************************/
  /*************************************************************************/
  /*                                                                       */
  /*                    L I S T   M A N A G E M E N T                      */
  /*                                                                       */
  /*************************************************************************/
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* <Section>                                                             */
  /*    list_processing                                                    */
  /*                                                                       */
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FT_ListNode                                                        */
  /*                                                                       */
  /* <Description>                                                         */
  /*     Many elements and objects in FreeType are listed through an       */
  /*     @FT_List record (see @FT_ListRec).  As its name suggests, an      */
  /*     FT_ListNode is a handle to a single list element.                 */
  /*                                                                       */
  typedef struct FT_ListNodeRec_*  FT_ListNode;


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    FT_List                                                            */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A handle to a list record (see @FT_ListRec).                       */
  /*                                                                       */
  typedef struct FT_ListRec_*  FT_List;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_ListNodeRec                                                     */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A structure used to hold a single list element.                    */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    prev :: The previous element in the list.  NULL if first.          */
  /*                                                                       */
  /*    next :: The next element in the list.  NULL if last.               */
  /*                                                                       */
  /*    data :: A typeless pointer to the listed object.                   */
  /*                                                                       */
  typedef struct  FT_ListNodeRec_
  {
    FT_ListNode  prev;
    FT_ListNode  next;
    void*        data;

  } FT_ListNodeRec;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_ListRec                                                         */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A structure used to hold a simple doubly-linked list.  These are   */
  /*    used in many parts of FreeType.                                    */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    head :: The head (first element) of doubly-linked list.            */
  /*                                                                       */
  /*    tail :: The tail (last element) of doubly-linked list.             */
  /*                                                                       */
  typedef struct  FT_ListRec_
  {
    FT_ListNode  head;
    FT_ListNode  tail;

  } FT_ListRec;


  /* */

#define FT_IS_EMPTY( list )  ( (list).head == 0 )

  /* return base error code (without module-specific prefix) */
#define FT_ERROR_BASE( x )    ( (x) & 0xFF )

  /* return module error code */
#define FT_ERROR_MODULE( x )  ( (x) & 0xFF00U )

#define FT_BOOL( x )  ( (FT_Bool)( x ) )

FT_END_HEADER

#endif /* __FTTYPES_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  ftwinfnt.h                                                             */
/*                                                                         */
/*    FreeType API for accessing Windows fnt-specific data.                */
/*                                                                         */
/*  Copyright 2003, 2004, 2008 by                                          */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __FTWINFNT_H__
#define __FTWINFNT_H__

#include <ft2build.h>
#include FT_FREETYPE_H

#ifdef FREETYPE_H
#error "freetype.h of FreeType 1 has been loaded!"
#error "Please fix the directory search order for header files"
#error "so that freetype.h of FreeType 2 is found first."
#endif


FT_BEGIN_HEADER


  /*************************************************************************/
  /*                                                                       */
  /* <Section>                                                             */
  /*    winfnt_fonts                                                       */
  /*                                                                       */
  /* <Title>                                                               */
  /*    Window FNT Files                                                   */
  /*                                                                       */
  /* <Abstract>                                                            */
  /*    Windows FNT specific API.                                          */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This section contains the declaration of Windows FNT specific      */
  /*    functions.                                                         */
  /*                                                                       */
  /*************************************************************************/


  /*************************************************************************
   *
   * @enum:
   *   FT_WinFNT_ID_XXX
   *
   * @description:
   *   A list of valid values for the `charset' byte in
   *   @FT_WinFNT_HeaderRec.  Exact mapping tables for the various cpXXXX
   *   encodings (except for cp1361) can be found at ftp://ftp.unicode.org
   *   in the MAPPINGS/VENDORS/MICSFT/WINDOWS subdirectory.  cp1361 is
   *   roughly a superset of MAPPINGS/OBSOLETE/EASTASIA/KSC/JOHAB.TXT.
   *
   * @values:
   *   FT_WinFNT_ID_DEFAULT ::
   *     This is used for font enumeration and font creation as a
   *     `don't care' value.  Valid font files don't contain this value.
   *     When querying for information about the character set of the font
   *     that is currently selected into a specified device context, this
   *     return value (of the related Windows API) simply denotes failure.
   *
   *   FT_WinFNT_ID_SYMBOL ::
   *     There is no known mapping table available.
   *
   *   FT_WinFNT_ID_MAC ::
   *     Mac Roman encoding.
   *
   *   FT_WinFNT_ID_OEM ::
   *     From Michael PÃ¶ttgen <michael@poettgen.de>:
   *
   *       The `Windows Font Mapping' article says that FT_WinFNT_ID_OEM
   *       is used for the charset of vector fonts, like `modern.fon',
   *       `roman.fon', and `script.fon' on Windows.
   *
   *       The `CreateFont' documentation says: The FT_WinFNT_ID_OEM value
   *       specifies a character set that is operating-system dependent.
   *
   *       The `IFIMETRICS' documentation from the `Windows Driver
   *       Development Kit' says: This font supports an OEM-specific
   *       character set.  The OEM character set is system dependent.
   *
   *       In general OEM, as opposed to ANSI (i.e., cp1252), denotes the
   *       second default codepage that most international versions of
   *       Windows have.  It is one of the OEM codepages from
   *
   *         http://www.microsoft.com/globaldev/reference/cphome.mspx,
   *
   *       and is used for the `DOS boxes', to support legacy applications.
   *       A German Windows version for example usually uses ANSI codepage
   *       1252 and OEM codepage 850.
   *
   *   FT_WinFNT_ID_CP874 ::
   *     A superset of Thai TIS 620 and ISO 8859-11.
   *
   *   FT_WinFNT_ID_CP932 ::
   *     A superset of Japanese Shift-JIS (with minor deviations).
   *
   *   FT_WinFNT_ID_CP936 ::
   *     A superset of simplified Chinese GB 2312-1980 (with different
   *     ordering and minor deviations).
   *
   *   FT_WinFNT_ID_CP949 ::
   *     A superset of Korean Hangul KS~C 5601-1987 (with different
   *     ordering and minor deviations).
   *
   *   FT_WinFNT_ID_CP950 ::
   *     A superset of traditional Chinese Big~5 ETen (with different
   *     ordering and minor deviations).
   *
   *   FT_WinFNT_ID_CP1250 ::
   *     A superset of East European ISO 8859-2 (with slightly different
   *     ordering).
   *
   *   FT_WinFNT_ID_CP1251 ::
   *     A superset of Russian ISO 8859-5 (with different ordering).
   *
   *   FT_WinFNT_ID_CP1252 ::
   *     ANSI encoding.  A superset of ISO 8859-1.
   *
   *   FT_WinFNT_ID_CP1253 ::
   *     A superset of Greek ISO 8859-7 (with minor modifications).
   *
   *   FT_WinFNT_ID_CP1254 ::
   *     A superset of Turkish ISO 8859-9.
   *
   *   FT_WinFNT_ID_CP1255 ::
   *     A superset of Hebrew ISO 8859-8 (with some modifications).
   *
   *   FT_WinFNT_ID_CP1256 ::
   *     A superset of Arabic ISO 8859-6 (with different ordering).
   *
   *   FT_WinFNT_ID_CP1257 ::
   *     A superset of Baltic ISO 8859-13 (with some deviations).
   *
   *   FT_WinFNT_ID_CP1258 ::
   *     For Vietnamese.  This encoding doesn't cover all necessary
   *     characters.
   *
   *   FT_WinFNT_ID_CP1361 ::
   *     Korean (Johab).
   */

#define FT_WinFNT_ID_CP1252    0
#define FT_WinFNT_ID_DEFAULT   1
#define FT_WinFNT_ID_SYMBOL    2
#define FT_WinFNT_ID_MAC      77
#define FT_WinFNT_ID_CP932   128
#define FT_WinFNT_ID_CP949   129
#define FT_WinFNT_ID_CP1361  130
#define FT_WinFNT_ID_CP936   134
#define FT_WinFNT_ID_CP950   136
#define FT_WinFNT_ID_CP1253  161
#define FT_WinFNT_ID_CP1254  162
#define FT_WinFNT_ID_CP1258  163
#define FT_WinFNT_ID_CP1255  177
#define FT_WinFNT_ID_CP1256  178
#define FT_WinFNT_ID_CP1257  186
#define FT_WinFNT_ID_CP1251  204
#define FT_WinFNT_ID_CP874   222
#define FT_WinFNT_ID_CP1250  238
#define FT_WinFNT_ID_OEM     255


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_WinFNT_HeaderRec                                                */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Windows FNT Header info.                                           */
  /*                                                                       */
  typedef struct  FT_WinFNT_HeaderRec_
  {
    FT_UShort  version;
    FT_ULong   file_size;
    FT_Byte    copyright[60];
    FT_UShort  file_type;
    FT_UShort  nominal_point_size;
    FT_UShort  vertical_resolution;
    FT_UShort  horizontal_resolution;
    FT_UShort  ascent;
    FT_UShort  internal_leading;
    FT_UShort  external_leading;
    FT_Byte    italic;
    FT_Byte    underline;
    FT_Byte    strike_out;
    FT_UShort  weight;
    FT_Byte    charset;
    FT_UShort  pixel_width;
    FT_UShort  pixel_height;
    FT_Byte    pitch_and_family;
    FT_UShort  avg_width;
    FT_UShort  max_width;
    FT_Byte    first_char;
    FT_Byte    last_char;
    FT_Byte    default_char;
    FT_Byte    break_char;
    FT_UShort  bytes_per_row;
    FT_ULong   device_offset;
    FT_ULong   face_name_offset;
    FT_ULong   bits_pointer;
    FT_ULong   bits_offset;
    FT_Byte    reserved;
    FT_ULong   flags;
    FT_UShort  A_space;
    FT_UShort  B_space;
    FT_UShort  C_space;
    FT_UShort  color_table_offset;
    FT_ULong   reserved1[4];

  } FT_WinFNT_HeaderRec;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_WinFNT_Header                                                   */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A handle to an @FT_WinFNT_HeaderRec structure.                     */
  /*                                                                       */
  typedef struct FT_WinFNT_HeaderRec_*  FT_WinFNT_Header;


  /**********************************************************************
   *
   * @function:
   *    FT_Get_WinFNT_Header
   *
   * @description:
   *    Retrieve a Windows FNT font info header.
   *
   * @input:
   *    face    :: A handle to the input face.
   *
   * @output:
   *    aheader :: The WinFNT header.
   *
   * @return:
   *   FreeType error code.  0~means success.
   *
   * @note:
   *   This function only works with Windows FNT faces, returning an error
   *   otherwise.
   */
  FT_EXPORT( FT_Error )
  FT_Get_WinFNT_Header( FT_Face               face,
                        FT_WinFNT_HeaderRec  *aheader );


  /* */

FT_END_HEADER

#endif /* __FTWINFNT_H__ */


/* END */


/* Local Variables: */
/* coding: utf-8    */
/* End:             */
/***************************************************************************/
/*                                                                         */
/*  ftxf86.h                                                               */
/*                                                                         */
/*    Support functions for X11.                                           */
/*                                                                         */
/*  Copyright 2002, 2003, 2004, 2006, 2007 by                              */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __FTXF86_H__
#define __FTXF86_H__

#include <ft2build.h>
#include FT_FREETYPE_H

#ifdef FREETYPE_H
#error "freetype.h of FreeType 1 has been loaded!"
#error "Please fix the directory search order for header files"
#error "so that freetype.h of FreeType 2 is found first."
#endif


FT_BEGIN_HEADER


  /*************************************************************************/
  /*                                                                       */
  /* <Section>                                                             */
  /*   font_formats                                                        */
  /*                                                                       */
  /* <Title>                                                               */
  /*   Font Formats                                                        */
  /*                                                                       */
  /* <Abstract>                                                            */
  /*   Getting the font format.                                            */
  /*                                                                       */
  /* <Description>                                                         */
  /*   The single function in this section can be used to get the font     */
  /*   format.  Note that this information is not needed normally;         */
  /*   however, there are special cases (like in PDF devices) where it is  */
  /*   important to differentiate, in spite of FreeType's uniform API.     */
  /*                                                                       */
  /*   This function is in the X11/xf86 namespace for historical reasons   */
  /*   and in no way depends on that windowing system.                     */
  /*                                                                       */
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*   FT_Get_X11_Font_Format                                              */
  /*                                                                       */
  /* <Description>                                                         */
  /*   Return a string describing the format of a given face, using values */
  /*   which can be used as an X11 FONT_PROPERTY.  Possible values are     */
  /*   `TrueType', `Type~1', `BDF', `PCF', `Type~42', `CID~Type~1', `CFF', */
  /*   `PFR', and `Windows~FNT'.                                           */
  /*                                                                       */
  /* <Input>                                                               */
  /*   face ::                                                             */
  /*     Input face handle.                                                */
  /*                                                                       */
  /* <Return>                                                              */
  /*   Font format string.  NULL in case of error.                         */
  /*                                                                       */
  FT_EXPORT( const char* )
  FT_Get_X11_Font_Format( FT_Face  face );

 /* */

FT_END_HEADER

#endif /* __FTXF86_H__ */
/***************************************************************************/
/*                                                                         */
/*  autohint.h                                                             */
/*                                                                         */
/*    High-level `autohint' module-specific interface (specification).     */
/*                                                                         */
/*  Copyright 1996-2001, 2002, 2007 by                                     */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* The auto-hinter is used to load and automatically hint glyphs if a    */
  /* format-specific hinter isn't available.                               */
  /*                                                                       */
  /*************************************************************************/


#ifndef __AUTOHINT_H__
#define __AUTOHINT_H__


  /*************************************************************************/
  /*                                                                       */
  /* A small technical note regarding automatic hinting in order to        */
  /* clarify this module interface.                                        */
  /*                                                                       */
  /* An automatic hinter might compute two kinds of data for a given face: */
  /*                                                                       */
  /* - global hints: Usually some metrics that describe global properties  */
  /*                 of the face.  It is computed by scanning more or less */
  /*                 aggressively the glyphs in the face, and thus can be  */
  /*                 very slow to compute (even if the size of global      */
  /*                 hints is really small).                               */
  /*                                                                       */
  /* - glyph hints:  These describe some important features of the glyph   */
  /*                 outline, as well as how to align them.  They are      */
  /*                 generally much faster to compute than global hints.   */
  /*                                                                       */
  /* The current FreeType auto-hinter does a pretty good job while         */
  /* performing fast computations for both global and glyph hints.         */
  /* However, we might be interested in introducing more complex and       */
  /* powerful algorithms in the future, like the one described in the John */
  /* D. Hobby paper, which unfortunately requires a lot more horsepower.   */
  /*                                                                       */
  /* Because a sufficiently sophisticated font management system would     */
  /* typically implement an LRU cache of opened face objects to reduce     */
  /* memory usage, it is a good idea to be able to avoid recomputing       */
  /* global hints every time the same face is re-opened.                   */
  /*                                                                       */
  /* We thus provide the ability to cache global hints outside of the face */
  /* object, in order to speed up font re-opening time.  Of course, this   */
  /* feature is purely optional, so most client programs won't even notice */
  /* it.                                                                   */
  /*                                                                       */
  /* I initially thought that it would be a good idea to cache the glyph   */
  /* hints too.  However, my general idea now is that if you really need   */
  /* to cache these too, you are simply in need of a new font format,      */
  /* where all this information could be stored within the font file and   */
  /* decoded on the fly.                                                   */
  /*                                                                       */
  /*************************************************************************/


#include <ft2build.h>
#include FT_FREETYPE_H


FT_BEGIN_HEADER


  typedef struct FT_AutoHinterRec_  *FT_AutoHinter;


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    FT_AutoHinter_GlobalGetFunc                                        */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Retrieves the global hints computed for a given face object the    */
  /*    resulting data is dissociated from the face and will survive a     */
  /*    call to FT_Done_Face().  It must be discarded through the API      */
  /*    FT_AutoHinter_GlobalDoneFunc().                                    */
  /*                                                                       */
  /* <Input>                                                               */
  /*    hinter        :: A handle to the source auto-hinter.               */
  /*                                                                       */
  /*    face          :: A handle to the source face object.               */
  /*                                                                       */
  /* <Output>                                                              */
  /*    global_hints  :: A typeless pointer to the global hints.           */
  /*                                                                       */
  /*    global_len    :: The size in bytes of the global hints.            */
  /*                                                                       */
  typedef void
  (*FT_AutoHinter_GlobalGetFunc)( FT_AutoHinter  hinter,
                                  FT_Face        face,
                                  void**         global_hints,
                                  long*          global_len );


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    FT_AutoHinter_GlobalDoneFunc                                       */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Discards the global hints retrieved through                        */
  /*    FT_AutoHinter_GlobalGetFunc().  This is the only way these hints   */
  /*    are freed from memory.                                             */
  /*                                                                       */
  /* <Input>                                                               */
  /*    hinter :: A handle to the auto-hinter module.                      */
  /*                                                                       */
  /*    global :: A pointer to retrieved global hints to discard.          */
  /*                                                                       */
  typedef void
  (*FT_AutoHinter_GlobalDoneFunc)( FT_AutoHinter  hinter,
                                   void*          global );


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    FT_AutoHinter_GlobalResetFunc                                      */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This function is used to recompute the global metrics in a given   */
  /*    font.  This is useful when global font data changes (e.g. Multiple */
  /*    Masters fonts where blend coordinates change).                     */
  /*                                                                       */
  /* <Input>                                                               */
  /*    hinter :: A handle to the source auto-hinter.                      */
  /*                                                                       */
  /*    face   :: A handle to the face.                                    */
  /*                                                                       */
  typedef void
  (*FT_AutoHinter_GlobalResetFunc)( FT_AutoHinter  hinter,
                                    FT_Face        face );


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    FT_AutoHinter_GlyphLoadFunc                                        */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This function is used to load, scale, and automatically hint a     */
  /*    glyph from a given face.                                           */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face        :: A handle to the face.                               */
  /*                                                                       */
  /*    glyph_index :: The glyph index.                                    */
  /*                                                                       */
  /*    load_flags  :: The load flags.                                     */
  /*                                                                       */
  /* <Note>                                                                */
  /*    This function is capable of loading composite glyphs by hinting    */
  /*    each sub-glyph independently (which improves quality).             */
  /*                                                                       */
  /*    It will call the font driver with FT_Load_Glyph(), with            */
  /*    FT_LOAD_NO_SCALE set.                                              */
  /*                                                                       */
  typedef FT_Error
  (*FT_AutoHinter_GlyphLoadFunc)( FT_AutoHinter  hinter,
                                  FT_GlyphSlot   slot,
                                  FT_Size        size,
                                  FT_UInt        glyph_index,
                                  FT_Int32       load_flags );


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_AutoHinter_ServiceRec                                           */
  /*                                                                       */
  /* <Description>                                                         */
  /*    The auto-hinter module's interface.                                */
  /*                                                                       */
  typedef struct  FT_AutoHinter_ServiceRec_
  {
    FT_AutoHinter_GlobalResetFunc  reset_face;
    FT_AutoHinter_GlobalGetFunc    get_global_hints;
    FT_AutoHinter_GlobalDoneFunc   done_global_hints;
    FT_AutoHinter_GlyphLoadFunc    load_glyph;

  } FT_AutoHinter_ServiceRec, *FT_AutoHinter_Service;

#ifndef FT_CONFIG_OPTION_PIC

#define FT_DEFINE_AUTOHINTER_SERVICE(class_, reset_face_, get_global_hints_, \
                                     done_global_hints_, load_glyph_)        \
  FT_CALLBACK_TABLE_DEF                                                      \
  const FT_AutoHinter_ServiceRec class_ =                                    \
  {                                                                          \
    reset_face_, get_global_hints_, done_global_hints_, load_glyph_          \
  };

#else /* FT_CONFIG_OPTION_PIC */ 

#define FT_DEFINE_AUTOHINTER_SERVICE(class_, reset_face_, get_global_hints_, \
                                     done_global_hints_, load_glyph_)        \
  void                                                                       \
  FT_Init_Class_##class_( FT_Library library,                                \
                          FT_AutoHinter_ServiceRec* clazz)                   \
  {                                                                          \
    FT_UNUSED(library);                                                      \
    clazz->reset_face = reset_face_;                                         \
    clazz->get_global_hints = get_global_hints_;                             \
    clazz->done_global_hints = done_global_hints_;                           \
    clazz->load_glyph = load_glyph_;                                         \
  } 

#endif /* FT_CONFIG_OPTION_PIC */ 

FT_END_HEADER

#endif /* __AUTOHINT_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  ftcalc.h                                                               */
/*                                                                         */
/*    Arithmetic computations (specification).                             */
/*                                                                         */
/*  Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2008, 2009 by       */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __FTCALC_H__
#define __FTCALC_H__


#include <ft2build.h>
#include FT_FREETYPE_H


FT_BEGIN_HEADER


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_FixedSqrt                                                       */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Computes the square root of a 16.16 fixed point value.             */
  /*                                                                       */
  /* <Input>                                                               */
  /*    x :: The value to compute the root for.                            */
  /*                                                                       */
  /* <Return>                                                              */
  /*    The result of `sqrt(x)'.                                           */
  /*                                                                       */
  /* <Note>                                                                */
  /*    This function is not very fast.                                    */
  /*                                                                       */
  FT_BASE( FT_Int32 )
  FT_SqrtFixed( FT_Int32  x );


#ifdef FT_CONFIG_OPTION_OLD_INTERNALS

  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Sqrt32                                                          */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Computes the square root of an Int32 integer (which will be        */
  /*    handled as an unsigned long value).                                */
  /*                                                                       */
  /* <Input>                                                               */
  /*    x :: The value to compute the root for.                            */
  /*                                                                       */
  /* <Return>                                                              */
  /*    The result of `sqrt(x)'.                                           */
  /*                                                                       */
  FT_EXPORT( FT_Int32 )
  FT_Sqrt32( FT_Int32  x );

#endif /* FT_CONFIG_OPTION_OLD_INTERNALS */


  /*************************************************************************/
  /*                                                                       */
  /* FT_MulDiv() and FT_MulFix() are declared in freetype.h.               */
  /*                                                                       */
  /*************************************************************************/


#ifdef TT_USE_BYTECODE_INTERPRETER

  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_MulDiv_No_Round                                                 */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A very simple function used to perform the computation `(a*b)/c'   */
  /*    (without rounding) with maximal accuracy (it uses a 64-bit         */
  /*    intermediate integer whenever necessary).                          */
  /*                                                                       */
  /*    This function isn't necessarily as fast as some processor specific */
  /*    operations, but is at least completely portable.                   */
  /*                                                                       */
  /* <Input>                                                               */
  /*    a :: The first multiplier.                                         */
  /*    b :: The second multiplier.                                        */
  /*    c :: The divisor.                                                  */
  /*                                                                       */
  /* <Return>                                                              */
  /*    The result of `(a*b)/c'.  This function never traps when trying to */
  /*    divide by zero; it simply returns `MaxInt' or `MinInt' depending   */
  /*    on the signs of `a' and `b'.                                       */
  /*                                                                       */
  FT_BASE( FT_Long )
  FT_MulDiv_No_Round( FT_Long  a,
                      FT_Long  b,
                      FT_Long  c );

#endif /* TT_USE_BYTECODE_INTERPRETER */


  /*
   *  A variant of FT_Matrix_Multiply which scales its result afterwards.
   *  The idea is that both `a' and `b' are scaled by factors of 10 so that
   *  the values are as precise as possible to get a correct result during
   *  the 64bit multiplication.  Let `sa' and `sb' be the scaling factors of
   *  `a' and `b', respectively, then the scaling factor of the result is
   *  `sa*sb'.
   */
  FT_BASE( void )
  FT_Matrix_Multiply_Scaled( const FT_Matrix*  a,
                             FT_Matrix        *b,
                             FT_Long           scaling );


  /*
   *  A variant of FT_Vector_Transform.  See comments for
   *  FT_Matrix_Multiply_Scaled.
   */

  FT_BASE( void )
  FT_Vector_Transform_Scaled( FT_Vector*        vector,
                              const FT_Matrix*  matrix,
                              FT_Long           scaling );


  /*
   *  Return -1, 0, or +1, depending on the orientation of a given corner.
   *  We use the Cartesian coordinate system, with positive vertical values
   *  going upwards.  The function returns +1 if the corner turns to the
   *  left, -1 to the right, and 0 for undecidable cases.
   */
  FT_BASE( FT_Int )
  ft_corner_orientation( FT_Pos  in_x,
                         FT_Pos  in_y,
                         FT_Pos  out_x,
                         FT_Pos  out_y );

  /*
   *  Return TRUE if a corner is flat or nearly flat.  This is equivalent to
   *  saying that the angle difference between the `in' and `out' vectors is
   *  very small.
   */
  FT_BASE( FT_Int )
  ft_corner_is_flat( FT_Pos  in_x,
                     FT_Pos  in_y,
                     FT_Pos  out_x,
                     FT_Pos  out_y );


#define INT_TO_F26DOT6( x )    ( (FT_Long)(x) << 6  )
#define INT_TO_F2DOT14( x )    ( (FT_Long)(x) << 14 )
#define INT_TO_FIXED( x )      ( (FT_Long)(x) << 16 )
#define F2DOT14_TO_FIXED( x )  ( (FT_Long)(x) << 2  )
#define FLOAT_TO_FIXED( x )    ( (FT_Long)( x * 65536.0 ) )
#define FIXED_TO_INT( x )      ( FT_RoundFix( x ) >> 16 )

#define ROUND_F26DOT6( x )     ( x >= 0 ? (    ( (x) + 32 ) & -64 )     \
                                        : ( -( ( 32 - (x) ) & -64 ) ) )


FT_END_HEADER

#endif /* __FTCALC_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  ftdebug.h                                                              */
/*                                                                         */
/*    Debugging and logging component (specification).                     */
/*                                                                         */
/*  Copyright 1996-2001, 2002, 2004, 2006, 2007, 2008, 2009 by             */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/*                                                                         */
/*  IMPORTANT: A description of FreeType's debugging support can be        */
/*             found in `docs/DEBUG.TXT'.  Read it if you need to use or   */
/*             understand this code.                                       */
/*                                                                         */
/***************************************************************************/


#ifndef __FTDEBUG_H__
#define __FTDEBUG_H__


#include <ft2build.h>
#include FT_CONFIG_CONFIG_H
#include FT_FREETYPE_H


FT_BEGIN_HEADER


  /* force the definition of FT_DEBUG_LEVEL_ERROR if FT_DEBUG_LEVEL_TRACE */
  /* is already defined; this simplifies the following #ifdefs            */
  /*                                                                      */
#ifdef FT_DEBUG_LEVEL_TRACE
#undef  FT_DEBUG_LEVEL_ERROR
#define FT_DEBUG_LEVEL_ERROR
#endif


  /*************************************************************************/
  /*                                                                       */
  /* Define the trace enums as well as the trace levels array when they    */
  /* are needed.                                                           */
  /*                                                                       */
  /*************************************************************************/

#ifdef FT_DEBUG_LEVEL_TRACE

#define FT_TRACE_DEF( x )  trace_ ## x ,

  /* defining the enumeration */
  typedef enum  FT_Trace_
  {
#include FT_INTERNAL_TRACE_H
    trace_count

  } FT_Trace;


  /* defining the array of trace levels, provided by `src/base/ftdebug.c' */
  extern int  ft_trace_levels[trace_count];

#undef FT_TRACE_DEF

#endif /* FT_DEBUG_LEVEL_TRACE */


  /*************************************************************************/
  /*                                                                       */
  /* Define the FT_TRACE macro                                             */
  /*                                                                       */
  /* IMPORTANT!                                                            */
  /*                                                                       */
  /* Each component must define the macro FT_COMPONENT to a valid FT_Trace */
  /* value before using any TRACE macro.                                   */
  /*                                                                       */
  /*************************************************************************/

#ifdef FT_DEBUG_LEVEL_TRACE

#define FT_TRACE( level, varformat )                      \
          do                                              \
          {                                               \
            if ( ft_trace_levels[FT_COMPONENT] >= level ) \
              FT_Message varformat;                       \
          } while ( 0 )

#else /* !FT_DEBUG_LEVEL_TRACE */

#define FT_TRACE( level, varformat )  do { } while ( 0 )      /* nothing */

#endif /* !FT_DEBUG_LEVEL_TRACE */


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Trace_Get_Count                                                 */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Return the number of available trace components.                   */
  /*                                                                       */
  /* <Return>                                                              */
  /*    The number of trace components.  0 if FreeType 2 is not built with */
  /*    FT_DEBUG_LEVEL_TRACE definition.                                   */
  /*                                                                       */
  /* <Note>                                                                */
  /*    This function may be useful if you want to access elements of      */
  /*    the internal `ft_trace_levels' array by an index.                  */
  /*                                                                       */
  FT_BASE( FT_Int )
  FT_Trace_Get_Count( void );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Trace_Get_Name                                                  */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Return the name of a trace component.                              */
  /*                                                                       */
  /* <Input>                                                               */
  /*    The index of the trace component.                                  */
  /*                                                                       */
  /* <Return>                                                              */
  /*    The name of the trace component.  This is a statically allocated   */
  /*    C string, so do not free it after use.  NULL if FreeType 2 is not  */
  /*    built with FT_DEBUG_LEVEL_TRACE definition.                        */
  /*                                                                       */
  /* <Note>                                                                */
  /*    Use @FT_Trace_Get_Count to get the number of available trace       */
  /*    components.                                                        */
  /*                                                                       */
  /*    This function may be useful if you want to control FreeType 2's    */
  /*    debug level in your application.                                   */
  /*                                                                       */
  FT_BASE( const char * )
  FT_Trace_Get_Name( FT_Int  idx );


  /*************************************************************************/
  /*                                                                       */
  /* You need two opening and closing parentheses!                         */
  /*                                                                       */
  /* Example: FT_TRACE0(( "Value is %i", foo ))                            */
  /*                                                                       */
  /* Output of the FT_TRACEX macros is sent to stderr.                     */
  /*                                                                       */
  /*************************************************************************/

#define FT_TRACE0( varformat )  FT_TRACE( 0, varformat )
#define FT_TRACE1( varformat )  FT_TRACE( 1, varformat )
#define FT_TRACE2( varformat )  FT_TRACE( 2, varformat )
#define FT_TRACE3( varformat )  FT_TRACE( 3, varformat )
#define FT_TRACE4( varformat )  FT_TRACE( 4, varformat )
#define FT_TRACE5( varformat )  FT_TRACE( 5, varformat )
#define FT_TRACE6( varformat )  FT_TRACE( 6, varformat )
#define FT_TRACE7( varformat )  FT_TRACE( 7, varformat )


  /*************************************************************************/
  /*                                                                       */
  /* Define the FT_ERROR macro.                                            */
  /*                                                                       */
  /* Output of this macro is sent to stderr.                               */
  /*                                                                       */
  /*************************************************************************/

#ifdef FT_DEBUG_LEVEL_ERROR

#define FT_ERROR( varformat )  FT_Message  varformat

#else  /* !FT_DEBUG_LEVEL_ERROR */

#define FT_ERROR( varformat )  do { } while ( 0 )      /* nothing */

#endif /* !FT_DEBUG_LEVEL_ERROR */


  /*************************************************************************/
  /*                                                                       */
  /* Define the FT_ASSERT macro.                                           */
  /*                                                                       */
  /*************************************************************************/

#ifdef FT_DEBUG_LEVEL_ERROR

#define FT_ASSERT( condition )                                      \
          do                                                        \
          {                                                         \
            if ( !( condition ) )                                   \
              FT_Panic( "assertion failed on line %d of file %s\n", \
                        __LINE__, __FILE__ );                       \
          } while ( 0 )

#else /* !FT_DEBUG_LEVEL_ERROR */

#define FT_ASSERT( condition )  do { } while ( 0 )

#endif /* !FT_DEBUG_LEVEL_ERROR */


  /*************************************************************************/
  /*                                                                       */
  /* Define `FT_Message' and `FT_Panic' when needed.                       */
  /*                                                                       */
  /*************************************************************************/

#ifdef FT_DEBUG_LEVEL_ERROR

#include "stdio.h"  /* for vfprintf() */

  /* print a message */
  FT_BASE( void )
  FT_Message( const char*  fmt,
              ... );

  /* print a message and exit */
  FT_BASE( void )
  FT_Panic( const char*  fmt,
            ... );

#endif /* FT_DEBUG_LEVEL_ERROR */


  FT_BASE( void )
  ft_debug_init( void );


#if defined( _MSC_VER )      /* Visual C++ (and Intel C++) */

  /* We disable the warning `conditional expression is constant' here */
  /* in order to compile cleanly with the maximum level of warnings.  */
#pragma warning( disable : 4127 )

#endif /* _MSC_VER */


FT_END_HEADER

#endif /* __FTDEBUG_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  ftdriver.h                                                             */
/*                                                                         */
/*    FreeType font driver interface (specification).                      */
/*                                                                         */
/*  Copyright 1996-2001, 2002, 2003, 2006, 2008 by                         */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __FTDRIVER_H__
#define __FTDRIVER_H__


#include <ft2build.h>
#include FT_MODULE_H


FT_BEGIN_HEADER


  typedef FT_Error
  (*FT_Face_InitFunc)( FT_Stream      stream,
                       FT_Face        face,
                       FT_Int         typeface_index,
                       FT_Int         num_params,
                       FT_Parameter*  parameters );

  typedef void
  (*FT_Face_DoneFunc)( FT_Face  face );


  typedef FT_Error
  (*FT_Size_InitFunc)( FT_Size  size );

  typedef void
  (*FT_Size_DoneFunc)( FT_Size  size );


  typedef FT_Error
  (*FT_Slot_InitFunc)( FT_GlyphSlot  slot );

  typedef void
  (*FT_Slot_DoneFunc)( FT_GlyphSlot  slot );


  typedef FT_Error
  (*FT_Size_RequestFunc)( FT_Size          size,
                          FT_Size_Request  req );

  typedef FT_Error
  (*FT_Size_SelectFunc)( FT_Size   size,
                         FT_ULong  size_index );

#ifdef FT_CONFIG_OPTION_OLD_INTERNALS

  typedef FT_Error
  (*FT_Size_ResetPointsFunc)( FT_Size     size,
                              FT_F26Dot6  char_width,
                              FT_F26Dot6  char_height,
                              FT_UInt     horz_resolution,
                              FT_UInt     vert_resolution );

  typedef FT_Error
  (*FT_Size_ResetPixelsFunc)( FT_Size  size,
                              FT_UInt  pixel_width,
                              FT_UInt  pixel_height );

#endif /* FT_CONFIG_OPTION_OLD_INTERNALS */

  typedef FT_Error
  (*FT_Slot_LoadFunc)( FT_GlyphSlot  slot,
                       FT_Size       size,
                       FT_UInt       glyph_index,
                       FT_Int32      load_flags );


  typedef FT_UInt
  (*FT_CharMap_CharIndexFunc)( FT_CharMap  charmap,
                               FT_Long     charcode );

  typedef FT_Long
  (*FT_CharMap_CharNextFunc)( FT_CharMap  charmap,
                              FT_Long     charcode );


  typedef FT_Error
  (*FT_Face_GetKerningFunc)( FT_Face     face,
                             FT_UInt     left_glyph,
                             FT_UInt     right_glyph,
                             FT_Vector*  kerning );


  typedef FT_Error
  (*FT_Face_AttachFunc)( FT_Face    face,
                         FT_Stream  stream );


  typedef FT_Error
  (*FT_Face_GetAdvancesFunc)( FT_Face    face,
                              FT_UInt    first,
                              FT_UInt    count,
                              FT_Int32   flags,
                              FT_Fixed*  advances );


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_Driver_ClassRec                                                 */
  /*                                                                       */
  /* <Description>                                                         */
  /*    The font driver class.  This structure mostly contains pointers to */
  /*    driver methods.                                                    */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    root             :: The parent module.                             */
  /*                                                                       */
  /*    face_object_size :: The size of a face object in bytes.            */
  /*                                                                       */
  /*    size_object_size :: The size of a size object in bytes.            */
  /*                                                                       */
  /*    slot_object_size :: The size of a glyph object in bytes.           */
  /*                                                                       */
  /*    init_face        :: The format-specific face constructor.          */
  /*                                                                       */
  /*    done_face        :: The format-specific face destructor.           */
  /*                                                                       */
  /*    init_size        :: The format-specific size constructor.          */
  /*                                                                       */
  /*    done_size        :: The format-specific size destructor.           */
  /*                                                                       */
  /*    init_slot        :: The format-specific slot constructor.          */
  /*                                                                       */
  /*    done_slot        :: The format-specific slot destructor.           */
  /*                                                                       */
  /*                                                                       */
  /*    load_glyph       :: A function handle to load a glyph to a slot.   */
  /*                        This field is mandatory!                       */
  /*                                                                       */
  /*    get_kerning      :: A function handle to return the unscaled       */
  /*                        kerning for a given pair of glyphs.  Can be    */
  /*                        set to 0 if the format doesn't support         */
  /*                        kerning.                                       */
  /*                                                                       */
  /*    attach_file      :: This function handle is used to read           */
  /*                        additional data for a face from another        */
  /*                        file/stream.  For example, this can be used to */
  /*                        add data from AFM or PFM files on a Type 1     */
  /*                        face, or a CIDMap on a CID-keyed face.         */
  /*                                                                       */
  /*    get_advances     :: A function handle used to return advance       */
  /*                        widths of `count' glyphs (in font units),      */
  /*                        starting at `first'.  The `vertical' flag must */
  /*                        be set to get vertical advance heights.  The   */
  /*                        `advances' buffer is caller-allocated.         */
  /*                        Currently not implemented.  The idea of this   */
  /*                        function is to be able to perform              */
  /*                        device-independent text layout without loading */
  /*                        a single glyph image.                          */
  /*                                                                       */
  /*    request_size     :: A handle to a function used to request the new */
  /*                        character size.  Can be set to 0 if the        */
  /*                        scaling done in the base layer suffices.       */
  /*                                                                       */
  /*    select_size      :: A handle to a function used to select a new    */
  /*                        fixed size.  It is used only if                */
  /*                        @FT_FACE_FLAG_FIXED_SIZES is set.  Can be set  */
  /*                        to 0 if the scaling done in the base layer     */
  /*                        suffices.                                      */
  /* <Note>                                                                */
  /*    Most function pointers, with the exception of `load_glyph', can be */
  /*    set to 0 to indicate a default behaviour.                          */
  /*                                                                       */
  typedef struct  FT_Driver_ClassRec_
  {
    FT_Module_Class           root;

    FT_Long                   face_object_size;
    FT_Long                   size_object_size;
    FT_Long                   slot_object_size;

    FT_Face_InitFunc          init_face;
    FT_Face_DoneFunc          done_face;

    FT_Size_InitFunc          init_size;
    FT_Size_DoneFunc          done_size;

    FT_Slot_InitFunc          init_slot;
    FT_Slot_DoneFunc          done_slot;

#ifdef FT_CONFIG_OPTION_OLD_INTERNALS

    FT_Size_ResetPointsFunc   set_char_sizes;
    FT_Size_ResetPixelsFunc   set_pixel_sizes;

#endif /* FT_CONFIG_OPTION_OLD_INTERNALS */

    FT_Slot_LoadFunc          load_glyph;

    FT_Face_GetKerningFunc    get_kerning;
    FT_Face_AttachFunc        attach_file;
    FT_Face_GetAdvancesFunc   get_advances;

    /* since version 2.2 */
    FT_Size_RequestFunc       request_size;
    FT_Size_SelectFunc        select_size;

  } FT_Driver_ClassRec, *FT_Driver_Class;


  /*
   *  The following functions are used as stubs for `set_char_sizes' and
   *  `set_pixel_sizes'; the code uses `request_size' and `select_size'
   *  functions instead.
   *
   *  Implementation is in `src/base/ftobjs.c'.
   */
#ifdef FT_CONFIG_OPTION_OLD_INTERNALS

  FT_BASE( FT_Error )
  ft_stub_set_char_sizes( FT_Size     size,
                          FT_F26Dot6  width,
                          FT_F26Dot6  height,
                          FT_UInt     horz_res,
                          FT_UInt     vert_res );

  FT_BASE( FT_Error )
  ft_stub_set_pixel_sizes( FT_Size  size,
                           FT_UInt  width,
                           FT_UInt  height );

#endif /* FT_CONFIG_OPTION_OLD_INTERNALS */

  /*************************************************************************/
  /*                                                                       */
  /* <Macro>                                                               */
  /*    FT_DECLARE_DRIVER                                                  */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Used to create a forward declaration of a                          */
  /*    FT_Driver_ClassRec stract instance.                                */
  /*                                                                       */
  /* <Macro>                                                               */
  /*    FT_DEFINE_DRIVER                                                   */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Used to initialize an instance of FT_Driver_ClassRec struct.       */
  /*                                                                       */
  /*    When FT_CONFIG_OPTION_PIC is defined a Create funtion will need    */
  /*    to called with a pointer where the allocated stracture is returned.*/
  /*    And when it is no longer needed a Destroy function needs           */
  /*    to be called to release that allocation.                           */
  /*    fcinit.c (ft_create_default_module_classes) already contains       */
  /*    a mechanism to call these functions for the default modules        */
  /*    described in ftmodule.h                                            */
  /*                                                                       */
  /*    Notice that the created Create and Destroy functions call          */
  /*    pic_init and pic_free function to allow you to manually allocate   */
  /*    and initialize any additional global data, like module specific    */
  /*    interface, and put them in the global pic container defined in     */
  /*    ftpic.h. if you don't need them just implement the functions as    */
  /*    empty to resolve the link error.                                   */
  /*                                                                       */
  /*    When FT_CONFIG_OPTION_PIC is not defined the struct will be        */
  /*    allocated in the global scope (or the scope where the macro        */
  /*    is used).                                                          */
  /*                                                                       */
#ifndef FT_CONFIG_OPTION_PIC

#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
#define FT_DEFINE_DRIVERS_OLD_INTERNALS(a_,b_) \
  a_, b_,
#else
  #define FT_DEFINE_DRIVERS_OLD_INTERNALS(a_,b_)
#endif

#define FT_DECLARE_DRIVER(class_)    \
  FT_CALLBACK_TABLE                  \
  const FT_Driver_ClassRec  class_;  

#define FT_DEFINE_DRIVER(class_,                                             \
                         flags_, size_, name_, version_, requires_,          \
                         interface_, init_, done_, get_interface_,           \
                         face_object_size_, size_object_size_,               \
                         slot_object_size_, init_face_, done_face_,          \
                         init_size_, done_size_, init_slot_, done_slot_,     \
                         old_set_char_sizes_, old_set_pixel_sizes_,          \
                         load_glyph_, get_kerning_, attach_file_,            \
                         get_advances_, request_size_, select_size_ )        \
  FT_CALLBACK_TABLE_DEF                                                      \
  const FT_Driver_ClassRec class_ =                                          \
  {                                                                          \
    FT_DEFINE_ROOT_MODULE(flags_,size_,name_,version_,requires_,interface_,  \
                          init_,done_,get_interface_)                        \
                                                                             \
    face_object_size_,                                                       \
    size_object_size_,                                                       \
    slot_object_size_,                                                       \
                                                                             \
    init_face_,                                                              \
    done_face_,                                                              \
                                                                             \
    init_size_,                                                              \
    done_size_,                                                              \
                                                                             \
    init_slot_,                                                              \
    done_slot_,                                                              \
                                                                             \
    FT_DEFINE_DRIVERS_OLD_INTERNALS(old_set_char_sizes_, old_set_pixel_sizes_) \
                                                                             \
    load_glyph_,                                                             \
                                                                             \
    get_kerning_,                                                            \
    attach_file_,                                                            \
    get_advances_,                                                           \
                                                                             \
    request_size_,                                                           \
    select_size_                                                             \
  };

#else /* FT_CONFIG_OPTION_PIC */ 

#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
#define FT_DEFINE_DRIVERS_OLD_INTERNALS(a_,b_) \
  clazz->set_char_sizes = a_; \
  clazz->set_pixel_sizes = b_;
#else
  #define FT_DEFINE_DRIVERS_OLD_INTERNALS(a_,b_)
#endif

#define FT_DECLARE_DRIVER(class_)    FT_DECLARE_MODULE(class_)

#define FT_DEFINE_DRIVER(class_,                                             \
                         flags_, size_, name_, version_, requires_,          \
                         interface_, init_, done_, get_interface_,           \
                         face_object_size_, size_object_size_,               \
                         slot_object_size_, init_face_, done_face_,          \
                         init_size_, done_size_, init_slot_, done_slot_,     \
                         old_set_char_sizes_, old_set_pixel_sizes_,          \
                         load_glyph_, get_kerning_, attach_file_,            \
                         get_advances_, request_size_, select_size_ )        \
  void class_##_pic_free( FT_Library library );                              \
  FT_Error class_##_pic_init( FT_Library library );                          \
                                                                             \
  void                                                                       \
  FT_Destroy_Class_##class_( FT_Library        library,                      \
                             FT_Module_Class*  clazz )                       \
  {                                                                          \
    FT_Memory       memory = library->memory;                                \
    FT_Driver_Class dclazz = (FT_Driver_Class)clazz;                         \
    class_##_pic_free( library );                                            \
    if ( dclazz )                                                            \
      FT_FREE( dclazz );                                                     \
  }                                                                          \
                                                                             \
  FT_Error                                                                   \
  FT_Create_Class_##class_( FT_Library        library,                       \
                            FT_Module_Class**  output_class )                \
  {                                                                          \
    FT_Driver_Class  clazz;                                                  \
    FT_Error         error;                                                  \
    FT_Memory        memory = library->memory;                               \
                                                                             \
    if ( FT_ALLOC( clazz, sizeof(*clazz) ) )                                 \
      return error;                                                          \
                                                                             \
    error = class_##_pic_init( library );                                    \
    if(error)                                                                \
    {                                                                        \
      FT_FREE( clazz );                                                      \
      return error;                                                          \
    }                                                                        \
                                                                             \
    FT_DEFINE_ROOT_MODULE(flags_,size_,name_,version_,requires_,interface_,  \
                          init_,done_,get_interface_)                        \
                                                                             \
    clazz->face_object_size    = face_object_size_;                          \
    clazz->size_object_size    = size_object_size_;                          \
    clazz->slot_object_size    = slot_object_size_;                          \
                                                                             \
    clazz->init_face           = init_face_;                                 \
    clazz->done_face           = done_face_;                                 \
                                                                             \
    clazz->init_size           = init_size_;                                 \
    clazz->done_size           = done_size_;                                 \
                                                                             \
    clazz->init_slot           = init_slot_;                                 \
    clazz->done_slot           = done_slot_;                                 \
                                                                             \
    FT_DEFINE_DRIVERS_OLD_INTERNALS(old_set_char_sizes_, old_set_pixel_sizes_) \
                                                                             \
    clazz->load_glyph          = load_glyph_;                                \
                                                                             \
    clazz->get_kerning         = get_kerning_;                               \
    clazz->attach_file         = attach_file_;                               \
    clazz->get_advances        = get_advances_;                              \
                                                                             \
    clazz->request_size        = request_size_;                              \
    clazz->select_size         = select_size_;                               \
                                                                             \
    *output_class = (FT_Module_Class*)clazz;                                 \
    return FT_Err_Ok;                                                        \
  }                


#endif /* FT_CONFIG_OPTION_PIC */

FT_END_HEADER

#endif /* __FTDRIVER_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  ftgloadr.h                                                             */
/*                                                                         */
/*    The FreeType glyph loader (specification).                           */
/*                                                                         */
/*  Copyright 2002, 2003, 2005, 2006 by                                    */
/*  David Turner, Robert Wilhelm, and Werner Lemberg                       */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __FTGLOADR_H__
#define __FTGLOADR_H__


#include <ft2build.h>
#include FT_FREETYPE_H


FT_BEGIN_HEADER


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_GlyphLoader                                                     */
  /*                                                                       */
  /* <Description>                                                         */
  /*    The glyph loader is an internal object used to load several glyphs */
  /*    together (for example, in the case of composites).                 */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The glyph loader implementation is not part of the high-level API, */
  /*    hence the forward structure declaration.                           */
  /*                                                                       */
  typedef struct FT_GlyphLoaderRec_*  FT_GlyphLoader ;


#if 0  /* moved to freetype.h in version 2.2 */
#define FT_SUBGLYPH_FLAG_ARGS_ARE_WORDS          1
#define FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES      2
#define FT_SUBGLYPH_FLAG_ROUND_XY_TO_GRID        4
#define FT_SUBGLYPH_FLAG_SCALE                   8
#define FT_SUBGLYPH_FLAG_XY_SCALE             0x40
#define FT_SUBGLYPH_FLAG_2X2                  0x80
#define FT_SUBGLYPH_FLAG_USE_MY_METRICS      0x200
#endif


  typedef struct  FT_SubGlyphRec_
  {
    FT_Int     index;
    FT_UShort  flags;
    FT_Int     arg1;
    FT_Int     arg2;
    FT_Matrix  transform;

  } FT_SubGlyphRec;


  typedef struct  FT_GlyphLoadRec_
  {
    FT_Outline   outline;       /* outline                   */
    FT_Vector*   extra_points;  /* extra points table        */
    FT_Vector*   extra_points2; /* second extra points table */
    FT_UInt      num_subglyphs; /* number of subglyphs       */
    FT_SubGlyph  subglyphs;     /* subglyphs                 */

  } FT_GlyphLoadRec, *FT_GlyphLoad;


  typedef struct  FT_GlyphLoaderRec_
  {
    FT_Memory        memory;
    FT_UInt          max_points;
    FT_UInt          max_contours;
    FT_UInt          max_subglyphs;
    FT_Bool          use_extra;

    FT_GlyphLoadRec  base;
    FT_GlyphLoadRec  current;

    void*            other;            /* for possible future extension? */

  } FT_GlyphLoaderRec;


  /* create new empty glyph loader */
  FT_BASE( FT_Error )
  FT_GlyphLoader_New( FT_Memory        memory,
                      FT_GlyphLoader  *aloader );

  /* add an extra points table to a glyph loader */
  FT_BASE( FT_Error )
  FT_GlyphLoader_CreateExtra( FT_GlyphLoader  loader );

  /* destroy a glyph loader */
  FT_BASE( void )
  FT_GlyphLoader_Done( FT_GlyphLoader  loader );

  /* reset a glyph loader (frees everything int it) */
  FT_BASE( void )
  FT_GlyphLoader_Reset( FT_GlyphLoader  loader );

  /* rewind a glyph loader */
  FT_BASE( void )
  FT_GlyphLoader_Rewind( FT_GlyphLoader  loader );

  /* check that there is enough space to add `n_points' and `n_contours' */
  /* to the glyph loader                                                 */
  FT_BASE( FT_Error )
  FT_GlyphLoader_CheckPoints( FT_GlyphLoader  loader,
                              FT_UInt         n_points,
                              FT_UInt         n_contours );


#define FT_GLYPHLOADER_CHECK_P( _loader, _count )                         \
   ( (_count) == 0 || ((_loader)->base.outline.n_points    +              \
                       (_loader)->current.outline.n_points +              \
                       (unsigned long)(_count)) <= (_loader)->max_points )

#define FT_GLYPHLOADER_CHECK_C( _loader, _count )                          \
  ( (_count) == 0 || ((_loader)->base.outline.n_contours    +              \
                      (_loader)->current.outline.n_contours +              \
                      (unsigned long)(_count)) <= (_loader)->max_contours )

#define FT_GLYPHLOADER_CHECK_POINTS( _loader, _points,_contours )      \
  ( ( FT_GLYPHLOADER_CHECK_P( _loader, _points )   &&                  \
      FT_GLYPHLOADER_CHECK_C( _loader, _contours ) )                   \
    ? 0                                                                \
    : FT_GlyphLoader_CheckPoints( (_loader), (_points), (_contours) ) )


  /* check that there is enough space to add `n_subs' sub-glyphs to */
  /* a glyph loader                                                 */
  FT_BASE( FT_Error )
  FT_GlyphLoader_CheckSubGlyphs( FT_GlyphLoader  loader,
                                 FT_UInt         n_subs );

  /* prepare a glyph loader, i.e. empty the current glyph */
  FT_BASE( void )
  FT_GlyphLoader_Prepare( FT_GlyphLoader  loader );

  /* add the current glyph to the base glyph */
  FT_BASE( void )
  FT_GlyphLoader_Add( FT_GlyphLoader  loader );

  /* copy points from one glyph loader to another */
  FT_BASE( FT_Error )
  FT_GlyphLoader_CopyPoints( FT_GlyphLoader  target,
                             FT_GlyphLoader  source );

 /* */


FT_END_HEADER

#endif /* __FTGLOADR_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  ftmemory.h                                                             */
/*                                                                         */
/*    The FreeType memory management macros (specification).               */
/*                                                                         */
/*  Copyright 1996-2001, 2002, 2004, 2005, 2006, 2007, 2010 by             */
/*  David Turner, Robert Wilhelm, and Werner Lemberg                       */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __FTMEMORY_H__
#define __FTMEMORY_H__


#include <ft2build.h>
#include FT_CONFIG_CONFIG_H
#include FT_TYPES_H


FT_BEGIN_HEADER


  /*************************************************************************/
  /*                                                                       */
  /* <Macro>                                                               */
  /*    FT_SET_ERROR                                                       */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This macro is used to set an implicit `error' variable to a given  */
  /*    expression's value (usually a function call), and convert it to a  */
  /*    boolean which is set whenever the value is != 0.                   */
  /*                                                                       */
#undef  FT_SET_ERROR
#define FT_SET_ERROR( expression ) \
          ( ( error = (expression) ) != 0 )



  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /****                                                                 ****/
  /****                                                                 ****/
  /****                           M E M O R Y                           ****/
  /****                                                                 ****/
  /****                                                                 ****/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/


  /*
   *  C++ refuses to handle statements like p = (void*)anything, with `p' a
   *  typed pointer.  Since we don't have a `typeof' operator in standard
   *  C++, we have to use a template to emulate it.
   */

#ifdef __cplusplus

  extern "C++"
  template <typename T> inline T*
  cplusplus_typeof(        T*,
                    void  *v )
  {
    return static_cast <T*> ( v );
  }

#define FT_ASSIGNP( p, val )  (p) = cplusplus_typeof( (p), (val) )

#else

#define FT_ASSIGNP( p, val )  (p) = (val)

#endif



#ifdef FT_DEBUG_MEMORY

  FT_BASE( const char* )  _ft_debug_file;
  FT_BASE( long )         _ft_debug_lineno;

#define FT_DEBUG_INNER( exp )  ( _ft_debug_file   = __FILE__, \
                                 _ft_debug_lineno = __LINE__, \
                                 (exp) )

#define FT_ASSIGNP_INNER( p, exp )  ( _ft_debug_file   = __FILE__, \
                                      _ft_debug_lineno = __LINE__, \
                                      FT_ASSIGNP( p, exp ) )

#else /* !FT_DEBUG_MEMORY */

#define FT_DEBUG_INNER( exp )       (exp)
#define FT_ASSIGNP_INNER( p, exp )  FT_ASSIGNP( p, exp )

#endif /* !FT_DEBUG_MEMORY */


  /*
   *  The allocation functions return a pointer, and the error code
   *  is written to through the `p_error' parameter.  See below for
   *  for documentation.
   */

  FT_BASE( FT_Pointer )
  ft_mem_alloc( FT_Memory  memory,
                FT_Long    size,
                FT_Error  *p_error );

  FT_BASE( FT_Pointer )
  ft_mem_qalloc( FT_Memory  memory,
                 FT_Long    size,
                 FT_Error  *p_error );

  FT_BASE( FT_Pointer )
  ft_mem_realloc( FT_Memory  memory,
                  FT_Long    item_size,
                  FT_Long    cur_count,
                  FT_Long    new_count,
                  void*      block,
                  FT_Error  *p_error );

  FT_BASE( FT_Pointer )
  ft_mem_qrealloc( FT_Memory  memory,
                   FT_Long    item_size,
                   FT_Long    cur_count,
                   FT_Long    new_count,
                   void*      block,
                   FT_Error  *p_error );

  FT_BASE( void )
  ft_mem_free( FT_Memory    memory,
               const void*  P );


#define FT_MEM_ALLOC( ptr, size )                                         \
          FT_ASSIGNP_INNER( ptr, ft_mem_alloc( memory, (size), &error ) )

#define FT_MEM_FREE( ptr )                \
          FT_BEGIN_STMNT                  \
            ft_mem_free( memory, (ptr) ); \
            (ptr) = NULL;                 \
          FT_END_STMNT

#define FT_MEM_NEW( ptr )                        \
          FT_MEM_ALLOC( ptr, sizeof ( *(ptr) ) )

#define FT_MEM_REALLOC( ptr, cursz, newsz )                        \
          FT_ASSIGNP_INNER( ptr, ft_mem_realloc( memory, 1,        \
                                                 (cursz), (newsz), \
                                                 (ptr), &error ) )

#define FT_MEM_QALLOC( ptr, size )                                         \
          FT_ASSIGNP_INNER( ptr, ft_mem_qalloc( memory, (size), &error ) )

#define FT_MEM_QNEW( ptr )                        \
          FT_MEM_QALLOC( ptr, sizeof ( *(ptr) ) )

#define FT_MEM_QREALLOC( ptr, cursz, newsz )                         \
          FT_ASSIGNP_INNER( ptr, ft_mem_qrealloc( memory, 1,        \
                                                  (cursz), (newsz), \
                                                  (ptr), &error ) )

#define FT_MEM_QRENEW_ARRAY( ptr, cursz, newsz )                             \
          FT_ASSIGNP_INNER( ptr, ft_mem_qrealloc( memory, sizeof ( *(ptr) ), \
                                                  (cursz), (newsz),          \
                                                  (ptr), &error ) )

#define FT_MEM_ALLOC_MULT( ptr, count, item_size )                    \
          FT_ASSIGNP_INNER( ptr, ft_mem_realloc( memory, (item_size), \
                                                 0, (count),          \
                                                 NULL, &error ) )

#define FT_MEM_REALLOC_MULT( ptr, oldcnt, newcnt, itmsz )            \
          FT_ASSIGNP_INNER( ptr, ft_mem_realloc( memory, (itmsz),    \
                                                 (oldcnt), (newcnt), \
                                                 (ptr), &error ) )

#define FT_MEM_QALLOC_MULT( ptr, count, item_size )                    \
          FT_ASSIGNP_INNER( ptr, ft_mem_qrealloc( memory, (item_size), \
                                                  0, (count),          \
                                                  NULL, &error ) )

#define FT_MEM_QREALLOC_MULT( ptr, oldcnt, newcnt, itmsz)             \
          FT_ASSIGNP_INNER( ptr, ft_mem_qrealloc( memory, (itmsz),    \
                                                  (oldcnt), (newcnt), \
                                                  (ptr), &error ) )


#define FT_MEM_SET_ERROR( cond )  ( (cond), error != 0 )


#define FT_MEM_SET( dest, byte, count )     ft_memset( dest, byte, count )

#define FT_MEM_COPY( dest, source, count )  ft_memcpy( dest, source, count )

#define FT_MEM_MOVE( dest, source, count )  ft_memmove( dest, source, count )


#define FT_MEM_ZERO( dest, count )  FT_MEM_SET( dest, 0, count )

#define FT_ZERO( p )                FT_MEM_ZERO( p, sizeof ( *(p) ) )


#define FT_ARRAY_ZERO( dest, count )                        \
          FT_MEM_ZERO( dest, (count) * sizeof ( *(dest) ) )

#define FT_ARRAY_COPY( dest, source, count )                        \
          FT_MEM_COPY( dest, source, (count) * sizeof ( *(dest) ) )

#define FT_ARRAY_MOVE( dest, source, count )                        \
          FT_MEM_MOVE( dest, source, (count) * sizeof ( *(dest) ) )


  /*
   *  Return the maximum number of addressable elements in an array.
   *  We limit ourselves to INT_MAX, rather than UINT_MAX, to avoid
   *  any problems.
   */
#define FT_ARRAY_MAX( ptr )           ( FT_INT_MAX / sizeof ( *(ptr) ) )

#define FT_ARRAY_CHECK( ptr, count )  ( (count) <= FT_ARRAY_MAX( ptr ) )


  /*************************************************************************/
  /*                                                                       */
  /* The following functions macros expect that their pointer argument is  */
  /* _typed_ in order to automatically compute array element sizes.        */
  /*                                                                       */

#define FT_MEM_NEW_ARRAY( ptr, count )                                      \
          FT_ASSIGNP_INNER( ptr, ft_mem_realloc( memory, sizeof ( *(ptr) ), \
                                                 0, (count),                \
                                                 NULL, &error ) )

#define FT_MEM_RENEW_ARRAY( ptr, cursz, newsz )                             \
          FT_ASSIGNP_INNER( ptr, ft_mem_realloc( memory, sizeof ( *(ptr) ), \
                                                 (cursz), (newsz),          \
                                                 (ptr), &error ) )

#define FT_MEM_QNEW_ARRAY( ptr, count )                                      \
          FT_ASSIGNP_INNER( ptr, ft_mem_qrealloc( memory, sizeof ( *(ptr) ), \
                                                  0, (count),                \
                                                  NULL, &error ) )

#define FT_MEM_QRENEW_ARRAY( ptr, cursz, newsz )                             \
          FT_ASSIGNP_INNER( ptr, ft_mem_qrealloc( memory, sizeof ( *(ptr) ), \
                                                  (cursz), (newsz),          \
                                                  (ptr), &error ) )


#define FT_ALLOC( ptr, size )                           \
          FT_MEM_SET_ERROR( FT_MEM_ALLOC( ptr, size ) )

#define FT_REALLOC( ptr, cursz, newsz )                           \
          FT_MEM_SET_ERROR( FT_MEM_REALLOC( ptr, cursz, newsz ) )

#define FT_ALLOC_MULT( ptr, count, item_size )                           \
          FT_MEM_SET_ERROR( FT_MEM_ALLOC_MULT( ptr, count, item_size ) )

#define FT_REALLOC_MULT( ptr, oldcnt, newcnt, itmsz )              \
          FT_MEM_SET_ERROR( FT_MEM_REALLOC_MULT( ptr, oldcnt,      \
                                                 newcnt, itmsz ) )

#define FT_QALLOC( ptr, size )                           \
          FT_MEM_SET_ERROR( FT_MEM_QALLOC( ptr, size ) )

#define FT_QREALLOC( ptr, cursz, newsz )                           \
          FT_MEM_SET_ERROR( FT_MEM_QREALLOC( ptr, cursz, newsz ) )

#define FT_QALLOC_MULT( ptr, count, item_size )                           \
          FT_MEM_SET_ERROR( FT_MEM_QALLOC_MULT( ptr, count, item_size ) )

#define FT_QREALLOC_MULT( ptr, oldcnt, newcnt, itmsz )              \
          FT_MEM_SET_ERROR( FT_MEM_QREALLOC_MULT( ptr, oldcnt,      \
                                                  newcnt, itmsz ) )

#define FT_FREE( ptr )  FT_MEM_FREE( ptr )

#define FT_NEW( ptr )  FT_MEM_SET_ERROR( FT_MEM_NEW( ptr ) )

#define FT_NEW_ARRAY( ptr, count )                           \
          FT_MEM_SET_ERROR( FT_MEM_NEW_ARRAY( ptr, count ) )

#define FT_RENEW_ARRAY( ptr, curcnt, newcnt )                           \
          FT_MEM_SET_ERROR( FT_MEM_RENEW_ARRAY( ptr, curcnt, newcnt ) )

#define FT_QNEW( ptr )                           \
          FT_MEM_SET_ERROR( FT_MEM_QNEW( ptr ) )

#define FT_QNEW_ARRAY( ptr, count )                          \
          FT_MEM_SET_ERROR( FT_MEM_NEW_ARRAY( ptr, count ) )

#define FT_QRENEW_ARRAY( ptr, curcnt, newcnt )                          \
          FT_MEM_SET_ERROR( FT_MEM_RENEW_ARRAY( ptr, curcnt, newcnt ) )


#ifdef FT_CONFIG_OPTION_OLD_INTERNALS

  FT_BASE( FT_Error )
  FT_Alloc( FT_Memory  memory,
            FT_Long    size,
            void*     *P );

  FT_BASE( FT_Error )
  FT_QAlloc( FT_Memory  memory,
             FT_Long    size,
             void*     *p );

  FT_BASE( FT_Error )
  FT_Realloc( FT_Memory  memory,
              FT_Long    current,
              FT_Long    size,
              void*     *P );

  FT_BASE( FT_Error )
  FT_QRealloc( FT_Memory  memory,
               FT_Long    current,
               FT_Long    size,
               void*     *p );

  FT_BASE( void )
  FT_Free( FT_Memory  memory,
           void*     *P );

#endif /* FT_CONFIG_OPTION_OLD_INTERNALS */


  FT_BASE( FT_Pointer )
  ft_mem_strdup( FT_Memory    memory,
                 const char*  str,
                 FT_Error    *p_error );

  FT_BASE( FT_Pointer )
  ft_mem_dup( FT_Memory    memory,
              const void*  address,
              FT_ULong     size,
              FT_Error    *p_error );

#define FT_MEM_STRDUP( dst, str )                                            \
          (dst) = (char*)ft_mem_strdup( memory, (const char*)(str), &error )

#define FT_STRDUP( dst, str )                           \
          FT_MEM_SET_ERROR( FT_MEM_STRDUP( dst, str ) )

#define FT_MEM_DUP( dst, address, size )                                    \
          (dst) = ft_mem_dup( memory, (address), (FT_ULong)(size), &error )

#define FT_DUP( dst, address, size )                           \
          FT_MEM_SET_ERROR( FT_MEM_DUP( dst, address, size ) )


  /* Return >= 1 if a truncation occurs.            */
  /* Return 0 if the source string fits the buffer. */
  /* This is *not* the same as strlcpy().           */
  FT_BASE( FT_Int )
  ft_mem_strcpyn( char*        dst,
                  const char*  src,
                  FT_ULong     size );

#define FT_STRCPYN( dst, src, size )                                         \
          ft_mem_strcpyn( (char*)dst, (const char*)(src), (FT_ULong)(size) )

 /* */


FT_END_HEADER

#endif /* __FTMEMORY_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  ftobjs.h                                                               */
/*                                                                         */
/*    The FreeType private base classes (specification).                   */
/*                                                                         */
/*  Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2008, 2010 by       */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /*  This file contains the definition of all internal FreeType classes.  */
  /*                                                                       */
  /*************************************************************************/


#ifndef __FTOBJS_H__
#define __FTOBJS_H__

#include <ft2build.h>
#include FT_RENDER_H
#include FT_SIZES_H
#include FT_LCD_FILTER_H
#include FT_INTERNAL_MEMORY_H
#include FT_INTERNAL_GLYPH_LOADER_H
#include FT_INTERNAL_DRIVER_H
#include FT_INTERNAL_AUTOHINT_H
#include FT_INTERNAL_SERVICE_H
#include FT_INTERNAL_PIC_H

#ifdef FT_CONFIG_OPTION_INCREMENTAL
#include FT_INCREMENTAL_H
#endif


FT_BEGIN_HEADER


  /*************************************************************************/
  /*                                                                       */
  /* Some generic definitions.                                             */
  /*                                                                       */
#ifndef TRUE
#define TRUE  1
#endif

#ifndef FALSE
#define FALSE  0
#endif

#ifndef NULL
#define NULL  (void*)0
#endif


  /*************************************************************************/
  /*                                                                       */
  /* The min and max functions missing in C.  As usual, be careful not to  */
  /* write things like FT_MIN( a++, b++ ) to avoid side effects.           */
  /*                                                                       */
#define FT_MIN( a, b )  ( (a) < (b) ? (a) : (b) )
#define FT_MAX( a, b )  ( (a) > (b) ? (a) : (b) )

#define FT_ABS( a )     ( (a) < 0 ? -(a) : (a) )


#define FT_PAD_FLOOR( x, n )  ( (x) & ~((n)-1) )
#define FT_PAD_ROUND( x, n )  FT_PAD_FLOOR( (x) + ((n)/2), n )
#define FT_PAD_CEIL( x, n )   FT_PAD_FLOOR( (x) + ((n)-1), n )

#define FT_PIX_FLOOR( x )     ( (x) & ~63 )
#define FT_PIX_ROUND( x )     FT_PIX_FLOOR( (x) + 32 )
#define FT_PIX_CEIL( x )      FT_PIX_FLOOR( (x) + 63 )


  /*
   *  Return the highest power of 2 that is <= value; this correspond to
   *  the highest bit in a given 32-bit value.
   */
  FT_BASE( FT_UInt32 )
  ft_highpow2( FT_UInt32  value );


  /*
   *  character classification functions -- since these are used to parse
   *  font files, we must not use those in <ctypes.h> which are
   *  locale-dependent
   */
#define  ft_isdigit( x )   ( ( (unsigned)(x) - '0' ) < 10U )

#define  ft_isxdigit( x )  ( ( (unsigned)(x) - '0' ) < 10U || \
                             ( (unsigned)(x) - 'a' ) < 6U  || \
                             ( (unsigned)(x) - 'A' ) < 6U  )

  /* the next two macros assume ASCII representation */
#define  ft_isupper( x )  ( ( (unsigned)(x) - 'A' ) < 26U )
#define  ft_islower( x )  ( ( (unsigned)(x) - 'a' ) < 26U )

#define  ft_isalpha( x )  ( ft_isupper( x ) || ft_islower( x ) )
#define  ft_isalnum( x )  ( ft_isdigit( x ) || ft_isalpha( x ) )


  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /****                                                                 ****/
  /****                                                                 ****/
  /****                       C H A R M A P S                           ****/
  /****                                                                 ****/
  /****                                                                 ****/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/

  /* handle to internal charmap object */
  typedef struct FT_CMapRec_*              FT_CMap;

  /* handle to charmap class structure */
  typedef const struct FT_CMap_ClassRec_*  FT_CMap_Class;

  /* internal charmap object structure */
  typedef struct  FT_CMapRec_
  {
    FT_CharMapRec  charmap;
    FT_CMap_Class  clazz;

  } FT_CMapRec;

  /* typecase any pointer to a charmap handle */
#define FT_CMAP( x )              ((FT_CMap)( x ))

  /* obvious macros */
#define FT_CMAP_PLATFORM_ID( x )  FT_CMAP( x )->charmap.platform_id
#define FT_CMAP_ENCODING_ID( x )  FT_CMAP( x )->charmap.encoding_id
#define FT_CMAP_ENCODING( x )     FT_CMAP( x )->charmap.encoding
#define FT_CMAP_FACE( x )         FT_CMAP( x )->charmap.face


  /* class method definitions */
  typedef FT_Error
  (*FT_CMap_InitFunc)( FT_CMap     cmap,
                       FT_Pointer  init_data );

  typedef void
  (*FT_CMap_DoneFunc)( FT_CMap  cmap );

  typedef FT_UInt
  (*FT_CMap_CharIndexFunc)( FT_CMap    cmap,
                            FT_UInt32  char_code );

  typedef FT_UInt
  (*FT_CMap_CharNextFunc)( FT_CMap     cmap,
                           FT_UInt32  *achar_code );

  typedef FT_UInt
  (*FT_CMap_CharVarIndexFunc)( FT_CMap    cmap,
                               FT_CMap    unicode_cmap,
                               FT_UInt32  char_code,
                               FT_UInt32  variant_selector );

  typedef FT_Bool
  (*FT_CMap_CharVarIsDefaultFunc)( FT_CMap    cmap,
                                   FT_UInt32  char_code,
                                   FT_UInt32  variant_selector );

  typedef FT_UInt32 *
  (*FT_CMap_VariantListFunc)( FT_CMap    cmap,
                              FT_Memory  mem );

  typedef FT_UInt32 *
  (*FT_CMap_CharVariantListFunc)( FT_CMap    cmap,
                                  FT_Memory  mem,
                                  FT_UInt32  char_code );

  typedef FT_UInt32 *
  (*FT_CMap_VariantCharListFunc)( FT_CMap    cmap,
                                  FT_Memory  mem,
                                  FT_UInt32  variant_selector );


  typedef struct  FT_CMap_ClassRec_
  {
    FT_ULong               size;
    FT_CMap_InitFunc       init;
    FT_CMap_DoneFunc       done;
    FT_CMap_CharIndexFunc  char_index;
    FT_CMap_CharNextFunc   char_next;

    /* Subsequent entries are special ones for format 14 -- the variant */
    /* selector subtable which behaves like no other                    */

    FT_CMap_CharVarIndexFunc      char_var_index;
    FT_CMap_CharVarIsDefaultFunc  char_var_default;
    FT_CMap_VariantListFunc       variant_list;
    FT_CMap_CharVariantListFunc   charvariant_list;
    FT_CMap_VariantCharListFunc   variantchar_list;

  } FT_CMap_ClassRec;

#ifndef FT_CONFIG_OPTION_PIC

#define FT_DECLARE_CMAP_CLASS(class_) \
    FT_CALLBACK_TABLE const FT_CMap_ClassRec class_;

#define FT_DEFINE_CMAP_CLASS(class_, size_, init_, done_, char_index_,       \
        char_next_, char_var_index_, char_var_default_, variant_list_,       \
        charvariant_list_, variantchar_list_)                                \
  FT_CALLBACK_TABLE_DEF                                                      \
  const FT_CMap_ClassRec class_ =                                            \
  {                                                                          \
    size_, init_, done_, char_index_, char_next_, char_var_index_,           \
    char_var_default_, variant_list_, charvariant_list_, variantchar_list_   \
  };
#else /* FT_CONFIG_OPTION_PIC */

#define FT_DECLARE_CMAP_CLASS(class_) \
    void FT_Init_Class_##class_( FT_Library library, FT_CMap_ClassRec*  clazz);

#define FT_DEFINE_CMAP_CLASS(class_, size_, init_, done_, char_index_,       \
        char_next_, char_var_index_, char_var_default_, variant_list_,       \
        charvariant_list_, variantchar_list_)                                \
  void                                                                       \
  FT_Init_Class_##class_( FT_Library library,                                \
                          FT_CMap_ClassRec*  clazz)                          \
  {                                                                          \
    FT_UNUSED(library);                                                      \
    clazz->size = size_;                                                     \
    clazz->init = init_;                                                     \
    clazz->done = done_;                                                     \
    clazz->char_index = char_index_;                                         \
    clazz->char_next = char_next_;                                           \
    clazz->char_var_index = char_var_index_;                                 \
    clazz->char_var_default = char_var_default_;                             \
    clazz->variant_list = variant_list_;                                     \
    clazz->charvariant_list = charvariant_list_;                             \
    clazz->variantchar_list = variantchar_list_;                             \
  } 
#endif /* FT_CONFIG_OPTION_PIC */

  /* create a new charmap and add it to charmap->face */
  FT_BASE( FT_Error )
  FT_CMap_New( FT_CMap_Class  clazz,
               FT_Pointer     init_data,
               FT_CharMap     charmap,
               FT_CMap       *acmap );

  /* destroy a charmap and remove it from face's list */
  FT_BASE( void )
  FT_CMap_Done( FT_CMap  cmap );


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_Face_InternalRec                                                */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This structure contains the internal fields of each FT_Face        */
  /*    object.  These fields may change between different releases of     */
  /*    FreeType.                                                          */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    max_points ::                                                      */
  /*      The maximal number of points used to store the vectorial outline */
  /*      of any glyph in this face.  If this value cannot be known in     */
  /*      advance, or if the face isn't scalable, this should be set to 0. */
  /*      Only relevant for scalable formats.                              */
  /*                                                                       */
  /*    max_contours ::                                                    */
  /*      The maximal number of contours used to store the vectorial       */
  /*      outline of any glyph in this face.  If this value cannot be      */
  /*      known in advance, or if the face isn't scalable, this should be  */
  /*      set to 0.  Only relevant for scalable formats.                   */
  /*                                                                       */
  /*    transform_matrix ::                                                */
  /*      A 2x2 matrix of 16.16 coefficients used to transform glyph       */
  /*      outlines after they are loaded from the font.  Only used by the  */
  /*      convenience functions.                                           */
  /*                                                                       */
  /*    transform_delta ::                                                 */
  /*      A translation vector used to transform glyph outlines after they */
  /*      are loaded from the font.  Only used by the convenience          */
  /*      functions.                                                       */
  /*                                                                       */
  /*    transform_flags ::                                                 */
  /*      Some flags used to classify the transform.  Only used by the     */
  /*      convenience functions.                                           */
  /*                                                                       */
  /*    services ::                                                        */
  /*      A cache for frequently used services.  It should be only         */
  /*      accessed with the macro `FT_FACE_LOOKUP_SERVICE'.                */
  /*                                                                       */
  /*    incremental_interface ::                                           */
  /*      If non-null, the interface through which glyph data and metrics  */
  /*      are loaded incrementally for faces that do not provide all of    */
  /*      this data when first opened.  This field exists only if          */
  /*      @FT_CONFIG_OPTION_INCREMENTAL is defined.                        */
  /*                                                                       */
  /*    ignore_unpatented_hinter ::                                        */
  /*      This boolean flag instructs the glyph loader to ignore the       */
  /*      native font hinter, if one is found.  This is exclusively used   */
  /*      in the case when the unpatented hinter is compiled within the    */
  /*      library.                                                         */
  /*                                                                       */
  /*    refcount ::                                                        */
  /*      A counter initialized to~1 at the time an @FT_Face structure is  */
  /*      created.  @FT_Reference_Face increments this counter, and        */
  /*      @FT_Done_Face only destroys a face if the counter is~1,          */
  /*      otherwise it simply decrements it.                               */
  /*                                                                       */
  typedef struct  FT_Face_InternalRec_
  {
#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
    FT_UShort           reserved1;
    FT_Short            reserved2;
#endif
    FT_Matrix           transform_matrix;
    FT_Vector           transform_delta;
    FT_Int              transform_flags;

    FT_ServiceCacheRec  services;

#ifdef FT_CONFIG_OPTION_INCREMENTAL
    FT_Incremental_InterfaceRec*  incremental_interface;
#endif

    FT_Bool             ignore_unpatented_hinter;
    FT_UInt             refcount;

  } FT_Face_InternalRec;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_Slot_InternalRec                                                */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This structure contains the internal fields of each FT_GlyphSlot   */
  /*    object.  These fields may change between different releases of     */
  /*    FreeType.                                                          */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    loader            :: The glyph loader object used to load outlines */
  /*                         into the glyph slot.                          */
  /*                                                                       */
  /*    flags             :: Possible values are zero or                   */
  /*                         FT_GLYPH_OWN_BITMAP.  The latter indicates    */
  /*                         that the FT_GlyphSlot structure owns the      */
  /*                         bitmap buffer.                                */
  /*                                                                       */
  /*    glyph_transformed :: Boolean.  Set to TRUE when the loaded glyph   */
  /*                         must be transformed through a specific        */
  /*                         font transformation.  This is _not_ the same  */
  /*                         as the face transform set through             */
  /*                         FT_Set_Transform().                           */
  /*                                                                       */
  /*    glyph_matrix      :: The 2x2 matrix corresponding to the glyph     */
  /*                         transformation, if necessary.                 */
  /*                                                                       */
  /*    glyph_delta       :: The 2d translation vector corresponding to    */
  /*                         the glyph transformation, if necessary.       */
  /*                                                                       */
  /*    glyph_hints       :: Format-specific glyph hints management.       */
  /*                                                                       */

#define FT_GLYPH_OWN_BITMAP  0x1

  typedef struct  FT_Slot_InternalRec_
  {
    FT_GlyphLoader  loader;
    FT_UInt         flags;
    FT_Bool         glyph_transformed;
    FT_Matrix       glyph_matrix;
    FT_Vector       glyph_delta;
    void*           glyph_hints;

  } FT_GlyphSlot_InternalRec;


#if 0

  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_Size_InternalRec                                                */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This structure contains the internal fields of each FT_Size        */
  /*    object.  Currently, it's empty.                                    */
  /*                                                                       */
  /*************************************************************************/

  typedef struct  FT_Size_InternalRec_
  {
    /* empty */

  } FT_Size_InternalRec;

#endif


  /*************************************************************************/
  /*************************************************************************/
  /****                                                                 ****/
  /****                                                                 ****/
  /****                         M O D U L E S                           ****/
  /****                                                                 ****/
  /****                                                                 ****/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_ModuleRec                                                       */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A module object instance.                                          */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    clazz   :: A pointer to the module's class.                        */
  /*                                                                       */
  /*    library :: A handle to the parent library object.                  */
  /*                                                                       */
  /*    memory  :: A handle to the memory manager.                         */
  /*                                                                       */
  /*    generic :: A generic structure for user-level extensibility (?).   */
  /*                                                                       */
  typedef struct  FT_ModuleRec_
  {
    FT_Module_Class*  clazz;
    FT_Library        library;
    FT_Memory         memory;
    FT_Generic        generic;

  } FT_ModuleRec;


  /* typecast an object to a FT_Module */
#define FT_MODULE( x )          ((FT_Module)( x ))
#define FT_MODULE_CLASS( x )    FT_MODULE( x )->clazz
#define FT_MODULE_LIBRARY( x )  FT_MODULE( x )->library
#define FT_MODULE_MEMORY( x )   FT_MODULE( x )->memory


#define FT_MODULE_IS_DRIVER( x )  ( FT_MODULE_CLASS( x )->module_flags & \
                                    FT_MODULE_FONT_DRIVER )

#define FT_MODULE_IS_RENDERER( x )  ( FT_MODULE_CLASS( x )->module_flags & \
                                      FT_MODULE_RENDERER )

#define FT_MODULE_IS_HINTER( x )  ( FT_MODULE_CLASS( x )->module_flags & \
                                    FT_MODULE_HINTER )

#define FT_MODULE_IS_STYLER( x )  ( FT_MODULE_CLASS( x )->module_flags & \
                                    FT_MODULE_STYLER )

#define FT_DRIVER_IS_SCALABLE( x )  ( FT_MODULE_CLASS( x )->module_flags & \
                                      FT_MODULE_DRIVER_SCALABLE )

#define FT_DRIVER_USES_OUTLINES( x )  !( FT_MODULE_CLASS( x )->module_flags & \
                                         FT_MODULE_DRIVER_NO_OUTLINES )

#define FT_DRIVER_HAS_HINTER( x )  ( FT_MODULE_CLASS( x )->module_flags & \
                                     FT_MODULE_DRIVER_HAS_HINTER )


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Get_Module_Interface                                            */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Finds a module and returns its specific interface as a typeless    */
  /*    pointer.                                                           */
  /*                                                                       */
  /* <Input>                                                               */
  /*    library     :: A handle to the library object.                     */
  /*                                                                       */
  /*    module_name :: The module's name (as an ASCII string).             */
  /*                                                                       */
  /* <Return>                                                              */
  /*    A module-specific interface if available, 0 otherwise.             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    You should better be familiar with FreeType internals to know      */
  /*    which module to look for, and what its interface is :-)            */
  /*                                                                       */
  FT_BASE( const void* )
  FT_Get_Module_Interface( FT_Library   library,
                           const char*  mod_name );

  FT_BASE( FT_Pointer )
  ft_module_get_service( FT_Module    module,
                         const char*  service_id );

 /* */


  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /****                                                                 ****/
  /****                                                                 ****/
  /****               FACE, SIZE & GLYPH SLOT OBJECTS                   ****/
  /****                                                                 ****/
  /****                                                                 ****/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/

  /* a few macros used to perform easy typecasts with minimal brain damage */

#define FT_FACE( x )          ((FT_Face)(x))
#define FT_SIZE( x )          ((FT_Size)(x))
#define FT_SLOT( x )          ((FT_GlyphSlot)(x))

#define FT_FACE_DRIVER( x )   FT_FACE( x )->driver
#define FT_FACE_LIBRARY( x )  FT_FACE_DRIVER( x )->root.library
#define FT_FACE_MEMORY( x )   FT_FACE( x )->memory
#define FT_FACE_STREAM( x )   FT_FACE( x )->stream

#define FT_SIZE_FACE( x )     FT_SIZE( x )->face
#define FT_SLOT_FACE( x )     FT_SLOT( x )->face

#define FT_FACE_SLOT( x )     FT_FACE( x )->glyph
#define FT_FACE_SIZE( x )     FT_FACE( x )->size


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_New_GlyphSlot                                                   */
  /*                                                                       */
  /* <Description>                                                         */
  /*    It is sometimes useful to have more than one glyph slot for a      */
  /*    given face object.  This function is used to create additional     */
  /*    slots.  All of them are automatically discarded when the face is   */
  /*    destroyed.                                                         */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face  :: A handle to a parent face object.                         */
  /*                                                                       */
  /* <Output>                                                              */
  /*    aslot :: A handle to a new glyph slot object.                      */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0 means success.                             */
  /*                                                                       */
  FT_BASE( FT_Error )
  FT_New_GlyphSlot( FT_Face        face,
                    FT_GlyphSlot  *aslot );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Done_GlyphSlot                                                  */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Destroys a given glyph slot.  Remember however that all slots are  */
  /*    automatically destroyed with its parent.  Using this function is   */
  /*    not always mandatory.                                              */
  /*                                                                       */
  /* <Input>                                                               */
  /*    slot :: A handle to a target glyph slot.                           */
  /*                                                                       */
  FT_BASE( void )
  FT_Done_GlyphSlot( FT_GlyphSlot  slot );

 /* */

#define FT_REQUEST_WIDTH( req )                                            \
          ( (req)->horiResolution                                          \
              ? (FT_Pos)( (req)->width * (req)->horiResolution + 36 ) / 72 \
              : (req)->width )

#define FT_REQUEST_HEIGHT( req )                                            \
          ( (req)->vertResolution                                           \
              ? (FT_Pos)( (req)->height * (req)->vertResolution + 36 ) / 72 \
              : (req)->height )


  /* Set the metrics according to a bitmap strike. */
  FT_BASE( void )
  FT_Select_Metrics( FT_Face   face,
                     FT_ULong  strike_index );


  /* Set the metrics according to a size request. */
  FT_BASE( void )
  FT_Request_Metrics( FT_Face          face,
                      FT_Size_Request  req );


  /* Match a size request against `available_sizes'. */
  FT_BASE( FT_Error )
  FT_Match_Size( FT_Face          face,
                 FT_Size_Request  req,
                 FT_Bool          ignore_width,
                 FT_ULong*        size_index );


  /* Use the horizontal metrics to synthesize the vertical metrics. */
  /* If `advance' is zero, it is also synthesized.                  */
  FT_BASE( void )
  ft_synthesize_vertical_metrics( FT_Glyph_Metrics*  metrics,
                                  FT_Pos             advance );


  /* Free the bitmap of a given glyphslot when needed (i.e., only when it */
  /* was allocated with ft_glyphslot_alloc_bitmap).                       */
  FT_BASE( void )
  ft_glyphslot_free_bitmap( FT_GlyphSlot  slot );


  /* Allocate a new bitmap buffer in a glyph slot. */
  FT_BASE( FT_Error )
  ft_glyphslot_alloc_bitmap( FT_GlyphSlot  slot,
                             FT_ULong      size );


  /* Set the bitmap buffer in a glyph slot to a given pointer.  The buffer */
  /* will not be freed by a later call to ft_glyphslot_free_bitmap.        */
  FT_BASE( void )
  ft_glyphslot_set_bitmap( FT_GlyphSlot  slot,
                           FT_Byte*      buffer );


  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /****                                                                 ****/
  /****                                                                 ****/
  /****                        R E N D E R E R S                        ****/
  /****                                                                 ****/
  /****                                                                 ****/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/


#define FT_RENDERER( x )      ((FT_Renderer)( x ))
#define FT_GLYPH( x )         ((FT_Glyph)( x ))
#define FT_BITMAP_GLYPH( x )  ((FT_BitmapGlyph)( x ))
#define FT_OUTLINE_GLYPH( x ) ((FT_OutlineGlyph)( x ))


  typedef struct  FT_RendererRec_
  {
    FT_ModuleRec            root;
    FT_Renderer_Class*      clazz;
    FT_Glyph_Format         glyph_format;
    FT_Glyph_Class          glyph_class;

    FT_Raster               raster;
    FT_Raster_Render_Func   raster_render;
    FT_Renderer_RenderFunc  render;

  } FT_RendererRec;


  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /****                                                                 ****/
  /****                                                                 ****/
  /****                    F O N T   D R I V E R S                      ****/
  /****                                                                 ****/
  /****                                                                 ****/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/


  /* typecast a module into a driver easily */
#define FT_DRIVER( x )        ((FT_Driver)(x))

  /* typecast a module as a driver, and get its driver class */
#define FT_DRIVER_CLASS( x )  FT_DRIVER( x )->clazz


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_DriverRec                                                       */
  /*                                                                       */
  /* <Description>                                                         */
  /*    The root font driver class.  A font driver is responsible for      */
  /*    managing and loading font files of a given format.                 */
  /*                                                                       */
  /*  <Fields>                                                             */
  /*     root         :: Contains the fields of the root module class.     */
  /*                                                                       */
  /*     clazz        :: A pointer to the font driver's class.  Note that  */
  /*                     this is NOT root.clazz.  `class' wasn't used      */
  /*                     as it is a reserved word in C++.                  */
  /*                                                                       */
  /*     faces_list   :: The list of faces currently opened by this        */
  /*                     driver.                                           */
  /*                                                                       */
  /*     extensions   :: A typeless pointer to the driver's extensions     */
  /*                     registry, if they are supported through the       */
  /*                     configuration macro FT_CONFIG_OPTION_EXTENSIONS.  */
  /*                                                                       */
  /*     glyph_loader :: The glyph loader for all faces managed by this    */
  /*                     driver.  This object isn't defined for unscalable */
  /*                     formats.                                          */
  /*                                                                       */
  typedef struct  FT_DriverRec_
  {
    FT_ModuleRec     root;
    FT_Driver_Class  clazz;

    FT_ListRec       faces_list;
    void*            extensions;

    FT_GlyphLoader   glyph_loader;

  } FT_DriverRec;


  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /****                                                                 ****/
  /****                                                                 ****/
  /****                       L I B R A R I E S                         ****/
  /****                                                                 ****/
  /****                                                                 ****/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/


  /* This hook is used by the TrueType debugger.  It must be set to an */
  /* alternate truetype bytecode interpreter function.                 */
#define FT_DEBUG_HOOK_TRUETYPE            0


  /* Set this debug hook to a non-null pointer to force unpatented hinting */
  /* for all faces when both TT_USE_BYTECODE_INTERPRETER and               */
  /* TT_CONFIG_OPTION_UNPATENTED_HINTING are defined.  This is only used   */
  /* during debugging.                                                     */
#define FT_DEBUG_HOOK_UNPATENTED_HINTING  1


  typedef void  (*FT_Bitmap_LcdFilterFunc)( FT_Bitmap*      bitmap,
                                            FT_Render_Mode  render_mode,
                                            FT_Library      library );


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    FT_LibraryRec                                                      */
  /*                                                                       */
  /* <Description>                                                         */
  /*    The FreeType library class.  This is the root of all FreeType      */
  /*    data.  Use FT_New_Library() to create a library object, and        */
  /*    FT_Done_Library() to discard it and all child objects.             */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    memory           :: The library's memory object.  Manages memory   */
  /*                        allocation.                                    */
  /*                                                                       */
  /*    generic          :: Client data variable.  Used to extend the      */
  /*                        Library class by higher levels and clients.    */
  /*                                                                       */
  /*    version_major    :: The major version number of the library.       */
  /*                                                                       */
  /*    version_minor    :: The minor version number of the library.       */
  /*                                                                       */
  /*    version_patch    :: The current patch level of the library.        */
  /*                                                                       */
  /*    num_modules      :: The number of modules currently registered     */
  /*                        within this library.  This is set to 0 for new */
  /*                        libraries.  New modules are added through the  */
  /*                        FT_Add_Module() API function.                  */
  /*                                                                       */
  /*    modules          :: A table used to store handles to the currently */
  /*                        registered modules. Note that each font driver */
  /*                        contains a list of its opened faces.           */
  /*                                                                       */
  /*    renderers        :: The list of renderers currently registered     */
  /*                        within the library.                            */
  /*                                                                       */
  /*    cur_renderer     :: The current outline renderer.  This is a       */
  /*                        shortcut used to avoid parsing the list on     */
  /*                        each call to FT_Outline_Render().  It is a     */
  /*                        handle to the current renderer for the         */
  /*                        FT_GLYPH_FORMAT_OUTLINE format.                */
  /*                                                                       */
  /*    auto_hinter      :: XXX                                            */
  /*                                                                       */
  /*    raster_pool      :: The raster object's render pool.  This can     */
  /*                        ideally be changed dynamically at run-time.    */
  /*                                                                       */
  /*    raster_pool_size :: The size of the render pool in bytes.          */
  /*                                                                       */
  /*    debug_hooks      :: XXX                                            */
  /*                                                                       */
  /*    lcd_filter       :: If subpixel rendering is activated, the        */
  /*                        selected LCD filter mode.                      */
  /*                                                                       */
  /*    lcd_extra        :: If subpixel rendering is activated, the number */
  /*                        of extra pixels needed for the LCD filter.     */
  /*                                                                       */
  /*    lcd_weights      :: If subpixel rendering is activated, the LCD    */
  /*                        filter weights, if any.                        */
  /*                                                                       */
  /*    lcd_filter_func  :: If subpixel rendering is activated, the LCD    */
  /*                        filtering callback function.                   */
  /*                                                                       */
  /*    pic_container    :: Contains global structs and tables, instead    */
  /*                        of defining them globallly.                    */
  /*                                                                       */
  /*    refcount         :: A counter initialized to~1 at the time an      */
  /*                        @FT_Library structure is created.              */
  /*                        @FT_Reference_Library increments this counter, */
  /*                        and @FT_Done_Library only destroys a library   */
  /*                        if the counter is~1, otherwise it simply       */
  /*                        decrements it.                                 */
  /*                                                                       */
  typedef struct  FT_LibraryRec_
  {
    FT_Memory          memory;           /* library's memory manager */

    FT_Generic         generic;

    FT_Int             version_major;
    FT_Int             version_minor;
    FT_Int             version_patch;

    FT_UInt            num_modules;
    FT_Module          modules[FT_MAX_MODULES];  /* module objects  */

    FT_ListRec         renderers;        /* list of renderers        */
    FT_Renderer        cur_renderer;     /* current outline renderer */
    FT_Module          auto_hinter;

    FT_Byte*           raster_pool;      /* scan-line conversion */
                                         /* render pool          */
    FT_ULong           raster_pool_size; /* size of render pool in bytes */

    FT_DebugHook_Func  debug_hooks[4];

#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
    FT_LcdFilter             lcd_filter;
    FT_Int                   lcd_extra;        /* number of extra pixels */
    FT_Byte                  lcd_weights[7];   /* filter weights, if any */
    FT_Bitmap_LcdFilterFunc  lcd_filter_func;  /* filtering callback     */
#endif

#ifdef FT_CONFIG_OPTION_PIC
    FT_PIC_Container   pic_container;
#endif

    FT_UInt            refcount;

  } FT_LibraryRec;


  FT_BASE( FT_Renderer )
  FT_Lookup_Renderer( FT_Library       library,
                      FT_Glyph_Format  format,
                      FT_ListNode*     node );

  FT_BASE( FT_Error )
  FT_Render_Glyph_Internal( FT_Library      library,
                            FT_GlyphSlot    slot,
                            FT_Render_Mode  render_mode );

  typedef const char*
  (*FT_Face_GetPostscriptNameFunc)( FT_Face  face );

  typedef FT_Error
  (*FT_Face_GetGlyphNameFunc)( FT_Face     face,
                               FT_UInt     glyph_index,
                               FT_Pointer  buffer,
                               FT_UInt     buffer_max );

  typedef FT_UInt
  (*FT_Face_GetGlyphNameIndexFunc)( FT_Face     face,
                                    FT_String*  glyph_name );


#ifndef FT_CONFIG_OPTION_NO_DEFAULT_SYSTEM

  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_New_Memory                                                      */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Creates a new memory object.                                       */
  /*                                                                       */
  /* <Return>                                                              */
  /*    A pointer to the new memory object.  0 in case of error.           */
  /*                                                                       */
  FT_BASE( FT_Memory )
  FT_New_Memory( void );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Done_Memory                                                     */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Discards memory manager.                                           */
  /*                                                                       */
  /* <Input>                                                               */
  /*    memory :: A handle to the memory manager.                          */
  /*                                                                       */
  FT_BASE( void )
  FT_Done_Memory( FT_Memory  memory );

#endif /* !FT_CONFIG_OPTION_NO_DEFAULT_SYSTEM */


  /* Define default raster's interface.  The default raster is located in  */
  /* `src/base/ftraster.c'.                                                */
  /*                                                                       */
  /* Client applications can register new rasters through the              */
  /* FT_Set_Raster() API.                                                  */

#ifndef FT_NO_DEFAULT_RASTER
  FT_EXPORT_VAR( FT_Raster_Funcs )  ft_default_raster;
#endif

  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /****                                                                 ****/
  /****                                                                 ****/
  /****              PIC-Support Macros for ftimage.h                   ****/
  /****                                                                 ****/
  /****                                                                 ****/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* <Macro>                                                               */
  /*    FT_DEFINE_OUTLINE_FUNCS                                            */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Used to initialize an instance of FT_Outline_Funcs struct.         */
  /*    When FT_CONFIG_OPTION_PIC is defined an init funtion will need to  */
  /*    called with a pre-allocated stracture to be filled.                */
  /*    When FT_CONFIG_OPTION_PIC is not defined the struct will be        */
  /*    allocated in the global scope (or the scope where the macro        */
  /*    is used).                                                          */
  /*                                                                       */
#ifndef FT_CONFIG_OPTION_PIC

#define FT_DEFINE_OUTLINE_FUNCS(class_, move_to_, line_to_, conic_to_,       \
                                cubic_to_, shift_, delta_)                   \
  static const FT_Outline_Funcs class_ =                                     \
  {                                                                          \
    move_to_, line_to_, conic_to_, cubic_to_, shift_, delta_                 \
  };

#else /* FT_CONFIG_OPTION_PIC */ 

#define FT_DEFINE_OUTLINE_FUNCS(class_, move_to_, line_to_, conic_to_,       \
                                cubic_to_, shift_, delta_)                   \
  static FT_Error                                                            \
  Init_Class_##class_( FT_Outline_Funcs*  clazz )                            \
  {                                                                          \
    clazz->move_to = move_to_;                                               \
    clazz->line_to = line_to_;                                               \
    clazz->conic_to = conic_to_;                                             \
    clazz->cubic_to = cubic_to_;                                             \
    clazz->shift = shift_;                                                   \
    clazz->delta = delta_;                                                   \
    return FT_Err_Ok;                                                        \
  } 

#endif /* FT_CONFIG_OPTION_PIC */ 

  /*************************************************************************/
  /*                                                                       */
  /* <Macro>                                                               */
  /*    FT_DEFINE_RASTER_FUNCS                                             */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Used to initialize an instance of FT_Raster_Funcs struct.          */
  /*    When FT_CONFIG_OPTION_PIC is defined an init funtion will need to  */
  /*    called with a pre-allocated stracture to be filled.                */
  /*    When FT_CONFIG_OPTION_PIC is not defined the struct will be        */
  /*    allocated in the global scope (or the scope where the macro        */
  /*    is used).                                                          */
  /*                                                                       */
#ifndef FT_CONFIG_OPTION_PIC

#define FT_DEFINE_RASTER_FUNCS(class_, glyph_format_, raster_new_,           \
                               raster_reset_, raster_set_mode_,              \
                               raster_render_, raster_done_)                 \
  const FT_Raster_Funcs class_ =                                      \
  {                                                                          \
    glyph_format_, raster_new_, raster_reset_,                               \
    raster_set_mode_, raster_render_, raster_done_                           \
  };

#else /* FT_CONFIG_OPTION_PIC */ 

#define FT_DEFINE_RASTER_FUNCS(class_, glyph_format_, raster_new_,           \
    raster_reset_, raster_set_mode_, raster_render_, raster_done_)           \
  void                                                                       \
  FT_Init_Class_##class_( FT_Raster_Funcs*  clazz )                          \
  {                                                                          \
    clazz->glyph_format = glyph_format_;                                     \
    clazz->raster_new = raster_new_;                                         \
    clazz->raster_reset = raster_reset_;                                     \
    clazz->raster_set_mode = raster_set_mode_;                               \
    clazz->raster_render = raster_render_;                                   \
    clazz->raster_done = raster_done_;                                       \
  } 

#endif /* FT_CONFIG_OPTION_PIC */ 

  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /****                                                                 ****/
  /****                                                                 ****/
  /****              PIC-Support Macros for ftrender.h                  ****/
  /****                                                                 ****/
  /****                                                                 ****/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/



  /*************************************************************************/
  /*                                                                       */
  /* <Macro>                                                               */
  /*    FT_DEFINE_GLYPH                                                    */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Used to initialize an instance of FT_Glyph_Class struct.           */
  /*    When FT_CONFIG_OPTION_PIC is defined an init funtion will need to  */
  /*    called with a pre-allocated stracture to be filled.                */
  /*    When FT_CONFIG_OPTION_PIC is not defined the struct will be        */
  /*    allocated in the global scope (or the scope where the macro        */
  /*    is used).                                                          */
  /*                                                                       */
#ifndef FT_CONFIG_OPTION_PIC

#define FT_DEFINE_GLYPH(class_, size_, format_, init_, done_, copy_,         \
                        transform_, bbox_, prepare_)                         \
  FT_CALLBACK_TABLE_DEF                                                      \
  const FT_Glyph_Class class_ =                                              \
  {                                                                          \
    size_, format_, init_, done_, copy_, transform_, bbox_, prepare_         \
  };

#else /* FT_CONFIG_OPTION_PIC */ 

#define FT_DEFINE_GLYPH(class_, size_, format_, init_, done_, copy_,         \
                        transform_, bbox_, prepare_)                         \
  void                                                                       \
  FT_Init_Class_##class_( FT_Glyph_Class*  clazz )                           \
  {                                                                          \
    clazz->glyph_size = size_;                                               \
    clazz->glyph_format = format_;                                           \
    clazz->glyph_init = init_;                                               \
    clazz->glyph_done = done_;                                               \
    clazz->glyph_copy = copy_;                                               \
    clazz->glyph_transform = transform_;                                     \
    clazz->glyph_bbox = bbox_;                                               \
    clazz->glyph_prepare = prepare_;                                         \
  } 

#endif /* FT_CONFIG_OPTION_PIC */ 

  /*************************************************************************/
  /*                                                                       */
  /* <Macro>                                                               */
  /*    FT_DECLARE_RENDERER                                                */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Used to create a forward declaration of a                          */
  /*    FT_Renderer_Class stract instance.                                 */
  /*                                                                       */
  /* <Macro>                                                               */
  /*    FT_DEFINE_RENDERER                                                 */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Used to initialize an instance of FT_Renderer_Class struct.        */
  /*                                                                       */
  /*    When FT_CONFIG_OPTION_PIC is defined a Create funtion will need    */
  /*    to called with a pointer where the allocated stracture is returned.*/
  /*    And when it is no longer needed a Destroy function needs           */
  /*    to be called to release that allocation.                           */
  /*    fcinit.c (ft_create_default_module_classes) already contains       */
  /*    a mechanism to call these functions for the default modules        */
  /*    described in ftmodule.h                                            */
  /*                                                                       */
  /*    Notice that the created Create and Destroy functions call          */
  /*    pic_init and pic_free function to allow you to manually allocate   */
  /*    and initialize any additional global data, like module specific    */
  /*    interface, and put them in the global pic container defined in     */
  /*    ftpic.h. if you don't need them just implement the functions as    */
  /*    empty to resolve the link error.                                   */
  /*                                                                       */
  /*    When FT_CONFIG_OPTION_PIC is not defined the struct will be        */
  /*    allocated in the global scope (or the scope where the macro        */
  /*    is used).                                                          */
  /*                                                                       */
#ifndef FT_CONFIG_OPTION_PIC

#define FT_DECLARE_RENDERER(class_)                                          \
    FT_EXPORT_VAR( const FT_Renderer_Class ) class_;

#define FT_DEFINE_RENDERER(class_,                                           \
                           flags_, size_, name_, version_, requires_,        \
                           interface_, init_, done_, get_interface_,         \
                           glyph_format_, render_glyph_, transform_glyph_,   \
                           get_glyph_cbox_, set_mode_, raster_class_ )       \
  FT_CALLBACK_TABLE_DEF                                                      \
  const FT_Renderer_Class  class_ =                                          \
  {                                                                          \
    FT_DEFINE_ROOT_MODULE(flags_,size_,name_,version_,requires_,             \
                          interface_,init_,done_,get_interface_)             \
    glyph_format_,                                                           \
                                                                             \
    render_glyph_,                                                           \
    transform_glyph_,                                                        \
    get_glyph_cbox_,                                                         \
    set_mode_,                                                               \
                                                                             \
    raster_class_                                                            \
  };

#else /* FT_CONFIG_OPTION_PIC */ 

#define FT_DECLARE_RENDERER(class_)  FT_DECLARE_MODULE(class_)

#define FT_DEFINE_RENDERER(class_, \
                           flags_, size_, name_, version_, requires_,        \
                           interface_, init_, done_, get_interface_,         \
                           glyph_format_, render_glyph_, transform_glyph_,   \
                           get_glyph_cbox_, set_mode_, raster_class_ )       \
  void class_##_pic_free( FT_Library library );                              \
  FT_Error class_##_pic_init( FT_Library library );                          \
                                                                             \
  void                                                                       \
  FT_Destroy_Class_##class_( FT_Library        library,                      \
                        FT_Module_Class*  clazz )                            \
  {                                                                          \
    FT_Renderer_Class* rclazz = (FT_Renderer_Class*)clazz;                   \
    FT_Memory         memory = library->memory;                              \
    class_##_pic_free( library );                                            \
    if ( rclazz )                                                            \
      FT_FREE( rclazz );                                                     \
  }                                                                          \
                                                                             \
  FT_Error                                                                   \
  FT_Create_Class_##class_( FT_Library         library,                      \
                            FT_Module_Class**  output_class )                \
  {                                                                          \
    FT_Renderer_Class*  clazz;                                               \
    FT_Error            error;                                               \
    FT_Memory           memory = library->memory;                            \
                                                                             \
    if ( FT_ALLOC( clazz, sizeof(*clazz) ) )                                 \
      return error;                                                          \
                                                                             \
    error = class_##_pic_init( library );                                    \
    if(error)                                                                \
    {                                                                        \
      FT_FREE( clazz );                                                      \
      return error;                                                          \
    }                                                                        \
                                                                             \
    FT_DEFINE_ROOT_MODULE(flags_,size_,name_,version_,requires_,             \
                          interface_,init_,done_,get_interface_)             \
                                                                             \
    clazz->glyph_format       = glyph_format_;                               \
                                                                             \
    clazz->render_glyph       = render_glyph_;                               \
    clazz->transform_glyph    = transform_glyph_;                            \
    clazz->get_glyph_cbox     = get_glyph_cbox_;                             \
    clazz->set_mode           = set_mode_;                                   \
                                                                             \
    clazz->raster_class       = raster_class_;                               \
                                                                             \
    *output_class = (FT_Module_Class*)clazz;                                 \
    return FT_Err_Ok;                                                        \
  } 



#endif /* FT_CONFIG_OPTION_PIC */ 

  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /****                                                                 ****/
  /****                                                                 ****/
  /****              PIC-Support Macros for ftmodapi.h                  ****/
  /****                                                                 ****/
  /****                                                                 ****/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/


#ifdef FT_CONFIG_OPTION_PIC

  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    FT_Module_Creator                                                  */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A function used to create (allocate) a new module class object.    */
  /*    The object's members are initialized, but the module itself is     */
  /*    not.                                                               */
  /*                                                                       */
  /* <Input>                                                               */
  /*    memory       :: A handle to the memory manager.                    */
  /*    output_class :: Initialized with the newly allocated class.        */
  /*                                                                       */
  typedef FT_Error
  (*FT_Module_Creator)( FT_Memory          memory,
                        FT_Module_Class**  output_class );

  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    FT_Module_Destroyer                                                */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A function used to destroy (deallocate) a module class object.     */
  /*                                                                       */
  /* <Input>                                                               */
  /*    memory :: A handle to the memory manager.                          */
  /*    clazz  :: Module class to destroy.                                 */
  /*                                                                       */
  typedef void
  (*FT_Module_Destroyer)( FT_Memory         memory,
                          FT_Module_Class*  clazz );

#endif

  /*************************************************************************/
  /*                                                                       */
  /* <Macro>                                                               */
  /*    FT_DECLARE_MODULE                                                  */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Used to create a forward declaration of a                          */
  /*    FT_Module_Class stract instance.                                   */
  /*                                                                       */
  /* <Macro>                                                               */
  /*    FT_DEFINE_MODULE                                                   */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Used to initialize an instance of FT_Module_Class struct.          */
  /*                                                                       */
  /*    When FT_CONFIG_OPTION_PIC is defined a Create funtion will need    */
  /*    to called with a pointer where the allocated stracture is returned.*/
  /*    And when it is no longer needed a Destroy function needs           */
  /*    to be called to release that allocation.                           */
  /*    fcinit.c (ft_create_default_module_classes) already contains       */
  /*    a mechanism to call these functions for the default modules        */
  /*    described in ftmodule.h                                            */
  /*                                                                       */
  /*    Notice that the created Create and Destroy functions call          */
  /*    pic_init and pic_free function to allow you to manually allocate   */
  /*    and initialize any additional global data, like module specific    */
  /*    interface, and put them in the global pic container defined in     */
  /*    ftpic.h. if you don't need them just implement the functions as    */
  /*    empty to resolve the link error.                                   */
  /*                                                                       */
  /*    When FT_CONFIG_OPTION_PIC is not defined the struct will be        */
  /*    allocated in the global scope (or the scope where the macro        */
  /*    is used).                                                          */
  /*                                                                       */
  /* <Macro>                                                               */
  /*    FT_DEFINE_ROOT_MODULE                                              */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Used to initialize an instance of FT_Module_Class struct inside    */
  /*    another stract that contains it or in a function that initializes  */
  /*    that containing stract                                             */
  /*                                                                       */
#ifndef FT_CONFIG_OPTION_PIC

#define FT_DECLARE_MODULE(class_)                                            \
  FT_CALLBACK_TABLE                                                          \
  const FT_Module_Class  class_;                                             \

#define FT_DEFINE_ROOT_MODULE(flags_, size_, name_, version_, requires_,     \
                              interface_, init_, done_, get_interface_)      \
  {                                                                          \
    flags_,                                                                  \
    size_,                                                                   \
                                                                             \
    name_,                                                                   \
    version_,                                                                \
    requires_,                                                               \
                                                                             \
    interface_,                                                              \
                                                                             \
    init_,                                                                   \
    done_,                                                                   \
    get_interface_,                                                          \
  },

#define FT_DEFINE_MODULE(class_, flags_, size_, name_, version_, requires_,  \
                         interface_, init_, done_, get_interface_)           \
  FT_CALLBACK_TABLE_DEF                                                      \
  const FT_Module_Class class_ =                                             \
  {                                                                          \
    flags_,                                                                  \
    size_,                                                                   \
                                                                             \
    name_,                                                                   \
    version_,                                                                \
    requires_,                                                               \
                                                                             \
    interface_,                                                              \
                                                                             \
    init_,                                                                   \
    done_,                                                                   \
    get_interface_,                                                          \
  };


#else /* FT_CONFIG_OPTION_PIC */

#define FT_DECLARE_MODULE(class_)                                            \
  FT_Error FT_Create_Class_##class_( FT_Library library,                     \
                                     FT_Module_Class** output_class );       \
  void     FT_Destroy_Class_##class_( FT_Library library,                    \
                                      FT_Module_Class*  clazz );

#define FT_DEFINE_ROOT_MODULE(flags_, size_, name_, version_, requires_,     \
                              interface_, init_, done_, get_interface_)      \
    clazz->root.module_flags       = flags_;                                 \
    clazz->root.module_size        = size_;                                  \
    clazz->root.module_name        = name_;                                  \
    clazz->root.module_version     = version_;                               \
    clazz->root.module_requires    = requires_;                              \
                                                                             \
    clazz->root.module_interface   = interface_;                             \
                                                                             \
    clazz->root.module_init        = init_;                                  \
    clazz->root.module_done        = done_;                                  \
    clazz->root.get_interface      = get_interface_;               

#define FT_DEFINE_MODULE(class_, flags_, size_, name_, version_, requires_,  \
                         interface_, init_, done_, get_interface_)           \
  void class_##_pic_free( FT_Library library );                              \
  FT_Error class_##_pic_init( FT_Library library );                          \
                                                                             \
  void                                                                       \
  FT_Destroy_Class_##class_( FT_Library library,                             \
                             FT_Module_Class*  clazz )                       \
  {                                                                          \
    FT_Memory memory = library->memory;                                      \
    class_##_pic_free( library );                                            \
    if ( clazz )                                                             \
      FT_FREE( clazz );                                                      \
  }                                                                          \
                                                                             \
  FT_Error                                                                   \
  FT_Create_Class_##class_( FT_Library library,                              \
                            FT_Module_Class**  output_class )                \
  {                                                                          \
    FT_Memory memory = library->memory;                                      \
    FT_Module_Class*  clazz;                                                 \
    FT_Error          error;                                                 \
                                                                             \
    if ( FT_ALLOC( clazz, sizeof(*clazz) ) )                                 \
      return error;                                                          \
    error = class_##_pic_init( library );                                    \
    if(error)                                                                \
    {                                                                        \
      FT_FREE( clazz );                                                      \
      return error;                                                          \
    }                                                                        \
                                                                             \
    clazz->module_flags       = flags_;                                      \
    clazz->module_size        = size_;                                       \
    clazz->module_name        = name_;                                       \
    clazz->module_version     = version_;                                    \
    clazz->module_requires    = requires_;                                   \
                                                                             \
    clazz->module_interface   = interface_;                                  \
                                                                             \
    clazz->module_init        = init_;                                       \
    clazz->module_done        = done_;                                       \
    clazz->get_interface      = get_interface_;                              \
                                                                             \
    *output_class = clazz;                                                   \
    return FT_Err_Ok;                                                        \
  } 

#endif /* FT_CONFIG_OPTION_PIC */


FT_END_HEADER

#endif /* __FTOBJS_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  ftpic.h                                                                */
/*                                                                         */
/*    The FreeType position independent code services (declaration).       */
/*                                                                         */
/*  Copyright 2009 by                                                      */
/*  Oran Agra and Mickey Gabel.                                            */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/

  /*************************************************************************/
  /*                                                                       */
  /*  Modules that ordinarily have const global data that need address     */
  /*  can instead define pointers here.                                    */
  /*                                                                       */
  /*************************************************************************/


#ifndef __FTPIC_H__
#define __FTPIC_H__

  
FT_BEGIN_HEADER

#ifdef FT_CONFIG_OPTION_PIC

  typedef struct FT_PIC_Container_
  {
    /* pic containers for base */
    void* base;
    /* pic containers for modules */
    void* autofit;   
    void* cff;    
    void* pshinter;    
    void* psnames;    
    void* raster;     
    void* sfnt;     
    void* smooth;     
    void* truetype;     
  } FT_PIC_Container;

  /* Initialize the various function tables, structs, etc. stored in the container. */
  FT_BASE( FT_Error )
  ft_pic_container_init( FT_Library library );


  /* Destroy the contents of the container. */
  FT_BASE( void )
  ft_pic_container_destroy( FT_Library library );

#endif /* FT_CONFIG_OPTION_PIC */

 /* */

FT_END_HEADER

#endif /* __FTPIC_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  ftrfork.h                                                              */
/*                                                                         */
/*    Embedded resource forks accessor (specification).                    */
/*                                                                         */
/*  Copyright 2004, 2006, 2007 by                                          */
/*  Masatake YAMATO and Redhat K.K.                                        */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/

/***************************************************************************/
/* Development of the code in this file is support of                      */
/* Information-technology Promotion Agency, Japan.                         */
/***************************************************************************/


#ifndef __FTRFORK_H__
#define __FTRFORK_H__


#include <ft2build.h>
#include FT_INTERNAL_OBJECTS_H


FT_BEGIN_HEADER


  /* Number of guessing rules supported in `FT_Raccess_Guess'.            */
  /* Don't forget to increment the number if you add a new guessing rule. */
#define FT_RACCESS_N_RULES  9


  /* A structure to describe a reference in a resource by its resource ID */
  /* and internal offset.  The `POST' resource expects to be concatenated */
  /* by the order of resource IDs instead of its appearance in the file.  */

  typedef struct  FT_RFork_Ref_
  {
    FT_UShort  res_id;
    FT_ULong   offset;

  } FT_RFork_Ref;


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Raccess_Guess                                                   */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Guess a file name and offset where the actual resource fork is     */
  /*    stored.  The macro FT_RACCESS_N_RULES holds the number of          */
  /*    guessing rules;  the guessed result for the Nth rule is            */
  /*    represented as a triplet: a new file name (new_names[N]), a file   */
  /*    offset (offsets[N]), and an error code (errors[N]).                */
  /*                                                                       */
  /* <Input>                                                               */
  /*    library ::                                                         */
  /*      A FreeType library instance.                                     */
  /*                                                                       */
  /*    stream ::                                                          */
  /*      A file stream containing the resource fork.                      */
  /*                                                                       */
  /*    base_name ::                                                       */
  /*      The (base) file name of the resource fork used for some          */
  /*      guessing rules.                                                  */
  /*                                                                       */
  /* <Output>                                                              */
  /*    new_names ::                                                       */
  /*      An array of guessed file names in which the resource forks may   */
  /*      exist.  If `new_names[N]' is NULL, the guessed file name is      */
  /*      equal to `base_name'.                                            */
  /*                                                                       */
  /*    offsets ::                                                         */
  /*      An array of guessed file offsets.  `offsets[N]' holds the file   */
  /*      offset of the possible start of the resource fork in file        */
  /*      `new_names[N]'.                                                  */
  /*                                                                       */
  /*    errors ::                                                          */
  /*      An array of FreeType error codes.  `errors[N]' is the error      */
  /*      code of Nth guessing rule function.  If `errors[N]' is not       */
  /*      FT_Err_Ok, `new_names[N]' and `offsets[N]' are meaningless.      */
  /*                                                                       */
  FT_BASE( void )
  FT_Raccess_Guess( FT_Library  library,
                    FT_Stream   stream,
                    char*       base_name,
                    char**      new_names,
                    FT_Long*    offsets,
                    FT_Error*   errors );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Raccess_Get_HeaderInfo                                          */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Get the information from the header of resource fork.  The         */
  /*    information includes the file offset where the resource map        */
  /*    starts, and the file offset where the resource data starts.        */
  /*    `FT_Raccess_Get_DataOffsets' requires these two data.              */
  /*                                                                       */
  /* <Input>                                                               */
  /*    library ::                                                         */
  /*      A FreeType library instance.                                     */
  /*                                                                       */
  /*    stream ::                                                          */
  /*      A file stream containing the resource fork.                      */
  /*                                                                       */
  /*    rfork_offset ::                                                    */
  /*      The file offset where the resource fork starts.                  */
  /*                                                                       */
  /* <Output>                                                              */
  /*    map_offset ::                                                      */
  /*      The file offset where the resource map starts.                   */
  /*                                                                       */
  /*    rdata_pos ::                                                       */
  /*      The file offset where the resource data starts.                  */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  FT_Err_Ok means success.                     */
  /*                                                                       */
  FT_BASE( FT_Error )
  FT_Raccess_Get_HeaderInfo( FT_Library  library,
                             FT_Stream   stream,
                             FT_Long     rfork_offset,
                             FT_Long    *map_offset,
                             FT_Long    *rdata_pos );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Raccess_Get_DataOffsets                                         */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Get the data offsets for a tag in a resource fork.  Offsets are    */
  /*    stored in an array because, in some cases, resources in a resource */
  /*    fork have the same tag.                                            */
  /*                                                                       */
  /* <Input>                                                               */
  /*    library ::                                                         */
  /*      A FreeType library instance.                                     */
  /*                                                                       */
  /*    stream ::                                                          */
  /*      A file stream containing the resource fork.                      */
  /*                                                                       */
  /*    map_offset ::                                                      */
  /*      The file offset where the resource map starts.                   */
  /*                                                                       */
  /*    rdata_pos ::                                                       */
  /*      The file offset where the resource data starts.                  */
  /*                                                                       */
  /*    tag ::                                                             */
  /*      The resource tag.                                                */
  /*                                                                       */
  /* <Output>                                                              */
  /*    offsets ::                                                         */
  /*      The stream offsets for the resource data specified by `tag'.     */
  /*      This array is allocated by the function, so you have to call     */
  /*      @ft_mem_free after use.                                          */
  /*                                                                       */
  /*    count ::                                                           */
  /*      The length of offsets array.                                     */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  FT_Err_Ok means success.                     */
  /*                                                                       */
  /* <Note>                                                                */
  /*    Normally you should use `FT_Raccess_Get_HeaderInfo' to get the     */
  /*    value for `map_offset' and `rdata_pos'.                            */
  /*                                                                       */
  FT_BASE( FT_Error )
  FT_Raccess_Get_DataOffsets( FT_Library  library,
                              FT_Stream   stream,
                              FT_Long     map_offset,
                              FT_Long     rdata_pos,
                              FT_Long     tag,
                              FT_Long   **offsets,
                              FT_Long    *count );


FT_END_HEADER

#endif /* __FTRFORK_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  ftserv.h                                                               */
/*                                                                         */
/*    The FreeType services (specification only).                          */
/*                                                                         */
/*  Copyright 2003, 2004, 2005, 2006, 2007 by                              */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/

  /*************************************************************************/
  /*                                                                       */
  /*  Each module can export one or more `services'.  Each service is      */
  /*  identified by a constant string and modeled by a pointer; the latter */
  /*  generally corresponds to a structure containing function pointers.   */
  /*                                                                       */
  /*  Note that a service's data cannot be a mere function pointer because */
  /*  in C it is possible that function pointers might be implemented      */
  /*  differently than data pointers (e.g. 48 bits instead of 32).         */
  /*                                                                       */
  /*************************************************************************/


#ifndef __FTSERV_H__
#define __FTSERV_H__


FT_BEGIN_HEADER

#if defined( _MSC_VER )      /* Visual C++ (and Intel C++) */

  /* we disable the warning `conditional expression is constant' here */
  /* in order to compile cleanly with the maximum level of warnings   */
#pragma warning( disable : 4127 )

#endif /* _MSC_VER */

  /*
   * @macro:
   *   FT_FACE_FIND_SERVICE
   *
   * @description:
   *   This macro is used to look up a service from a face's driver module.
   *
   * @input:
   *   face ::
   *     The source face handle.
   *
   *   id ::
   *     A string describing the service as defined in the service's
   *     header files (e.g. FT_SERVICE_ID_MULTI_MASTERS which expands to
   *     `multi-masters').  It is automatically prefixed with
   *     `FT_SERVICE_ID_'.
   *
   * @output:
   *   ptr ::
   *     A variable that receives the service pointer.  Will be NULL
   *     if not found.
   */
#ifdef __cplusplus

#define FT_FACE_FIND_SERVICE( face, ptr, id )                               \
  FT_BEGIN_STMNT                                                            \
    FT_Module    module = FT_MODULE( FT_FACE( face )->driver );             \
    FT_Pointer   _tmp_  = NULL;                                             \
    FT_Pointer*  _pptr_ = (FT_Pointer*)&(ptr);                              \
                                                                            \
                                                                            \
    if ( module->clazz->get_interface )                                     \
      _tmp_ = module->clazz->get_interface( module, FT_SERVICE_ID_ ## id ); \
    *_pptr_ = _tmp_;                                                        \
  FT_END_STMNT

#else /* !C++ */

#define FT_FACE_FIND_SERVICE( face, ptr, id )                               \
  FT_BEGIN_STMNT                                                            \
    FT_Module   module = FT_MODULE( FT_FACE( face )->driver );              \
    FT_Pointer  _tmp_  = NULL;                                              \
                                                                            \
    if ( module->clazz->get_interface )                                     \
      _tmp_ = module->clazz->get_interface( module, FT_SERVICE_ID_ ## id ); \
    ptr = _tmp_;                                                            \
  FT_END_STMNT

#endif /* !C++ */

  /*
   * @macro:
   *   FT_FACE_FIND_GLOBAL_SERVICE
   *
   * @description:
   *   This macro is used to look up a service from all modules.
   *
   * @input:
   *   face ::
   *     The source face handle.
   *
   *   id ::
   *     A string describing the service as defined in the service's
   *     header files (e.g. FT_SERVICE_ID_MULTI_MASTERS which expands to
   *     `multi-masters').  It is automatically prefixed with
   *     `FT_SERVICE_ID_'.
   *
   * @output:
   *   ptr ::
   *     A variable that receives the service pointer.  Will be NULL
   *     if not found.
   */
#ifdef __cplusplus

#define FT_FACE_FIND_GLOBAL_SERVICE( face, ptr, id )               \
  FT_BEGIN_STMNT                                                   \
    FT_Module    module = FT_MODULE( FT_FACE( face )->driver );    \
    FT_Pointer   _tmp_;                                            \
    FT_Pointer*  _pptr_ = (FT_Pointer*)&(ptr);                     \
                                                                   \
                                                                   \
    _tmp_ = ft_module_get_service( module, FT_SERVICE_ID_ ## id ); \
    *_pptr_ = _tmp_;                                               \
  FT_END_STMNT

#else /* !C++ */

#define FT_FACE_FIND_GLOBAL_SERVICE( face, ptr, id )               \
  FT_BEGIN_STMNT                                                   \
    FT_Module   module = FT_MODULE( FT_FACE( face )->driver );     \
    FT_Pointer  _tmp_;                                             \
                                                                   \
                                                                   \
    _tmp_ = ft_module_get_service( module, FT_SERVICE_ID_ ## id ); \
    ptr   = _tmp_;                                                 \
  FT_END_STMNT

#endif /* !C++ */


  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****         S E R V I C E   D E S C R I P T O R S                 *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/

  /*
   *  The following structure is used to _describe_ a given service
   *  to the library.  This is useful to build simple static service lists.
   */
  typedef struct  FT_ServiceDescRec_
  {
    const char*  serv_id;     /* service name         */
    const void*  serv_data;   /* service pointer/data */

  } FT_ServiceDescRec;

  typedef const FT_ServiceDescRec*  FT_ServiceDesc;

  /*************************************************************************/
  /*                                                                       */
  /* <Macro>                                                               */
  /*    FT_DEFINE_SERVICEDESCREC1 .. FT_DEFINE_SERVICEDESCREC6             */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Used to initialize an array of FT_ServiceDescRec structs.          */
  /*                                                                       */
  /*    When FT_CONFIG_OPTION_PIC is defined a Create funtion will need    */
  /*    to called with a pointer where the allocated array is returned.    */
  /*    And when it is no longer needed a Destroy function needs           */
  /*    to be called to release that allocation.                           */
  /*                                                                       */
  /*    These functions should be manyally called from the pic_init and    */
  /*    pic_free functions of your module (see FT_DEFINE_MODULE)           */
  /*                                                                       */
  /*    When FT_CONFIG_OPTION_PIC is not defined the array will be         */
  /*    allocated in the global scope (or the scope where the macro        */
  /*    is used).                                                          */
  /*                                                                       */
#ifndef FT_CONFIG_OPTION_PIC

#define FT_DEFINE_SERVICEDESCREC1(class_, serv_id_1, serv_data_1)            \
  static const FT_ServiceDescRec class_[] =                                  \
  {                                                                          \
  {serv_id_1, serv_data_1},                                                  \
  {NULL, NULL}                                                               \
  };
#define FT_DEFINE_SERVICEDESCREC2(class_, serv_id_1, serv_data_1,            \
        serv_id_2, serv_data_2)                                              \
  static const FT_ServiceDescRec class_[] =                                  \
  {                                                                          \
  {serv_id_1, serv_data_1},                                                  \
  {serv_id_2, serv_data_2},                                                  \
  {NULL, NULL}                                                               \
  };
#define FT_DEFINE_SERVICEDESCREC3(class_, serv_id_1, serv_data_1,            \
        serv_id_2, serv_data_2, serv_id_3, serv_data_3)                      \
  static const FT_ServiceDescRec class_[] =                                  \
  {                                                                          \
  {serv_id_1, serv_data_1},                                                  \
  {serv_id_2, serv_data_2},                                                  \
  {serv_id_3, serv_data_3},                                                  \
  {NULL, NULL}                                                               \
  };
#define FT_DEFINE_SERVICEDESCREC4(class_, serv_id_1, serv_data_1,            \
        serv_id_2, serv_data_2, serv_id_3, serv_data_3,                      \
        serv_id_4, serv_data_4)                                              \
  static const FT_ServiceDescRec class_[] =                                  \
  {                                                                          \
  {serv_id_1, serv_data_1},                                                  \
  {serv_id_2, serv_data_2},                                                  \
  {serv_id_3, serv_data_3},                                                  \
  {serv_id_4, serv_data_4},                                                  \
  {NULL, NULL}                                                               \
  };
#define FT_DEFINE_SERVICEDESCREC5(class_, serv_id_1, serv_data_1,            \
        serv_id_2, serv_data_2, serv_id_3, serv_data_3,                      \
        serv_id_4, serv_data_4, serv_id_5, serv_data_5)                      \
  static const FT_ServiceDescRec class_[] =                                  \
  {                                                                          \
  {serv_id_1, serv_data_1},                                                  \
  {serv_id_2, serv_data_2},                                                  \
  {serv_id_3, serv_data_3},                                                  \
  {serv_id_4, serv_data_4},                                                  \
  {serv_id_5, serv_data_5},                                                  \
  {NULL, NULL}                                                               \
  };
#define FT_DEFINE_SERVICEDESCREC6(class_, serv_id_1, serv_data_1,            \
        serv_id_2, serv_data_2, serv_id_3, serv_data_3,                      \
        serv_id_4, serv_data_4, serv_id_5, serv_data_5,                      \
        serv_id_6, serv_data_6)                                              \
  static const FT_ServiceDescRec class_[] =                                  \
  {                                                                          \
  {serv_id_1, serv_data_1},                                                  \
  {serv_id_2, serv_data_2},                                                  \
  {serv_id_3, serv_data_3},                                                  \
  {serv_id_4, serv_data_4},                                                  \
  {serv_id_5, serv_data_5},                                                  \
  {serv_id_6, serv_data_6},                                                  \
  {NULL, NULL}                                                               \
  };

#else /* FT_CONFIG_OPTION_PIC */ 

#define FT_DEFINE_SERVICEDESCREC1(class_, serv_id_1, serv_data_1)            \
  void                                                                       \
  FT_Destroy_Class_##class_( FT_Library library,                             \
                             FT_ServiceDescRec* clazz )                      \
  {                                                                          \
    FT_Memory memory = library->memory;                                      \
    if ( clazz )                                                             \
      FT_FREE( clazz );                                                      \
  }                                                                          \
                                                                             \
  FT_Error                                                                   \
  FT_Create_Class_##class_( FT_Library library,                              \
                            FT_ServiceDescRec** output_class)                \
  {                                                                          \
    FT_ServiceDescRec*  clazz;                                               \
    FT_Error          error;                                                 \
    FT_Memory memory = library->memory;                                      \
                                                                             \
    if ( FT_ALLOC( clazz, sizeof(*clazz)*2 ) )                               \
      return error;                                                          \
    clazz[0].serv_id = serv_id_1;                                            \
    clazz[0].serv_data = serv_data_1;                                        \
    clazz[1].serv_id = NULL;                                                 \
    clazz[1].serv_data = NULL;                                               \
    *output_class = clazz;                                                   \
    return FT_Err_Ok;                                                        \
  } 

#define FT_DEFINE_SERVICEDESCREC2(class_, serv_id_1, serv_data_1,            \
        serv_id_2, serv_data_2)                                              \
  void                                                                       \
  FT_Destroy_Class_##class_( FT_Library library,                             \
                             FT_ServiceDescRec* clazz )                      \
  {                                                                          \
    FT_Memory memory = library->memory;                                      \
    if ( clazz )                                                             \
      FT_FREE( clazz );                                                      \
  }                                                                          \
                                                                             \
  FT_Error                                                                   \
  FT_Create_Class_##class_( FT_Library library,                              \
                            FT_ServiceDescRec** output_class)                \
  {                                                                          \
    FT_ServiceDescRec*  clazz;                                               \
    FT_Error          error;                                                 \
    FT_Memory memory = library->memory;                                      \
                                                                             \
    if ( FT_ALLOC( clazz, sizeof(*clazz)*3 ) )                               \
      return error;                                                          \
    clazz[0].serv_id = serv_id_1;                                            \
    clazz[0].serv_data = serv_data_1;                                        \
    clazz[1].serv_id = serv_id_2;                                            \
    clazz[1].serv_data = serv_data_2;                                        \
    clazz[2].serv_id = NULL;                                                 \
    clazz[2].serv_data = NULL;                                               \
    *output_class = clazz;                                                   \
    return FT_Err_Ok;                                                        \
  } 

#define FT_DEFINE_SERVICEDESCREC3(class_, serv_id_1, serv_data_1,            \
        serv_id_2, serv_data_2, serv_id_3, serv_data_3)                      \
  void                                                                       \
  FT_Destroy_Class_##class_( FT_Library library,                             \
                             FT_ServiceDescRec* clazz )                      \
  {                                                                          \
    FT_Memory memory = library->memory;                                      \
    if ( clazz )                                                             \
      FT_FREE( clazz );                                                      \
  }                                                                          \
                                                                             \
  FT_Error                                                                   \
  FT_Create_Class_##class_( FT_Library library,                              \
                            FT_ServiceDescRec** output_class)                \
  {                                                                          \
    FT_ServiceDescRec*  clazz;                                               \
    FT_Error          error;                                                 \
    FT_Memory memory = library->memory;                                      \
                                                                             \
    if ( FT_ALLOC( clazz, sizeof(*clazz)*4 ) )                               \
      return error;                                                          \
    clazz[0].serv_id = serv_id_1;                                            \
    clazz[0].serv_data = serv_data_1;                                        \
    clazz[1].serv_id = serv_id_2;                                            \
    clazz[1].serv_data = serv_data_2;                                        \
    clazz[2].serv_id = serv_id_3;                                            \
    clazz[2].serv_data = serv_data_3;                                        \
    clazz[3].serv_id = NULL;                                                 \
    clazz[3].serv_data = NULL;                                               \
    *output_class = clazz;                                                   \
    return FT_Err_Ok;                                                        \
  } 

#define FT_DEFINE_SERVICEDESCREC4(class_, serv_id_1, serv_data_1,            \
        serv_id_2, serv_data_2, serv_id_3, serv_data_3,                      \
        serv_id_4, serv_data_4)                                              \
  void                                                                       \
  FT_Destroy_Class_##class_( FT_Library library,                             \
                             FT_ServiceDescRec* clazz )                      \
  {                                                                          \
    FT_Memory memory = library->memory;                                      \
    if ( clazz )                                                             \
      FT_FREE( clazz );                                                      \
  }                                                                          \
                                                                             \
  FT_Error                                                                   \
  FT_Create_Class_##class_( FT_Library library,                              \
                            FT_ServiceDescRec** output_class)                \
  {                                                                          \
    FT_ServiceDescRec*  clazz;                                               \
    FT_Error          error;                                                 \
    FT_Memory memory = library->memory;                                      \
                                                                             \
    if ( FT_ALLOC( clazz, sizeof(*clazz)*5 ) )                               \
      return error;                                                          \
    clazz[0].serv_id = serv_id_1;                                            \
    clazz[0].serv_data = serv_data_1;                                        \
    clazz[1].serv_id = serv_id_2;                                            \
    clazz[1].serv_data = serv_data_2;                                        \
    clazz[2].serv_id = serv_id_3;                                            \
    clazz[2].serv_data = serv_data_3;                                        \
    clazz[3].serv_id = serv_id_4;                                            \
    clazz[3].serv_data = serv_data_4;                                        \
    clazz[4].serv_id = NULL;                                                 \
    clazz[4].serv_data = NULL;                                               \
    *output_class = clazz;                                                   \
    return FT_Err_Ok;                                                        \
  } 

#define FT_DEFINE_SERVICEDESCREC5(class_, serv_id_1, serv_data_1,            \
        serv_id_2, serv_data_2, serv_id_3, serv_data_3, serv_id_4,           \
        serv_data_4, serv_id_5, serv_data_5)                                 \
  void                                                                       \
  FT_Destroy_Class_##class_( FT_Library library,                             \
                             FT_ServiceDescRec* clazz )                      \
  {                                                                          \
    FT_Memory memory = library->memory;                                      \
    if ( clazz )                                                             \
      FT_FREE( clazz );                                                      \
  }                                                                          \
                                                                             \
  FT_Error                                                                   \
  FT_Create_Class_##class_( FT_Library library,                              \
                            FT_ServiceDescRec** output_class)                \
  {                                                                          \
    FT_ServiceDescRec*  clazz;                                               \
    FT_Error          error;                                                 \
    FT_Memory memory = library->memory;                                      \
                                                                             \
    if ( FT_ALLOC( clazz, sizeof(*clazz)*6 ) )                               \
      return error;                                                          \
    clazz[0].serv_id = serv_id_1;                                            \
    clazz[0].serv_data = serv_data_1;                                        \
    clazz[1].serv_id = serv_id_2;                                            \
    clazz[1].serv_data = serv_data_2;                                        \
    clazz[2].serv_id = serv_id_3;                                            \
    clazz[2].serv_data = serv_data_3;                                        \
    clazz[3].serv_id = serv_id_4;                                            \
    clazz[3].serv_data = serv_data_4;                                        \
    clazz[4].serv_id = serv_id_5;                                            \
    clazz[4].serv_data = serv_data_5;                                        \
    clazz[5].serv_id = NULL;                                                 \
    clazz[5].serv_data = NULL;                                               \
    *output_class = clazz;                                                   \
    return FT_Err_Ok;                                                        \
  } 

#define FT_DEFINE_SERVICEDESCREC6(class_, serv_id_1, serv_data_1,            \
        serv_id_2, serv_data_2, serv_id_3, serv_data_3,                      \
        serv_id_4, serv_data_4, serv_id_5, serv_data_5,                      \
        serv_id_6, serv_data_6)                                              \
  void                                                                       \
  FT_Destroy_Class_##class_( FT_Library library,                             \
                             FT_ServiceDescRec* clazz )                      \
  {                                                                          \
    FT_Memory memory = library->memory;                                      \
    if ( clazz )                                                             \
      FT_FREE( clazz );                                                      \
  }                                                                          \
                                                                             \
  FT_Error                                                                   \
  FT_Create_Class_##class_( FT_Library library,                              \
                            FT_ServiceDescRec** output_class)                \
  {                                                                          \
    FT_ServiceDescRec*  clazz;                                               \
    FT_Error          error;                                                 \
    FT_Memory memory = library->memory;                                      \
                                                                             \
    if ( FT_ALLOC( clazz, sizeof(*clazz)*7 ) )                               \
      return error;                                                          \
    clazz[0].serv_id = serv_id_1;                                            \
    clazz[0].serv_data = serv_data_1;                                        \
    clazz[1].serv_id = serv_id_2;                                            \
    clazz[1].serv_data = serv_data_2;                                        \
    clazz[2].serv_id = serv_id_3;                                            \
    clazz[2].serv_data = serv_data_3;                                        \
    clazz[3].serv_id = serv_id_4;                                            \
    clazz[3].serv_data = serv_data_4;                                        \
    clazz[4].serv_id = serv_id_5;                                            \
    clazz[4].serv_data = serv_data_5;                                        \
    clazz[5].serv_id = serv_id_6;                                            \
    clazz[5].serv_data = serv_data_6;                                        \
    clazz[6].serv_id = NULL;                                                 \
    clazz[6].serv_data = NULL;                                               \
    *output_class = clazz;                                                   \
    return FT_Err_Ok;                                                        \
  } 
#endif /* FT_CONFIG_OPTION_PIC */ 

  /*
   *  Parse a list of FT_ServiceDescRec descriptors and look for
   *  a specific service by ID.  Note that the last element in the
   *  array must be { NULL, NULL }, and that the function should
   *  return NULL if the service isn't available.
   *
   *  This function can be used by modules to implement their
   *  `get_service' method.
   */
  FT_BASE( FT_Pointer )
  ft_service_list_lookup( FT_ServiceDesc  service_descriptors,
                          const char*     service_id );


  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****             S E R V I C E S   C A C H E                       *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/

  /*
   *  This structure is used to store a cache for several frequently used
   *  services.  It is the type of `face->internal->services'.  You
   *  should only use FT_FACE_LOOKUP_SERVICE to access it.
   *
   *  All fields should have the type FT_Pointer to relax compilation
   *  dependencies.  We assume the developer isn't completely stupid.
   *
   *  Each field must be named `service_XXXX' where `XXX' corresponds to
   *  the correct FT_SERVICE_ID_XXXX macro.  See the definition of
   *  FT_FACE_LOOKUP_SERVICE below how this is implemented.
   *
   */
  typedef struct  FT_ServiceCacheRec_
  {
    FT_Pointer  service_POSTSCRIPT_FONT_NAME;
    FT_Pointer  service_MULTI_MASTERS;
    FT_Pointer  service_GLYPH_DICT;
    FT_Pointer  service_PFR_METRICS;
    FT_Pointer  service_WINFNT;

  } FT_ServiceCacheRec, *FT_ServiceCache;


  /*
   *  A magic number used within the services cache.
   */
#define FT_SERVICE_UNAVAILABLE  ((FT_Pointer)-2)  /* magic number */


  /*
   * @macro:
   *   FT_FACE_LOOKUP_SERVICE
   *
   * @description:
   *   This macro is used to lookup a service from a face's driver module
   *   using its cache.
   *
   * @input:
   *   face::
   *     The source face handle containing the cache.
   *
   *   field ::
   *     The field name in the cache.
   *
   *   id ::
   *     The service ID.
   *
   * @output:
   *   ptr ::
   *     A variable receiving the service data.  NULL if not available.
   */
#ifdef __cplusplus

#define FT_FACE_LOOKUP_SERVICE( face, ptr, id )                \
  FT_BEGIN_STMNT                                               \
    FT_Pointer   svc;                                          \
    FT_Pointer*  Pptr = (FT_Pointer*)&(ptr);                   \
                                                               \
                                                               \
    svc = FT_FACE( face )->internal->services. service_ ## id; \
    if ( svc == FT_SERVICE_UNAVAILABLE )                       \
      svc = NULL;                                              \
    else if ( svc == NULL )                                    \
    {                                                          \
      FT_FACE_FIND_SERVICE( face, svc, id );                   \
                                                               \
      FT_FACE( face )->internal->services. service_ ## id =    \
        (FT_Pointer)( svc != NULL ? svc                        \
                                  : FT_SERVICE_UNAVAILABLE );  \
    }                                                          \
    *Pptr = svc;                                               \
  FT_END_STMNT

#else /* !C++ */

#define FT_FACE_LOOKUP_SERVICE( face, ptr, id )                \
  FT_BEGIN_STMNT                                               \
    FT_Pointer  svc;                                           \
                                                               \
                                                               \
    svc = FT_FACE( face )->internal->services. service_ ## id; \
    if ( svc == FT_SERVICE_UNAVAILABLE )                       \
      svc = NULL;                                              \
    else if ( svc == NULL )                                    \
    {                                                          \
      FT_FACE_FIND_SERVICE( face, svc, id );                   \
                                                               \
      FT_FACE( face )->internal->services. service_ ## id =    \
        (FT_Pointer)( svc != NULL ? svc                        \
                                  : FT_SERVICE_UNAVAILABLE );  \
    }                                                          \
    ptr = svc;                                                 \
  FT_END_STMNT

#endif /* !C++ */

  /*
   *  A macro used to define new service structure types.
   */

#define FT_DEFINE_SERVICE( name )            \
  typedef struct FT_Service_ ## name ## Rec_ \
    FT_Service_ ## name ## Rec ;             \
  typedef struct FT_Service_ ## name ## Rec_ \
    const * FT_Service_ ## name ;            \
  struct FT_Service_ ## name ## Rec_

  /* */

  /*
   *  The header files containing the services.
   */

#define FT_SERVICE_BDF_H                <freetype/internal/services/svbdf.h>
#define FT_SERVICE_CID_H                <freetype/internal/services/svcid.h>
#define FT_SERVICE_GLYPH_DICT_H         <freetype/internal/services/svgldict.h>
#define FT_SERVICE_GX_VALIDATE_H        <freetype/internal/services/svgxval.h>
#define FT_SERVICE_KERNING_H            <freetype/internal/services/svkern.h>
#define FT_SERVICE_MULTIPLE_MASTERS_H   <freetype/internal/services/svmm.h>
#define FT_SERVICE_OPENTYPE_VALIDATE_H  <freetype/internal/services/svotval.h>
#define FT_SERVICE_PFR_H                <freetype/internal/services/svpfr.h>
#define FT_SERVICE_POSTSCRIPT_CMAPS_H   <freetype/internal/services/svpscmap.h>
#define FT_SERVICE_POSTSCRIPT_INFO_H    <freetype/internal/services/svpsinfo.h>
#define FT_SERVICE_POSTSCRIPT_NAME_H    <freetype/internal/services/svpostnm.h>
#define FT_SERVICE_SFNT_H               <freetype/internal/services/svsfnt.h>
#define FT_SERVICE_TRUETYPE_ENGINE_H    <freetype/internal/services/svtteng.h>
#define FT_SERVICE_TT_CMAP_H            <freetype/internal/services/svttcmap.h>
#define FT_SERVICE_WINFNT_H             <freetype/internal/services/svwinfnt.h>
#define FT_SERVICE_XFREE86_NAME_H       <freetype/internal/services/svxf86nm.h>
#define FT_SERVICE_TRUETYPE_GLYF_H      <freetype/internal/services/svttglyf.h>

 /* */

FT_END_HEADER

#endif /* __FTSERV_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  ftstream.h                                                             */
/*                                                                         */
/*    Stream handling (specification).                                     */
/*                                                                         */
/*  Copyright 1996-2001, 2002, 2004, 2005, 2006 by                         */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __FTSTREAM_H__
#define __FTSTREAM_H__


#include <ft2build.h>
#include FT_SYSTEM_H
#include FT_INTERNAL_OBJECTS_H


FT_BEGIN_HEADER


  /* format of an 8-bit frame_op value:           */
  /*                                              */
  /* bit  76543210                                */
  /*      xxxxxxes                                */
  /*                                              */
  /* s is set to 1 if the value is signed.        */
  /* e is set to 1 if the value is little-endian. */
  /* xxx is a command.                            */

#define FT_FRAME_OP_SHIFT         2
#define FT_FRAME_OP_SIGNED        1
#define FT_FRAME_OP_LITTLE        2
#define FT_FRAME_OP_COMMAND( x )  ( x >> FT_FRAME_OP_SHIFT )

#define FT_MAKE_FRAME_OP( command, little, sign ) \
          ( ( command << FT_FRAME_OP_SHIFT ) | ( little << 1 ) | sign )

#define FT_FRAME_OP_END    0
#define FT_FRAME_OP_START  1  /* start a new frame     */
#define FT_FRAME_OP_BYTE   2  /* read 1-byte value     */
#define FT_FRAME_OP_SHORT  3  /* read 2-byte value     */
#define FT_FRAME_OP_LONG   4  /* read 4-byte value     */
#define FT_FRAME_OP_OFF3   5  /* read 3-byte value     */
#define FT_FRAME_OP_BYTES  6  /* read a bytes sequence */


  typedef enum  FT_Frame_Op_
  {
    ft_frame_end       = 0,
    ft_frame_start     = FT_MAKE_FRAME_OP( FT_FRAME_OP_START, 0, 0 ),

    ft_frame_byte      = FT_MAKE_FRAME_OP( FT_FRAME_OP_BYTE,  0, 0 ),
    ft_frame_schar     = FT_MAKE_FRAME_OP( FT_FRAME_OP_BYTE,  0, 1 ),

    ft_frame_ushort_be = FT_MAKE_FRAME_OP( FT_FRAME_OP_SHORT, 0, 0 ),
    ft_frame_short_be  = FT_MAKE_FRAME_OP( FT_FRAME_OP_SHORT, 0, 1 ),
    ft_frame_ushort_le = FT_MAKE_FRAME_OP( FT_FRAME_OP_SHORT, 1, 0 ),
    ft_frame_short_le  = FT_MAKE_FRAME_OP( FT_FRAME_OP_SHORT, 1, 1 ),

    ft_frame_ulong_be  = FT_MAKE_FRAME_OP( FT_FRAME_OP_LONG, 0, 0 ),
    ft_frame_long_be   = FT_MAKE_FRAME_OP( FT_FRAME_OP_LONG, 0, 1 ),
    ft_frame_ulong_le  = FT_MAKE_FRAME_OP( FT_FRAME_OP_LONG, 1, 0 ),
    ft_frame_long_le   = FT_MAKE_FRAME_OP( FT_FRAME_OP_LONG, 1, 1 ),

    ft_frame_uoff3_be  = FT_MAKE_FRAME_OP( FT_FRAME_OP_OFF3, 0, 0 ),
    ft_frame_off3_be   = FT_MAKE_FRAME_OP( FT_FRAME_OP_OFF3, 0, 1 ),
    ft_frame_uoff3_le  = FT_MAKE_FRAME_OP( FT_FRAME_OP_OFF3, 1, 0 ),
    ft_frame_off3_le   = FT_MAKE_FRAME_OP( FT_FRAME_OP_OFF3, 1, 1 ),

    ft_frame_bytes     = FT_MAKE_FRAME_OP( FT_FRAME_OP_BYTES, 0, 0 ),
    ft_frame_skip      = FT_MAKE_FRAME_OP( FT_FRAME_OP_BYTES, 0, 1 )

  } FT_Frame_Op;


  typedef struct  FT_Frame_Field_
  {
    FT_Byte    value;
    FT_Byte    size;
    FT_UShort  offset;

  } FT_Frame_Field;


  /* Construct an FT_Frame_Field out of a structure type and a field name. */
  /* The structure type must be set in the FT_STRUCTURE macro before       */
  /* calling the FT_FRAME_START() macro.                                   */
  /*                                                                       */
#define FT_FIELD_SIZE( f ) \
          (FT_Byte)sizeof ( ((FT_STRUCTURE*)0)->f )

#define FT_FIELD_SIZE_DELTA( f ) \
          (FT_Byte)sizeof ( ((FT_STRUCTURE*)0)->f[0] )

#define FT_FIELD_OFFSET( f ) \
          (FT_UShort)( offsetof( FT_STRUCTURE, f ) )

#define FT_FRAME_FIELD( frame_op, field ) \
          {                               \
            frame_op,                     \
            FT_FIELD_SIZE( field ),       \
            FT_FIELD_OFFSET( field )      \
          }

#define FT_MAKE_EMPTY_FIELD( frame_op )  { frame_op, 0, 0 }

#define FT_FRAME_START( size )   { ft_frame_start, 0, size }
#define FT_FRAME_END             { ft_frame_end, 0, 0 }

#define FT_FRAME_LONG( f )       FT_FRAME_FIELD( ft_frame_long_be, f )
#define FT_FRAME_ULONG( f )      FT_FRAME_FIELD( ft_frame_ulong_be, f )
#define FT_FRAME_SHORT( f )      FT_FRAME_FIELD( ft_frame_short_be, f )
#define FT_FRAME_USHORT( f )     FT_FRAME_FIELD( ft_frame_ushort_be, f )
#define FT_FRAME_OFF3( f )       FT_FRAME_FIELD( ft_frame_off3_be, f )
#define FT_FRAME_UOFF3( f )      FT_FRAME_FIELD( ft_frame_uoff3_be, f )
#define FT_FRAME_BYTE( f )       FT_FRAME_FIELD( ft_frame_byte, f )
#define FT_FRAME_CHAR( f )       FT_FRAME_FIELD( ft_frame_schar, f )

#define FT_FRAME_LONG_LE( f )    FT_FRAME_FIELD( ft_frame_long_le, f )
#define FT_FRAME_ULONG_LE( f )   FT_FRAME_FIELD( ft_frame_ulong_le, f )
#define FT_FRAME_SHORT_LE( f )   FT_FRAME_FIELD( ft_frame_short_le, f )
#define FT_FRAME_USHORT_LE( f )  FT_FRAME_FIELD( ft_frame_ushort_le, f )
#define FT_FRAME_OFF3_LE( f )    FT_FRAME_FIELD( ft_frame_off3_le, f )
#define FT_FRAME_UOFF3_LE( f )   FT_FRAME_FIELD( ft_frame_uoff3_le, f )

#define FT_FRAME_SKIP_LONG       { ft_frame_long_be, 0, 0 }
#define FT_FRAME_SKIP_SHORT      { ft_frame_short_be, 0, 0 }
#define FT_FRAME_SKIP_BYTE       { ft_frame_byte, 0, 0 }

#define FT_FRAME_BYTES( field, count ) \
          {                            \
            ft_frame_bytes,            \
            count,                     \
            FT_FIELD_OFFSET( field )   \
          }

#define FT_FRAME_SKIP_BYTES( count )  { ft_frame_skip, count, 0 }


  /*************************************************************************/
  /*                                                                       */
  /* Integer extraction macros -- the `buffer' parameter must ALWAYS be of */
  /* type `char*' or equivalent (1-byte elements).                         */
  /*                                                                       */

#define FT_BYTE_( p, i )  ( ((const FT_Byte*)(p))[(i)] )
#define FT_INT8_( p, i )  ( ((const FT_Char*)(p))[(i)] )

#define FT_INT16( x )   ( (FT_Int16)(x)  )
#define FT_UINT16( x )  ( (FT_UInt16)(x) )
#define FT_INT32( x )   ( (FT_Int32)(x)  )
#define FT_UINT32( x )  ( (FT_UInt32)(x) )

#define FT_BYTE_I16( p, i, s )  ( FT_INT16(  FT_BYTE_( p, i ) ) << (s) )
#define FT_BYTE_U16( p, i, s )  ( FT_UINT16( FT_BYTE_( p, i ) ) << (s) )
#define FT_BYTE_I32( p, i, s )  ( FT_INT32(  FT_BYTE_( p, i ) ) << (s) )
#define FT_BYTE_U32( p, i, s )  ( FT_UINT32( FT_BYTE_( p, i ) ) << (s) )

#define FT_INT8_I16( p, i, s )  ( FT_INT16(  FT_INT8_( p, i ) ) << (s) )
#define FT_INT8_U16( p, i, s )  ( FT_UINT16( FT_INT8_( p, i ) ) << (s) )
#define FT_INT8_I32( p, i, s )  ( FT_INT32(  FT_INT8_( p, i ) ) << (s) )
#define FT_INT8_U32( p, i, s )  ( FT_UINT32( FT_INT8_( p, i ) ) << (s) )


#define FT_PEEK_SHORT( p )  FT_INT16( FT_INT8_I16( p, 0, 8) | \
                                      FT_BYTE_I16( p, 1, 0) )

#define FT_PEEK_USHORT( p )  FT_UINT16( FT_BYTE_U16( p, 0, 8 ) | \
                                        FT_BYTE_U16( p, 1, 0 ) )

#define FT_PEEK_LONG( p )  FT_INT32( FT_INT8_I32( p, 0, 24 ) | \
                                     FT_BYTE_I32( p, 1, 16 ) | \
                                     FT_BYTE_I32( p, 2,  8 ) | \
                                     FT_BYTE_I32( p, 3,  0 ) )

#define FT_PEEK_ULONG( p )  FT_UINT32( FT_BYTE_U32( p, 0, 24 ) | \
                                       FT_BYTE_U32( p, 1, 16 ) | \
                                       FT_BYTE_U32( p, 2,  8 ) | \
                                       FT_BYTE_U32( p, 3,  0 ) )

#define FT_PEEK_OFF3( p )  FT_INT32( FT_INT8_I32( p, 0, 16 ) | \
                                     FT_BYTE_I32( p, 1,  8 ) | \
                                     FT_BYTE_I32( p, 2,  0 ) )

#define FT_PEEK_UOFF3( p )  FT_UINT32( FT_BYTE_U32( p, 0, 16 ) | \
                                       FT_BYTE_U32( p, 1,  8 ) | \
                                       FT_BYTE_U32( p, 2,  0 ) )

#define FT_PEEK_SHORT_LE( p )  FT_INT16( FT_INT8_I16( p, 1, 8 ) | \
                                         FT_BYTE_I16( p, 0, 0 ) )

#define FT_PEEK_USHORT_LE( p )  FT_UINT16( FT_BYTE_U16( p, 1, 8 ) |  \
                                           FT_BYTE_U16( p, 0, 0 ) )

#define FT_PEEK_LONG_LE( p )  FT_INT32( FT_INT8_I32( p, 3, 24 ) | \
                                        FT_BYTE_I32( p, 2, 16 ) | \
                                        FT_BYTE_I32( p, 1,  8 ) | \
                                        FT_BYTE_I32( p, 0,  0 ) )

#define FT_PEEK_ULONG_LE( p )  FT_UINT32( FT_BYTE_U32( p, 3, 24 ) | \
                                          FT_BYTE_U32( p, 2, 16 ) | \
                                          FT_BYTE_U32( p, 1,  8 ) | \
                                          FT_BYTE_U32( p, 0,  0 ) )

#define FT_PEEK_OFF3_LE( p )  FT_INT32( FT_INT8_I32( p, 2, 16 ) | \
                                        FT_BYTE_I32( p, 1,  8 ) | \
                                        FT_BYTE_I32( p, 0,  0 ) )

#define FT_PEEK_UOFF3_LE( p )  FT_UINT32( FT_BYTE_U32( p, 2, 16 ) | \
                                          FT_BYTE_U32( p, 1,  8 ) | \
                                          FT_BYTE_U32( p, 0,  0 ) )


#define FT_NEXT_CHAR( buffer )       \
          ( (signed char)*buffer++ )

#define FT_NEXT_BYTE( buffer )         \
          ( (unsigned char)*buffer++ )

#define FT_NEXT_SHORT( buffer )                                   \
          ( (short)( buffer += 2, FT_PEEK_SHORT( buffer - 2 ) ) )

#define FT_NEXT_USHORT( buffer )                                            \
          ( (unsigned short)( buffer += 2, FT_PEEK_USHORT( buffer - 2 ) ) )

#define FT_NEXT_OFF3( buffer )                                  \
          ( (long)( buffer += 3, FT_PEEK_OFF3( buffer - 3 ) ) )

#define FT_NEXT_UOFF3( buffer )                                           \
          ( (unsigned long)( buffer += 3, FT_PEEK_UOFF3( buffer - 3 ) ) )

#define FT_NEXT_LONG( buffer )                                  \
          ( (long)( buffer += 4, FT_PEEK_LONG( buffer - 4 ) ) )

#define FT_NEXT_ULONG( buffer )                                           \
          ( (unsigned long)( buffer += 4, FT_PEEK_ULONG( buffer - 4 ) ) )


#define FT_NEXT_SHORT_LE( buffer )                                   \
          ( (short)( buffer += 2, FT_PEEK_SHORT_LE( buffer - 2 ) ) )

#define FT_NEXT_USHORT_LE( buffer )                                            \
          ( (unsigned short)( buffer += 2, FT_PEEK_USHORT_LE( buffer - 2 ) ) )

#define FT_NEXT_OFF3_LE( buffer )                                  \
          ( (long)( buffer += 3, FT_PEEK_OFF3_LE( buffer - 3 ) ) )

#define FT_NEXT_UOFF3_LE( buffer )                                           \
          ( (unsigned long)( buffer += 3, FT_PEEK_UOFF3_LE( buffer - 3 ) ) )

#define FT_NEXT_LONG_LE( buffer )                                  \
          ( (long)( buffer += 4, FT_PEEK_LONG_LE( buffer - 4 ) ) )

#define FT_NEXT_ULONG_LE( buffer )                                           \
          ( (unsigned long)( buffer += 4, FT_PEEK_ULONG_LE( buffer - 4 ) ) )


  /*************************************************************************/
  /*                                                                       */
  /* Each GET_xxxx() macro uses an implicit `stream' variable.             */
  /*                                                                       */
#if 0
#define FT_GET_MACRO( type )    FT_NEXT_ ## type ( stream->cursor )

#define FT_GET_CHAR()       FT_GET_MACRO( CHAR )
#define FT_GET_BYTE()       FT_GET_MACRO( BYTE )
#define FT_GET_SHORT()      FT_GET_MACRO( SHORT )
#define FT_GET_USHORT()     FT_GET_MACRO( USHORT )
#define FT_GET_OFF3()       FT_GET_MACRO( OFF3 )
#define FT_GET_UOFF3()      FT_GET_MACRO( UOFF3 )
#define FT_GET_LONG()       FT_GET_MACRO( LONG )
#define FT_GET_ULONG()      FT_GET_MACRO( ULONG )
#define FT_GET_TAG4()       FT_GET_MACRO( ULONG )

#define FT_GET_SHORT_LE()   FT_GET_MACRO( SHORT_LE )
#define FT_GET_USHORT_LE()  FT_GET_MACRO( USHORT_LE )
#define FT_GET_LONG_LE()    FT_GET_MACRO( LONG_LE )
#define FT_GET_ULONG_LE()   FT_GET_MACRO( ULONG_LE )

#else
#define FT_GET_MACRO( func, type )        ( (type)func( stream ) )

#define FT_GET_CHAR()       FT_GET_MACRO( FT_Stream_GetChar, FT_Char )
#define FT_GET_BYTE()       FT_GET_MACRO( FT_Stream_GetChar, FT_Byte )
#define FT_GET_SHORT()      FT_GET_MACRO( FT_Stream_GetShort, FT_Short )
#define FT_GET_USHORT()     FT_GET_MACRO( FT_Stream_GetShort, FT_UShort )
#define FT_GET_OFF3()       FT_GET_MACRO( FT_Stream_GetOffset, FT_Long )
#define FT_GET_UOFF3()      FT_GET_MACRO( FT_Stream_GetOffset, FT_ULong )
#define FT_GET_LONG()       FT_GET_MACRO( FT_Stream_GetLong, FT_Long )
#define FT_GET_ULONG()      FT_GET_MACRO( FT_Stream_GetLong, FT_ULong )
#define FT_GET_TAG4()       FT_GET_MACRO( FT_Stream_GetLong, FT_ULong )

#define FT_GET_SHORT_LE()   FT_GET_MACRO( FT_Stream_GetShortLE, FT_Short )
#define FT_GET_USHORT_LE()  FT_GET_MACRO( FT_Stream_GetShortLE, FT_UShort )
#define FT_GET_LONG_LE()    FT_GET_MACRO( FT_Stream_GetLongLE, FT_Long )
#define FT_GET_ULONG_LE()   FT_GET_MACRO( FT_Stream_GetLongLE, FT_ULong )
#endif

#define FT_READ_MACRO( func, type, var )        \
          ( var = (type)func( stream, &error ), \
            error != FT_Err_Ok )

#define FT_READ_BYTE( var )       FT_READ_MACRO( FT_Stream_ReadChar, FT_Byte, var )
#define FT_READ_CHAR( var )       FT_READ_MACRO( FT_Stream_ReadChar, FT_Char, var )
#define FT_READ_SHORT( var )      FT_READ_MACRO( FT_Stream_ReadShort, FT_Short, var )
#define FT_READ_USHORT( var )     FT_READ_MACRO( FT_Stream_ReadShort, FT_UShort, var )
#define FT_READ_OFF3( var )       FT_READ_MACRO( FT_Stream_ReadOffset, FT_Long, var )
#define FT_READ_UOFF3( var )      FT_READ_MACRO( FT_Stream_ReadOffset, FT_ULong, var )
#define FT_READ_LONG( var )       FT_READ_MACRO( FT_Stream_ReadLong, FT_Long, var )
#define FT_READ_ULONG( var )      FT_READ_MACRO( FT_Stream_ReadLong, FT_ULong, var )

#define FT_READ_SHORT_LE( var )   FT_READ_MACRO( FT_Stream_ReadShortLE, FT_Short, var )
#define FT_READ_USHORT_LE( var )  FT_READ_MACRO( FT_Stream_ReadShortLE, FT_UShort, var )
#define FT_READ_LONG_LE( var )    FT_READ_MACRO( FT_Stream_ReadLongLE, FT_Long, var )
#define FT_READ_ULONG_LE( var )   FT_READ_MACRO( FT_Stream_ReadLongLE, FT_ULong, var )


#ifndef FT_CONFIG_OPTION_NO_DEFAULT_SYSTEM

  /* initialize a stream for reading a regular system stream */
  FT_BASE( FT_Error )
  FT_Stream_Open( FT_Stream    stream,
                  const char*  filepathname );

#endif /* FT_CONFIG_OPTION_NO_DEFAULT_SYSTEM */


  /* create a new (input) stream from an FT_Open_Args structure */
  FT_BASE( FT_Error )
  FT_Stream_New( FT_Library           library,
                 const FT_Open_Args*  args,
                 FT_Stream           *astream );

  /* free a stream */
  FT_BASE( void )
  FT_Stream_Free( FT_Stream  stream,
                  FT_Int     external );

  /* initialize a stream for reading in-memory data */
  FT_BASE( void )
  FT_Stream_OpenMemory( FT_Stream       stream,
                        const FT_Byte*  base,
                        FT_ULong        size );

  /* close a stream (does not destroy the stream structure) */
  FT_BASE( void )
  FT_Stream_Close( FT_Stream  stream );


  /* seek within a stream. position is relative to start of stream */
  FT_BASE( FT_Error )
  FT_Stream_Seek( FT_Stream  stream,
                  FT_ULong   pos );

  /* skip bytes in a stream */
  FT_BASE( FT_Error )
  FT_Stream_Skip( FT_Stream  stream,
                  FT_Long    distance );

  /* return current stream position */
  FT_BASE( FT_Long )
  FT_Stream_Pos( FT_Stream  stream );

  /* read bytes from a stream into a user-allocated buffer, returns an */
  /* error if not all bytes could be read.                             */
  FT_BASE( FT_Error )
  FT_Stream_Read( FT_Stream  stream,
                  FT_Byte*   buffer,
                  FT_ULong   count );

  /* read bytes from a stream at a given position */
  FT_BASE( FT_Error )
  FT_Stream_ReadAt( FT_Stream  stream,
                    FT_ULong   pos,
                    FT_Byte*   buffer,
                    FT_ULong   count );

  /* try to read bytes at the end of a stream; return number of bytes */
  /* really available                                                 */
  FT_BASE( FT_ULong )
  FT_Stream_TryRead( FT_Stream  stream,
                     FT_Byte*   buffer,
                     FT_ULong   count );

  /* Enter a frame of `count' consecutive bytes in a stream.  Returns an */
  /* error if the frame could not be read/accessed.  The caller can use  */
  /* the FT_Stream_Get_XXX functions to retrieve frame data without      */
  /* error checks.                                                       */
  /*                                                                     */
  /* You must _always_ call FT_Stream_ExitFrame() once you have entered  */
  /* a stream frame!                                                     */
  /*                                                                     */
  FT_BASE( FT_Error )
  FT_Stream_EnterFrame( FT_Stream  stream,
                        FT_ULong   count );

  /* exit a stream frame */
  FT_BASE( void )
  FT_Stream_ExitFrame( FT_Stream  stream );

  /* Extract a stream frame.  If the stream is disk-based, a heap block */
  /* is allocated and the frame bytes are read into it.  If the stream  */
  /* is memory-based, this function simply set a pointer to the data.   */
  /*                                                                    */
  /* Useful to optimize access to memory-based streams transparently.   */
  /*                                                                    */
  /* All extracted frames must be `freed' with a call to the function   */
  /* FT_Stream_ReleaseFrame().                                          */
  /*                                                                    */
  FT_BASE( FT_Error )
  FT_Stream_ExtractFrame( FT_Stream  stream,
                          FT_ULong   count,
                          FT_Byte**  pbytes );

  /* release an extract frame (see FT_Stream_ExtractFrame) */
  FT_BASE( void )
  FT_Stream_ReleaseFrame( FT_Stream  stream,
                          FT_Byte**  pbytes );

  /* read a byte from an entered frame */
  FT_BASE( FT_Char )
  FT_Stream_GetChar( FT_Stream  stream );

  /* read a 16-bit big-endian integer from an entered frame */
  FT_BASE( FT_Short )
  FT_Stream_GetShort( FT_Stream  stream );

  /* read a 24-bit big-endian integer from an entered frame */
  FT_BASE( FT_Long )
  FT_Stream_GetOffset( FT_Stream  stream );

  /* read a 32-bit big-endian integer from an entered frame */
  FT_BASE( FT_Long )
  FT_Stream_GetLong( FT_Stream  stream );

  /* read a 16-bit little-endian integer from an entered frame */
  FT_BASE( FT_Short )
  FT_Stream_GetShortLE( FT_Stream  stream );

  /* read a 32-bit little-endian integer from an entered frame */
  FT_BASE( FT_Long )
  FT_Stream_GetLongLE( FT_Stream  stream );


  /* read a byte from a stream */
  FT_BASE( FT_Char )
  FT_Stream_ReadChar( FT_Stream  stream,
                      FT_Error*  error );

  /* read a 16-bit big-endian integer from a stream */
  FT_BASE( FT_Short )
  FT_Stream_ReadShort( FT_Stream  stream,
                       FT_Error*  error );

  /* read a 24-bit big-endian integer from a stream */
  FT_BASE( FT_Long )
  FT_Stream_ReadOffset( FT_Stream  stream,
                        FT_Error*  error );

  /* read a 32-bit big-endian integer from a stream */
  FT_BASE( FT_Long )
  FT_Stream_ReadLong( FT_Stream  stream,
                      FT_Error*  error );

  /* read a 16-bit little-endian integer from a stream */
  FT_BASE( FT_Short )
  FT_Stream_ReadShortLE( FT_Stream  stream,
                         FT_Error*  error );

  /* read a 32-bit little-endian integer from a stream */
  FT_BASE( FT_Long )
  FT_Stream_ReadLongLE( FT_Stream  stream,
                        FT_Error*  error );

  /* Read a structure from a stream.  The structure must be described */
  /* by an array of FT_Frame_Field records.                           */
  FT_BASE( FT_Error )
  FT_Stream_ReadFields( FT_Stream              stream,
                        const FT_Frame_Field*  fields,
                        void*                  structure );


#define FT_STREAM_POS()           \
          FT_Stream_Pos( stream )

#define FT_STREAM_SEEK( position )                           \
          FT_SET_ERROR( FT_Stream_Seek( stream, position ) )

#define FT_STREAM_SKIP( distance )                           \
          FT_SET_ERROR( FT_Stream_Skip( stream, distance ) )

#define FT_STREAM_READ( buffer, count )                   \
          FT_SET_ERROR( FT_Stream_Read( stream,           \
                                        (FT_Byte*)buffer, \
                                        count ) )

#define FT_STREAM_READ_AT( position, buffer, count )         \
          FT_SET_ERROR( FT_Stream_ReadAt( stream,            \
                                           position,         \
                                           (FT_Byte*)buffer, \
                                           count ) )

#define FT_STREAM_READ_FIELDS( fields, object )                          \
          FT_SET_ERROR( FT_Stream_ReadFields( stream, fields, object ) )


#define FT_FRAME_ENTER( size )                                       \
          FT_SET_ERROR(                                              \
            FT_DEBUG_INNER( FT_Stream_EnterFrame( stream, size ) ) )

#define FT_FRAME_EXIT()                 \
          FT_DEBUG_INNER( FT_Stream_ExitFrame( stream ) )

#define FT_FRAME_EXTRACT( size, bytes )                                       \
          FT_SET_ERROR(                                                       \
            FT_DEBUG_INNER( FT_Stream_ExtractFrame( stream, size,             \
                                                    (FT_Byte**)&(bytes) ) ) )

#define FT_FRAME_RELEASE( bytes )                                         \
          FT_DEBUG_INNER( FT_Stream_ReleaseFrame( stream,                 \
                                                  (FT_Byte**)&(bytes) ) )


FT_END_HEADER

#endif /* __FTSTREAM_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  fttrace.h                                                              */
/*                                                                         */
/*    Tracing handling (specification only).                               */
/*                                                                         */
/*  Copyright 2002, 2004, 2005, 2006, 2007 by                              */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


  /* definitions of trace levels for FreeType 2 */

  /* the first level must always be `trace_any' */
FT_TRACE_DEF( any )

  /* base components */
FT_TRACE_DEF( calc )      /* calculations            (ftcalc.c)   */
FT_TRACE_DEF( memory )    /* memory manager          (ftobjs.c)   */
FT_TRACE_DEF( stream )    /* stream manager          (ftstream.c) */
FT_TRACE_DEF( io )        /* i/o interface           (ftsystem.c) */
FT_TRACE_DEF( list )      /* list management         (ftlist.c)   */
FT_TRACE_DEF( init )      /* initialization          (ftinit.c)   */
FT_TRACE_DEF( objs )      /* base objects            (ftobjs.c)   */
FT_TRACE_DEF( outline )   /* outline management      (ftoutln.c)  */
FT_TRACE_DEF( glyph )     /* glyph management        (ftglyph.c)  */
FT_TRACE_DEF( gloader )   /* glyph loader            (ftgloadr.c) */

FT_TRACE_DEF( raster )    /* monochrome rasterizer   (ftraster.c) */
FT_TRACE_DEF( smooth )    /* anti-aliasing raster    (ftgrays.c)  */
FT_TRACE_DEF( mm )        /* MM interface            (ftmm.c)     */
FT_TRACE_DEF( raccess )   /* resource fork accessor  (ftrfork.c)  */
FT_TRACE_DEF( synth )     /* bold/slant synthesizer  (ftsynth.c)  */

  /* Cache sub-system */
FT_TRACE_DEF( cache )     /* cache sub-system        (ftcache.c, etc.) */

  /* SFNT driver components */
FT_TRACE_DEF( sfdriver )  /* SFNT font driver        (sfdriver.c) */
FT_TRACE_DEF( sfobjs )    /* SFNT object handler     (sfobjs.c)   */
FT_TRACE_DEF( ttcmap )    /* charmap handler         (ttcmap.c)   */
FT_TRACE_DEF( ttkern )    /* kerning handler         (ttkern.c)   */
FT_TRACE_DEF( ttload )    /* basic TrueType tables   (ttload.c)   */
FT_TRACE_DEF( ttmtx )     /* metrics-related tables  (ttmtx.c)    */
FT_TRACE_DEF( ttpost )    /* PS table processing     (ttpost.c)   */
FT_TRACE_DEF( ttsbit )    /* TrueType sbit handling  (ttsbit.c)   */
FT_TRACE_DEF( ttbdf )     /* TrueType embedded BDF   (ttbdf.c)    */

  /* TrueType driver components */
FT_TRACE_DEF( ttdriver )  /* TT font driver          (ttdriver.c) */
FT_TRACE_DEF( ttgload )   /* TT glyph loader         (ttgload.c)  */
FT_TRACE_DEF( ttinterp )  /* bytecode interpreter    (ttinterp.c) */
FT_TRACE_DEF( ttobjs )    /* TT objects manager      (ttobjs.c)   */
FT_TRACE_DEF( ttpload )   /* TT data/program loader  (ttpload.c)  */
FT_TRACE_DEF( ttgxvar )   /* TrueType GX var handler (ttgxvar.c)  */

  /* Type 1 driver components */
FT_TRACE_DEF( t1afm )
FT_TRACE_DEF( t1driver )
FT_TRACE_DEF( t1gload )
FT_TRACE_DEF( t1hint )
FT_TRACE_DEF( t1load )
FT_TRACE_DEF( t1objs )
FT_TRACE_DEF( t1parse )

  /* PostScript helper module `psaux' */
FT_TRACE_DEF( t1decode )
FT_TRACE_DEF( psobjs )

  /* PostScript hinting module `pshinter' */
FT_TRACE_DEF( pshrec )
FT_TRACE_DEF( pshalgo1 )
FT_TRACE_DEF( pshalgo2 )

  /* Type 2 driver components */
FT_TRACE_DEF( cffdriver )
FT_TRACE_DEF( cffgload )
FT_TRACE_DEF( cffload )
FT_TRACE_DEF( cffobjs )
FT_TRACE_DEF( cffparse )

  /* Type 42 driver component */
FT_TRACE_DEF( t42 )

  /* CID driver components */
FT_TRACE_DEF( cidafm )
FT_TRACE_DEF( ciddriver )
FT_TRACE_DEF( cidgload )
FT_TRACE_DEF( cidload )
FT_TRACE_DEF( cidobjs )
FT_TRACE_DEF( cidparse )

  /* Windows font component */
FT_TRACE_DEF( winfnt )

  /* PCF font components */
FT_TRACE_DEF( pcfdriver )
FT_TRACE_DEF( pcfread )

  /* BDF font components */
FT_TRACE_DEF( bdfdriver )
FT_TRACE_DEF( bdflib )

  /* PFR font component */
FT_TRACE_DEF( pfr )

  /* OpenType validation components */
FT_TRACE_DEF( otvmodule )
FT_TRACE_DEF( otvcommon )
FT_TRACE_DEF( otvbase )
FT_TRACE_DEF( otvgdef )
FT_TRACE_DEF( otvgpos )
FT_TRACE_DEF( otvgsub )
FT_TRACE_DEF( otvjstf )
FT_TRACE_DEF( otvmath )

  /* TrueTypeGX/AAT validation components */
FT_TRACE_DEF( gxvmodule )
FT_TRACE_DEF( gxvcommon )
FT_TRACE_DEF( gxvfeat )
FT_TRACE_DEF( gxvmort )
FT_TRACE_DEF( gxvmorx )
FT_TRACE_DEF( gxvbsln )
FT_TRACE_DEF( gxvjust )
FT_TRACE_DEF( gxvkern )
FT_TRACE_DEF( gxvopbd )
FT_TRACE_DEF( gxvtrak )
FT_TRACE_DEF( gxvprop )
FT_TRACE_DEF( gxvlcar )


/* END */
/***************************************************************************/
/*                                                                         */
/*  ftvalid.h                                                              */
/*                                                                         */
/*    FreeType validation support (specification).                         */
/*                                                                         */
/*  Copyright 2004 by                                                      */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __FTVALID_H__
#define __FTVALID_H__

#include <ft2build.h>
#include FT_CONFIG_STANDARD_LIBRARY_H   /* for ft_setjmp and ft_longjmp */


FT_BEGIN_HEADER


  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /****                                                                 ****/
  /****                                                                 ****/
  /****                    V A L I D A T I O N                          ****/
  /****                                                                 ****/
  /****                                                                 ****/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/

  /* handle to a validation object */
  typedef struct FT_ValidatorRec_ volatile*  FT_Validator;


  /*************************************************************************/
  /*                                                                       */
  /* There are three distinct validation levels defined here:              */
  /*                                                                       */
  /* FT_VALIDATE_DEFAULT ::                                                */
  /*   A table that passes this validation level can be used reliably by   */
  /*   FreeType.  It generally means that all offsets have been checked to */
  /*   prevent out-of-bound reads, that array counts are correct, etc.     */
  /*                                                                       */
  /* FT_VALIDATE_TIGHT ::                                                  */
  /*   A table that passes this validation level can be used reliably and  */
  /*   doesn't contain invalid data.  For example, a charmap table that    */
  /*   returns invalid glyph indices will not pass, even though it can     */
  /*   be used with FreeType in default mode (the library will simply      */
  /*   return an error later when trying to load the glyph).               */
  /*                                                                       */
  /*   It also checks that fields which must be a multiple of 2, 4, or 8,  */
  /*   don't have incorrect values, etc.                                   */
  /*                                                                       */
  /* FT_VALIDATE_PARANOID ::                                               */
  /*   Only for font debugging.  Checks that a table follows the           */
  /*   specification by 100%.  Very few fonts will be able to pass this    */
  /*   level anyway but it can be useful for certain tools like font       */
  /*   editors/converters.                                                 */
  /*                                                                       */
  typedef enum  FT_ValidationLevel_
  {
    FT_VALIDATE_DEFAULT = 0,
    FT_VALIDATE_TIGHT,
    FT_VALIDATE_PARANOID

  } FT_ValidationLevel;


  /* validator structure */
  typedef struct  FT_ValidatorRec_
  {
    const FT_Byte*      base;        /* address of table in memory       */
    const FT_Byte*      limit;       /* `base' + sizeof(table) in memory */
    FT_ValidationLevel  level;       /* validation level                 */
    FT_Error            error;       /* error returned. 0 means success  */

    ft_jmp_buf          jump_buffer; /* used for exception handling      */

  } FT_ValidatorRec;


#define FT_VALIDATOR( x )  ((FT_Validator)( x ))


  FT_BASE( void )
  ft_validator_init( FT_Validator        valid,
                     const FT_Byte*      base,
                     const FT_Byte*      limit,
                     FT_ValidationLevel  level );

  /* Do not use this. It's broken and will cause your validator to crash */
  /* if you run it on an invalid font.                                   */
  FT_BASE( FT_Int )
  ft_validator_run( FT_Validator  valid );

  /* Sets the error field in a validator, then calls `longjmp' to return */
  /* to high-level caller.  Using `setjmp/longjmp' avoids many stupid    */
  /* error checks within the validation routines.                        */
  /*                                                                     */
  FT_BASE( void )
  ft_validator_error( FT_Validator  valid,
                      FT_Error      error );


  /* Calls ft_validate_error.  Assumes that the `valid' local variable */
  /* holds a pointer to the current validator object.                  */
  /*                                                                   */
  /* Use preprocessor prescan to pass FT_ERR_PREFIX.                   */
  /*                                                                   */
#define FT_INVALID( _prefix, _error )  FT_INVALID_( _prefix, _error )
#define FT_INVALID_( _prefix, _error ) \
          ft_validator_error( valid, _prefix ## _error )

  /* called when a broken table is detected */
#define FT_INVALID_TOO_SHORT \
          FT_INVALID( FT_ERR_PREFIX, Invalid_Table )

  /* called when an invalid offset is detected */
#define FT_INVALID_OFFSET \
          FT_INVALID( FT_ERR_PREFIX, Invalid_Offset )

  /* called when an invalid format/value is detected */
#define FT_INVALID_FORMAT \
          FT_INVALID( FT_ERR_PREFIX, Invalid_Table )

  /* called when an invalid glyph index is detected */
#define FT_INVALID_GLYPH_ID \
          FT_INVALID( FT_ERR_PREFIX, Invalid_Glyph_Index )

  /* called when an invalid field value is detected */
#define FT_INVALID_DATA \
          FT_INVALID( FT_ERR_PREFIX, Invalid_Table )


FT_END_HEADER

#endif /* __FTVALID_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  internal.h                                                             */
/*                                                                         */
/*    Internal header files (specification only).                          */
/*                                                                         */
/*  Copyright 1996-2001, 2002, 2003, 2004 by                               */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* This file is automatically included by `ft2build.h'.                  */
  /* Do not include it manually!                                           */
  /*                                                                       */
  /*************************************************************************/


#define FT_INTERNAL_OBJECTS_H             <freetype/internal/ftobjs.h>
#define FT_INTERNAL_PIC_H                 <freetype/internal/ftpic.h>
#define FT_INTERNAL_STREAM_H              <freetype/internal/ftstream.h>
#define FT_INTERNAL_MEMORY_H              <freetype/internal/ftmemory.h>
#define FT_INTERNAL_DEBUG_H               <freetype/internal/ftdebug.h>
#define FT_INTERNAL_CALC_H                <freetype/internal/ftcalc.h>
#define FT_INTERNAL_DRIVER_H              <freetype/internal/ftdriver.h>
#define FT_INTERNAL_TRACE_H               <freetype/internal/fttrace.h>
#define FT_INTERNAL_GLYPH_LOADER_H        <freetype/internal/ftgloadr.h>
#define FT_INTERNAL_SFNT_H                <freetype/internal/sfnt.h>
#define FT_INTERNAL_SERVICE_H             <freetype/internal/ftserv.h>
#define FT_INTERNAL_RFORK_H               <freetype/internal/ftrfork.h>
#define FT_INTERNAL_VALIDATE_H            <freetype/internal/ftvalid.h>

#define FT_INTERNAL_TRUETYPE_TYPES_H      <freetype/internal/tttypes.h>
#define FT_INTERNAL_TYPE1_TYPES_H         <freetype/internal/t1types.h>

#define FT_INTERNAL_POSTSCRIPT_AUX_H      <freetype/internal/psaux.h>
#define FT_INTERNAL_POSTSCRIPT_HINTS_H    <freetype/internal/pshints.h>
#define FT_INTERNAL_POSTSCRIPT_GLOBALS_H  <freetype/internal/psglobal.h>

#define FT_INTERNAL_AUTOHINT_H            <freetype/internal/autohint.h>


/* END */
/*  pcftypes.h

  FreeType font driver for pcf fonts

  Copyright (C) 2000, 2001, 2002 by
  Francesco Zappa Nardelli

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 THE
AUTHORS OR COPYRIGHT HOLDERS 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 __PCFTYPES_H__
#define __PCFTYPES_H__


#include <ft2build.h>
#include FT_FREETYPE_H


FT_BEGIN_HEADER


  typedef struct  PCF_Public_FaceRec_
  {
    FT_FaceRec    root;
    FT_StreamRec  gzip_stream;
    FT_Stream     gzip_source;

    char*         charset_encoding;
    char*         charset_registry;

  } PCF_Public_FaceRec, *PCF_Public_Face;


FT_END_HEADER

#endif  /* __PCFTYPES_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  psaux.h                                                                */
/*                                                                         */
/*    Auxiliary functions and data structures related to PostScript fonts  */
/*    (specification).                                                     */
/*                                                                         */
/*  Copyright 1996-2001, 2002, 2003, 2004, 2006, 2008, 2009 by             */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __PSAUX_H__
#define __PSAUX_H__


#include <ft2build.h>
#include FT_INTERNAL_OBJECTS_H
#include FT_INTERNAL_TYPE1_TYPES_H
#include FT_SERVICE_POSTSCRIPT_CMAPS_H


FT_BEGIN_HEADER


  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****                             T1_TABLE                          *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/


  typedef struct PS_TableRec_*              PS_Table;
  typedef const struct PS_Table_FuncsRec_*  PS_Table_Funcs;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    PS_Table_FuncsRec                                                  */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A set of function pointers to manage PS_Table objects.             */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    table_init    :: Used to initialize a table.                       */
  /*                                                                       */
  /*    table_done    :: Finalizes resp. destroy a given table.            */
  /*                                                                       */
  /*    table_add     :: Adds a new object to a table.                     */
  /*                                                                       */
  /*    table_release :: Releases table data, then finalizes it.           */
  /*                                                                       */
  typedef struct  PS_Table_FuncsRec_
  {
    FT_Error
    (*init)( PS_Table   table,
             FT_Int     count,
             FT_Memory  memory );

    void
    (*done)( PS_Table  table );

    FT_Error
    (*add)( PS_Table    table,
            FT_Int      idx,
            void*       object,
            FT_PtrDist  length );

    void
    (*release)( PS_Table  table );

  } PS_Table_FuncsRec;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    PS_TableRec                                                        */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A PS_Table is a simple object used to store an array of objects in */
  /*    a single memory block.                                             */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    block     :: The address in memory of the growheap's block.  This  */
  /*                 can change between two object adds, due to            */
  /*                 reallocation.                                         */
  /*                                                                       */
  /*    cursor    :: The current top of the grow heap within its block.    */
  /*                                                                       */
  /*    capacity  :: The current size of the heap block.  Increments by    */
  /*                 1kByte chunks.                                        */
  /*                                                                       */
  /*    max_elems :: The maximum number of elements in table.              */
  /*                                                                       */
  /*    num_elems :: The current number of elements in table.              */
  /*                                                                       */
  /*    elements  :: A table of element addresses within the block.        */
  /*                                                                       */
  /*    lengths   :: A table of element sizes within the block.            */
  /*                                                                       */
  /*    memory    :: The object used for memory operations                 */
  /*                 (alloc/realloc).                                      */
  /*                                                                       */
  /*    funcs     :: A table of method pointers for this object.           */
  /*                                                                       */
  typedef struct  PS_TableRec_
  {
    FT_Byte*           block;          /* current memory block           */
    FT_Offset          cursor;         /* current cursor in memory block */
    FT_Offset          capacity;       /* current size of memory block   */
    FT_Long            init;

    FT_Int             max_elems;
    FT_Int             num_elems;
    FT_Byte**          elements;       /* addresses of table elements */
    FT_PtrDist*        lengths;        /* lengths of table elements   */

    FT_Memory          memory;
    PS_Table_FuncsRec  funcs;

  } PS_TableRec;


  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****                       T1 FIELDS & TOKENS                      *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/

  typedef struct PS_ParserRec_*  PS_Parser;

  typedef struct T1_TokenRec_*   T1_Token;

  typedef struct T1_FieldRec_*   T1_Field;


  /* simple enumeration type used to identify token types */
  typedef enum  T1_TokenType_
  {
    T1_TOKEN_TYPE_NONE = 0,
    T1_TOKEN_TYPE_ANY,
    T1_TOKEN_TYPE_STRING,
    T1_TOKEN_TYPE_ARRAY,
    T1_TOKEN_TYPE_KEY, /* aka `name' */

    /* do not remove */
    T1_TOKEN_TYPE_MAX

  } T1_TokenType;


  /* a simple structure used to identify tokens */
  typedef struct  T1_TokenRec_
  {
    FT_Byte*      start;   /* first character of token in input stream */
    FT_Byte*      limit;   /* first character after the token          */
    T1_TokenType  type;    /* type of token                            */

  } T1_TokenRec;


  /* enumeration type used to identify object fields */
  typedef enum  T1_FieldType_
  {
    T1_FIELD_TYPE_NONE = 0,
    T1_FIELD_TYPE_BOOL,
    T1_FIELD_TYPE_INTEGER,
    T1_FIELD_TYPE_FIXED,
    T1_FIELD_TYPE_FIXED_1000,
    T1_FIELD_TYPE_STRING,
    T1_FIELD_TYPE_KEY,
    T1_FIELD_TYPE_BBOX,
    T1_FIELD_TYPE_INTEGER_ARRAY,
    T1_FIELD_TYPE_FIXED_ARRAY,
    T1_FIELD_TYPE_CALLBACK,

    /* do not remove */
    T1_FIELD_TYPE_MAX

  } T1_FieldType;


  typedef enum  T1_FieldLocation_
  {
    T1_FIELD_LOCATION_CID_INFO,
    T1_FIELD_LOCATION_FONT_DICT,
    T1_FIELD_LOCATION_FONT_EXTRA,
    T1_FIELD_LOCATION_FONT_INFO,
    T1_FIELD_LOCATION_PRIVATE,
    T1_FIELD_LOCATION_BBOX,
    T1_FIELD_LOCATION_LOADER,
    T1_FIELD_LOCATION_FACE,
    T1_FIELD_LOCATION_BLEND,

    /* do not remove */
    T1_FIELD_LOCATION_MAX

  } T1_FieldLocation;


  typedef void
  (*T1_Field_ParseFunc)( FT_Face     face,
                         FT_Pointer  parser );


  /* structure type used to model object fields */
  typedef struct  T1_FieldRec_
  {
    const char*         ident;        /* field identifier               */
    T1_FieldLocation    location;
    T1_FieldType        type;         /* type of field                  */
    T1_Field_ParseFunc  reader;
    FT_UInt             offset;       /* offset of field in object      */
    FT_Byte             size;         /* size of field in bytes         */
    FT_UInt             array_max;    /* maximal number of elements for */
                                      /* array                          */
    FT_UInt             count_offset; /* offset of element count for    */
                                      /* arrays; must not be zero if in */
                                      /* use -- in other words, a       */
                                      /* `num_FOO' element must not     */
                                      /* start the used structure if we */
                                      /* parse a `FOO' array            */
    FT_UInt             dict;         /* where we expect it             */
  } T1_FieldRec;

#define T1_FIELD_DICT_FONTDICT ( 1 << 0 ) /* also FontInfo and FDArray */
#define T1_FIELD_DICT_PRIVATE  ( 1 << 1 )



#define T1_NEW_SIMPLE_FIELD( _ident, _type, _fname, _dict ) \
          {                                                 \
            _ident, T1CODE, _type,                          \
            0,                                              \
            FT_FIELD_OFFSET( _fname ),                      \
            FT_FIELD_SIZE( _fname ),                        \
            0, 0,                                           \
            _dict                                           \
          },

#define T1_NEW_CALLBACK_FIELD( _ident, _reader, _dict ) \
          {                                             \
            _ident, T1CODE, T1_FIELD_TYPE_CALLBACK,     \
            (T1_Field_ParseFunc)_reader,                \
            0, 0,                                       \
            0, 0,                                       \
            _dict                                       \
          },

#define T1_NEW_TABLE_FIELD( _ident, _type, _fname, _max, _dict ) \
          {                                                      \
            _ident, T1CODE, _type,                               \
            0,                                                   \
            FT_FIELD_OFFSET( _fname ),                           \
            FT_FIELD_SIZE_DELTA( _fname ),                       \
            _max,                                                \
            FT_FIELD_OFFSET( num_ ## _fname ),                   \
            _dict                                                \
          },

#define T1_NEW_TABLE_FIELD2( _ident, _type, _fname, _max, _dict ) \
          {                                                       \
            _ident, T1CODE, _type,                                \
            0,                                                    \
            FT_FIELD_OFFSET( _fname ),                            \
            FT_FIELD_SIZE_DELTA( _fname ),                        \
            _max, 0,                                              \
            _dict                                                 \
          },


#define T1_FIELD_BOOL( _ident, _fname, _dict )                             \
          T1_NEW_SIMPLE_FIELD( _ident, T1_FIELD_TYPE_BOOL, _fname, _dict )

#define T1_FIELD_NUM( _ident, _fname, _dict )                                 \
          T1_NEW_SIMPLE_FIELD( _ident, T1_FIELD_TYPE_INTEGER, _fname, _dict )

#define T1_FIELD_FIXED( _ident, _fname, _dict )                             \
          T1_NEW_SIMPLE_FIELD( _ident, T1_FIELD_TYPE_FIXED, _fname, _dict )

#define T1_FIELD_FIXED_1000( _ident, _fname, _dict )                     \
          T1_NEW_SIMPLE_FIELD( _ident, T1_FIELD_TYPE_FIXED_1000, _fname, \
                               _dict )

#define T1_FIELD_STRING( _ident, _fname, _dict )                             \
          T1_NEW_SIMPLE_FIELD( _ident, T1_FIELD_TYPE_STRING, _fname, _dict )

#define T1_FIELD_KEY( _ident, _fname, _dict )                             \
          T1_NEW_SIMPLE_FIELD( _ident, T1_FIELD_TYPE_KEY, _fname, _dict )

#define T1_FIELD_BBOX( _ident, _fname, _dict )                             \
          T1_NEW_SIMPLE_FIELD( _ident, T1_FIELD_TYPE_BBOX, _fname, _dict )


#define T1_FIELD_NUM_TABLE( _ident, _fname, _fmax, _dict )         \
          T1_NEW_TABLE_FIELD( _ident, T1_FIELD_TYPE_INTEGER_ARRAY, \
                              _fname, _fmax, _dict )

#define T1_FIELD_FIXED_TABLE( _ident, _fname, _fmax, _dict )     \
          T1_NEW_TABLE_FIELD( _ident, T1_FIELD_TYPE_FIXED_ARRAY, \
                              _fname, _fmax, _dict )

#define T1_FIELD_NUM_TABLE2( _ident, _fname, _fmax, _dict )         \
          T1_NEW_TABLE_FIELD2( _ident, T1_FIELD_TYPE_INTEGER_ARRAY, \
                               _fname, _fmax, _dict )

#define T1_FIELD_FIXED_TABLE2( _ident, _fname, _fmax, _dict )     \
          T1_NEW_TABLE_FIELD2( _ident, T1_FIELD_TYPE_FIXED_ARRAY, \
                               _fname, _fmax, _dict )

#define T1_FIELD_CALLBACK( _ident, _name, _dict )       \
          T1_NEW_CALLBACK_FIELD( _ident, _name, _dict )


  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****                            T1 PARSER                          *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/

  typedef const struct PS_Parser_FuncsRec_*  PS_Parser_Funcs;

  typedef struct  PS_Parser_FuncsRec_
  {
    void
    (*init)( PS_Parser  parser,
             FT_Byte*   base,
             FT_Byte*   limit,
             FT_Memory  memory );

    void
    (*done)( PS_Parser  parser );

    void
    (*skip_spaces)( PS_Parser  parser );
    void
    (*skip_PS_token)( PS_Parser  parser );

    FT_Long
    (*to_int)( PS_Parser  parser );
    FT_Fixed
    (*to_fixed)( PS_Parser  parser,
                 FT_Int     power_ten );

    FT_Error
    (*to_bytes)( PS_Parser  parser,
                 FT_Byte*   bytes,
                 FT_Offset  max_bytes,
                 FT_Long*   pnum_bytes,
                 FT_Bool    delimiters );

    FT_Int
    (*to_coord_array)( PS_Parser  parser,
                       FT_Int     max_coords,
                       FT_Short*  coords );
    FT_Int
    (*to_fixed_array)( PS_Parser  parser,
                       FT_Int     max_values,
                       FT_Fixed*  values,
                       FT_Int     power_ten );

    void
    (*to_token)( PS_Parser  parser,
                 T1_Token   token );
    void
    (*to_token_array)( PS_Parser  parser,
                       T1_Token   tokens,
                       FT_UInt    max_tokens,
                       FT_Int*    pnum_tokens );

    FT_Error
    (*load_field)( PS_Parser       parser,
                   const T1_Field  field,
                   void**          objects,
                   FT_UInt         max_objects,
                   FT_ULong*       pflags );

    FT_Error
    (*load_field_table)( PS_Parser       parser,
                         const T1_Field  field,
                         void**          objects,
                         FT_UInt         max_objects,
                         FT_ULong*       pflags );

  } PS_Parser_FuncsRec;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    PS_ParserRec                                                       */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A PS_Parser is an object used to parse a Type 1 font very quickly. */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    cursor :: The current position in the text.                        */
  /*                                                                       */
  /*    base   :: Start of the processed text.                             */
  /*                                                                       */
  /*    limit  :: End of the processed text.                               */
  /*                                                                       */
  /*    error  :: The last error returned.                                 */
  /*                                                                       */
  /*    memory :: The object used for memory operations (alloc/realloc).   */
  /*                                                                       */
  /*    funcs  :: A table of functions for the parser.                     */
  /*                                                                       */
  typedef struct  PS_ParserRec_
  {
    FT_Byte*   cursor;
    FT_Byte*   base;
    FT_Byte*   limit;
    FT_Error   error;
    FT_Memory  memory;

    PS_Parser_FuncsRec  funcs;

  } PS_ParserRec;


  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****                         T1 BUILDER                            *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/


  typedef struct T1_BuilderRec_*  T1_Builder;


  typedef FT_Error
  (*T1_Builder_Check_Points_Func)( T1_Builder  builder,
                                   FT_Int      count );

  typedef void
  (*T1_Builder_Add_Point_Func)( T1_Builder  builder,
                                FT_Pos      x,
                                FT_Pos      y,
                                FT_Byte     flag );

  typedef FT_Error
  (*T1_Builder_Add_Point1_Func)( T1_Builder  builder,
                                 FT_Pos      x,
                                 FT_Pos      y );

  typedef FT_Error
  (*T1_Builder_Add_Contour_Func)( T1_Builder  builder );

  typedef FT_Error
  (*T1_Builder_Start_Point_Func)( T1_Builder  builder,
                                  FT_Pos      x,
                                  FT_Pos      y );

  typedef void
  (*T1_Builder_Close_Contour_Func)( T1_Builder  builder );


  typedef const struct T1_Builder_FuncsRec_*  T1_Builder_Funcs;

  typedef struct  T1_Builder_FuncsRec_
  {
    void
    (*init)( T1_Builder    builder,
             FT_Face       face,
             FT_Size       size,
             FT_GlyphSlot  slot,
             FT_Bool       hinting );

    void
    (*done)( T1_Builder   builder );

    T1_Builder_Check_Points_Func   check_points;
    T1_Builder_Add_Point_Func      add_point;
    T1_Builder_Add_Point1_Func     add_point1;
    T1_Builder_Add_Contour_Func    add_contour;
    T1_Builder_Start_Point_Func    start_point;
    T1_Builder_Close_Contour_Func  close_contour;

  } T1_Builder_FuncsRec;


  /* an enumeration type to handle charstring parsing states */
  typedef enum  T1_ParseState_
  {
    T1_Parse_Start,
    T1_Parse_Have_Width,
    T1_Parse_Have_Moveto,
    T1_Parse_Have_Path

  } T1_ParseState;


  /*************************************************************************/
  /*                                                                       */
  /* <Structure>                                                           */
  /*    T1_BuilderRec                                                      */
  /*                                                                       */
  /* <Description>                                                         */
  /*     A structure used during glyph loading to store its outline.       */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    memory       :: The current memory object.                         */
  /*                                                                       */
  /*    face         :: The current face object.                           */
  /*                                                                       */
  /*    glyph        :: The current glyph slot.                            */
  /*                                                                       */
  /*    loader       :: XXX                                                */
  /*                                                                       */
  /*    base         :: The base glyph outline.                            */
  /*                                                                       */
  /*    current      :: The current glyph outline.                         */
  /*                                                                       */
  /*    max_points   :: maximum points in builder outline                  */
  /*                                                                       */
  /*    max_contours :: Maximal number of contours in builder outline.     */
  /*                                                                       */
  /*    pos_x        :: The horizontal translation (if composite glyph).   */
  /*                                                                       */
  /*    pos_y        :: The vertical translation (if composite glyph).     */
  /*                                                                       */
  /*    left_bearing :: The left side bearing point.                       */
  /*                                                                       */
  /*    advance      :: The horizontal advance vector.                     */
  /*                                                                       */
  /*    bbox         :: Unused.                                            */
  /*                                                                       */
  /*    parse_state  :: An enumeration which controls the charstring       */
  /*                    parsing state.                                     */
  /*                                                                       */
  /*    load_points  :: If this flag is not set, no points are loaded.     */
  /*                                                                       */
  /*    no_recurse   :: Set but not used.                                  */
  /*                                                                       */
  /*    metrics_only :: A boolean indicating that we only want to compute  */
  /*                    the metrics of a given glyph, not load all of its  */
  /*                    points.                                            */
  /*                                                                       */
  /*    funcs        :: An array of function pointers for the builder.     */
  /*                                                                       */
  typedef struct  T1_BuilderRec_
  {
    FT_Memory       memory;
    FT_Face         face;
    FT_GlyphSlot    glyph;
    FT_GlyphLoader  loader;
    FT_Outline*     base;
    FT_Outline*     current;

    FT_Pos          pos_x;
    FT_Pos          pos_y;

    FT_Vector       left_bearing;
    FT_Vector       advance;

    FT_BBox         bbox;          /* bounding box */
    T1_ParseState   parse_state;
    FT_Bool         load_points;
    FT_Bool         no_recurse;

    FT_Bool         metrics_only;

    void*           hints_funcs;    /* hinter-specific */
    void*           hints_globals;  /* hinter-specific */

    T1_Builder_FuncsRec  funcs;

  } T1_BuilderRec;


  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****                         T1 DECODER                            *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/

#if 0

  /*************************************************************************/
  /*                                                                       */
  /* T1_MAX_SUBRS_CALLS details the maximum number of nested sub-routine   */
  /* calls during glyph loading.                                           */
  /*                                                                       */
#define T1_MAX_SUBRS_CALLS  8


  /*************************************************************************/
  /*                                                                       */
  /* T1_MAX_CHARSTRING_OPERANDS is the charstring stack's capacity.  A     */
  /* minimum of 16 is required.                                            */
  /*                                                                       */
#define T1_MAX_CHARSTRINGS_OPERANDS  32

#endif /* 0 */


  typedef struct  T1_Decoder_ZoneRec_
  {
    FT_Byte*  cursor;
    FT_Byte*  base;
    FT_Byte*  limit;

  } T1_Decoder_ZoneRec, *T1_Decoder_Zone;


  typedef struct T1_DecoderRec_*              T1_Decoder;
  typedef const struct T1_Decoder_FuncsRec_*  T1_Decoder_Funcs;


  typedef FT_Error
  (*T1_Decoder_Callback)( T1_Decoder  decoder,
                          FT_UInt     glyph_index );


  typedef struct  T1_Decoder_FuncsRec_
  {
    FT_Error
    (*init)( T1_Decoder           decoder,
             FT_Face              face,
             FT_Size              size,
             FT_GlyphSlot         slot,
             FT_Byte**            glyph_names,
             PS_Blend             blend,
             FT_Bool              hinting,
             FT_Render_Mode       hint_mode,
             T1_Decoder_Callback  callback );

    void
    (*done)( T1_Decoder  decoder );

    FT_Error
    (*parse_charstrings)( T1_Decoder  decoder,
                          FT_Byte*    base,
                          FT_UInt     len );

  } T1_Decoder_FuncsRec;


  typedef struct  T1_DecoderRec_
  {
    T1_BuilderRec        builder;

    FT_Long              stack[T1_MAX_CHARSTRINGS_OPERANDS];
    FT_Long*             top;

    T1_Decoder_ZoneRec   zones[T1_MAX_SUBRS_CALLS + 1];
    T1_Decoder_Zone      zone;

    FT_Service_PsCMaps   psnames;      /* for seac */
    FT_UInt              num_glyphs;
    FT_Byte**            glyph_names;

    FT_Int               lenIV;        /* internal for sub routine calls */
    FT_UInt              num_subrs;
    FT_Byte**            subrs;
    FT_PtrDist*          subrs_len;    /* array of subrs length (optional) */

    FT_Matrix            font_matrix;
    FT_Vector            font_offset;

    FT_Int               flex_state;
    FT_Int               num_flex_vectors;
    FT_Vector            flex_vectors[7];

    PS_Blend             blend;       /* for multiple master support */

    FT_Render_Mode       hint_mode;

    T1_Decoder_Callback  parse_callback;
    T1_Decoder_FuncsRec  funcs;

    FT_Long*             buildchar;
    FT_UInt              len_buildchar;

    FT_Bool              seac;

  } T1_DecoderRec;


  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****                            AFM PARSER                         *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/

  typedef struct AFM_ParserRec_*  AFM_Parser;

  typedef struct  AFM_Parser_FuncsRec_
  {
    FT_Error
    (*init)( AFM_Parser  parser,
             FT_Memory   memory,
             FT_Byte*    base,
             FT_Byte*    limit );

    void
    (*done)( AFM_Parser  parser );

    FT_Error
    (*parse)( AFM_Parser  parser );

  } AFM_Parser_FuncsRec;


  typedef struct AFM_StreamRec_*  AFM_Stream;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    AFM_ParserRec                                                      */
  /*                                                                       */
  /* <Description>                                                         */
  /*    An AFM_Parser is a parser for the AFM files.                       */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    memory    :: The object used for memory operations (alloc and      */
  /*                 realloc).                                             */
  /*                                                                       */
  /*    stream    :: This is an opaque object.                             */
  /*                                                                       */
  /*    FontInfo  :: The result will be stored here.                       */
  /*                                                                       */
  /*    get_index :: A user provided function to get a glyph index by its  */
  /*                 name.                                                 */
  /*                                                                       */
  typedef struct  AFM_ParserRec_
  {
    FT_Memory     memory;
    AFM_Stream    stream;

    AFM_FontInfo  FontInfo;

    FT_Int
    (*get_index)( const char*  name,
                  FT_Offset    len,
                  void*        user_data );

    void*         user_data;

  } AFM_ParserRec;


  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****                     TYPE1 CHARMAPS                            *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/

  typedef const struct T1_CMap_ClassesRec_*  T1_CMap_Classes;

  typedef struct T1_CMap_ClassesRec_
  {
    FT_CMap_Class  standard;
    FT_CMap_Class  expert;
    FT_CMap_Class  custom;
    FT_CMap_Class  unicode;

  } T1_CMap_ClassesRec;


  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****                        PSAux Module Interface                 *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/

  typedef struct  PSAux_ServiceRec_
  {
    /* don't use `PS_Table_Funcs' and friends to avoid compiler warnings */
    const PS_Table_FuncsRec*    ps_table_funcs;
    const PS_Parser_FuncsRec*   ps_parser_funcs;
    const T1_Builder_FuncsRec*  t1_builder_funcs;
    const T1_Decoder_FuncsRec*  t1_decoder_funcs;

    void
    (*t1_decrypt)( FT_Byte*   buffer,
                   FT_Offset  length,
                   FT_UShort  seed );

    T1_CMap_Classes  t1_cmap_classes;

    /* fields after this comment line were added after version 2.1.10 */
    const AFM_Parser_FuncsRec*  afm_parser_funcs;

  } PSAux_ServiceRec, *PSAux_Service;

  /* backwards-compatible type definition */
  typedef PSAux_ServiceRec   PSAux_Interface;


  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****                 Some convenience functions                    *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/

#define IS_PS_NEWLINE( ch ) \
  ( (ch) == '\r' ||         \
    (ch) == '\n' )

#define IS_PS_SPACE( ch )  \
  ( (ch) == ' '         || \
    IS_PS_NEWLINE( ch ) || \
    (ch) == '\t'        || \
    (ch) == '\f'        || \
    (ch) == '\0' )

#define IS_PS_SPECIAL( ch )       \
  ( (ch) == '/'                || \
    (ch) == '(' || (ch) == ')' || \
    (ch) == '<' || (ch) == '>' || \
    (ch) == '[' || (ch) == ']' || \
    (ch) == '{' || (ch) == '}' || \
    (ch) == '%'                )

#define IS_PS_DELIM( ch )  \
  ( IS_PS_SPACE( ch )   || \
    IS_PS_SPECIAL( ch ) )

#define IS_PS_DIGIT( ch )        \
  ( (ch) >= '0' && (ch) <= '9' )

#define IS_PS_XDIGIT( ch )            \
  ( IS_PS_DIGIT( ch )              || \
    ( (ch) >= 'A' && (ch) <= 'F' ) || \
    ( (ch) >= 'a' && (ch) <= 'f' ) )

#define IS_PS_BASE85( ch )       \
  ( (ch) >= '!' && (ch) <= 'u' )

#define IS_PS_TOKEN( cur, limit, token )                                \
  ( (char)(cur)[0] == (token)[0]                                     && \
    ( (cur) + sizeof ( (token) ) == (limit) ||                          \
      ( (cur) + sizeof( (token) ) < (limit)          &&                 \
        IS_PS_DELIM( (cur)[sizeof ( (token) ) - 1] ) ) )             && \
    ft_strncmp( (char*)(cur), (token), sizeof ( (token) ) - 1 ) == 0 )


FT_END_HEADER

#endif /* __PSAUX_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  pshints.h                                                              */
/*                                                                         */
/*    Interface to Postscript-specific (Type 1 and Type 2) hints           */
/*    recorders (specification only).  These are used to support native    */
/*    T1/T2 hints in the `type1', `cid', and `cff' font drivers.           */
/*                                                                         */
/*  Copyright 2001, 2002, 2003, 2005, 2006, 2007, 2009 by                  */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __PSHINTS_H__
#define __PSHINTS_H__


#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_TYPE1_TABLES_H


FT_BEGIN_HEADER


  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****               INTERNAL REPRESENTATION OF GLOBALS              *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/

  typedef struct PSH_GlobalsRec_*  PSH_Globals;

  typedef FT_Error
  (*PSH_Globals_NewFunc)( FT_Memory     memory,
                          T1_Private*   private_dict,
                          PSH_Globals*  aglobals );

  typedef FT_Error
  (*PSH_Globals_SetScaleFunc)( PSH_Globals  globals,
                               FT_Fixed     x_scale,
                               FT_Fixed     y_scale,
                               FT_Fixed     x_delta,
                               FT_Fixed     y_delta );

  typedef void
  (*PSH_Globals_DestroyFunc)( PSH_Globals  globals );


  typedef struct  PSH_Globals_FuncsRec_
  {
    PSH_Globals_NewFunc       create;
    PSH_Globals_SetScaleFunc  set_scale;
    PSH_Globals_DestroyFunc   destroy;

  } PSH_Globals_FuncsRec, *PSH_Globals_Funcs;


  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****                  PUBLIC TYPE 1 HINTS RECORDER                 *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/

  /*************************************************************************
   *
   * @type:
   *   T1_Hints
   *
   * @description:
   *   This is a handle to an opaque structure used to record glyph hints
   *   from a Type 1 character glyph character string.
   *
   *   The methods used to operate on this object are defined by the
   *   @T1_Hints_FuncsRec structure.  Recording glyph hints is normally
   *   achieved through the following scheme:
   *
   *   - Open a new hint recording session by calling the `open' method.
   *     This rewinds the recorder and prepare it for new input.
   *
   *   - For each hint found in the glyph charstring, call the corresponding
   *     method (`stem', `stem3', or `reset').  Note that these functions do
   *     not return an error code.
   *
   *   - Close the recording session by calling the `close' method.  It
   *     returns an error code if the hints were invalid or something
   *     strange happened (e.g., memory shortage).
   *
   *   The hints accumulated in the object can later be used by the
   *   PostScript hinter.
   *
   */
  typedef struct T1_HintsRec_*  T1_Hints;


  /*************************************************************************
   *
   * @type:
   *   T1_Hints_Funcs
   *
   * @description:
   *   A pointer to the @T1_Hints_FuncsRec structure that defines the API of
   *   a given @T1_Hints object.
   *
   */
  typedef const struct T1_Hints_FuncsRec_*  T1_Hints_Funcs;


  /*************************************************************************
   *
   * @functype:
   *   T1_Hints_OpenFunc
   *
   * @description:
   *   A method of the @T1_Hints class used to prepare it for a new Type 1
   *   hints recording session.
   *
   * @input:
   *   hints ::
   *     A handle to the Type 1 hints recorder.
   *
   * @note:
   *   You should always call the @T1_Hints_CloseFunc method in order to
   *   close an opened recording session.
   *
   */
  typedef void
  (*T1_Hints_OpenFunc)( T1_Hints  hints );


  /*************************************************************************
   *
   * @functype:
   *   T1_Hints_SetStemFunc
   *
   * @description:
   *   A method of the @T1_Hints class used to record a new horizontal or
   *   vertical stem.  This corresponds to the Type 1 `hstem' and `vstem'
   *   operators.
   *
   * @input:
   *   hints ::
   *     A handle to the Type 1 hints recorder.
   *
   *   dimension ::
   *     0 for horizontal stems (hstem), 1 for vertical ones (vstem).
   *
   *   coords ::
   *     Array of 2 coordinates in 16.16 format, used as (position,length)
   *     stem descriptor.
   *
   * @note:
   *   Use vertical coordinates (y) for horizontal stems (dim=0).  Use
   *   horizontal coordinates (x) for vertical stems (dim=1).
   *
   *   `coords[0]' is the absolute stem position (lowest coordinate);
   *   `coords[1]' is the length.
   *
   *   The length can be negative, in which case it must be either -20 or
   *   -21.  It is interpreted as a `ghost' stem, according to the Type 1
   *   specification.
   *
   *   If the length is -21 (corresponding to a bottom ghost stem), then
   *   the real stem position is `coords[0]+coords[1]'.
   *
   */
  typedef void
  (*T1_Hints_SetStemFunc)( T1_Hints   hints,
                           FT_UInt    dimension,
                           FT_Fixed*  coords );


  /*************************************************************************
   *
   * @functype:
   *   T1_Hints_SetStem3Func
   *
   * @description:
   *   A method of the @T1_Hints class used to record three
   *   counter-controlled horizontal or vertical stems at once.
   *
   * @input:
   *   hints ::
   *     A handle to the Type 1 hints recorder.
   *
   *   dimension ::
   *     0 for horizontal stems, 1 for vertical ones.
   *
   *   coords ::
   *     An array of 6 values in 16.16 format, holding 3 (position,length)
   *     pairs for the counter-controlled stems.
   *
   * @note:
   *   Use vertical coordinates (y) for horizontal stems (dim=0).  Use
   *   horizontal coordinates (x) for vertical stems (dim=1).
   *
   *   The lengths cannot be negative (ghost stems are never
   *   counter-controlled).
   *
   */
  typedef void
  (*T1_Hints_SetStem3Func)( T1_Hints   hints,
                            FT_UInt    dimension,
                            FT_Fixed*  coords );


  /*************************************************************************
   *
   * @functype:
   *   T1_Hints_ResetFunc
   *
   * @description:
   *   A method of the @T1_Hints class used to reset the stems hints in a
   *   recording session.
   *
   * @input:
   *   hints ::
   *     A handle to the Type 1 hints recorder.
   *
   *   end_point ::
   *     The index of the last point in the input glyph in which the
   *     previously defined hints apply.
   *
   */
  typedef void
  (*T1_Hints_ResetFunc)( T1_Hints  hints,
                         FT_UInt   end_point );


  /*************************************************************************
   *
   * @functype:
   *   T1_Hints_CloseFunc
   *
   * @description:
   *   A method of the @T1_Hints class used to close a hint recording
   *   session.
   *
   * @input:
   *   hints ::
   *     A handle to the Type 1 hints recorder.
   *
   *   end_point ::
   *     The index of the last point in the input glyph.
   *
   * @return:
   *   FreeType error code.  0 means success.
   *
   * @note:
   *   The error code is set to indicate that an error occurred during the
   *   recording session.
   *
   */
  typedef FT_Error
  (*T1_Hints_CloseFunc)( T1_Hints  hints,
                         FT_UInt   end_point );


  /*************************************************************************
   *
   * @functype:
   *   T1_Hints_ApplyFunc
   *
   * @description:
   *   A method of the @T1_Hints class used to apply hints to the
   *   corresponding glyph outline.  Must be called once all hints have been
   *   recorded.
   *
   * @input:
   *   hints ::
   *     A handle to the Type 1 hints recorder.
   *
   *   outline ::
   *     A pointer to the target outline descriptor.
   *
   *   globals ::
   *     The hinter globals for this font.
   *
   *   hint_mode ::
   *     Hinting information.
   *
   * @return:
   *   FreeType error code.  0 means success.
   *
   * @note:
   *   On input, all points within the outline are in font coordinates. On
   *   output, they are in 1/64th of pixels.
   *
   *   The scaling transformation is taken from the `globals' object which
   *   must correspond to the same font as the glyph.
   *
   */
  typedef FT_Error
  (*T1_Hints_ApplyFunc)( T1_Hints        hints,
                         FT_Outline*     outline,
                         PSH_Globals     globals,
                         FT_Render_Mode  hint_mode );


  /*************************************************************************
   *
   * @struct:
   *   T1_Hints_FuncsRec
   *
   * @description:
   *   The structure used to provide the API to @T1_Hints objects.
   *
   * @fields:
   *   hints ::
   *     A handle to the T1 Hints recorder.
   *
   *   open ::
   *     The function to open a recording session.
   *
   *   close ::
   *     The function to close a recording session.
   *
   *   stem ::
   *     The function to set a simple stem.
   *
   *   stem3 ::
   *     The function to set counter-controlled stems.
   *
   *   reset ::
   *     The function to reset stem hints.
   *
   *   apply ::
   *     The function to apply the hints to the corresponding glyph outline.
   *
   */
  typedef struct  T1_Hints_FuncsRec_
  {
    T1_Hints               hints;
    T1_Hints_OpenFunc      open;
    T1_Hints_CloseFunc     close;
    T1_Hints_SetStemFunc   stem;
    T1_Hints_SetStem3Func  stem3;
    T1_Hints_ResetFunc     reset;
    T1_Hints_ApplyFunc     apply;

  } T1_Hints_FuncsRec;


  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****                  PUBLIC TYPE 2 HINTS RECORDER                 *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/

  /*************************************************************************
   *
   * @type:
   *   T2_Hints
   *
   * @description:
   *   This is a handle to an opaque structure used to record glyph hints
   *   from a Type 2 character glyph character string.
   *
   *   The methods used to operate on this object are defined by the
   *   @T2_Hints_FuncsRec structure.  Recording glyph hints is normally
   *   achieved through the following scheme:
   *
   *   - Open a new hint recording session by calling the `open' method.
   *     This rewinds the recorder and prepare it for new input.
   *
   *   - For each hint found in the glyph charstring, call the corresponding
   *     method (`stems', `hintmask', `counters').  Note that these
   *     functions do not return an error code.
   *
   *   - Close the recording session by calling the `close' method.  It
   *     returns an error code if the hints were invalid or something
   *     strange happened (e.g., memory shortage).
   *
   *   The hints accumulated in the object can later be used by the
   *   Postscript hinter.
   *
   */
  typedef struct T2_HintsRec_*  T2_Hints;


  /*************************************************************************
   *
   * @type:
   *   T2_Hints_Funcs
   *
   * @description:
   *   A pointer to the @T2_Hints_FuncsRec structure that defines the API of
   *   a given @T2_Hints object.
   *
   */
  typedef const struct T2_Hints_FuncsRec_*  T2_Hints_Funcs;


  /*************************************************************************
   *
   * @functype:
   *   T2_Hints_OpenFunc
   *
   * @description:
   *   A method of the @T2_Hints class used to prepare it for a new Type 2
   *   hints recording session.
   *
   * @input:
   *   hints ::
   *     A handle to the Type 2 hints recorder.
   *
   * @note:
   *   You should always call the @T2_Hints_CloseFunc method in order to
   *   close an opened recording session.
   *
   */
  typedef void
  (*T2_Hints_OpenFunc)( T2_Hints  hints );


  /*************************************************************************
   *
   * @functype:
   *   T2_Hints_StemsFunc
   *
   * @description:
   *   A method of the @T2_Hints class used to set the table of stems in
   *   either the vertical or horizontal dimension.  Equivalent to the
   *   `hstem', `vstem', `hstemhm', and `vstemhm' Type 2 operators.
   *
   * @input:
   *   hints ::
   *     A handle to the Type 2 hints recorder.
   *
   *   dimension ::
   *     0 for horizontal stems (hstem), 1 for vertical ones (vstem).
   *
   *   count ::
   *     The number of stems.
   *
   *   coords ::
   *     An array of `count' (position,length) pairs in 16.16 format.
   *
   * @note:
   *   Use vertical coordinates (y) for horizontal stems (dim=0).  Use
   *   horizontal coordinates (x) for vertical stems (dim=1).
   *
   *   There are `2*count' elements in the `coords' array.  Each even
   *   element is an absolute position in font units, each odd element is a
   *   length in font units.
   *
   *   A length can be negative, in which case it must be either -20 or
   *   -21.  It is interpreted as a `ghost' stem, according to the Type 1
   *   specification.
   *
   */
  typedef void
  (*T2_Hints_StemsFunc)( T2_Hints   hints,
                         FT_UInt    dimension,
                         FT_UInt    count,
                         FT_Fixed*  coordinates );


  /*************************************************************************
   *
   * @functype:
   *   T2_Hints_MaskFunc
   *
   * @description:
   *   A method of the @T2_Hints class used to set a given hintmask (this
   *   corresponds to the `hintmask' Type 2 operator).
   *
   * @input:
   *   hints ::
   *     A handle to the Type 2 hints recorder.
   *
   *   end_point ::
   *     The glyph index of the last point to which the previously defined
   *     or activated hints apply.
   *
   *   bit_count ::
   *     The number of bits in the hint mask.
   *
   *   bytes ::
   *     An array of bytes modelling the hint mask.
   *
   * @note:
   *   If the hintmask starts the charstring (before any glyph point
   *   definition), the value of `end_point' should be 0.
   *
   *   `bit_count' is the number of meaningful bits in the `bytes' array; it
   *   must be equal to the total number of hints defined so far (i.e.,
   *   horizontal+verticals).
   *
   *   The `bytes' array can come directly from the Type 2 charstring and
   *   respects the same format.
   *
   */
  typedef void
  (*T2_Hints_MaskFunc)( T2_Hints        hints,
                        FT_UInt         end_point,
                        FT_UInt         bit_count,
                        const FT_Byte*  bytes );


  /*************************************************************************
   *
   * @functype:
   *   T2_Hints_CounterFunc
   *
   * @description:
   *   A method of the @T2_Hints class used to set a given counter mask
   *   (this corresponds to the `hintmask' Type 2 operator).
   *
   * @input:
   *   hints ::
   *     A handle to the Type 2 hints recorder.
   *
   *   end_point ::
   *     A glyph index of the last point to which the previously defined or
   *     active hints apply.
   *
   *   bit_count ::
   *     The number of bits in the hint mask.
   *
   *   bytes ::
   *     An array of bytes modelling the hint mask.
   *
   * @note:
   *   If the hintmask starts the charstring (before any glyph point
   *   definition), the value of `end_point' should be 0.
   *
   *   `bit_count' is the number of meaningful bits in the `bytes' array; it
   *   must be equal to the total number of hints defined so far (i.e.,
   *   horizontal+verticals).
   *
   *    The `bytes' array can come directly from the Type 2 charstring and
   *    respects the same format.
   *
   */
  typedef void
  (*T2_Hints_CounterFunc)( T2_Hints        hints,
                           FT_UInt         bit_count,
                           const FT_Byte*  bytes );


  /*************************************************************************
   *
   * @functype:
   *   T2_Hints_CloseFunc
   *
   * @description:
   *   A method of the @T2_Hints class used to close a hint recording
   *   session.
   *
   * @input:
   *   hints ::
   *     A handle to the Type 2 hints recorder.
   *
   *   end_point ::
   *     The index of the last point in the input glyph.
   *
   * @return:
   *   FreeType error code.  0 means success.
   *
   * @note:
   *   The error code is set to indicate that an error occurred during the
   *   recording session.
   *
   */
  typedef FT_Error
  (*T2_Hints_CloseFunc)( T2_Hints  hints,
                         FT_UInt   end_point );


  /*************************************************************************
   *
   * @functype:
   *   T2_Hints_ApplyFunc
   *
   * @description:
   *   A method of the @T2_Hints class used to apply hints to the
   *   corresponding glyph outline.  Must be called after the `close'
   *   method.
   *
   * @input:
   *   hints ::
   *     A handle to the Type 2 hints recorder.
   *
   *   outline ::
   *     A pointer to the target outline descriptor.
   *
   *   globals ::
   *     The hinter globals for this font.
   *
   *   hint_mode ::
   *     Hinting information.
   *
   * @return:
   *   FreeType error code.  0 means success.
   *
   * @note:
   *   On input, all points within the outline are in font coordinates. On
   *   output, they are in 1/64th of pixels.
   *
   *   The scaling transformation is taken from the `globals' object which
   *   must correspond to the same font than the glyph.
   *
   */
  typedef FT_Error
  (*T2_Hints_ApplyFunc)( T2_Hints        hints,
                         FT_Outline*     outline,
                         PSH_Globals     globals,
                         FT_Render_Mode  hint_mode );


  /*************************************************************************
   *
   * @struct:
   *   T2_Hints_FuncsRec
   *
   * @description:
   *   The structure used to provide the API to @T2_Hints objects.
   *
   * @fields:
   *   hints ::
   *     A handle to the T2 hints recorder object.
   *
   *   open ::
   *     The function to open a recording session.
   *
   *   close ::
   *     The function to close a recording session.
   *
   *   stems ::
   *     The function to set the dimension's stems table.
   *
   *   hintmask ::
   *     The function to set hint masks.
   *
   *   counter ::
   *     The function to set counter masks.
   *
   *   apply ::
   *     The function to apply the hints on the corresponding glyph outline.
   *
   */
  typedef struct  T2_Hints_FuncsRec_
  {
    T2_Hints              hints;
    T2_Hints_OpenFunc     open;
    T2_Hints_CloseFunc    close;
    T2_Hints_StemsFunc    stems;
    T2_Hints_MaskFunc     hintmask;
    T2_Hints_CounterFunc  counter;
    T2_Hints_ApplyFunc    apply;

  } T2_Hints_FuncsRec;


  /* */


  typedef struct  PSHinter_Interface_
  {
    PSH_Globals_Funcs  (*get_globals_funcs)( FT_Module  module );
    T1_Hints_Funcs     (*get_t1_funcs)     ( FT_Module  module );
    T2_Hints_Funcs     (*get_t2_funcs)     ( FT_Module  module );

  } PSHinter_Interface;

  typedef PSHinter_Interface*  PSHinter_Service;

#ifndef FT_CONFIG_OPTION_PIC

#define FT_DEFINE_PSHINTER_INTERFACE(class_, get_globals_funcs_,             \
                                     get_t1_funcs_, get_t2_funcs_)           \
  static const PSHinter_Interface class_ =                                   \
  {                                                                          \
    get_globals_funcs_, get_t1_funcs_, get_t2_funcs_                         \
  };

#else /* FT_CONFIG_OPTION_PIC */ 

#define FT_DEFINE_PSHINTER_INTERFACE(class_, get_globals_funcs_,             \
                                     get_t1_funcs_, get_t2_funcs_)           \
  void                                                                       \
  FT_Init_Class_##class_( FT_Library library,                                \
                          PSHinter_Interface*  clazz)                        \
  {                                                                          \
    FT_UNUSED(library);                                                      \
    clazz->get_globals_funcs = get_globals_funcs_;                           \
    clazz->get_t1_funcs = get_t1_funcs_;                                     \
    clazz->get_t2_funcs = get_t2_funcs_;                                     \
  } 

#endif /* FT_CONFIG_OPTION_PIC */ 

FT_END_HEADER

#endif /* __PSHINTS_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  svbdf.h                                                                */
/*                                                                         */
/*    The FreeType BDF services (specification).                           */
/*                                                                         */
/*  Copyright 2003 by                                                      */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __SVBDF_H__
#define __SVBDF_H__

#include FT_BDF_H
#include FT_INTERNAL_SERVICE_H


FT_BEGIN_HEADER


#define FT_SERVICE_ID_BDF  "bdf"

  typedef FT_Error
  (*FT_BDF_GetCharsetIdFunc)( FT_Face       face,
                              const char*  *acharset_encoding,
                              const char*  *acharset_registry );

  typedef FT_Error
  (*FT_BDF_GetPropertyFunc)( FT_Face           face,
                             const char*       prop_name,
                             BDF_PropertyRec  *aproperty );


  FT_DEFINE_SERVICE( BDF )
  {
    FT_BDF_GetCharsetIdFunc  get_charset_id;
    FT_BDF_GetPropertyFunc   get_property;
  };

#ifndef FT_CONFIG_OPTION_PIC

#define FT_DEFINE_SERVICE_BDFRec(class_, get_charset_id_, get_property_) \
  static const FT_Service_BDFRec class_ =                                \
  {                                                                      \
    get_charset_id_, get_property_                                       \
  };

#else /* FT_CONFIG_OPTION_PIC */ 

#define FT_DEFINE_SERVICE_BDFRec(class_, get_charset_id_, get_property_) \
  void                                                                   \
  FT_Init_Class_##class_( FT_Service_BDFRec*  clazz )                    \
  {                                                                      \
    clazz->get_charset_id = get_charset_id_;                             \
    clazz->get_property = get_property_;                                 \
  } 

#endif /* FT_CONFIG_OPTION_PIC */ 

  /* */


FT_END_HEADER


#endif /* __SVBDF_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  svcid.h                                                                */
/*                                                                         */
/*    The FreeType CID font services (specification).                      */
/*                                                                         */
/*  Copyright 2007, 2009 by Derek Clegg, Michael Toftdal.                  */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __SVCID_H__
#define __SVCID_H__

#include FT_INTERNAL_SERVICE_H


FT_BEGIN_HEADER


#define FT_SERVICE_ID_CID  "CID"

  typedef FT_Error
  (*FT_CID_GetRegistryOrderingSupplementFunc)( FT_Face       face,
                                               const char*  *registry,
                                               const char*  *ordering,
                                               FT_Int       *supplement );
  typedef FT_Error
  (*FT_CID_GetIsInternallyCIDKeyedFunc)( FT_Face   face,
                                         FT_Bool  *is_cid );
  typedef FT_Error
  (*FT_CID_GetCIDFromGlyphIndexFunc)( FT_Face   face,
                                      FT_UInt   glyph_index,
                                      FT_UInt  *cid );

  FT_DEFINE_SERVICE( CID )
  {
    FT_CID_GetRegistryOrderingSupplementFunc  get_ros;
    FT_CID_GetIsInternallyCIDKeyedFunc        get_is_cid;
    FT_CID_GetCIDFromGlyphIndexFunc           get_cid_from_glyph_index;
  };

#ifndef FT_CONFIG_OPTION_PIC

#define FT_DEFINE_SERVICE_CIDREC(class_, get_ros_,                           \
        get_is_cid_, get_cid_from_glyph_index_ )                             \
  static const FT_Service_CIDRec class_ =                                    \
  {                                                                          \
    get_ros_, get_is_cid_, get_cid_from_glyph_index_                         \
  };

#else /* FT_CONFIG_OPTION_PIC */ 

#define FT_DEFINE_SERVICE_CIDREC(class_, get_ros_,                           \
        get_is_cid_, get_cid_from_glyph_index_ )                             \
  void                                                                       \
  FT_Init_Class_##class_( FT_Library library,                                \
                          FT_Service_CIDRec* clazz)                          \
  {                                                                          \
    FT_UNUSED(library);                                                      \
    clazz->get_ros = get_ros_;                                               \
    clazz->get_is_cid = get_is_cid_;                                         \
    clazz->get_cid_from_glyph_index = get_cid_from_glyph_index_;             \
  } 

#endif /* FT_CONFIG_OPTION_PIC */ 

  /* */


FT_END_HEADER


#endif /* __SVCID_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  svgldict.h                                                             */
/*                                                                         */
/*    The FreeType glyph dictionary services (specification).              */
/*                                                                         */
/*  Copyright 2003 by                                                      */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __SVGLDICT_H__
#define __SVGLDICT_H__

#include FT_INTERNAL_SERVICE_H


FT_BEGIN_HEADER


  /*
   *  A service used to retrieve glyph names, as well as to find the
   *  index of a given glyph name in a font.
   *
   */

#define FT_SERVICE_ID_GLYPH_DICT  "glyph-dict"


  typedef FT_Error
  (*FT_GlyphDict_GetNameFunc)( FT_Face     face,
                               FT_UInt     glyph_index,
                               FT_Pointer  buffer,
                               FT_UInt     buffer_max );

  typedef FT_UInt
  (*FT_GlyphDict_NameIndexFunc)( FT_Face     face,
                                 FT_String*  glyph_name );


  FT_DEFINE_SERVICE( GlyphDict )
  {
    FT_GlyphDict_GetNameFunc    get_name;
    FT_GlyphDict_NameIndexFunc  name_index;  /* optional */
  };

#ifndef FT_CONFIG_OPTION_PIC

#define FT_DEFINE_SERVICE_GLYPHDICTREC(class_, get_name_, name_index_) \
  static const FT_Service_GlyphDictRec class_ =                        \
  {                                                                    \
    get_name_, name_index_                                             \
  };

#else /* FT_CONFIG_OPTION_PIC */ 

#define FT_DEFINE_SERVICE_GLYPHDICTREC(class_, get_name_, name_index_) \
  void                                                                 \
  FT_Init_Class_##class_( FT_Library library,                          \
                          FT_Service_GlyphDictRec* clazz)              \
  {                                                                    \
    FT_UNUSED(library);                                                \
    clazz->get_name = get_name_;                                       \
    clazz->name_index = name_index_;                                   \
  } 

#endif /* FT_CONFIG_OPTION_PIC */ 

  /* */


FT_END_HEADER


#endif /* __SVGLDICT_H__ */
/***************************************************************************/
/*                                                                         */
/*  svgxval.h                                                              */
/*                                                                         */
/*    FreeType API for validating TrueTypeGX/AAT tables (specification).   */
/*                                                                         */
/*  Copyright 2004, 2005 by                                                */
/*  Masatake YAMATO, Red Hat K.K.,                                         */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/

/***************************************************************************/
/*                                                                         */
/* gxvalid is derived from both gxlayout module and otvalid module.        */
/* Development of gxlayout is supported by the Information-technology      */
/* Promotion Agency(IPA), Japan.                                           */
/*                                                                         */
/***************************************************************************/


#ifndef __SVGXVAL_H__
#define __SVGXVAL_H__

#include FT_GX_VALIDATE_H
#include FT_INTERNAL_VALIDATE_H

FT_BEGIN_HEADER


#define FT_SERVICE_ID_GX_VALIDATE           "truetypegx-validate"
#define FT_SERVICE_ID_CLASSICKERN_VALIDATE  "classickern-validate"

  typedef FT_Error
  (*gxv_validate_func)( FT_Face   face,
                        FT_UInt   gx_flags,
                        FT_Bytes  tables[FT_VALIDATE_GX_LENGTH],
                        FT_UInt   table_length );


  typedef FT_Error
  (*ckern_validate_func)( FT_Face   face,
                          FT_UInt   ckern_flags,
                          FT_Bytes  *ckern_table );


  FT_DEFINE_SERVICE( GXvalidate )
  {
    gxv_validate_func  validate;
  };

  FT_DEFINE_SERVICE( CKERNvalidate )
  {
    ckern_validate_func  validate;
  };

  /* */


FT_END_HEADER


#endif /* __SVGXVAL_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  svkern.h                                                               */
/*                                                                         */
/*    The FreeType Kerning service (specification).                        */
/*                                                                         */
/*  Copyright 2006 by                                                      */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __SVKERN_H__
#define __SVKERN_H__

#include FT_INTERNAL_SERVICE_H
#include FT_TRUETYPE_TABLES_H


FT_BEGIN_HEADER

#define FT_SERVICE_ID_KERNING  "kerning"


  typedef FT_Error
  (*FT_Kerning_TrackGetFunc)( FT_Face    face,
                              FT_Fixed   point_size,
                              FT_Int     degree,
                              FT_Fixed*  akerning );

  FT_DEFINE_SERVICE( Kerning )
  {
    FT_Kerning_TrackGetFunc  get_track;
  };

  /* */


FT_END_HEADER


#endif /* __SVKERN_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  svmm.h                                                                 */
/*                                                                         */
/*    The FreeType Multiple Masters and GX var services (specification).   */
/*                                                                         */
/*  Copyright 2003, 2004 by                                                */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __SVMM_H__
#define __SVMM_H__

#include FT_INTERNAL_SERVICE_H


FT_BEGIN_HEADER


  /*
   *  A service used to manage multiple-masters data in a given face.
   *
   *  See the related APIs in `ftmm.h' (FT_MULTIPLE_MASTERS_H).
   *
   */

#define FT_SERVICE_ID_MULTI_MASTERS  "multi-masters"


  typedef FT_Error
  (*FT_Get_MM_Func)( FT_Face           face,
                     FT_Multi_Master*  master );

  typedef FT_Error
  (*FT_Get_MM_Var_Func)( FT_Face      face,
                         FT_MM_Var*  *master );

  typedef FT_Error
  (*FT_Set_MM_Design_Func)( FT_Face   face,
                            FT_UInt   num_coords,
                            FT_Long*  coords );

  typedef FT_Error
  (*FT_Set_Var_Design_Func)( FT_Face    face,
                             FT_UInt    num_coords,
                             FT_Fixed*  coords );

  typedef FT_Error
  (*FT_Set_MM_Blend_Func)( FT_Face   face,
                           FT_UInt   num_coords,
                           FT_Long*  coords );


  FT_DEFINE_SERVICE( MultiMasters )
  {
    FT_Get_MM_Func          get_mm;
    FT_Set_MM_Design_Func   set_mm_design;
    FT_Set_MM_Blend_Func    set_mm_blend;
    FT_Get_MM_Var_Func      get_mm_var;
    FT_Set_Var_Design_Func  set_var_design;
  };

#ifndef FT_CONFIG_OPTION_PIC

#define FT_DEFINE_SERVICE_MULTIMASTERSREC(class_, get_mm_, set_mm_design_,   \
        set_mm_blend_, get_mm_var_, set_var_design_)                         \
  static const FT_Service_MultiMastersRec class_ =                           \
  {                                                                          \
    get_mm_, set_mm_design_, set_mm_blend_, get_mm_var_, set_var_design_     \
  };

#else /* FT_CONFIG_OPTION_PIC */ 

#define FT_DEFINE_SERVICE_MULTIMASTERSREC(class_, get_mm_, set_mm_design_,   \
        set_mm_blend_, get_mm_var_, set_var_design_)                         \
  void                                                                       \
  FT_Init_Class_##class_( FT_Service_MultiMastersRec*  clazz )               \
  {                                                                          \
    clazz->get_mm = get_mm_;                                                 \
    clazz->set_mm_design = set_mm_design_;                                   \
    clazz->set_mm_blend = set_mm_blend_;                                     \
    clazz->get_mm_var = get_mm_var_;                                         \
    clazz->set_var_design = set_var_design_;                                 \
  } 

#endif /* FT_CONFIG_OPTION_PIC */ 

  /* */


FT_END_HEADER

#endif /* __SVMM_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  svotval.h                                                              */
/*                                                                         */
/*    The FreeType OpenType validation service (specification).            */
/*                                                                         */
/*  Copyright 2004, 2006 by                                                */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __SVOTVAL_H__
#define __SVOTVAL_H__

#include FT_OPENTYPE_VALIDATE_H
#include FT_INTERNAL_VALIDATE_H

FT_BEGIN_HEADER


#define FT_SERVICE_ID_OPENTYPE_VALIDATE  "opentype-validate"


  typedef FT_Error
  (*otv_validate_func)( FT_Face volatile  face,
                        FT_UInt           ot_flags,
                        FT_Bytes         *base,
                        FT_Bytes         *gdef,
                        FT_Bytes         *gpos,
                        FT_Bytes         *gsub,
                        FT_Bytes         *jstf );


  FT_DEFINE_SERVICE( OTvalidate )
  {
    otv_validate_func  validate;
  };

  /* */


FT_END_HEADER


#endif /* __SVOTVAL_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  svpfr.h                                                                */
/*                                                                         */
/*    Internal PFR service functions (specification).                      */
/*                                                                         */
/*  Copyright 2003, 2006 by                                                */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __SVPFR_H__
#define __SVPFR_H__

#include FT_PFR_H
#include FT_INTERNAL_SERVICE_H


FT_BEGIN_HEADER


#define FT_SERVICE_ID_PFR_METRICS  "pfr-metrics"


  typedef FT_Error
  (*FT_PFR_GetMetricsFunc)( FT_Face    face,
                            FT_UInt   *aoutline,
                            FT_UInt   *ametrics,
                            FT_Fixed  *ax_scale,
                            FT_Fixed  *ay_scale );

  typedef FT_Error
  (*FT_PFR_GetKerningFunc)( FT_Face     face,
                            FT_UInt     left,
                            FT_UInt     right,
                            FT_Vector  *avector );

  typedef FT_Error
  (*FT_PFR_GetAdvanceFunc)( FT_Face   face,
                            FT_UInt   gindex,
                            FT_Pos   *aadvance );


  FT_DEFINE_SERVICE( PfrMetrics )
  {
    FT_PFR_GetMetricsFunc  get_metrics;
    FT_PFR_GetKerningFunc  get_kerning;
    FT_PFR_GetAdvanceFunc  get_advance;

  };

 /* */

FT_END_HEADER

#endif /* __SVPFR_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  svpostnm.h                                                             */
/*                                                                         */
/*    The FreeType PostScript name services (specification).               */
/*                                                                         */
/*  Copyright 2003, 2007 by                                                */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __SVPOSTNM_H__
#define __SVPOSTNM_H__

#include FT_INTERNAL_SERVICE_H


FT_BEGIN_HEADER

  /*
   *  A trivial service used to retrieve the PostScript name of a given
   *  font when available.  The `get_name' field should never be NULL.
   *
   *  The corresponding function can return NULL to indicate that the
   *  PostScript name is not available.
   *
   *  The name is owned by the face and will be destroyed with it.
   */

#define FT_SERVICE_ID_POSTSCRIPT_FONT_NAME  "postscript-font-name"


  typedef const char*
  (*FT_PsName_GetFunc)( FT_Face  face );


  FT_DEFINE_SERVICE( PsFontName )
  {
    FT_PsName_GetFunc  get_ps_font_name;
  };

#ifndef FT_CONFIG_OPTION_PIC

#define FT_DEFINE_SERVICE_PSFONTNAMEREC(class_, get_ps_font_name_) \
  static const FT_Service_PsFontNameRec class_ =                   \
  {                                                                \
    get_ps_font_name_                                              \
  };

#else /* FT_CONFIG_OPTION_PIC */ 

#define FT_DEFINE_SERVICE_PSFONTNAMEREC(class_, get_ps_font_name_) \
  void                                                             \
  FT_Init_Class_##class_( FT_Library library,                      \
                          FT_Service_PsFontNameRec* clazz)         \
  {                                                                \
    FT_UNUSED(library);                                            \
    clazz->get_ps_font_name = get_ps_font_name_;                   \
  } 

#endif /* FT_CONFIG_OPTION_PIC */ 

  /* */


FT_END_HEADER


#endif /* __SVPOSTNM_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  svpscmap.h                                                             */
/*                                                                         */
/*    The FreeType PostScript charmap service (specification).             */
/*                                                                         */
/*  Copyright 2003, 2006 by                                                */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __SVPSCMAP_H__
#define __SVPSCMAP_H__

#include FT_INTERNAL_OBJECTS_H


FT_BEGIN_HEADER


#define FT_SERVICE_ID_POSTSCRIPT_CMAPS  "postscript-cmaps"


  /*
   *  Adobe glyph name to unicode value.
   */
  typedef FT_UInt32
  (*PS_Unicode_ValueFunc)( const char*  glyph_name );

  /*
   *  Macintosh name id to glyph name.  NULL if invalid index.
   */
  typedef const char*
  (*PS_Macintosh_NameFunc)( FT_UInt  name_index );

  /*
   *  Adobe standard string ID to glyph name.  NULL if invalid index.
   */
  typedef const char*
  (*PS_Adobe_Std_StringsFunc)( FT_UInt  string_index );


  /*
   *  Simple unicode -> glyph index charmap built from font glyph names
   *  table.
   */
  typedef struct  PS_UniMap_
  {
    FT_UInt32  unicode;      /* bit 31 set: is glyph variant */
    FT_UInt    glyph_index;

  } PS_UniMap;


  typedef struct PS_UnicodesRec_*  PS_Unicodes;

  typedef struct  PS_UnicodesRec_
  {
    FT_CMapRec  cmap;
    FT_UInt     num_maps;
    PS_UniMap*  maps;

  } PS_UnicodesRec;


  /*
   *  A function which returns a glyph name for a given index.  Returns
   *  NULL if invalid index.
   */
  typedef const char*
  (*PS_GetGlyphNameFunc)( FT_Pointer  data,
                          FT_UInt     string_index );

  /*
   *  A function used to release the glyph name returned by
   *  PS_GetGlyphNameFunc, when needed
   */
  typedef void
  (*PS_FreeGlyphNameFunc)( FT_Pointer  data,
                           const char*  name );

  typedef FT_Error
  (*PS_Unicodes_InitFunc)( FT_Memory             memory,
                           PS_Unicodes           unicodes,
                           FT_UInt               num_glyphs,
                           PS_GetGlyphNameFunc   get_glyph_name,
                           PS_FreeGlyphNameFunc  free_glyph_name,
                           FT_Pointer            glyph_data );

  typedef FT_UInt
  (*PS_Unicodes_CharIndexFunc)( PS_Unicodes  unicodes,
                                FT_UInt32    unicode );

  typedef FT_UInt32
  (*PS_Unicodes_CharNextFunc)( PS_Unicodes  unicodes,
                               FT_UInt32   *unicode );


  FT_DEFINE_SERVICE( PsCMaps )
  {
    PS_Unicode_ValueFunc       unicode_value;

    PS_Unicodes_InitFunc       unicodes_init;
    PS_Unicodes_CharIndexFunc  unicodes_char_index;
    PS_Unicodes_CharNextFunc   unicodes_char_next;

    PS_Macintosh_NameFunc      macintosh_name;
    PS_Adobe_Std_StringsFunc   adobe_std_strings;
    const unsigned short*      adobe_std_encoding;
    const unsigned short*      adobe_expert_encoding;
  };


#ifndef FT_CONFIG_OPTION_PIC

#define FT_DEFINE_SERVICE_PSCMAPSREC(class_, unicode_value_, unicodes_init_, \
        unicodes_char_index_, unicodes_char_next_, macintosh_name_,          \
        adobe_std_strings_, adobe_std_encoding_, adobe_expert_encoding_)     \
  static const FT_Service_PsCMapsRec class_ =                                \
  {                                                                          \
    unicode_value_, unicodes_init_,                                          \
    unicodes_char_index_, unicodes_char_next_, macintosh_name_,              \
    adobe_std_strings_, adobe_std_encoding_, adobe_expert_encoding_          \
  };

#else /* FT_CONFIG_OPTION_PIC */ 

#define FT_DEFINE_SERVICE_PSCMAPSREC(class_, unicode_value_, unicodes_init_, \
        unicodes_char_index_, unicodes_char_next_, macintosh_name_,          \
        adobe_std_strings_, adobe_std_encoding_, adobe_expert_encoding_)     \
  void                                                                       \
  FT_Init_Class_##class_( FT_Library library,                                \
                          FT_Service_PsCMapsRec* clazz)                      \
  {                                                                          \
    FT_UNUSED(library);                                                      \
    clazz->unicode_value = unicode_value_;                                   \
    clazz->unicodes_init = unicodes_init_;                                   \
    clazz->unicodes_char_index = unicodes_char_index_;                       \
    clazz->unicodes_char_next = unicodes_char_next_;                         \
    clazz->macintosh_name = macintosh_name_;                                 \
    clazz->adobe_std_strings = adobe_std_strings_;                           \
    clazz->adobe_std_encoding = adobe_std_encoding_;                         \
    clazz->adobe_expert_encoding = adobe_expert_encoding_;                   \
  } 

#endif /* FT_CONFIG_OPTION_PIC */ 

  /* */


FT_END_HEADER


#endif /* __SVPSCMAP_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  svpsinfo.h                                                             */
/*                                                                         */
/*    The FreeType PostScript info service (specification).                */
/*                                                                         */
/*  Copyright 2003, 2004, 2009 by                                          */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __SVPSINFO_H__
#define __SVPSINFO_H__

#include FT_INTERNAL_SERVICE_H
#include FT_INTERNAL_TYPE1_TYPES_H


FT_BEGIN_HEADER


#define FT_SERVICE_ID_POSTSCRIPT_INFO  "postscript-info"


  typedef FT_Error
  (*PS_GetFontInfoFunc)( FT_Face          face,
                         PS_FontInfoRec*  afont_info );

  typedef FT_Error
  (*PS_GetFontExtraFunc)( FT_Face           face,
                          PS_FontExtraRec*  afont_extra );

  typedef FT_Int
  (*PS_HasGlyphNamesFunc)( FT_Face   face );

  typedef FT_Error
  (*PS_GetFontPrivateFunc)( FT_Face         face,
                            PS_PrivateRec*  afont_private );


  FT_DEFINE_SERVICE( PsInfo )
  {
    PS_GetFontInfoFunc     ps_get_font_info;
    PS_GetFontExtraFunc    ps_get_font_extra;
    PS_HasGlyphNamesFunc   ps_has_glyph_names;
    PS_GetFontPrivateFunc  ps_get_font_private;
  };

#ifndef FT_CONFIG_OPTION_PIC

#define FT_DEFINE_SERVICE_PSINFOREC(class_, get_font_info_,      \
        ps_get_font_extra_, has_glyph_names_, get_font_private_) \
  static const FT_Service_PsInfoRec class_ =                     \
  {                                                              \
    get_font_info_, ps_get_font_extra_, has_glyph_names_,        \
    get_font_private_                                            \
  };

#else /* FT_CONFIG_OPTION_PIC */ 

#define FT_DEFINE_SERVICE_PSINFOREC(class_, get_font_info_,      \
        ps_get_font_extra_, has_glyph_names_, get_font_private_) \
  void                                                           \
  FT_Init_Class_##class_( FT_Library library,                    \
                          FT_Service_PsInfoRec*  clazz)          \
  {                                                              \
    FT_UNUSED(library);                                          \
    clazz->ps_get_font_info = get_font_info_;                    \
    clazz->ps_get_font_extra = ps_get_font_extra_;               \
    clazz->ps_has_glyph_names = has_glyph_names_;                \
    clazz->ps_get_font_private = get_font_private_;              \
  } 

#endif /* FT_CONFIG_OPTION_PIC */ 

  /* */


FT_END_HEADER


#endif /* __SVPSINFO_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  svsfnt.h                                                               */
/*                                                                         */
/*    The FreeType SFNT table loading service (specification).             */
/*                                                                         */
/*  Copyright 2003, 2004 by                                                */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __SVSFNT_H__
#define __SVSFNT_H__

#include FT_INTERNAL_SERVICE_H
#include FT_TRUETYPE_TABLES_H


FT_BEGIN_HEADER


  /*
   *  SFNT table loading service.
   */

#define FT_SERVICE_ID_SFNT_TABLE  "sfnt-table"


  /*
   * Used to implement FT_Load_Sfnt_Table().
   */
  typedef FT_Error
  (*FT_SFNT_TableLoadFunc)( FT_Face    face,
                            FT_ULong   tag,
                            FT_Long    offset,
                            FT_Byte*   buffer,
                            FT_ULong*  length );

  /*
   * Used to implement FT_Get_Sfnt_Table().
   */
  typedef void*
  (*FT_SFNT_TableGetFunc)( FT_Face      face,
                           FT_Sfnt_Tag  tag );


  /*
   * Used to implement FT_Sfnt_Table_Info().
   */
  typedef FT_Error
  (*FT_SFNT_TableInfoFunc)( FT_Face    face,
                            FT_UInt    idx,
                            FT_ULong  *tag,
                            FT_ULong  *offset,
                            FT_ULong  *length );


  FT_DEFINE_SERVICE( SFNT_Table )
  {
    FT_SFNT_TableLoadFunc  load_table;
    FT_SFNT_TableGetFunc   get_table;
    FT_SFNT_TableInfoFunc  table_info;
  };

#ifndef FT_CONFIG_OPTION_PIC

#define FT_DEFINE_SERVICE_SFNT_TABLEREC(class_, load_, get_, info_)  \
  static const FT_Service_SFNT_TableRec class_ =                     \
  {                                                                  \
    load_, get_, info_                                               \
  };

#else /* FT_CONFIG_OPTION_PIC */ 

#define FT_DEFINE_SERVICE_SFNT_TABLEREC(class_, load_, get_, info_) \
  void                                                              \
  FT_Init_Class_##class_( FT_Service_SFNT_TableRec*  clazz )        \
  {                                                                 \
    clazz->load_table = load_;                                      \
    clazz->get_table = get_;                                        \
    clazz->table_info = info_;                                      \
  } 

#endif /* FT_CONFIG_OPTION_PIC */ 

  /* */


FT_END_HEADER


#endif /* __SVSFNT_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  svttcmap.h                                                             */
/*                                                                         */
/*    The FreeType TrueType/sfnt cmap extra information service.           */
/*                                                                         */
/*  Copyright 2003 by                                                      */
/*  Masatake YAMATO, Redhat K.K.                                           */
/*                                                                         */
/*  Copyright 2003, 2008 by                                                */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/

/* Development of this service is support of
   Information-technology Promotion Agency, Japan. */

#ifndef __SVTTCMAP_H__
#define __SVTTCMAP_H__

#include FT_INTERNAL_SERVICE_H
#include FT_TRUETYPE_TABLES_H


FT_BEGIN_HEADER


#define FT_SERVICE_ID_TT_CMAP "tt-cmaps"


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    TT_CMapInfo                                                        */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A structure used to store TrueType/sfnt specific cmap information  */
  /*    which is not covered by the generic @FT_CharMap structure.  This   */
  /*    structure can be accessed with the @FT_Get_TT_CMap_Info function.  */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    language ::                                                        */
  /*      The language ID used in Mac fonts.  Definitions of values are in */
  /*      freetype/ttnameid.h.                                             */
  /*                                                                       */
  /*    format ::                                                          */
  /*      The cmap format.  OpenType 1.5 defines the formats 0 (byte       */
  /*      encoding table), 2~(high-byte mapping through table), 4~(segment */
  /*      mapping to delta values), 6~(trimmed table mapping), 8~(mixed    */
  /*      16-bit and 32-bit coverage), 10~(trimmed array), 12~(segmented   */
  /*      coverage), and 14 (Unicode Variation Sequences).                 */
  /*                                                                       */
  typedef struct  TT_CMapInfo_
  {
    FT_ULong language;
    FT_Long  format;

  } TT_CMapInfo;


  typedef FT_Error
  (*TT_CMap_Info_GetFunc)( FT_CharMap    charmap,
                           TT_CMapInfo  *cmap_info );


  FT_DEFINE_SERVICE( TTCMaps )
  {
    TT_CMap_Info_GetFunc  get_cmap_info;
  };

#ifndef FT_CONFIG_OPTION_PIC

#define FT_DEFINE_SERVICE_TTCMAPSREC(class_, get_cmap_info_)  \
  static const FT_Service_TTCMapsRec class_ =                 \
  {                                                           \
    get_cmap_info_                                            \
  };

#else /* FT_CONFIG_OPTION_PIC */ 

#define FT_DEFINE_SERVICE_TTCMAPSREC(class_, get_cmap_info_) \
  void                                                       \
  FT_Init_Class_##class_( FT_Library library,                \
                          FT_Service_TTCMapsRec*  clazz)     \
  {                                                          \
    FT_UNUSED(library);                                      \
    clazz->get_cmap_info = get_cmap_info_;                   \
  } 

#endif /* FT_CONFIG_OPTION_PIC */ 

  /* */


FT_END_HEADER

#endif /* __SVTTCMAP_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  svtteng.h                                                              */
/*                                                                         */
/*    The FreeType TrueType engine query service (specification).          */
/*                                                                         */
/*  Copyright 2006 by                                                      */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __SVTTENG_H__
#define __SVTTENG_H__

#include FT_INTERNAL_SERVICE_H
#include FT_MODULE_H


FT_BEGIN_HEADER


  /*
   *  SFNT table loading service.
   */

#define FT_SERVICE_ID_TRUETYPE_ENGINE  "truetype-engine"

  /*
   * Used to implement FT_Get_TrueType_Engine_Type
   */

  FT_DEFINE_SERVICE( TrueTypeEngine )
  {
    FT_TrueTypeEngineType  engine_type;
  };

  /* */


FT_END_HEADER


#endif /* __SVTTENG_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  svttglyf.h                                                             */
/*                                                                         */
/*    The FreeType TrueType glyph service.                                 */
/*                                                                         */
/*  Copyright 2007 by David Turner.                                        */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/

#ifndef __SVTTGLYF_H__
#define __SVTTGLYF_H__

#include FT_INTERNAL_SERVICE_H
#include FT_TRUETYPE_TABLES_H


FT_BEGIN_HEADER


#define FT_SERVICE_ID_TT_GLYF "tt-glyf"


  typedef FT_ULong
  (*TT_Glyf_GetLocationFunc)( FT_Face    face,
                              FT_UInt    gindex,
                              FT_ULong  *psize );

  FT_DEFINE_SERVICE( TTGlyf )
  {
    TT_Glyf_GetLocationFunc  get_location;
  };

#ifndef FT_CONFIG_OPTION_PIC

#define FT_DEFINE_SERVICE_TTGLYFREC(class_, get_location_ )   \
  static const FT_Service_TTGlyfRec class_ =                  \
  {                                                           \
    get_location_                                             \
  };

#else /* FT_CONFIG_OPTION_PIC */ 

#define FT_DEFINE_SERVICE_TTGLYFREC(class_, get_location_ )   \
  void                                                        \
  FT_Init_Class_##class_( FT_Service_TTGlyfRec*  clazz )      \
  {                                                           \
    clazz->get_location = get_location_;                      \
  } 

#endif /* FT_CONFIG_OPTION_PIC */ 

  /* */


FT_END_HEADER

#endif /* __SVTTGLYF_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  svwinfnt.h                                                             */
/*                                                                         */
/*    The FreeType Windows FNT/FONT service (specification).               */
/*                                                                         */
/*  Copyright 2003 by                                                      */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __SVWINFNT_H__
#define __SVWINFNT_H__

#include FT_INTERNAL_SERVICE_H
#include FT_WINFONTS_H


FT_BEGIN_HEADER


#define FT_SERVICE_ID_WINFNT  "winfonts"

  typedef FT_Error
  (*FT_WinFnt_GetHeaderFunc)( FT_Face               face,
                              FT_WinFNT_HeaderRec  *aheader );


  FT_DEFINE_SERVICE( WinFnt )
  {
    FT_WinFnt_GetHeaderFunc  get_header;
  };

  /* */


FT_END_HEADER


#endif /* __SVWINFNT_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  svxf86nm.h                                                             */
/*                                                                         */
/*    The FreeType XFree86 services (specification only).                  */
/*                                                                         */
/*  Copyright 2003 by                                                      */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __SVXF86NM_H__
#define __SVXF86NM_H__

#include FT_INTERNAL_SERVICE_H


FT_BEGIN_HEADER


  /*
   *  A trivial service used to return the name of a face's font driver,
   *  according to the XFree86 nomenclature.  Note that the service data
   *  is a simple constant string pointer.
   */

#define FT_SERVICE_ID_XF86_NAME  "xf86-driver-name"

#define FT_XF86_FORMAT_TRUETYPE  "TrueType"
#define FT_XF86_FORMAT_TYPE_1    "Type 1"
#define FT_XF86_FORMAT_BDF       "BDF"
#define FT_XF86_FORMAT_PCF       "PCF"
#define FT_XF86_FORMAT_TYPE_42   "Type 42"
#define FT_XF86_FORMAT_CID       "CID Type 1"
#define FT_XF86_FORMAT_CFF       "CFF"
#define FT_XF86_FORMAT_PFR       "PFR"
#define FT_XF86_FORMAT_WINFNT    "Windows FNT"

  /* */


FT_END_HEADER


#endif /* __SVXF86NM_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  sfnt.h                                                                 */
/*                                                                         */
/*    High-level `sfnt' driver interface (specification).                  */
/*                                                                         */
/*  Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006 by                   */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __SFNT_H__
#define __SFNT_H__


#include <ft2build.h>
#include FT_INTERNAL_DRIVER_H
#include FT_INTERNAL_TRUETYPE_TYPES_H


FT_BEGIN_HEADER


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    TT_Init_Face_Func                                                  */
  /*                                                                       */
  /* <Description>                                                         */
  /*    First part of the SFNT face object initialization.  This finds     */
  /*    the face in a SFNT file or collection, and load its format tag in  */
  /*    face->format_tag.                                                  */
  /*                                                                       */
  /* <Input>                                                               */
  /*    stream     :: The input stream.                                    */
  /*                                                                       */
  /*    face       :: A handle to the target face object.                  */
  /*                                                                       */
  /*    face_index :: The index of the TrueType font, if we are opening a  */
  /*                  collection.                                          */
  /*                                                                       */
  /*    num_params :: The number of additional parameters.                 */
  /*                                                                       */
  /*    params     :: Optional additional parameters.                      */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0 means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The stream cursor must be at the font file's origin.               */
  /*                                                                       */
  /*    This function recognizes fonts embedded in a `TrueType             */
  /*    collection'.                                                       */
  /*                                                                       */
  /*    Once the format tag has been validated by the font driver, it      */
  /*    should then call the TT_Load_Face_Func() callback to read the rest */
  /*    of the SFNT tables in the object.                                  */
  /*                                                                       */
  typedef FT_Error
  (*TT_Init_Face_Func)( FT_Stream      stream,
                        TT_Face        face,
                        FT_Int         face_index,
                        FT_Int         num_params,
                        FT_Parameter*  params );


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    TT_Load_Face_Func                                                  */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Second part of the SFNT face object initialization.  This loads    */
  /*    the common SFNT tables (head, OS/2, maxp, metrics, etc.) in the    */
  /*    face object.                                                       */
  /*                                                                       */
  /* <Input>                                                               */
  /*    stream     :: The input stream.                                    */
  /*                                                                       */
  /*    face       :: A handle to the target face object.                  */
  /*                                                                       */
  /*    face_index :: The index of the TrueType font, if we are opening a  */
  /*                  collection.                                          */
  /*                                                                       */
  /*    num_params :: The number of additional parameters.                 */
  /*                                                                       */
  /*    params     :: Optional additional parameters.                      */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0 means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    This function must be called after TT_Init_Face_Func().            */
  /*                                                                       */
  typedef FT_Error
  (*TT_Load_Face_Func)( FT_Stream      stream,
                        TT_Face        face,
                        FT_Int         face_index,
                        FT_Int         num_params,
                        FT_Parameter*  params );


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    TT_Done_Face_Func                                                  */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A callback used to delete the common SFNT data from a face.        */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face :: A handle to the target face object.                        */
  /*                                                                       */
  /* <Note>                                                                */
  /*    This function does NOT destroy the face object.                    */
  /*                                                                       */
  typedef void
  (*TT_Done_Face_Func)( TT_Face  face );


#ifdef FT_CONFIG_OPTION_OLD_INTERNALS

  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    TT_Load_SFNT_HeaderRec_Func                                        */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Loads the header of a SFNT font file.  Supports collections.       */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face       :: A handle to the target face object.                  */
  /*                                                                       */
  /*    stream     :: The input stream.                                    */
  /*                                                                       */
  /*    face_index :: The index of the TrueType font, if we are opening a  */
  /*                  collection.                                          */
  /*                                                                       */
  /* <Output>                                                              */
  /*    sfnt       :: The SFNT header.                                     */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0 means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The stream cursor must be at the font file's origin.               */
  /*                                                                       */
  /*    This function recognizes fonts embedded in a `TrueType             */
  /*    collection'.                                                       */
  /*                                                                       */
  /*    This function checks that the header is valid by looking at the    */
  /*    values of `search_range', `entry_selector', and `range_shift'.     */
  /*                                                                       */
  typedef FT_Error
  (*TT_Load_SFNT_HeaderRec_Func)( TT_Face      face,
                                  FT_Stream    stream,
                                  FT_Long      face_index,
                                  SFNT_Header  sfnt );


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    TT_Load_Directory_Func                                             */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Loads the table directory into a face object.                      */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face   :: A handle to the target face object.                      */
  /*                                                                       */
  /*    stream :: The input stream.                                        */
  /*                                                                       */
  /*    sfnt   :: The SFNT header.                                         */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0 means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The stream cursor must be on the first byte after the 4-byte font  */
  /*    format tag.  This is the case just after a call to                 */
  /*    TT_Load_Format_Tag().                                              */
  /*                                                                       */
  typedef FT_Error
  (*TT_Load_Directory_Func)( TT_Face      face,
                             FT_Stream    stream,
                             SFNT_Header  sfnt );

#endif /* FT_CONFIG_OPTION_OLD_INTERNALS */


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    TT_Load_Any_Func                                                   */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Load any font table into client memory.                            */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face   :: The face object to look for.                             */
  /*                                                                       */
  /*    tag    :: The tag of table to load.  Use the value 0 if you want   */
  /*              to access the whole font file, else set this parameter   */
  /*              to a valid TrueType table tag that you can forge with    */
  /*              the MAKE_TT_TAG macro.                                   */
  /*                                                                       */
  /*    offset :: The starting offset in the table (or the file if         */
  /*              tag == 0).                                               */
  /*                                                                       */
  /*    length :: The address of the decision variable:                    */
  /*                                                                       */
  /*                If length == NULL:                                     */
  /*                  Loads the whole table.  Returns an error if          */
  /*                  `offset' == 0!                                       */
  /*                                                                       */
  /*                If *length == 0:                                       */
  /*                  Exits immediately; returning the length of the given */
  /*                  table or of the font file, depending on the value of */
  /*                  `tag'.                                               */
  /*                                                                       */
  /*                If *length != 0:                                       */
  /*                  Loads the next `length' bytes of table or font,      */
  /*                  starting at offset `offset' (in table or font too).  */
  /*                                                                       */
  /* <Output>                                                              */
  /*    buffer :: The address of target buffer.                            */
  /*                                                                       */
  /* <Return>                                                              */
  /*    TrueType error code.  0 means success.                             */
  /*                                                                       */
  typedef FT_Error
  (*TT_Load_Any_Func)( TT_Face    face,
                       FT_ULong   tag,
                       FT_Long    offset,
                       FT_Byte   *buffer,
                       FT_ULong*  length );


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    TT_Find_SBit_Image_Func                                            */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Check whether an embedded bitmap (an `sbit') exists for a given    */
  /*    glyph, at a given strike.                                          */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face          :: The target face object.                           */
  /*                                                                       */
  /*    glyph_index   :: The glyph index.                                  */
  /*                                                                       */
  /*    strike_index  :: The current strike index.                         */
  /*                                                                       */
  /* <Output>                                                              */
  /*    arange        :: The SBit range containing the glyph index.        */
  /*                                                                       */
  /*    astrike       :: The SBit strike containing the glyph index.       */
  /*                                                                       */
  /*    aglyph_offset :: The offset of the glyph data in `EBDT' table.     */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0 means success.  Returns                    */
  /*    SFNT_Err_Invalid_Argument if no sbit exists for the requested      */
  /*    glyph.                                                             */
  /*                                                                       */
  typedef FT_Error
  (*TT_Find_SBit_Image_Func)( TT_Face          face,
                              FT_UInt          glyph_index,
                              FT_ULong         strike_index,
                              TT_SBit_Range   *arange,
                              TT_SBit_Strike  *astrike,
                              FT_ULong        *aglyph_offset );


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    TT_Load_SBit_Metrics_Func                                          */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Get the big metrics for a given embedded bitmap.                   */
  /*                                                                       */
  /* <Input>                                                               */
  /*    stream      :: The input stream.                                   */
  /*                                                                       */
  /*    range       :: The SBit range containing the glyph.                */
  /*                                                                       */
  /* <Output>                                                              */
  /*    big_metrics :: A big SBit metrics structure for the glyph.         */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0 means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The stream cursor must be positioned at the glyph's offset within  */
  /*    the `EBDT' table before the call.                                  */
  /*                                                                       */
  /*    If the image format uses variable metrics, the stream cursor is    */
  /*    positioned just after the metrics header in the `EBDT' table on    */
  /*    function exit.                                                     */
  /*                                                                       */
  typedef FT_Error
  (*TT_Load_SBit_Metrics_Func)( FT_Stream        stream,
                                TT_SBit_Range    range,
                                TT_SBit_Metrics  metrics );


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    TT_Load_SBit_Image_Func                                            */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Load a given glyph sbit image from the font resource.  This also   */
  /*    returns its metrics.                                               */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face ::                                                            */
  /*      The target face object.                                          */
  /*                                                                       */
  /*    strike_index ::                                                    */
  /*      The strike index.                                                */
  /*                                                                       */
  /*    glyph_index ::                                                     */
  /*      The current glyph index.                                         */
  /*                                                                       */
  /*    load_flags ::                                                      */
  /*      The current load flags.                                          */
  /*                                                                       */
  /*    stream ::                                                          */
  /*      The input stream.                                                */
  /*                                                                       */
  /* <Output>                                                              */
  /*    amap ::                                                            */
  /*      The target pixmap.                                               */
  /*                                                                       */
  /*    ametrics ::                                                        */
  /*      A big sbit metrics structure for the glyph image.                */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0 means success.  Returns an error if no     */
  /*    glyph sbit exists for the index.                                   */
  /*                                                                       */
  /*  <Note>                                                               */
  /*    The `map.buffer' field is always freed before the glyph is loaded. */
  /*                                                                       */
  typedef FT_Error
  (*TT_Load_SBit_Image_Func)( TT_Face              face,
                              FT_ULong             strike_index,
                              FT_UInt              glyph_index,
                              FT_UInt              load_flags,
                              FT_Stream            stream,
                              FT_Bitmap           *amap,
                              TT_SBit_MetricsRec  *ametrics );


#ifdef FT_CONFIG_OPTION_OLD_INTERNALS

  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    TT_Set_SBit_Strike_OldFunc                                         */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Select an sbit strike for a given size request.                    */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face          :: The target face object.                           */
  /*                                                                       */
  /*    req           :: The size request.                                 */
  /*                                                                       */
  /* <Output>                                                              */
  /*    astrike_index :: The index of the sbit strike.                     */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0 means success.  Returns an error if no     */
  /*    sbit strike exists for the selected ppem values.                   */
  /*                                                                       */
  typedef FT_Error
  (*TT_Set_SBit_Strike_OldFunc)( TT_Face    face,
                                 FT_UInt    x_ppem,
                                 FT_UInt    y_ppem,
                                 FT_ULong*  astrike_index );


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    TT_CharMap_Load_Func                                               */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Loads a given TrueType character map into memory.                  */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face   :: A handle to the parent face object.                      */
  /*                                                                       */
  /*    stream :: A handle to the current stream object.                   */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    cmap   :: A pointer to a cmap object.                              */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0 means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The function assumes that the stream is already in use (i.e.,      */
  /*    opened).  In case of error, all partially allocated tables are     */
  /*    released.                                                          */
  /*                                                                       */
  typedef FT_Error
  (*TT_CharMap_Load_Func)( TT_Face    face,
                           void*      cmap,
                           FT_Stream  input );


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    TT_CharMap_Free_Func                                               */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Destroys a character mapping table.                                */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face :: A handle to the parent face object.                        */
  /*                                                                       */
  /*    cmap :: A handle to a cmap object.                                 */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0 means success.                             */
  /*                                                                       */
  typedef FT_Error
  (*TT_CharMap_Free_Func)( TT_Face       face,
                           void*         cmap );

#endif /* FT_CONFIG_OPTION_OLD_INTERNALS */


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    TT_Set_SBit_Strike_Func                                            */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Select an sbit strike for a given size request.                    */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face          :: The target face object.                           */
  /*                                                                       */
  /*    req           :: The size request.                                 */
  /*                                                                       */
  /* <Output>                                                              */
  /*    astrike_index :: The index of the sbit strike.                     */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0 means success.  Returns an error if no     */
  /*    sbit strike exists for the selected ppem values.                   */
  /*                                                                       */
  typedef FT_Error
  (*TT_Set_SBit_Strike_Func)( TT_Face          face,
                              FT_Size_Request  req,
                              FT_ULong*        astrike_index );


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    TT_Load_Strike_Metrics_Func                                        */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Load the metrics of a given strike.                                */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face          :: The target face object.                           */
  /*                                                                       */
  /*    strike_index  :: The strike index.                                 */
  /*                                                                       */
  /* <Output>                                                              */
  /*    metrics       :: the metrics of the strike.                        */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0 means success.  Returns an error if no     */
  /*    such sbit strike exists.                                           */
  /*                                                                       */
  typedef FT_Error
  (*TT_Load_Strike_Metrics_Func)( TT_Face           face,
                                  FT_ULong          strike_index,
                                  FT_Size_Metrics*  metrics );


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    TT_Get_PS_Name_Func                                                */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Get the PostScript glyph name of a glyph.                          */
  /*                                                                       */
  /* <Input>                                                               */
  /*    idx  :: The glyph index.                                           */
  /*                                                                       */
  /*    PSname :: The address of a string pointer.  Will be NULL in case   */
  /*              of error, otherwise it is a pointer to the glyph name.   */
  /*                                                                       */
  /*              You must not modify the returned string!                 */
  /*                                                                       */
  /* <Output>                                                              */
  /*    FreeType error code.  0 means success.                             */
  /*                                                                       */
  typedef FT_Error
  (*TT_Get_PS_Name_Func)( TT_Face      face,
                          FT_UInt      idx,
                          FT_String**  PSname );


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    TT_Load_Metrics_Func                                               */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Load a metrics table, which is a table with a horizontal and a     */
  /*    vertical version.                                                  */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face     :: A handle to the target face object.                    */
  /*                                                                       */
  /*    stream   :: The input stream.                                      */
  /*                                                                       */
  /*    vertical :: A boolean flag.  If set, load the vertical one.        */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0 means success.                             */
  /*                                                                       */
  typedef FT_Error
  (*TT_Load_Metrics_Func)( TT_Face    face,
                           FT_Stream  stream,
                           FT_Bool    vertical );


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    TT_Get_Metrics_Func                                                */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Load the horizontal or vertical header in a face object.           */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face     :: A handle to the target face object.                    */
  /*                                                                       */
  /*    stream   :: The input stream.                                      */
  /*                                                                       */
  /*    vertical :: A boolean flag.  If set, load vertical metrics.        */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0 means success.                             */
  /*                                                                       */
  typedef FT_Error
  (*TT_Get_Metrics_Func)( TT_Face     face,
                          FT_Bool     vertical,
                          FT_UInt     gindex,
                          FT_Short*   abearing,
                          FT_UShort*  aadvance );


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    TT_Load_Table_Func                                                 */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Load a given TrueType table.                                       */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face   :: A handle to the target face object.                      */
  /*                                                                       */
  /*    stream :: The input stream.                                        */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0 means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The function uses `face->goto_table' to seek the stream to the     */
  /*    start of the table, except while loading the font directory.       */
  /*                                                                       */
  typedef FT_Error
  (*TT_Load_Table_Func)( TT_Face    face,
                         FT_Stream  stream );


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    TT_Free_Table_Func                                                 */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Free a given TrueType table.                                       */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face :: A handle to the target face object.                        */
  /*                                                                       */
  typedef void
  (*TT_Free_Table_Func)( TT_Face  face );


  /*
   * @functype:
   *    TT_Face_GetKerningFunc
   *
   * @description:
   *    Return the horizontal kerning value between two glyphs.
   *
   * @input:
   *    face        :: A handle to the source face object.
   *    left_glyph  :: The left glyph index.
   *    right_glyph :: The right glyph index.
   *
   * @return:
   *    The kerning value in font units.
   */
  typedef FT_Int
  (*TT_Face_GetKerningFunc)( TT_Face  face,
                             FT_UInt  left_glyph,
                             FT_UInt  right_glyph );


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    SFNT_Interface                                                     */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This structure holds pointers to the functions used to load and    */
  /*    free the basic tables that are required in a `sfnt' font file.     */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    Check the various xxx_Func() descriptions for details.             */
  /*                                                                       */
  typedef struct  SFNT_Interface_
  {
    TT_Loader_GotoTableFunc      goto_table;

    TT_Init_Face_Func            init_face;
    TT_Load_Face_Func            load_face;
    TT_Done_Face_Func            done_face;
    FT_Module_Requester          get_interface;

    TT_Load_Any_Func             load_any;

#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
    TT_Load_SFNT_HeaderRec_Func  load_sfnt_header;
    TT_Load_Directory_Func       load_directory;
#endif

    /* these functions are called by `load_face' but they can also  */
    /* be called from external modules, if there is a need to do so */
    TT_Load_Table_Func           load_head;
    TT_Load_Metrics_Func         load_hhea;
    TT_Load_Table_Func           load_cmap;
    TT_Load_Table_Func           load_maxp;
    TT_Load_Table_Func           load_os2;
    TT_Load_Table_Func           load_post;

    TT_Load_Table_Func           load_name;
    TT_Free_Table_Func           free_name;

    /* optional tables */
#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
    TT_Load_Table_Func           load_hdmx_stub;
    TT_Free_Table_Func           free_hdmx_stub;
#endif

    /* this field was called `load_kerning' up to version 2.1.10 */
    TT_Load_Table_Func           load_kern;

    TT_Load_Table_Func           load_gasp;
    TT_Load_Table_Func           load_pclt;

    /* see `ttload.h'; this field was called `load_bitmap_header' up to */
    /* version 2.1.10                                                   */
    TT_Load_Table_Func           load_bhed;

#ifdef FT_CONFIG_OPTION_OLD_INTERNALS

    /* see `ttsbit.h' */
    TT_Set_SBit_Strike_OldFunc   set_sbit_strike_stub;
    TT_Load_Table_Func           load_sbits_stub;

    /*
     *  The following two fields appeared in version 2.1.8, and were placed
     *  between `load_sbits' and `load_sbit_image'.  We support them as a
     *  special exception since they are used by Xfont library within the
     *  X.Org xserver, and because the probability that other rogue clients
     *  use the other version 2.1.7 fields below is _extremely_ low.
     *
     *  Note that this forces us to disable an interesting memory-saving
     *  optimization though...
     */

    TT_Find_SBit_Image_Func      find_sbit_image;
    TT_Load_SBit_Metrics_Func    load_sbit_metrics;

#endif

    TT_Load_SBit_Image_Func      load_sbit_image;

#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
    TT_Free_Table_Func           free_sbits_stub;
#endif

    /* see `ttpost.h' */
    TT_Get_PS_Name_Func          get_psname;
    TT_Free_Table_Func           free_psnames;

#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
    TT_CharMap_Load_Func         load_charmap_stub;
    TT_CharMap_Free_Func         free_charmap_stub;
#endif

    /* starting here, the structure differs from version 2.1.7 */

    /* this field was introduced in version 2.1.8, named `get_psname' */
    TT_Face_GetKerningFunc       get_kerning;

    /* new elements introduced after version 2.1.10 */

    /* load the font directory, i.e., the offset table and */
    /* the table directory                                 */
    TT_Load_Table_Func           load_font_dir;
    TT_Load_Metrics_Func         load_hmtx;

    TT_Load_Table_Func           load_eblc;
    TT_Free_Table_Func           free_eblc;

    TT_Set_SBit_Strike_Func      set_sbit_strike;
    TT_Load_Strike_Metrics_Func  load_strike_metrics;

    TT_Get_Metrics_Func          get_metrics;

  } SFNT_Interface;


  /* transitional */
  typedef SFNT_Interface*   SFNT_Service;

#ifndef FT_CONFIG_OPTION_PIC

#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
#define FT_DEFINE_DRIVERS_OLD_INTERNAL(a) \
  a, 
#else
  #define FT_DEFINE_DRIVERS_OLD_INTERNAL(a)
#endif
#define FT_INTERNAL(a) \
  a, 

#define FT_DEFINE_SFNT_INTERFACE(class_,                                     \
    goto_table_, init_face_, load_face_, done_face_, get_interface_,         \
    load_any_, load_sfnt_header_, load_directory_, load_head_,               \
    load_hhea_, load_cmap_, load_maxp_, load_os2_, load_post_,               \
    load_name_, free_name_, load_hdmx_stub_, free_hdmx_stub_,                \
    load_kern_, load_gasp_, load_pclt_, load_bhed_,                          \
    set_sbit_strike_stub_, load_sbits_stub_, find_sbit_image_,               \
    load_sbit_metrics_, load_sbit_image_, free_sbits_stub_,                  \
    get_psname_, free_psnames_, load_charmap_stub_, free_charmap_stub_,      \
    get_kerning_, load_font_dir_, load_hmtx_, load_eblc_, free_eblc_,        \
    set_sbit_strike_, load_strike_metrics_, get_metrics_ )                   \
  static const SFNT_Interface class_ =                                       \
  {                                                                          \
    FT_INTERNAL(goto_table_) \
    FT_INTERNAL(init_face_) \
    FT_INTERNAL(load_face_) \
    FT_INTERNAL(done_face_) \
    FT_INTERNAL(get_interface_) \
    FT_INTERNAL(load_any_) \
    FT_DEFINE_DRIVERS_OLD_INTERNAL(load_sfnt_header_) \
    FT_DEFINE_DRIVERS_OLD_INTERNAL(load_directory_) \
    FT_INTERNAL(load_head_) \
    FT_INTERNAL(load_hhea_) \
    FT_INTERNAL(load_cmap_) \
    FT_INTERNAL(load_maxp_) \
    FT_INTERNAL(load_os2_) \
    FT_INTERNAL(load_post_) \
    FT_INTERNAL(load_name_) \
    FT_INTERNAL(free_name_) \
    FT_DEFINE_DRIVERS_OLD_INTERNAL(load_hdmx_stub_) \
    FT_DEFINE_DRIVERS_OLD_INTERNAL(free_hdmx_stub_) \
    FT_INTERNAL(load_kern_) \
    FT_INTERNAL(load_gasp_) \
    FT_INTERNAL(load_pclt_) \
    FT_INTERNAL(load_bhed_) \
    FT_DEFINE_DRIVERS_OLD_INTERNAL(set_sbit_strike_stub_) \
    FT_DEFINE_DRIVERS_OLD_INTERNAL(load_sbits_stub_) \
    FT_DEFINE_DRIVERS_OLD_INTERNAL(find_sbit_image_) \
    FT_DEFINE_DRIVERS_OLD_INTERNAL(load_sbit_metrics_) \
    FT_INTERNAL(load_sbit_image_) \
    FT_DEFINE_DRIVERS_OLD_INTERNAL(free_sbits_stub_) \
    FT_INTERNAL(get_psname_) \
    FT_INTERNAL(free_psnames_) \
    FT_DEFINE_DRIVERS_OLD_INTERNAL(load_charmap_stub_) \
    FT_DEFINE_DRIVERS_OLD_INTERNAL(free_charmap_stub_) \
    FT_INTERNAL(get_kerning_) \
    FT_INTERNAL(load_font_dir_) \
    FT_INTERNAL(load_hmtx_) \
    FT_INTERNAL(load_eblc_) \
    FT_INTERNAL(free_eblc_) \
    FT_INTERNAL(set_sbit_strike_) \
    FT_INTERNAL(load_strike_metrics_) \
    FT_INTERNAL(get_metrics_) \
  };

#else /* FT_CONFIG_OPTION_PIC */ 

#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
#define FT_DEFINE_DRIVERS_OLD_INTERNAL(a, a_) \
  clazz->a = a_;
#else
  #define FT_DEFINE_DRIVERS_OLD_INTERNAL(a, a_)
#endif
#define FT_INTERNAL(a, a_) \
  clazz->a = a_;

#define FT_DEFINE_SFNT_INTERFACE(class_,                                     \
    goto_table_, init_face_, load_face_, done_face_, get_interface_,         \
    load_any_, load_sfnt_header_, load_directory_, load_head_,               \
    load_hhea_, load_cmap_, load_maxp_, load_os2_, load_post_,               \
    load_name_, free_name_, load_hdmx_stub_, free_hdmx_stub_,                \
    load_kern_, load_gasp_, load_pclt_, load_bhed_,                          \
    set_sbit_strike_stub_, load_sbits_stub_, find_sbit_image_,               \
    load_sbit_metrics_, load_sbit_image_, free_sbits_stub_,                  \
    get_psname_, free_psnames_, load_charmap_stub_, free_charmap_stub_,      \
    get_kerning_, load_font_dir_, load_hmtx_, load_eblc_, free_eblc_,        \
    set_sbit_strike_, load_strike_metrics_, get_metrics_ )                   \
  void                                                                       \
  FT_Init_Class_##class_( FT_Library library, SFNT_Interface*  clazz )       \
  {                                                                          \
    FT_UNUSED(library);                                                      \
    FT_INTERNAL(goto_table,goto_table_) \
    FT_INTERNAL(init_face,init_face_) \
    FT_INTERNAL(load_face,load_face_) \
    FT_INTERNAL(done_face,done_face_) \
    FT_INTERNAL(get_interface,get_interface_) \
    FT_INTERNAL(load_any,load_any_) \
    FT_DEFINE_DRIVERS_OLD_INTERNAL(load_sfnt_header,load_sfnt_header_) \
    FT_DEFINE_DRIVERS_OLD_INTERNAL(load_directory,load_directory_) \
    FT_INTERNAL(load_head,load_head_) \
    FT_INTERNAL(load_hhea,load_hhea_) \
    FT_INTERNAL(load_cmap,load_cmap_) \
    FT_INTERNAL(load_maxp,load_maxp_) \
    FT_INTERNAL(load_os2,load_os2_) \
    FT_INTERNAL(load_post,load_post_) \
    FT_INTERNAL(load_name,load_name_) \
    FT_INTERNAL(free_name,free_name_) \
    FT_DEFINE_DRIVERS_OLD_INTERNAL(load_hdmx_stub,load_hdmx_stub_) \
    FT_DEFINE_DRIVERS_OLD_INTERNAL(free_hdmx_stub,free_hdmx_stub_) \
    FT_INTERNAL(load_kern,load_kern_) \
    FT_INTERNAL(load_gasp,load_gasp_) \
    FT_INTERNAL(load_pclt,load_pclt_) \
    FT_INTERNAL(load_bhed,load_bhed_) \
    FT_DEFINE_DRIVERS_OLD_INTERNAL(set_sbit_strike_stub,set_sbit_strike_stub_) \
    FT_DEFINE_DRIVERS_OLD_INTERNAL(load_sbits_stub,load_sbits_stub_) \
    FT_DEFINE_DRIVERS_OLD_INTERNAL(find_sbit_image,find_sbit_image_) \
    FT_DEFINE_DRIVERS_OLD_INTERNAL(load_sbit_metrics,load_sbit_metrics_) \
    FT_INTERNAL(load_sbit_image,load_sbit_image_) \
    FT_DEFINE_DRIVERS_OLD_INTERNAL(free_sbits_stub,free_sbits_stub_) \
    FT_INTERNAL(get_psname,get_psname_) \
    FT_INTERNAL(free_psnames,free_psnames_) \
    FT_DEFINE_DRIVERS_OLD_INTERNAL(load_charmap_stub,load_charmap_stub_) \
    FT_DEFINE_DRIVERS_OLD_INTERNAL(free_charmap_stub,free_charmap_stub_) \
    FT_INTERNAL(get_kerning,get_kerning_) \
    FT_INTERNAL(load_font_dir,load_font_dir_) \
    FT_INTERNAL(load_hmtx,load_hmtx_) \
    FT_INTERNAL(load_eblc,load_eblc_) \
    FT_INTERNAL(free_eblc,free_eblc_) \
    FT_INTERNAL(set_sbit_strike,set_sbit_strike_) \
    FT_INTERNAL(load_strike_metrics,load_strike_metrics_) \
    FT_INTERNAL(get_metrics,get_metrics_) \
  } 

#endif /* FT_CONFIG_OPTION_PIC */ 

FT_END_HEADER

#endif /* __SFNT_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  t1types.h                                                              */
/*                                                                         */
/*    Basic Type1/Type2 type definitions and interface (specification      */
/*    only).                                                               */
/*                                                                         */
/*  Copyright 1996-2001, 2002, 2003, 2004, 2006, 2008, 2009 by             */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __T1TYPES_H__
#define __T1TYPES_H__


#include <ft2build.h>
#include FT_TYPE1_TABLES_H
#include FT_INTERNAL_POSTSCRIPT_HINTS_H
#include FT_INTERNAL_SERVICE_H
#include FT_SERVICE_POSTSCRIPT_CMAPS_H


FT_BEGIN_HEADER


  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /***                                                                   ***/
  /***                                                                   ***/
  /***              REQUIRED TYPE1/TYPE2 TABLES DEFINITIONS              ***/
  /***                                                                   ***/
  /***                                                                   ***/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    T1_EncodingRec                                                     */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A structure modeling a custom encoding.                            */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    num_chars  :: The number of character codes in the encoding.       */
  /*                  Usually 256.                                         */
  /*                                                                       */
  /*    code_first :: The lowest valid character code in the encoding.     */
  /*                                                                       */
  /*    code_last  :: The highest valid character code in the encoding     */
  /*                  + 1. When equal to code_first there are no valid     */
  /*                  character codes.                                     */
  /*                                                                       */
  /*    char_index :: An array of corresponding glyph indices.             */
  /*                                                                       */
  /*    char_name  :: An array of corresponding glyph names.               */
  /*                                                                       */
  typedef struct  T1_EncodingRecRec_
  {
    FT_Int       num_chars;
    FT_Int       code_first;
    FT_Int       code_last;

    FT_UShort*   char_index;
    FT_String**  char_name;

  } T1_EncodingRec, *T1_Encoding;


  typedef enum  T1_EncodingType_
  {
    T1_ENCODING_TYPE_NONE = 0,
    T1_ENCODING_TYPE_ARRAY,
    T1_ENCODING_TYPE_STANDARD,
    T1_ENCODING_TYPE_ISOLATIN1,
    T1_ENCODING_TYPE_EXPERT

  } T1_EncodingType;


  /* used to hold extra data of PS_FontInfoRec that
   * cannot be stored in the publicly defined structure.
   *
   * Note these can't be blended with multiple-masters.
   */
  typedef struct  PS_FontExtraRec_
  {
    FT_UShort  fs_type;

  } PS_FontExtraRec;


  typedef struct  T1_FontRec_
  {
    PS_FontInfoRec   font_info;         /* font info dictionary   */
    PS_FontExtraRec  font_extra;        /* font info extra fields */
    PS_PrivateRec    private_dict;      /* private dictionary     */
    FT_String*       font_name;         /* top-level dictionary   */

    T1_EncodingType  encoding_type;
    T1_EncodingRec   encoding;

    FT_Byte*         subrs_block;
    FT_Byte*         charstrings_block;
    FT_Byte*         glyph_names_block;

    FT_Int           num_subrs;
    FT_Byte**        subrs;
    FT_PtrDist*      subrs_len;

    FT_Int           num_glyphs;
    FT_String**      glyph_names;       /* array of glyph names       */
    FT_Byte**        charstrings;       /* array of glyph charstrings */
    FT_PtrDist*      charstrings_len;

    FT_Byte          paint_type;
    FT_Byte          font_type;
    FT_Matrix        font_matrix;
    FT_Vector        font_offset;
    FT_BBox          font_bbox;
    FT_Long          font_id;

    FT_Fixed         stroke_width;

  } T1_FontRec, *T1_Font;


  typedef struct  CID_SubrsRec_
  {
    FT_UInt    num_subrs;
    FT_Byte**  code;

  } CID_SubrsRec, *CID_Subrs;


  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /***                                                                   ***/
  /***                                                                   ***/
  /***                AFM FONT INFORMATION STRUCTURES                    ***/
  /***                                                                   ***/
  /***                                                                   ***/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/

  typedef struct  AFM_TrackKernRec_
  {
    FT_Int    degree;
    FT_Fixed  min_ptsize;
    FT_Fixed  min_kern;
    FT_Fixed  max_ptsize;
    FT_Fixed  max_kern;

  } AFM_TrackKernRec, *AFM_TrackKern;

  typedef struct  AFM_KernPairRec_
  {
    FT_Int  index1;
    FT_Int  index2;
    FT_Int  x;
    FT_Int  y;

  } AFM_KernPairRec, *AFM_KernPair;

  typedef struct  AFM_FontInfoRec_
  {
    FT_Bool        IsCIDFont;
    FT_BBox        FontBBox;
    FT_Fixed       Ascender;
    FT_Fixed       Descender;
    AFM_TrackKern  TrackKerns;   /* free if non-NULL */
    FT_Int         NumTrackKern;
    AFM_KernPair   KernPairs;    /* free if non-NULL */
    FT_Int         NumKernPair;

  } AFM_FontInfoRec, *AFM_FontInfo;


  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /***                                                                   ***/
  /***                                                                   ***/
  /***                ORIGINAL T1_FACE CLASS DEFINITION                  ***/
  /***                                                                   ***/
  /***                                                                   ***/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/


  typedef struct T1_FaceRec_*   T1_Face;
  typedef struct CID_FaceRec_*  CID_Face;


  typedef struct  T1_FaceRec_
  {
    FT_FaceRec      root;
    T1_FontRec      type1;
    const void*     psnames;
    const void*     psaux;
    const void*     afm_data;
    FT_CharMapRec   charmaprecs[2];
    FT_CharMap      charmaps[2];

#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
    PS_Unicodes     unicode_map;
#endif

    /* support for Multiple Masters fonts */
    PS_Blend        blend;

    /* undocumented, optional: indices of subroutines that express      */
    /* the NormalizeDesignVector and the ConvertDesignVector procedure, */
    /* respectively, as Type 2 charstrings; -1 if keywords not present  */
    FT_Int           ndv_idx;
    FT_Int           cdv_idx;

    /* undocumented, optional: has the same meaning as len_buildchar */
    /* for Type 2 fonts; manipulated by othersubrs 19, 24, and 25    */
    FT_UInt          len_buildchar;
    FT_Long*         buildchar;

    /* since version 2.1 - interface to PostScript hinter */
    const void*     pshinter;

  } T1_FaceRec;


  typedef struct  CID_FaceRec_
  {
    FT_FaceRec       root;
    void*            psnames;
    void*            psaux;
    CID_FaceInfoRec  cid;
    PS_FontExtraRec  font_extra;
#if 0
    void*            afm_data;
#endif
    CID_Subrs        subrs;

    /* since version 2.1 - interface to PostScript hinter */
    void*            pshinter;

    /* since version 2.1.8, but was originally positioned after `afm_data' */
    FT_Byte*         binary_data; /* used if hex data has been converted */
    FT_Stream        cid_stream;

  } CID_FaceRec;


FT_END_HEADER

#endif /* __T1TYPES_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  tttypes.h                                                              */
/*                                                                         */
/*    Basic SFNT/TrueType type definitions and interface (specification    */
/*    only).                                                               */
/*                                                                         */
/*  Copyright 1996-2001, 2002, 2004, 2005, 2006, 2007, 2008 by             */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __TTTYPES_H__
#define __TTTYPES_H__


#include <ft2build.h>
#include FT_TRUETYPE_TABLES_H
#include FT_INTERNAL_OBJECTS_H

#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
#include FT_MULTIPLE_MASTERS_H
#endif


FT_BEGIN_HEADER


  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /***                                                                   ***/
  /***                                                                   ***/
  /***             REQUIRED TRUETYPE/OPENTYPE TABLES DEFINITIONS         ***/
  /***                                                                   ***/
  /***                                                                   ***/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    TTC_HeaderRec                                                      */
  /*                                                                       */
  /* <Description>                                                         */
  /*    TrueType collection header.  This table contains the offsets of    */
  /*    the font headers of each distinct TrueType face in the file.       */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    tag     :: Must be `ttc ' to indicate a TrueType collection.       */
  /*                                                                       */
  /*    version :: The version number.                                     */
  /*                                                                       */
  /*    count   :: The number of faces in the collection.  The             */
  /*               specification says this should be an unsigned long, but */
  /*               we use a signed long since we need the value -1 for     */
  /*               specific purposes.                                      */
  /*                                                                       */
  /*    offsets :: The offsets of the font headers, one per face.          */
  /*                                                                       */
  typedef struct  TTC_HeaderRec_
  {
    FT_ULong   tag;
    FT_Fixed   version;
    FT_Long    count;
    FT_ULong*  offsets;

  } TTC_HeaderRec;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    SFNT_HeaderRec                                                     */
  /*                                                                       */
  /* <Description>                                                         */
  /*    SFNT file format header.                                           */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    format_tag     :: The font format tag.                             */
  /*                                                                       */
  /*    num_tables     :: The number of tables in file.                    */
  /*                                                                       */
  /*    search_range   :: Must be `16 * (max power of 2 <= num_tables)'.   */
  /*                                                                       */
  /*    entry_selector :: Must be log2 of `search_range / 16'.             */
  /*                                                                       */
  /*    range_shift    :: Must be `num_tables * 16 - search_range'.        */
  /*                                                                       */
  typedef struct  SFNT_HeaderRec_
  {
    FT_ULong   format_tag;
    FT_UShort  num_tables;
    FT_UShort  search_range;
    FT_UShort  entry_selector;
    FT_UShort  range_shift;

    FT_ULong   offset;  /* not in file */

  } SFNT_HeaderRec, *SFNT_Header;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    TT_TableRec                                                        */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This structure describes a given table of a TrueType font.         */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    Tag      :: A four-bytes tag describing the table.                 */
  /*                                                                       */
  /*    CheckSum :: The table checksum.  This value can be ignored.        */
  /*                                                                       */
  /*    Offset   :: The offset of the table from the start of the TrueType */
  /*                font in its resource.                                  */
  /*                                                                       */
  /*    Length   :: The table length (in bytes).                           */
  /*                                                                       */
  typedef struct  TT_TableRec_
  {
    FT_ULong  Tag;        /*        table type */
    FT_ULong  CheckSum;   /*    table checksum */
    FT_ULong  Offset;     /* table file offset */
    FT_ULong  Length;     /*      table length */

  } TT_TableRec, *TT_Table;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    TT_LongMetricsRec                                                  */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A structure modeling the long metrics of the `hmtx' and `vmtx'     */
  /*    TrueType tables.  The values are expressed in font units.          */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    advance :: The advance width or height for the glyph.              */
  /*                                                                       */
  /*    bearing :: The left-side or top-side bearing for the glyph.        */
  /*                                                                       */
  typedef struct  TT_LongMetricsRec_
  {
    FT_UShort  advance;
    FT_Short   bearing;

  } TT_LongMetricsRec, *TT_LongMetrics;


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    TT_ShortMetrics                                                    */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A simple type to model the short metrics of the `hmtx' and `vmtx'  */
  /*    tables.                                                            */
  /*                                                                       */
  typedef FT_Short  TT_ShortMetrics;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    TT_NameEntryRec                                                    */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A structure modeling TrueType name records.  Name records are used */
  /*    to store important strings like family name, style name,           */
  /*    copyright, etc. in _localized_ versions (i.e., language, encoding, */
  /*    etc).                                                              */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    platformID   :: The ID of the name's encoding platform.            */
  /*                                                                       */
  /*    encodingID   :: The platform-specific ID for the name's encoding.  */
  /*                                                                       */
  /*    languageID   :: The platform-specific ID for the name's language.  */
  /*                                                                       */
  /*    nameID       :: The ID specifying what kind of name this is.       */
  /*                                                                       */
  /*    stringLength :: The length of the string in bytes.                 */
  /*                                                                       */
  /*    stringOffset :: The offset to the string in the `name' table.      */
  /*                                                                       */
  /*    string       :: A pointer to the string's bytes.  Note that these  */
  /*                    are usually UTF-16 encoded characters.             */
  /*                                                                       */
  typedef struct  TT_NameEntryRec_
  {
    FT_UShort  platformID;
    FT_UShort  encodingID;
    FT_UShort  languageID;
    FT_UShort  nameID;
    FT_UShort  stringLength;
    FT_ULong   stringOffset;

    /* this last field is not defined in the spec */
    /* but used by the FreeType engine            */

    FT_Byte*   string;

  } TT_NameEntryRec, *TT_NameEntry;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    TT_NameTableRec                                                    */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A structure modeling the TrueType name table.                      */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    format         :: The format of the name table.                    */
  /*                                                                       */
  /*    numNameRecords :: The number of names in table.                    */
  /*                                                                       */
  /*    storageOffset  :: The offset of the name table in the `name'       */
  /*                      TrueType table.                                  */
  /*                                                                       */
  /*    names          :: An array of name records.                        */
  /*                                                                       */
  /*    stream         :: the file's input stream.                         */
  /*                                                                       */
  typedef struct  TT_NameTableRec_
  {
    FT_UShort         format;
    FT_UInt           numNameRecords;
    FT_UInt           storageOffset;
    TT_NameEntryRec*  names;
    FT_Stream         stream;

  } TT_NameTableRec, *TT_NameTable;


  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /***                                                                   ***/
  /***                                                                   ***/
  /***             OPTIONAL TRUETYPE/OPENTYPE TABLES DEFINITIONS         ***/
  /***                                                                   ***/
  /***                                                                   ***/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    TT_GaspRangeRec                                                    */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A tiny structure used to model a gasp range according to the       */
  /*    TrueType specification.                                            */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    maxPPEM  :: The maximum ppem value to which `gaspFlag' applies.    */
  /*                                                                       */
  /*    gaspFlag :: A flag describing the grid-fitting and anti-aliasing   */
  /*                modes to be used.                                      */
  /*                                                                       */
  typedef struct  TT_GaspRangeRec_
  {
    FT_UShort  maxPPEM;
    FT_UShort  gaspFlag;

  } TT_GaspRangeRec, *TT_GaspRange;


#define TT_GASP_GRIDFIT  0x01
#define TT_GASP_DOGRAY   0x02


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    TT_GaspRec                                                         */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A structure modeling the TrueType `gasp' table used to specify     */
  /*    grid-fitting and anti-aliasing behaviour.                          */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    version    :: The version number.                                  */
  /*                                                                       */
  /*    numRanges  :: The number of gasp ranges in table.                  */
  /*                                                                       */
  /*    gaspRanges :: An array of gasp ranges.                             */
  /*                                                                       */
  typedef struct  TT_Gasp_
  {
    FT_UShort     version;
    FT_UShort     numRanges;
    TT_GaspRange  gaspRanges;

  } TT_GaspRec;


#ifdef FT_CONFIG_OPTION_OLD_INTERNALS

  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    TT_HdmxEntryRec                                                    */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A small structure used to model the pre-computed widths of a given */
  /*    size.  They are found in the `hdmx' table.                         */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    ppem      :: The pixels per EM value at which these metrics apply. */
  /*                                                                       */
  /*    max_width :: The maximum advance width for this metric.            */
  /*                                                                       */
  /*    widths    :: An array of widths.  Note: These are 8-bit bytes.     */
  /*                                                                       */
  typedef struct  TT_HdmxEntryRec_
  {
    FT_Byte   ppem;
    FT_Byte   max_width;
    FT_Byte*  widths;

  } TT_HdmxEntryRec, *TT_HdmxEntry;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    TT_HdmxRec                                                         */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A structure used to model the `hdmx' table, which contains         */
  /*    pre-computed widths for a set of given sizes/dimensions.           */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    version     :: The version number.                                 */
  /*                                                                       */
  /*    num_records :: The number of hdmx records.                         */
  /*                                                                       */
  /*    records     :: An array of hdmx records.                           */
  /*                                                                       */
  typedef struct  TT_HdmxRec_
  {
    FT_UShort     version;
    FT_Short      num_records;
    TT_HdmxEntry  records;

  } TT_HdmxRec, *TT_Hdmx;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    TT_Kern0_PairRec                                                   */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A structure used to model a kerning pair for the kerning table     */
  /*    format 0.  The engine now loads this table if it finds one in the  */
  /*    font file.                                                         */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    left  :: The index of the left glyph in pair.                      */
  /*                                                                       */
  /*    right :: The index of the right glyph in pair.                     */
  /*                                                                       */
  /*    value :: The kerning distance.  A positive value spaces the        */
  /*             glyphs, a negative one makes them closer.                 */
  /*                                                                       */
  typedef struct  TT_Kern0_PairRec_
  {
    FT_UShort  left;   /* index of left  glyph in pair */
    FT_UShort  right;  /* index of right glyph in pair */
    FT_FWord   value;  /* kerning value                */

  } TT_Kern0_PairRec, *TT_Kern0_Pair;

#endif /* FT_CONFIG_OPTION_OLD_INTERNALS */


  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /***                                                                   ***/
  /***                                                                   ***/
  /***                    EMBEDDED BITMAPS SUPPORT                       ***/
  /***                                                                   ***/
  /***                                                                   ***/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    TT_SBit_MetricsRec                                                 */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A structure used to hold the big metrics of a given glyph bitmap   */
  /*    in a TrueType or OpenType font.  These are usually found in the    */
  /*    `EBDT' (Microsoft) or `bloc' (Apple) table.                        */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    height       :: The glyph height in pixels.                        */
  /*                                                                       */
  /*    width        :: The glyph width in pixels.                         */
  /*                                                                       */
  /*    horiBearingX :: The horizontal left bearing.                       */
  /*                                                                       */
  /*    horiBearingY :: The horizontal top bearing.                        */
  /*                                                                       */
  /*    horiAdvance  :: The horizontal advance.                            */
  /*                                                                       */
  /*    vertBearingX :: The vertical left bearing.                         */
  /*                                                                       */
  /*    vertBearingY :: The vertical top bearing.                          */
  /*                                                                       */
  /*    vertAdvance  :: The vertical advance.                              */
  /*                                                                       */
  typedef struct  TT_SBit_MetricsRec_
  {
    FT_Byte  height;
    FT_Byte  width;

    FT_Char  horiBearingX;
    FT_Char  horiBearingY;
    FT_Byte  horiAdvance;

    FT_Char  vertBearingX;
    FT_Char  vertBearingY;
    FT_Byte  vertAdvance;

  } TT_SBit_MetricsRec, *TT_SBit_Metrics;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    TT_SBit_SmallMetricsRec                                            */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A structure used to hold the small metrics of a given glyph bitmap */
  /*    in a TrueType or OpenType font.  These are usually found in the    */
  /*    `EBDT' (Microsoft) or the `bdat' (Apple) table.                    */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    height   :: The glyph height in pixels.                            */
  /*                                                                       */
  /*    width    :: The glyph width in pixels.                             */
  /*                                                                       */
  /*    bearingX :: The left-side bearing.                                 */
  /*                                                                       */
  /*    bearingY :: The top-side bearing.                                  */
  /*                                                                       */
  /*    advance  :: The advance width or height.                           */
  /*                                                                       */
  typedef struct  TT_SBit_Small_Metrics_
  {
    FT_Byte  height;
    FT_Byte  width;

    FT_Char  bearingX;
    FT_Char  bearingY;
    FT_Byte  advance;

  } TT_SBit_SmallMetricsRec, *TT_SBit_SmallMetrics;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    TT_SBit_LineMetricsRec                                             */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A structure used to describe the text line metrics of a given      */
  /*    bitmap strike, for either a horizontal or vertical layout.         */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    ascender                :: The ascender in pixels.                 */
  /*                                                                       */
  /*    descender               :: The descender in pixels.                */
  /*                                                                       */
  /*    max_width               :: The maximum glyph width in pixels.      */
  /*                                                                       */
  /*    caret_slope_enumerator  :: Rise of the caret slope, typically set  */
  /*                               to 1 for non-italic fonts.              */
  /*                                                                       */
  /*    caret_slope_denominator :: Rise of the caret slope, typically set  */
  /*                               to 0 for non-italic fonts.              */
  /*                                                                       */
  /*    caret_offset            :: Offset in pixels to move the caret for  */
  /*                               proper positioning.                     */
  /*                                                                       */
  /*    min_origin_SB           :: Minimum of horiBearingX (resp.          */
  /*                               vertBearingY).                          */
  /*    min_advance_SB          :: Minimum of                              */
  /*                                                                       */
  /*                                 horizontal advance -                  */
  /*                                   ( horiBearingX + width )            */
  /*                                                                       */
  /*                               resp.                                   */
  /*                                                                       */
  /*                                 vertical advance -                    */
  /*                                   ( vertBearingY + height )           */
  /*                                                                       */
  /*    max_before_BL           :: Maximum of horiBearingY (resp.          */
  /*                               vertBearingY).                          */
  /*                                                                       */
  /*    min_after_BL            :: Minimum of                              */
  /*                                                                       */
  /*                                 horiBearingY - height                 */
  /*                                                                       */
  /*                               resp.                                   */
  /*                                                                       */
  /*                                 vertBearingX - width                  */
  /*                                                                       */
  /*    pads                    :: Unused (to make the size of the record  */
  /*                               a multiple of 32 bits.                  */
  /*                                                                       */
  typedef struct  TT_SBit_LineMetricsRec_
  {
    FT_Char  ascender;
    FT_Char  descender;
    FT_Byte  max_width;
    FT_Char  caret_slope_numerator;
    FT_Char  caret_slope_denominator;
    FT_Char  caret_offset;
    FT_Char  min_origin_SB;
    FT_Char  min_advance_SB;
    FT_Char  max_before_BL;
    FT_Char  min_after_BL;
    FT_Char  pads[2];

  } TT_SBit_LineMetricsRec, *TT_SBit_LineMetrics;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    TT_SBit_RangeRec                                                   */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A TrueType/OpenType subIndexTable as defined in the `EBLC'         */
  /*    (Microsoft) or `bloc' (Apple) tables.                              */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    first_glyph   :: The first glyph index in the range.               */
  /*                                                                       */
  /*    last_glyph    :: The last glyph index in the range.                */
  /*                                                                       */
  /*    index_format  :: The format of index table.  Valid values are 1    */
  /*                     to 5.                                             */
  /*                                                                       */
  /*    image_format  :: The format of `EBDT' image data.                  */
  /*                                                                       */
  /*    image_offset  :: The offset to image data in `EBDT'.               */
  /*                                                                       */
  /*    image_size    :: For index formats 2 and 5.  This is the size in   */
  /*                     bytes of each glyph bitmap.                       */
  /*                                                                       */
  /*    big_metrics   :: For index formats 2 and 5.  This is the big       */
  /*                     metrics for each glyph bitmap.                    */
  /*                                                                       */
  /*    num_glyphs    :: For index formats 4 and 5.  This is the number of */
  /*                     glyphs in the code array.                         */
  /*                                                                       */
  /*    glyph_offsets :: For index formats 1 and 3.                        */
  /*                                                                       */
  /*    glyph_codes   :: For index formats 4 and 5.                        */
  /*                                                                       */
  /*    table_offset  :: The offset of the index table in the `EBLC'       */
  /*                     table.  Only used during strike loading.          */
  /*                                                                       */
  typedef struct  TT_SBit_RangeRec_
  {
    FT_UShort           first_glyph;
    FT_UShort           last_glyph;

    FT_UShort           index_format;
    FT_UShort           image_format;
    FT_ULong            image_offset;

    FT_ULong            image_size;
    TT_SBit_MetricsRec  metrics;
    FT_ULong            num_glyphs;

    FT_ULong*           glyph_offsets;
    FT_UShort*          glyph_codes;

    FT_ULong            table_offset;

  } TT_SBit_RangeRec, *TT_SBit_Range;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    TT_SBit_StrikeRec                                                  */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A structure used describe a given bitmap strike in the `EBLC'      */
  /*    (Microsoft) or `bloc' (Apple) tables.                              */
  /*                                                                       */
  /* <Fields>                                                              */
  /*   num_index_ranges :: The number of index ranges.                     */
  /*                                                                       */
  /*   index_ranges     :: An array of glyph index ranges.                 */
  /*                                                                       */
  /*   color_ref        :: Unused.  `color_ref' is put in for future       */
  /*                       enhancements, but these fields are already      */
  /*                       in use by other platforms (e.g. Newton).        */
  /*                       For details, please see                         */
  /*                                                                       */
  /*                         http://fonts.apple.com/                       */
  /*                                TTRefMan/RM06/Chap6bloc.html           */
  /*                                                                       */
  /*   hori             :: The line metrics for horizontal layouts.        */
  /*                                                                       */
  /*   vert             :: The line metrics for vertical layouts.          */
  /*                                                                       */
  /*   start_glyph      :: The lowest glyph index for this strike.         */
  /*                                                                       */
  /*   end_glyph        :: The highest glyph index for this strike.        */
  /*                                                                       */
  /*   x_ppem           :: The number of horizontal pixels per EM.         */
  /*                                                                       */
  /*   y_ppem           :: The number of vertical pixels per EM.           */
  /*                                                                       */
  /*   bit_depth        :: The bit depth.  Valid values are 1, 2, 4,       */
  /*                       and 8.                                          */
  /*                                                                       */
  /*   flags            :: Is this a vertical or horizontal strike?  For   */
  /*                       details, please see                             */
  /*                                                                       */
  /*                         http://fonts.apple.com/                       */
  /*                                TTRefMan/RM06/Chap6bloc.html           */
  /*                                                                       */
  typedef struct  TT_SBit_StrikeRec_
  {
    FT_Int                  num_ranges;
    TT_SBit_Range           sbit_ranges;
    FT_ULong                ranges_offset;

    FT_ULong                color_ref;

    TT_SBit_LineMetricsRec  hori;
    TT_SBit_LineMetricsRec  vert;

    FT_UShort               start_glyph;
    FT_UShort               end_glyph;

    FT_Byte                 x_ppem;
    FT_Byte                 y_ppem;

    FT_Byte                 bit_depth;
    FT_Char                 flags;

  } TT_SBit_StrikeRec, *TT_SBit_Strike;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    TT_SBit_ComponentRec                                               */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A simple structure to describe a compound sbit element.            */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    glyph_code :: The element's glyph index.                           */
  /*                                                                       */
  /*    x_offset   :: The element's left bearing.                          */
  /*                                                                       */
  /*    y_offset   :: The element's top bearing.                           */
  /*                                                                       */
  typedef struct  TT_SBit_ComponentRec_
  {
    FT_UShort  glyph_code;
    FT_Char    x_offset;
    FT_Char    y_offset;

  } TT_SBit_ComponentRec, *TT_SBit_Component;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    TT_SBit_ScaleRec                                                   */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A structure used describe a given bitmap scaling table, as defined */
  /*    in the `EBSC' table.                                               */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    hori              :: The horizontal line metrics.                  */
  /*                                                                       */
  /*    vert              :: The vertical line metrics.                    */
  /*                                                                       */
  /*    x_ppem            :: The number of horizontal pixels per EM.       */
  /*                                                                       */
  /*    y_ppem            :: The number of vertical pixels per EM.         */
  /*                                                                       */
  /*    x_ppem_substitute :: Substitution x_ppem value.                    */
  /*                                                                       */
  /*    y_ppem_substitute :: Substitution y_ppem value.                    */
  /*                                                                       */
  typedef struct  TT_SBit_ScaleRec_
  {
    TT_SBit_LineMetricsRec  hori;
    TT_SBit_LineMetricsRec  vert;

    FT_Byte                 x_ppem;
    FT_Byte                 y_ppem;

    FT_Byte                 x_ppem_substitute;
    FT_Byte                 y_ppem_substitute;

  } TT_SBit_ScaleRec, *TT_SBit_Scale;


  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /***                                                                   ***/
  /***                                                                   ***/
  /***                  POSTSCRIPT GLYPH NAMES SUPPORT                   ***/
  /***                                                                   ***/
  /***                                                                   ***/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    TT_Post_20Rec                                                      */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Postscript names sub-table, format 2.0.  Stores the PS name of     */
  /*    each glyph in the font face.                                       */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    num_glyphs    :: The number of named glyphs in the table.          */
  /*                                                                       */
  /*    num_names     :: The number of PS names stored in the table.       */
  /*                                                                       */
  /*    glyph_indices :: The indices of the glyphs in the names arrays.    */
  /*                                                                       */
  /*    glyph_names   :: The PS names not in Mac Encoding.                 */
  /*                                                                       */
  typedef struct  TT_Post_20Rec_
  {
    FT_UShort   num_glyphs;
    FT_UShort   num_names;
    FT_UShort*  glyph_indices;
    FT_Char**   glyph_names;

  } TT_Post_20Rec, *TT_Post_20;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    TT_Post_25Rec                                                      */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Postscript names sub-table, format 2.5.  Stores the PS name of     */
  /*    each glyph in the font face.                                       */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    num_glyphs :: The number of glyphs in the table.                   */
  /*                                                                       */
  /*    offsets    :: An array of signed offsets in a normal Mac           */
  /*                  Postscript name encoding.                            */
  /*                                                                       */
  typedef struct  TT_Post_25_
  {
    FT_UShort  num_glyphs;
    FT_Char*   offsets;

  } TT_Post_25Rec, *TT_Post_25;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    TT_Post_NamesRec                                                   */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Postscript names table, either format 2.0 or 2.5.                  */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    loaded    :: A flag to indicate whether the PS names are loaded.   */
  /*                                                                       */
  /*    format_20 :: The sub-table used for format 2.0.                    */
  /*                                                                       */
  /*    format_25 :: The sub-table used for format 2.5.                    */
  /*                                                                       */
  typedef struct  TT_Post_NamesRec_
  {
    FT_Bool  loaded;

    union
    {
      TT_Post_20Rec  format_20;
      TT_Post_25Rec  format_25;

    } names;

  } TT_Post_NamesRec, *TT_Post_Names;


  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /***                                                                   ***/
  /***                                                                   ***/
  /***                    GX VARIATION TABLE SUPPORT                     ***/
  /***                                                                   ***/
  /***                                                                   ***/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/


#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
  typedef struct GX_BlendRec_  *GX_Blend;
#endif

  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /***                                                                   ***/
  /***                                                                   ***/
  /***              EMBEDDED BDF PROPERTIES TABLE SUPPORT                ***/
  /***                                                                   ***/
  /***                                                                   ***/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/

  /*
   * These types are used to support a `BDF ' table that isn't part of the
   * official TrueType specification.  It is mainly used in SFNT-based
   * bitmap fonts that were generated from a set of BDF fonts.
   *
   * The format of the table is as follows.
   *
   *   USHORT   version      `BDF ' table version number, should be 0x0001.
   *   USHORT   strikeCount  Number of strikes (bitmap sizes) in this table.
   *   ULONG    stringTable  Offset (from start of BDF table) to string
   *                         table.
   *
   * This is followed by an array of `strikeCount' descriptors, having the
   * following format.
   *
   *   USHORT   ppem         Vertical pixels per EM for this strike.
   *   USHORT   numItems     Number of items for this strike (properties and
   *                         atoms).  Maximum is 255.
   *
   * This array in turn is followed by `strikeCount' value sets.  Each
   * `value set' is an array of `numItems' items with the following format.
   *
   *   ULONG    item_name    Offset in string table to item name.
   *   USHORT   item_type    The item type.  Possible values are
   *                            0 => string (e.g., COMMENT)
   *                            1 => atom   (e.g., FONT or even SIZE)
   *                            2 => int32
   *                            3 => uint32
   *                         0x10 => A flag to indicate a properties.  This
   *                                 is ORed with the above values.
   *   ULONG    item_value   For strings  => Offset into string table without
   *                                         the corresponding double quotes.
   *                         For atoms    => Offset into string table.
   *                         For integers => Direct value.
   *
   * All strings in the string table consist of bytes and are
   * zero-terminated.
   *
   */

#ifdef TT_CONFIG_OPTION_BDF

  typedef struct  TT_BDFRec_
  {
    FT_Byte*   table;
    FT_Byte*   table_end;
    FT_Byte*   strings;
    FT_ULong   strings_size;
    FT_UInt    num_strikes;
    FT_Bool    loaded;

  } TT_BDFRec, *TT_BDF;

#endif /* TT_CONFIG_OPTION_BDF */

  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /***                                                                   ***/
  /***                                                                   ***/
  /***                  ORIGINAL TT_FACE CLASS DEFINITION                ***/
  /***                                                                   ***/
  /***                                                                   ***/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* This structure/class is defined here because it is common to the      */
  /* following formats: TTF, OpenType-TT, and OpenType-CFF.                */
  /*                                                                       */
  /* Note, however, that the classes TT_Size and TT_GlyphSlot are not      */
  /* shared between font drivers, and are thus defined in `ttobjs.h'.      */
  /*                                                                       */
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* <Type>                                                                */
  /*    TT_Face                                                            */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A handle to a TrueType face/font object.  A TT_Face encapsulates   */
  /*    the resolution and scaling independent parts of a TrueType font    */
  /*    resource.                                                          */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The TT_Face structure is also used as a `parent class' for the     */
  /*    OpenType-CFF class (T2_Face).                                      */
  /*                                                                       */
  typedef struct TT_FaceRec_*  TT_Face;


  /* a function type used for the truetype bytecode interpreter hooks */
  typedef FT_Error
  (*TT_Interpreter)( void*  exec_context );

  /* forward declaration */
  typedef struct TT_LoaderRec_*  TT_Loader;


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    TT_Loader_GotoTableFunc                                            */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Seeks a stream to the start of a given TrueType table.             */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face   :: A handle to the target face object.                      */
  /*                                                                       */
  /*    tag    :: A 4-byte tag used to name the table.                     */
  /*                                                                       */
  /*    stream :: The input stream.                                        */
  /*                                                                       */
  /* <Output>                                                              */
  /*    length :: The length of the table in bytes.  Set to 0 if not       */
  /*              needed.                                                  */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0 means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The stream cursor must be at the font file's origin.               */
  /*                                                                       */
  typedef FT_Error
  (*TT_Loader_GotoTableFunc)( TT_Face    face,
                              FT_ULong   tag,
                              FT_Stream  stream,
                              FT_ULong*  length );


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    TT_Loader_StartGlyphFunc                                           */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Seeks a stream to the start of a given glyph element, and opens a  */
  /*    frame for it.                                                      */
  /*                                                                       */
  /* <Input>                                                               */
  /*    loader      :: The current TrueType glyph loader object.           */
  /*                                                                       */
  /*    glyph index :: The index of the glyph to access.                   */
  /*                                                                       */
  /*    offset      :: The offset of the glyph according to the            */
  /*                   `locations' table.                                  */
  /*                                                                       */
  /*    byte_count  :: The size of the frame in bytes.                     */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0 means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    This function is normally equivalent to FT_STREAM_SEEK(offset)     */
  /*    followed by FT_FRAME_ENTER(byte_count) with the loader's stream,   */
  /*    but alternative formats (e.g. compressed ones) might use something */
  /*    different.                                                         */
  /*                                                                       */
  typedef FT_Error
  (*TT_Loader_StartGlyphFunc)( TT_Loader  loader,
                               FT_UInt    glyph_index,
                               FT_ULong   offset,
                               FT_UInt    byte_count );


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    TT_Loader_ReadGlyphFunc                                            */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Reads one glyph element (its header, a simple glyph, or a          */
  /*    composite) from the loader's current stream frame.                 */
  /*                                                                       */
  /* <Input>                                                               */
  /*    loader :: The current TrueType glyph loader object.                */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0 means success.                             */
  /*                                                                       */
  typedef FT_Error
  (*TT_Loader_ReadGlyphFunc)( TT_Loader  loader );


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    TT_Loader_EndGlyphFunc                                             */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Closes the current loader stream frame for the glyph.              */
  /*                                                                       */
  /* <Input>                                                               */
  /*    loader :: The current TrueType glyph loader object.                */
  /*                                                                       */
  typedef void
  (*TT_Loader_EndGlyphFunc)( TT_Loader  loader );


  /*************************************************************************/
  /*                                                                       */
  /*                         TrueType Face Type                            */
  /*                                                                       */
  /* <Struct>                                                              */
  /*    TT_Face                                                            */
  /*                                                                       */
  /* <Description>                                                         */
  /*    The TrueType face class.  These objects model the resolution and   */
  /*    point-size independent data found in a TrueType font file.         */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    root                 :: The base FT_Face structure, managed by the */
  /*                            base layer.                                */
  /*                                                                       */
  /*    ttc_header           :: The TrueType collection header, used when  */
  /*                            the file is a `ttc' rather than a `ttf'.   */
  /*                            For ordinary font files, the field         */
  /*                            `ttc_header.count' is set to 0.            */
  /*                                                                       */
  /*    format_tag           :: The font format tag.                       */
  /*                                                                       */
  /*    num_tables           :: The number of TrueType tables in this font */
  /*                            file.                                      */
  /*                                                                       */
  /*    dir_tables           :: The directory of TrueType tables for this  */
  /*                            font file.                                 */
  /*                                                                       */
  /*    header               :: The font's font header (`head' table).     */
  /*                            Read on font opening.                      */
  /*                                                                       */
  /*    horizontal           :: The font's horizontal header (`hhea'       */
  /*                            table).  This field also contains the      */
  /*                            associated horizontal metrics table        */
  /*                            (`hmtx').                                  */
  /*                                                                       */
  /*    max_profile          :: The font's maximum profile table.  Read on */
  /*                            font opening.  Note that some maximum      */
  /*                            values cannot be taken directly from this  */
  /*                            table.  We thus define additional fields   */
  /*                            below to hold the computed maxima.         */
  /*                                                                       */
  /*    vertical_info        :: A boolean which is set when the font file  */
  /*                            contains vertical metrics.  If not, the    */
  /*                            value of the `vertical' field is           */
  /*                            undefined.                                 */
  /*                                                                       */
  /*    vertical             :: The font's vertical header (`vhea' table). */
  /*                            This field also contains the associated    */
  /*                            vertical metrics table (`vmtx'), if found. */
  /*                            IMPORTANT: The contents of this field is   */
  /*                            undefined if the `verticalInfo' field is   */
  /*                            unset.                                     */
  /*                                                                       */
  /*    num_names            :: The number of name records within this     */
  /*                            TrueType font.                             */
  /*                                                                       */
  /*    name_table           :: The table of name records (`name').        */
  /*                                                                       */
  /*    os2                  :: The font's OS/2 table (`OS/2').            */
  /*                                                                       */
  /*    postscript           :: The font's PostScript table (`post'        */
  /*                            table).  The PostScript glyph names are    */
  /*                            not loaded by the driver on face opening.  */
  /*                            See the `ttpost' module for more details.  */
  /*                                                                       */
  /*    cmap_table           :: Address of the face's `cmap' SFNT table    */
  /*                            in memory (it's an extracted frame).       */
  /*                                                                       */
  /*    cmap_size            :: The size in bytes of the `cmap_table'      */
  /*                            described above.                           */
  /*                                                                       */
  /*    goto_table           :: A function called by each TrueType table   */
  /*                            loader to position a stream's cursor to    */
  /*                            the start of a given table according to    */
  /*                            its tag.  It defaults to TT_Goto_Face but  */
  /*                            can be different for strange formats (e.g. */
  /*                            Type 42).                                  */
  /*                                                                       */
  /*    access_glyph_frame   :: A function used to access the frame of a   */
  /*                            given glyph within the face's font file.   */
  /*                                                                       */
  /*    forget_glyph_frame   :: A function used to forget the frame of a   */
  /*                            given glyph when all data has been loaded. */
  /*                                                                       */
  /*    read_glyph_header    :: A function used to read a glyph header.    */
  /*                            It must be called between an `access' and  */
  /*                            `forget'.                                  */
  /*                                                                       */
  /*    read_simple_glyph    :: A function used to read a simple glyph.    */
  /*                            It must be called after the header was     */
  /*                            read, and before the `forget'.             */
  /*                                                                       */
  /*    read_composite_glyph :: A function used to read a composite glyph. */
  /*                            It must be called after the header was     */
  /*                            read, and before the `forget'.             */
  /*                                                                       */
  /*    sfnt                 :: A pointer to the SFNT service.             */
  /*                                                                       */
  /*    psnames              :: A pointer to the PostScript names service. */
  /*                                                                       */
  /*    hdmx                 :: The face's horizontal device metrics       */
  /*                            (`hdmx' table).  This table is optional in */
  /*                            TrueType/OpenType fonts.                   */
  /*                                                                       */
  /*    gasp                 :: The grid-fitting and scaling properties    */
  /*                            table (`gasp').  This table is optional in */
  /*                            TrueType/OpenType fonts.                   */
  /*                                                                       */
  /*    pclt                 :: The `pclt' SFNT table.                     */
  /*                                                                       */
  /*    num_sbit_strikes     :: The number of sbit strikes, i.e., bitmap   */
  /*                            sizes, embedded in this font.              */
  /*                                                                       */
  /*    sbit_strikes         :: An array of sbit strikes embedded in this  */
  /*                            font.  This table is optional in a         */
  /*                            TrueType/OpenType font.                    */
  /*                                                                       */
  /*    num_sbit_scales      :: The number of sbit scales for this font.   */
  /*                                                                       */
  /*    sbit_scales          :: Array of sbit scales embedded in this      */
  /*                            font.  This table is optional in a         */
  /*                            TrueType/OpenType font.                    */
  /*                                                                       */
  /*    postscript_names     :: A table used to store the Postscript names */
  /*                            of  the glyphs for this font.  See the     */
  /*                            file  `ttconfig.h' for comments on the     */
  /*                            TT_CONFIG_OPTION_POSTSCRIPT_NAMES option.  */
  /*                                                                       */
  /*    num_locations        :: The number of glyph locations in this      */
  /*                            TrueType file.  This should be             */
  /*                            identical to the number of glyphs.         */
  /*                            Ignored for Type 2 fonts.                  */
  /*                                                                       */
  /*    glyph_locations      :: An array of longs.  These are offsets to   */
  /*                            glyph data within the `glyf' table.        */
  /*                            Ignored for Type 2 font faces.             */
  /*                                                                       */
  /*    glyf_len             :: The length of the `glyf' table.  Needed    */
  /*                            for malformed `loca' tables.               */
  /*                                                                       */
  /*    font_program_size    :: Size in bytecodes of the face's font       */
  /*                            program.  0 if none defined.  Ignored for  */
  /*                            Type 2 fonts.                              */
  /*                                                                       */
  /*    font_program         :: The face's font program (bytecode stream)  */
  /*                            executed at load time, also used during    */
  /*                            glyph rendering.  Comes from the `fpgm'    */
  /*                            table.  Ignored for Type 2 font fonts.     */
  /*                                                                       */
  /*    cvt_program_size     :: The size in bytecodes of the face's cvt    */
  /*                            program.  Ignored for Type 2 fonts.        */
  /*                                                                       */
  /*    cvt_program          :: The face's cvt program (bytecode stream)   */
  /*                            executed each time an instance/size is     */
  /*                            changed/reset.  Comes from the `prep'      */
  /*                            table.  Ignored for Type 2 fonts.          */
  /*                                                                       */
  /*    cvt_size             :: Size of the control value table (in        */
  /*                            entries).   Ignored for Type 2 fonts.      */
  /*                                                                       */
  /*    cvt                  :: The face's original control value table.   */
  /*                            Coordinates are expressed in unscaled font */
  /*                            units.  Comes from the `cvt ' table.       */
  /*                            Ignored for Type 2 fonts.                  */
  /*                                                                       */
  /*    num_kern_pairs       :: The number of kerning pairs present in the */
  /*                            font file.  The engine only loads the      */
  /*                            first horizontal format 0 kern table it    */
  /*                            finds in the font file.  Ignored for       */
  /*                            Type 2 fonts.                              */
  /*                                                                       */
  /*    kern_table_index     :: The index of the kerning table in the font */
  /*                            kerning directory.  Ignored for Type 2     */
  /*                            fonts.                                     */
  /*                                                                       */
  /*    interpreter          :: A pointer to the TrueType bytecode         */
  /*                            interpreters field is also used to hook    */
  /*                            the debugger in `ttdebug'.                 */
  /*                                                                       */
  /*    unpatented_hinting   :: If true, use only unpatented methods in    */
  /*                            the bytecode interpreter.                  */
  /*                                                                       */
  /*    doblend              :: A boolean which is set if the font should  */
  /*                            be blended (this is for GX var).           */
  /*                                                                       */
  /*    blend                :: Contains the data needed to control GX     */
  /*                            variation tables (rather like Multiple     */
  /*                            Master data).                              */
  /*                                                                       */
  /*    extra                :: Reserved for third-party font drivers.     */
  /*                                                                       */
  /*    postscript_name      :: The PS name of the font.  Used by the      */
  /*                            postscript name service.                   */
  /*                                                                       */
  typedef struct  TT_FaceRec_
  {
    FT_FaceRec            root;

    TTC_HeaderRec         ttc_header;

    FT_ULong              format_tag;
    FT_UShort             num_tables;
    TT_Table              dir_tables;

    TT_Header             header;       /* TrueType header table          */
    TT_HoriHeader         horizontal;   /* TrueType horizontal header     */

    TT_MaxProfile         max_profile;
#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
    FT_ULong              max_components;  /* stubbed to 0 */
#endif

    FT_Bool               vertical_info;
    TT_VertHeader         vertical;     /* TT Vertical header, if present */

    FT_UShort             num_names;    /* number of name records  */
    TT_NameTableRec       name_table;   /* name table              */

    TT_OS2                os2;          /* TrueType OS/2 table            */
    TT_Postscript         postscript;   /* TrueType Postscript table      */

    FT_Byte*              cmap_table;   /* extracted `cmap' table */
    FT_ULong              cmap_size;

    TT_Loader_GotoTableFunc   goto_table;

    TT_Loader_StartGlyphFunc  access_glyph_frame;
    TT_Loader_EndGlyphFunc    forget_glyph_frame;
    TT_Loader_ReadGlyphFunc   read_glyph_header;
    TT_Loader_ReadGlyphFunc   read_simple_glyph;
    TT_Loader_ReadGlyphFunc   read_composite_glyph;

    /* a typeless pointer to the SFNT_Interface table used to load */
    /* the basic TrueType tables in the face object                */
    void*                 sfnt;

    /* a typeless pointer to the FT_Service_PsCMapsRec table used to */
    /* handle glyph names <-> unicode & Mac values                   */
    void*                 psnames;


    /***********************************************************************/
    /*                                                                     */
    /* Optional TrueType/OpenType tables                                   */
    /*                                                                     */
    /***********************************************************************/

    /* horizontal device metrics */
#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
    TT_HdmxRec            hdmx;
#endif

    /* grid-fitting and scaling table */
    TT_GaspRec            gasp;                 /* the `gasp' table */

    /* PCL 5 table */
    TT_PCLT               pclt;

    /* embedded bitmaps support */
#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
    FT_ULong              num_sbit_strikes;
    TT_SBit_Strike        sbit_strikes;
#endif

    FT_ULong              num_sbit_scales;
    TT_SBit_Scale         sbit_scales;

    /* postscript names table */
    TT_Post_NamesRec      postscript_names;


    /***********************************************************************/
    /*                                                                     */
    /* TrueType-specific fields (ignored by the OTF-Type2 driver)          */
    /*                                                                     */
    /***********************************************************************/

    /* the glyph locations */
#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
    FT_UShort             num_locations_stub;
    FT_Long*              glyph_locations_stub;
#endif

    /* the font program, if any */
    FT_ULong              font_program_size;
    FT_Byte*              font_program;

    /* the cvt program, if any */
    FT_ULong              cvt_program_size;
    FT_Byte*              cvt_program;

    /* the original, unscaled, control value table */
    FT_ULong              cvt_size;
    FT_Short*             cvt;

#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
    /* the format 0 kerning table, if any */
    FT_Int                num_kern_pairs;
    FT_Int                kern_table_index;
    TT_Kern0_Pair         kern_pairs;
#endif

    /* A pointer to the bytecode interpreter to use.  This is also */
    /* used to hook the debugger for the `ttdebug' utility.        */
    TT_Interpreter        interpreter;

#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
    /* Use unpatented hinting only. */
    FT_Bool               unpatented_hinting;
#endif

    /***********************************************************************/
    /*                                                                     */
    /* Other tables or fields. This is used by derivative formats like     */
    /* OpenType.                                                           */
    /*                                                                     */
    /***********************************************************************/

    FT_Generic            extra;

    const char*           postscript_name;

    /* since version 2.1.8, but was originally placed after */
    /* `glyph_locations_stub'                               */
    FT_ULong              glyf_len;

    /* since version 2.1.8, but was originally placed before `extra' */
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
    FT_Bool               doblend;
    GX_Blend              blend;
#endif

    /* since version 2.2 */

    FT_Byte*              horz_metrics;
    FT_ULong              horz_metrics_size;

    FT_Byte*              vert_metrics;
    FT_ULong              vert_metrics_size;

    FT_ULong              num_locations; /* in broken TTF, gid > 0xFFFF */ 
    FT_Byte*              glyph_locations;

    FT_Byte*              hdmx_table;
    FT_ULong              hdmx_table_size;
    FT_UInt               hdmx_record_count;
    FT_ULong              hdmx_record_size;
    FT_Byte*              hdmx_record_sizes;

    FT_Byte*              sbit_table;
    FT_ULong              sbit_table_size;
    FT_UInt               sbit_num_strikes;

    FT_Byte*              kern_table;
    FT_ULong              kern_table_size;
    FT_UInt               num_kern_tables;
    FT_UInt32             kern_avail_bits;
    FT_UInt32             kern_order_bits;

#ifdef TT_CONFIG_OPTION_BDF
    TT_BDFRec             bdf;
#endif /* TT_CONFIG_OPTION_BDF */

    /* since 2.3.0 */
    FT_ULong              horz_metrics_offset;
    FT_ULong              vert_metrics_offset;

  } TT_FaceRec;


  /*************************************************************************/
  /*                                                                       */
  /*  <Struct>                                                             */
  /*     TT_GlyphZoneRec                                                   */
  /*                                                                       */
  /*  <Description>                                                        */
  /*     A glyph zone is used to load, scale and hint glyph outline        */
  /*     coordinates.                                                      */
  /*                                                                       */
  /*  <Fields>                                                             */
  /*     memory       :: A handle to the memory manager.                   */
  /*                                                                       */
  /*     max_points   :: The maximal size in points of the zone.           */
  /*                                                                       */
  /*     max_contours :: Max size in links contours of the zone.           */
  /*                                                                       */
  /*     n_points     :: The current number of points in the zone.         */
  /*                                                                       */
  /*     n_contours   :: The current number of contours in the zone.       */
  /*                                                                       */
  /*     org          :: The original glyph coordinates (font              */
  /*                     units/scaled).                                    */
  /*                                                                       */
  /*     cur          :: The current glyph coordinates (scaled/hinted).    */
  /*                                                                       */
  /*     tags         :: The point control tags.                           */
  /*                                                                       */
  /*     contours     :: The contours end points.                          */
  /*                                                                       */
  /*     first_point  :: Offset of the current subglyph's first point.     */
  /*                                                                       */
  typedef struct  TT_GlyphZoneRec_
  {
    FT_Memory   memory;
    FT_UShort   max_points;
    FT_UShort   max_contours;
    FT_UShort   n_points;    /* number of points in zone    */
    FT_Short    n_contours;  /* number of contours          */

    FT_Vector*  org;         /* original point coordinates  */
    FT_Vector*  cur;         /* current point coordinates   */
    FT_Vector*  orus;        /* original (unscaled) point coordinates */

    FT_Byte*    tags;        /* current touch flags         */
    FT_UShort*  contours;    /* contour end points          */

    FT_UShort   first_point; /* offset of first (#0) point  */

  } TT_GlyphZoneRec, *TT_GlyphZone;


  /* handle to execution context */
  typedef struct TT_ExecContextRec_*  TT_ExecContext;

  /* glyph loader structure */
  typedef struct  TT_LoaderRec_
  {
    FT_Face          face;
    FT_Size          size;
    FT_GlyphSlot     glyph;
    FT_GlyphLoader   gloader;

    FT_ULong         load_flags;
    FT_UInt          glyph_index;

    FT_Stream        stream;
    FT_Int           byte_len;

    FT_Short         n_contours;
    FT_BBox          bbox;
    FT_Int           left_bearing;
    FT_Int           advance;
    FT_Int           linear;
    FT_Bool          linear_def;
    FT_Bool          preserve_pps;
    FT_Vector        pp1;
    FT_Vector        pp2;

    FT_ULong         glyf_offset;

    /* the zone where we load our glyphs */
    TT_GlyphZoneRec  base;
    TT_GlyphZoneRec  zone;

    TT_ExecContext   exec;
    FT_Byte*         instructions;
    FT_ULong         ins_pos;

    /* for possible extensibility in other formats */
    void*            other;

    /* since version 2.1.8 */
    FT_Int           top_bearing;
    FT_Int           vadvance;
    FT_Vector        pp3;
    FT_Vector        pp4;

    /* since version 2.2.1 */
    FT_Byte*         cursor;
    FT_Byte*         limit;

  } TT_LoaderRec;


FT_END_HEADER

#endif /* __TTTYPES_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  t1tables.h                                                             */
/*                                                                         */
/*    Basic Type 1/Type 2 tables definitions and interface (specification  */
/*    only).                                                               */
/*                                                                         */
/*  Copyright 1996-2001, 2002, 2003, 2004, 2006, 2008, 2009 by             */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __T1TABLES_H__
#define __T1TABLES_H__


#include <ft2build.h>
#include FT_FREETYPE_H

#ifdef FREETYPE_H
#error "freetype.h of FreeType 1 has been loaded!"
#error "Please fix the directory search order for header files"
#error "so that freetype.h of FreeType 2 is found first."
#endif


FT_BEGIN_HEADER


  /*************************************************************************/
  /*                                                                       */
  /* <Section>                                                             */
  /*    type1_tables                                                       */
  /*                                                                       */
  /* <Title>                                                               */
  /*    Type 1 Tables                                                      */
  /*                                                                       */
  /* <Abstract>                                                            */
  /*    Type~1 (PostScript) specific font tables.                          */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This section contains the definition of Type 1-specific tables,    */
  /*    including structures related to other PostScript font formats.     */
  /*                                                                       */
  /*************************************************************************/


  /* Note that we separate font data in PS_FontInfoRec and PS_PrivateRec */
  /* structures in order to support Multiple Master fonts.               */


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    PS_FontInfoRec                                                     */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A structure used to model a Type~1 or Type~2 FontInfo dictionary.  */
  /*    Note that for Multiple Master fonts, each instance has its own     */
  /*    FontInfo dictionary.                                               */
  /*                                                                       */
  typedef struct  PS_FontInfoRec_
  {
    FT_String*  version;
    FT_String*  notice;
    FT_String*  full_name;
    FT_String*  family_name;
    FT_String*  weight;
    FT_Long     italic_angle;
    FT_Bool     is_fixed_pitch;
    FT_Short    underline_position;
    FT_UShort   underline_thickness;

  } PS_FontInfoRec;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    PS_FontInfo                                                        */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A handle to a @PS_FontInfoRec structure.                           */
  /*                                                                       */
  typedef struct PS_FontInfoRec_*  PS_FontInfo;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    T1_FontInfo                                                        */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This type is equivalent to @PS_FontInfoRec.  It is deprecated but  */
  /*    kept to maintain source compatibility between various versions of  */
  /*    FreeType.                                                          */
  /*                                                                       */
  typedef PS_FontInfoRec  T1_FontInfo;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    PS_PrivateRec                                                      */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A structure used to model a Type~1 or Type~2 private dictionary.   */
  /*    Note that for Multiple Master fonts, each instance has its own     */
  /*    Private dictionary.                                                */
  /*                                                                       */
  typedef struct  PS_PrivateRec_
  {
    FT_Int     unique_id;
    FT_Int     lenIV;

    FT_Byte    num_blue_values;
    FT_Byte    num_other_blues;
    FT_Byte    num_family_blues;
    FT_Byte    num_family_other_blues;

    FT_Short   blue_values[14];
    FT_Short   other_blues[10];

    FT_Short   family_blues      [14];
    FT_Short   family_other_blues[10];

    FT_Fixed   blue_scale;
    FT_Int     blue_shift;
    FT_Int     blue_fuzz;

    FT_UShort  standard_width[1];
    FT_UShort  standard_height[1];

    FT_Byte    num_snap_widths;
    FT_Byte    num_snap_heights;
    FT_Bool    force_bold;
    FT_Bool    round_stem_up;

    FT_Short   snap_widths [13];  /* including std width  */
    FT_Short   snap_heights[13];  /* including std height */

    FT_Fixed   expansion_factor;

    FT_Long    language_group;
    FT_Long    password;

    FT_Short   min_feature[2];

  } PS_PrivateRec;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    PS_Private                                                         */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A handle to a @PS_PrivateRec structure.                            */
  /*                                                                       */
  typedef struct PS_PrivateRec_*  PS_Private;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    T1_Private                                                         */
  /*                                                                       */
  /* <Description>                                                         */
  /*   This type is equivalent to @PS_PrivateRec.  It is deprecated but    */
  /*   kept to maintain source compatibility between various versions of   */
  /*   FreeType.                                                           */
  /*                                                                       */
  typedef PS_PrivateRec  T1_Private;


  /*************************************************************************/
  /*                                                                       */
  /* <Enum>                                                                */
  /*    T1_Blend_Flags                                                     */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A set of flags used to indicate which fields are present in a      */
  /*    given blend dictionary (font info or private).  Used to support    */
  /*    Multiple Masters fonts.                                            */
  /*                                                                       */
  typedef enum  T1_Blend_Flags_
  {
    /*# required fields in a FontInfo blend dictionary */
    T1_BLEND_UNDERLINE_POSITION = 0,
    T1_BLEND_UNDERLINE_THICKNESS,
    T1_BLEND_ITALIC_ANGLE,

    /*# required fields in a Private blend dictionary */
    T1_BLEND_BLUE_VALUES,
    T1_BLEND_OTHER_BLUES,
    T1_BLEND_STANDARD_WIDTH,
    T1_BLEND_STANDARD_HEIGHT,
    T1_BLEND_STEM_SNAP_WIDTHS,
    T1_BLEND_STEM_SNAP_HEIGHTS,
    T1_BLEND_BLUE_SCALE,
    T1_BLEND_BLUE_SHIFT,
    T1_BLEND_FAMILY_BLUES,
    T1_BLEND_FAMILY_OTHER_BLUES,
    T1_BLEND_FORCE_BOLD,

    /*# never remove */
    T1_BLEND_MAX

  } T1_Blend_Flags;

  /* */


  /*# backwards compatible definitions */
#define t1_blend_underline_position   T1_BLEND_UNDERLINE_POSITION
#define t1_blend_underline_thickness  T1_BLEND_UNDERLINE_THICKNESS
#define t1_blend_italic_angle         T1_BLEND_ITALIC_ANGLE
#define t1_blend_blue_values          T1_BLEND_BLUE_VALUES
#define t1_blend_other_blues          T1_BLEND_OTHER_BLUES
#define t1_blend_standard_widths      T1_BLEND_STANDARD_WIDTH
#define t1_blend_standard_height      T1_BLEND_STANDARD_HEIGHT
#define t1_blend_stem_snap_widths     T1_BLEND_STEM_SNAP_WIDTHS
#define t1_blend_stem_snap_heights    T1_BLEND_STEM_SNAP_HEIGHTS
#define t1_blend_blue_scale           T1_BLEND_BLUE_SCALE
#define t1_blend_blue_shift           T1_BLEND_BLUE_SHIFT
#define t1_blend_family_blues         T1_BLEND_FAMILY_BLUES
#define t1_blend_family_other_blues   T1_BLEND_FAMILY_OTHER_BLUES
#define t1_blend_force_bold           T1_BLEND_FORCE_BOLD
#define t1_blend_max                  T1_BLEND_MAX


  /* maximum number of Multiple Masters designs, as defined in the spec */
#define T1_MAX_MM_DESIGNS     16

  /* maximum number of Multiple Masters axes, as defined in the spec */
#define T1_MAX_MM_AXIS        4

  /* maximum number of elements in a design map */
#define T1_MAX_MM_MAP_POINTS  20


  /* this structure is used to store the BlendDesignMap entry for an axis */
  typedef struct  PS_DesignMap_
  {
    FT_Byte    num_points;
    FT_Long*   design_points;
    FT_Fixed*  blend_points;

  } PS_DesignMapRec, *PS_DesignMap;

  /* backwards-compatible definition */
  typedef PS_DesignMapRec  T1_DesignMap;


  typedef struct  PS_BlendRec_
  {
    FT_UInt          num_designs;
    FT_UInt          num_axis;

    FT_String*       axis_names[T1_MAX_MM_AXIS];
    FT_Fixed*        design_pos[T1_MAX_MM_DESIGNS];
    PS_DesignMapRec  design_map[T1_MAX_MM_AXIS];

    FT_Fixed*        weight_vector;
    FT_Fixed*        default_weight_vector;

    PS_FontInfo      font_infos[T1_MAX_MM_DESIGNS + 1];
    PS_Private       privates  [T1_MAX_MM_DESIGNS + 1];

    FT_ULong         blend_bitflags;

    FT_BBox*         bboxes    [T1_MAX_MM_DESIGNS + 1];

    /* since 2.3.0 */

    /* undocumented, optional: the default design instance;   */
    /* corresponds to default_weight_vector --                */
    /* num_default_design_vector == 0 means it is not present */
    /* in the font and associated metrics files               */
    FT_UInt          default_design_vector[T1_MAX_MM_DESIGNS];
    FT_UInt          num_default_design_vector;

  } PS_BlendRec, *PS_Blend;


  /* backwards-compatible definition */
  typedef PS_BlendRec  T1_Blend;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    CID_FaceDictRec                                                    */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A structure used to represent data in a CID top-level dictionary.  */
  /*                                                                       */
  typedef struct  CID_FaceDictRec_
  {
    PS_PrivateRec  private_dict;

    FT_UInt        len_buildchar;
    FT_Fixed       forcebold_threshold;
    FT_Pos         stroke_width;
    FT_Fixed       expansion_factor;

    FT_Byte        paint_type;
    FT_Byte        font_type;
    FT_Matrix      font_matrix;
    FT_Vector      font_offset;

    FT_UInt        num_subrs;
    FT_ULong       subrmap_offset;
    FT_Int         sd_bytes;

  } CID_FaceDictRec;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    CID_FaceDict                                                       */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A handle to a @CID_FaceDictRec structure.                          */
  /*                                                                       */
  typedef struct CID_FaceDictRec_*  CID_FaceDict;

  /* */


  /* backwards-compatible definition */
  typedef CID_FaceDictRec  CID_FontDict;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    CID_FaceInfoRec                                                    */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A structure used to represent CID Face information.                */
  /*                                                                       */
  typedef struct  CID_FaceInfoRec_
  {
    FT_String*      cid_font_name;
    FT_Fixed        cid_version;
    FT_Int          cid_font_type;

    FT_String*      registry;
    FT_String*      ordering;
    FT_Int          supplement;

    PS_FontInfoRec  font_info;
    FT_BBox         font_bbox;
    FT_ULong        uid_base;

    FT_Int          num_xuid;
    FT_ULong        xuid[16];

    FT_ULong        cidmap_offset;
    FT_Int          fd_bytes;
    FT_Int          gd_bytes;
    FT_ULong        cid_count;

    FT_Int          num_dicts;
    CID_FaceDict    font_dicts;

    FT_ULong        data_offset;

  } CID_FaceInfoRec;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    CID_FaceInfo                                                       */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A handle to a @CID_FaceInfoRec structure.                          */
  /*                                                                       */
  typedef struct CID_FaceInfoRec_*  CID_FaceInfo;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    CID_Info                                                           */
  /*                                                                       */
  /* <Description>                                                         */
  /*   This type is equivalent to @CID_FaceInfoRec.  It is deprecated but  */
  /*   kept to maintain source compatibility between various versions of   */
  /*   FreeType.                                                           */
  /*                                                                       */
  typedef CID_FaceInfoRec  CID_Info;


  /************************************************************************
   *
   * @function:
   *    FT_Has_PS_Glyph_Names
   *
   * @description:
   *    Return true if a given face provides reliable PostScript glyph
   *    names.  This is similar to using the @FT_HAS_GLYPH_NAMES macro,
   *    except that certain fonts (mostly TrueType) contain incorrect
   *    glyph name tables.
   *
   *    When this function returns true, the caller is sure that the glyph
   *    names returned by @FT_Get_Glyph_Name are reliable.
   *
   * @input:
   *    face ::
   *       face handle
   *
   * @return:
   *    Boolean.  True if glyph names are reliable.
   *
   */
  FT_EXPORT( FT_Int )
  FT_Has_PS_Glyph_Names( FT_Face  face );


  /************************************************************************
   *
   * @function:
   *    FT_Get_PS_Font_Info
   *
   * @description:
   *    Retrieve the @PS_FontInfoRec structure corresponding to a given
   *    PostScript font.
   *
   * @input:
   *    face ::
   *       PostScript face handle.
   *
   * @output:
   *    afont_info ::
   *       Output font info structure pointer.
   *
   * @return:
   *    FreeType error code.  0~means success.
   *
   * @note:
   *    The string pointers within the font info structure are owned by
   *    the face and don't need to be freed by the caller.
   *
   *    If the font's format is not PostScript-based, this function will
   *    return the `FT_Err_Invalid_Argument' error code.
   *
   */
  FT_EXPORT( FT_Error )
  FT_Get_PS_Font_Info( FT_Face      face,
                       PS_FontInfo  afont_info );


  /************************************************************************
   *
   * @function:
   *    FT_Get_PS_Font_Private
   *
   * @description:
   *    Retrieve the @PS_PrivateRec structure corresponding to a given
   *    PostScript font.
   *
   * @input:
   *    face ::
   *       PostScript face handle.
   *
   * @output:
   *    afont_private ::
   *       Output private dictionary structure pointer.
   *
   * @return:
   *    FreeType error code.  0~means success.
   *
   * @note:
   *    The string pointers within the @PS_PrivateRec structure are owned by
   *    the face and don't need to be freed by the caller.
   *
   *    If the font's format is not PostScript-based, this function returns
   *    the `FT_Err_Invalid_Argument' error code.
   *
   */
  FT_EXPORT( FT_Error )
  FT_Get_PS_Font_Private( FT_Face     face,
                          PS_Private  afont_private );

  /* */


FT_END_HEADER

#endif /* __T1TABLES_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  ttnameid.h                                                             */
/*                                                                         */
/*    TrueType name ID definitions (specification only).                   */
/*                                                                         */
/*  Copyright 1996-2002, 2003, 2004, 2006, 2007, 2008 by                   */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __TTNAMEID_H__
#define __TTNAMEID_H__


#include <ft2build.h>


FT_BEGIN_HEADER


  /*************************************************************************/
  /*                                                                       */
  /* <Section>                                                             */
  /*    truetype_tables                                                    */
  /*                                                                       */


  /*************************************************************************/
  /*                                                                       */
  /* Possible values for the `platform' identifier code in the name        */
  /* records of the TTF `name' table.                                      */
  /*                                                                       */
  /*************************************************************************/


  /***********************************************************************
   *
   * @enum:
   *   TT_PLATFORM_XXX
   *
   * @description:
   *   A list of valid values for the `platform_id' identifier code in
   *   @FT_CharMapRec and @FT_SfntName structures.
   *
   * @values:
   *   TT_PLATFORM_APPLE_UNICODE ::
   *     Used by Apple to indicate a Unicode character map and/or name entry.
   *     See @TT_APPLE_ID_XXX for corresponding `encoding_id' values.  Note
   *     that name entries in this format are coded as big-endian UCS-2
   *     character codes _only_.
   *
   *   TT_PLATFORM_MACINTOSH ::
   *     Used by Apple to indicate a MacOS-specific charmap and/or name entry.
   *     See @TT_MAC_ID_XXX for corresponding `encoding_id' values.  Note that
   *     most TrueType fonts contain an Apple roman charmap to be usable on
   *     MacOS systems (even if they contain a Microsoft charmap as well).
   *
   *   TT_PLATFORM_ISO ::
   *     This value was used to specify ISO/IEC 10646 charmaps.  It is however
   *     now deprecated.  See @TT_ISO_ID_XXX for a list of corresponding
   *     `encoding_id' values.
   *
   *   TT_PLATFORM_MICROSOFT ::
   *     Used by Microsoft to indicate Windows-specific charmaps.  See
   *     @TT_MS_ID_XXX for a list of corresponding `encoding_id' values.
   *     Note that most fonts contain a Unicode charmap using
   *     (TT_PLATFORM_MICROSOFT, @TT_MS_ID_UNICODE_CS).
   *
   *   TT_PLATFORM_CUSTOM ::
   *     Used to indicate application-specific charmaps.
   *
   *   TT_PLATFORM_ADOBE ::
   *     This value isn't part of any font format specification, but is used
   *     by FreeType to report Adobe-specific charmaps in an @FT_CharMapRec
   *     structure.  See @TT_ADOBE_ID_XXX.
   */

#define TT_PLATFORM_APPLE_UNICODE  0
#define TT_PLATFORM_MACINTOSH      1
#define TT_PLATFORM_ISO            2 /* deprecated */
#define TT_PLATFORM_MICROSOFT      3
#define TT_PLATFORM_CUSTOM         4
#define TT_PLATFORM_ADOBE          7 /* artificial */


  /***********************************************************************
   *
   * @enum:
   *   TT_APPLE_ID_XXX
   *
   * @description:
   *   A list of valid values for the `encoding_id' for
   *   @TT_PLATFORM_APPLE_UNICODE charmaps and name entries.
   *
   * @values:
   *   TT_APPLE_ID_DEFAULT ::
   *     Unicode version 1.0.
   *
   *   TT_APPLE_ID_UNICODE_1_1 ::
   *     Unicode 1.1; specifies Hangul characters starting at U+34xx.
   *
   *   TT_APPLE_ID_ISO_10646 ::
   *     Deprecated (identical to preceding).
   *
   *   TT_APPLE_ID_UNICODE_2_0 ::
   *     Unicode 2.0 and beyond (UTF-16 BMP only).
   *
   *   TT_APPLE_ID_UNICODE_32 ::
   *     Unicode 3.1 and beyond, using UTF-32.
   *
   *   TT_APPLE_ID_VARIANT_SELECTOR ::
   *     From Adobe, not Apple.  Not a normal cmap.  Specifies variations
   *     on a real cmap.
   */

#define TT_APPLE_ID_DEFAULT           0 /* Unicode 1.0 */
#define TT_APPLE_ID_UNICODE_1_1       1 /* specify Hangul at U+34xx */
#define TT_APPLE_ID_ISO_10646         2 /* deprecated */
#define TT_APPLE_ID_UNICODE_2_0       3 /* or later */
#define TT_APPLE_ID_UNICODE_32        4 /* 2.0 or later, full repertoire */
#define TT_APPLE_ID_VARIANT_SELECTOR  5 /* variation selector data */


  /***********************************************************************
   *
   * @enum:
   *   TT_MAC_ID_XXX
   *
   * @description:
   *   A list of valid values for the `encoding_id' for
   *   @TT_PLATFORM_MACINTOSH charmaps and name entries.
   *
   * @values:
   *   TT_MAC_ID_ROMAN ::
   *   TT_MAC_ID_JAPANESE ::
   *   TT_MAC_ID_TRADITIONAL_CHINESE ::
   *   TT_MAC_ID_KOREAN ::
   *   TT_MAC_ID_ARABIC ::
   *   TT_MAC_ID_HEBREW ::
   *   TT_MAC_ID_GREEK ::
   *   TT_MAC_ID_RUSSIAN ::
   *   TT_MAC_ID_RSYMBOL ::
   *   TT_MAC_ID_DEVANAGARI ::
   *   TT_MAC_ID_GURMUKHI ::
   *   TT_MAC_ID_GUJARATI ::
   *   TT_MAC_ID_ORIYA ::
   *   TT_MAC_ID_BENGALI ::
   *   TT_MAC_ID_TAMIL ::
   *   TT_MAC_ID_TELUGU ::
   *   TT_MAC_ID_KANNADA ::
   *   TT_MAC_ID_MALAYALAM ::
   *   TT_MAC_ID_SINHALESE ::
   *   TT_MAC_ID_BURMESE ::
   *   TT_MAC_ID_KHMER ::
   *   TT_MAC_ID_THAI ::
   *   TT_MAC_ID_LAOTIAN ::
   *   TT_MAC_ID_GEORGIAN ::
   *   TT_MAC_ID_ARMENIAN ::
   *   TT_MAC_ID_MALDIVIAN ::
   *   TT_MAC_ID_SIMPLIFIED_CHINESE ::
   *   TT_MAC_ID_TIBETAN ::
   *   TT_MAC_ID_MONGOLIAN ::
   *   TT_MAC_ID_GEEZ ::
   *   TT_MAC_ID_SLAVIC ::
   *   TT_MAC_ID_VIETNAMESE ::
   *   TT_MAC_ID_SINDHI ::
   *   TT_MAC_ID_UNINTERP ::
   */

#define TT_MAC_ID_ROMAN                 0
#define TT_MAC_ID_JAPANESE              1
#define TT_MAC_ID_TRADITIONAL_CHINESE   2
#define TT_MAC_ID_KOREAN                3
#define TT_MAC_ID_ARABIC                4
#define TT_MAC_ID_HEBREW                5
#define TT_MAC_ID_GREEK                 6
#define TT_MAC_ID_RUSSIAN               7
#define TT_MAC_ID_RSYMBOL               8
#define TT_MAC_ID_DEVANAGARI            9
#define TT_MAC_ID_GURMUKHI             10
#define TT_MAC_ID_GUJARATI             11
#define TT_MAC_ID_ORIYA                12
#define TT_MAC_ID_BENGALI              13
#define TT_MAC_ID_TAMIL                14
#define TT_MAC_ID_TELUGU               15
#define TT_MAC_ID_KANNADA              16
#define TT_MAC_ID_MALAYALAM            17
#define TT_MAC_ID_SINHALESE            18
#define TT_MAC_ID_BURMESE              19
#define TT_MAC_ID_KHMER                20
#define TT_MAC_ID_THAI                 21
#define TT_MAC_ID_LAOTIAN              22
#define TT_MAC_ID_GEORGIAN             23
#define TT_MAC_ID_ARMENIAN             24
#define TT_MAC_ID_MALDIVIAN            25
#define TT_MAC_ID_SIMPLIFIED_CHINESE   25
#define TT_MAC_ID_TIBETAN              26
#define TT_MAC_ID_MONGOLIAN            27
#define TT_MAC_ID_GEEZ                 28
#define TT_MAC_ID_SLAVIC               29
#define TT_MAC_ID_VIETNAMESE           30
#define TT_MAC_ID_SINDHI               31
#define TT_MAC_ID_UNINTERP             32


  /***********************************************************************
   *
   * @enum:
   *   TT_ISO_ID_XXX
   *
   * @description:
   *   A list of valid values for the `encoding_id' for
   *   @TT_PLATFORM_ISO charmaps and name entries.
   *
   *   Their use is now deprecated.
   *
   * @values:
   *   TT_ISO_ID_7BIT_ASCII ::
   *     ASCII.
   *   TT_ISO_ID_10646 ::
   *     ISO/10646.
   *   TT_ISO_ID_8859_1 ::
   *     Also known as Latin-1.
   */

#define TT_ISO_ID_7BIT_ASCII  0
#define TT_ISO_ID_10646       1
#define TT_ISO_ID_8859_1      2


  /***********************************************************************
   *
   * @enum:
   *   TT_MS_ID_XXX
   *
   * @description:
   *   A list of valid values for the `encoding_id' for
   *   @TT_PLATFORM_MICROSOFT charmaps and name entries.
   *
   * @values:
   *   TT_MS_ID_SYMBOL_CS ::
   *     Corresponds to Microsoft symbol encoding. See
   *     @FT_ENCODING_MS_SYMBOL.
   *
   *   TT_MS_ID_UNICODE_CS ::
   *     Corresponds to a Microsoft WGL4 charmap, matching Unicode.  See
   *     @FT_ENCODING_UNICODE.
   *
   *   TT_MS_ID_SJIS ::
   *     Corresponds to SJIS Japanese encoding.  See @FT_ENCODING_SJIS.
   *
   *   TT_MS_ID_GB2312 ::
   *     Corresponds to Simplified Chinese as used in Mainland China.  See
   *     @FT_ENCODING_GB2312.
   *
   *   TT_MS_ID_BIG_5 ::
   *     Corresponds to Traditional Chinese as used in Taiwan and Hong Kong.
   *     See @FT_ENCODING_BIG5.
   *
   *   TT_MS_ID_WANSUNG ::
   *     Corresponds to Korean Wansung encoding.  See @FT_ENCODING_WANSUNG.
   *
   *   TT_MS_ID_JOHAB ::
   *     Corresponds to Johab encoding.  See @FT_ENCODING_JOHAB.
   *
   *   TT_MS_ID_UCS_4 ::
   *     Corresponds to UCS-4 or UTF-32 charmaps.  This has been added to
   *     the OpenType specification version 1.4 (mid-2001.)
   */

#define TT_MS_ID_SYMBOL_CS    0
#define TT_MS_ID_UNICODE_CS   1
#define TT_MS_ID_SJIS         2
#define TT_MS_ID_GB2312       3
#define TT_MS_ID_BIG_5        4
#define TT_MS_ID_WANSUNG      5
#define TT_MS_ID_JOHAB        6
#define TT_MS_ID_UCS_4       10


  /***********************************************************************
   *
   * @enum:
   *   TT_ADOBE_ID_XXX
   *
   * @description:
   *   A list of valid values for the `encoding_id' for
   *   @TT_PLATFORM_ADOBE charmaps.  This is a FreeType-specific extension!
   *
   * @values:
   *   TT_ADOBE_ID_STANDARD ::
   *     Adobe standard encoding.
   *   TT_ADOBE_ID_EXPERT ::
   *     Adobe expert encoding.
   *   TT_ADOBE_ID_CUSTOM ::
   *     Adobe custom encoding.
   *   TT_ADOBE_ID_LATIN_1 ::
   *     Adobe Latin~1 encoding.
   */

#define TT_ADOBE_ID_STANDARD  0
#define TT_ADOBE_ID_EXPERT    1
#define TT_ADOBE_ID_CUSTOM    2
#define TT_ADOBE_ID_LATIN_1   3


  /*************************************************************************/
  /*                                                                       */
  /* Possible values of the language identifier field in the name records  */
  /* of the TTF `name' table if the `platform' identifier code is          */
  /* TT_PLATFORM_MACINTOSH.                                                */
  /*                                                                       */
  /* The canonical source for the Apple assigned Language ID's is at       */
  /*                                                                       */
  /*   http://fonts.apple.com/TTRefMan/RM06/Chap6name.html                 */
  /*                                                                       */
#define TT_MAC_LANGID_ENGLISH                       0
#define TT_MAC_LANGID_FRENCH                        1
#define TT_MAC_LANGID_GERMAN                        2
#define TT_MAC_LANGID_ITALIAN                       3
#define TT_MAC_LANGID_DUTCH                         4
#define TT_MAC_LANGID_SWEDISH                       5
#define TT_MAC_LANGID_SPANISH                       6
#define TT_MAC_LANGID_DANISH                        7
#define TT_MAC_LANGID_PORTUGUESE                    8
#define TT_MAC_LANGID_NORWEGIAN                     9
#define TT_MAC_LANGID_HEBREW                       10
#define TT_MAC_LANGID_JAPANESE                     11
#define TT_MAC_LANGID_ARABIC                       12
#define TT_MAC_LANGID_FINNISH                      13
#define TT_MAC_LANGID_GREEK                        14
#define TT_MAC_LANGID_ICELANDIC                    15
#define TT_MAC_LANGID_MALTESE                      16
#define TT_MAC_LANGID_TURKISH                      17
#define TT_MAC_LANGID_CROATIAN                     18
#define TT_MAC_LANGID_CHINESE_TRADITIONAL          19
#define TT_MAC_LANGID_URDU                         20
#define TT_MAC_LANGID_HINDI                        21
#define TT_MAC_LANGID_THAI                         22
#define TT_MAC_LANGID_KOREAN                       23
#define TT_MAC_LANGID_LITHUANIAN                   24
#define TT_MAC_LANGID_POLISH                       25
#define TT_MAC_LANGID_HUNGARIAN                    26
#define TT_MAC_LANGID_ESTONIAN                     27
#define TT_MAC_LANGID_LETTISH                      28
#define TT_MAC_LANGID_SAAMISK                      29
#define TT_MAC_LANGID_FAEROESE                     30
#define TT_MAC_LANGID_FARSI                        31
#define TT_MAC_LANGID_RUSSIAN                      32
#define TT_MAC_LANGID_CHINESE_SIMPLIFIED           33
#define TT_MAC_LANGID_FLEMISH                      34
#define TT_MAC_LANGID_IRISH                        35
#define TT_MAC_LANGID_ALBANIAN                     36
#define TT_MAC_LANGID_ROMANIAN                     37
#define TT_MAC_LANGID_CZECH                        38
#define TT_MAC_LANGID_SLOVAK                       39
#define TT_MAC_LANGID_SLOVENIAN                    40
#define TT_MAC_LANGID_YIDDISH                      41
#define TT_MAC_LANGID_SERBIAN                      42
#define TT_MAC_LANGID_MACEDONIAN                   43
#define TT_MAC_LANGID_BULGARIAN                    44
#define TT_MAC_LANGID_UKRAINIAN                    45
#define TT_MAC_LANGID_BYELORUSSIAN                 46
#define TT_MAC_LANGID_UZBEK                        47
#define TT_MAC_LANGID_KAZAKH                       48
#define TT_MAC_LANGID_AZERBAIJANI                  49
#define TT_MAC_LANGID_AZERBAIJANI_CYRILLIC_SCRIPT  49
#define TT_MAC_LANGID_AZERBAIJANI_ARABIC_SCRIPT    50
#define TT_MAC_LANGID_ARMENIAN                     51
#define TT_MAC_LANGID_GEORGIAN                     52
#define TT_MAC_LANGID_MOLDAVIAN                    53
#define TT_MAC_LANGID_KIRGHIZ                      54
#define TT_MAC_LANGID_TAJIKI                       55
#define TT_MAC_LANGID_TURKMEN                      56
#define TT_MAC_LANGID_MONGOLIAN                    57
#define TT_MAC_LANGID_MONGOLIAN_MONGOLIAN_SCRIPT   57
#define TT_MAC_LANGID_MONGOLIAN_CYRILLIC_SCRIPT    58
#define TT_MAC_LANGID_PASHTO                       59
#define TT_MAC_LANGID_KURDISH                      60
#define TT_MAC_LANGID_KASHMIRI                     61
#define TT_MAC_LANGID_SINDHI                       62
#define TT_MAC_LANGID_TIBETAN                      63
#define TT_MAC_LANGID_NEPALI                       64
#define TT_MAC_LANGID_SANSKRIT                     65
#define TT_MAC_LANGID_MARATHI                      66
#define TT_MAC_LANGID_BENGALI                      67
#define TT_MAC_LANGID_ASSAMESE                     68
#define TT_MAC_LANGID_GUJARATI                     69
#define TT_MAC_LANGID_PUNJABI                      70
#define TT_MAC_LANGID_ORIYA                        71
#define TT_MAC_LANGID_MALAYALAM                    72
#define TT_MAC_LANGID_KANNADA                      73
#define TT_MAC_LANGID_TAMIL                        74
#define TT_MAC_LANGID_TELUGU                       75
#define TT_MAC_LANGID_SINHALESE                    76
#define TT_MAC_LANGID_BURMESE                      77
#define TT_MAC_LANGID_KHMER                        78
#define TT_MAC_LANGID_LAO                          79
#define TT_MAC_LANGID_VIETNAMESE                   80
#define TT_MAC_LANGID_INDONESIAN                   81
#define TT_MAC_LANGID_TAGALOG                      82
#define TT_MAC_LANGID_MALAY_ROMAN_SCRIPT           83
#define TT_MAC_LANGID_MALAY_ARABIC_SCRIPT          84
#define TT_MAC_LANGID_AMHARIC                      85
#define TT_MAC_LANGID_TIGRINYA                     86
#define TT_MAC_LANGID_GALLA                        87
#define TT_MAC_LANGID_SOMALI                       88
#define TT_MAC_LANGID_SWAHILI                      89
#define TT_MAC_LANGID_RUANDA                       90
#define TT_MAC_LANGID_RUNDI                        91
#define TT_MAC_LANGID_CHEWA                        92
#define TT_MAC_LANGID_MALAGASY                     93
#define TT_MAC_LANGID_ESPERANTO                    94
#define TT_MAC_LANGID_WELSH                       128
#define TT_MAC_LANGID_BASQUE                      129
#define TT_MAC_LANGID_CATALAN                     130
#define TT_MAC_LANGID_LATIN                       131
#define TT_MAC_LANGID_QUECHUA                     132
#define TT_MAC_LANGID_GUARANI                     133
#define TT_MAC_LANGID_AYMARA                      134
#define TT_MAC_LANGID_TATAR                       135
#define TT_MAC_LANGID_UIGHUR                      136
#define TT_MAC_LANGID_DZONGKHA                    137
#define TT_MAC_LANGID_JAVANESE                    138
#define TT_MAC_LANGID_SUNDANESE                   139


#if 0  /* these seem to be errors that have been dropped */

#define TT_MAC_LANGID_SCOTTISH_GAELIC             140
#define TT_MAC_LANGID_IRISH_GAELIC                141

#endif


  /* The following codes are new as of 2000-03-10 */
#define TT_MAC_LANGID_GALICIAN                    140
#define TT_MAC_LANGID_AFRIKAANS                   141
#define TT_MAC_LANGID_BRETON                      142
#define TT_MAC_LANGID_INUKTITUT                   143
#define TT_MAC_LANGID_SCOTTISH_GAELIC             144
#define TT_MAC_LANGID_MANX_GAELIC                 145
#define TT_MAC_LANGID_IRISH_GAELIC                146
#define TT_MAC_LANGID_TONGAN                      147
#define TT_MAC_LANGID_GREEK_POLYTONIC             148
#define TT_MAC_LANGID_GREELANDIC                  149
#define TT_MAC_LANGID_AZERBAIJANI_ROMAN_SCRIPT    150


  /*************************************************************************/
  /*                                                                       */
  /* Possible values of the language identifier field in the name records  */
  /* of the TTF `name' table if the `platform' identifier code is          */
  /* TT_PLATFORM_MICROSOFT.                                                */
  /*                                                                       */
  /* The canonical source for the MS assigned LCID's (seems to) be at      */
  /*                                                                       */
  /*   http://www.microsoft.com/globaldev/reference/lcid-all.mspx          */
  /*                                                                       */
  /* It used to be at various places, among them                           */
  /*                                                                       */
  /*   http://www.microsoft.com/typography/OTSPEC/lcid-cp.txt              */
  /*   http://www.microsoft.com/globaldev/reference/loclanghome.asp        */
  /*   http://support.microsoft.com/support/kb/articles/Q224/8/04.ASP      */
  /*   http://msdn.microsoft.com/library/en-us/passport25/                 */
  /*           NET_Passport_VBScript_Documentation/Single_Sign_In/         */
  /*           Advanced_Single_Sign_In/Localization_and_LCIDs.asp          */
  /*                                                                       */
  /* Hopefully, it seems now that the Globaldev site prevails...           */
  /*                                   (updated by Antoine, 2004-02-17)    */

#define TT_MS_LANGID_ARABIC_GENERAL                    0x0001
#define TT_MS_LANGID_ARABIC_SAUDI_ARABIA               0x0401
#define TT_MS_LANGID_ARABIC_IRAQ                       0x0801
#define TT_MS_LANGID_ARABIC_EGYPT                      0x0c01
#define TT_MS_LANGID_ARABIC_LIBYA                      0x1001
#define TT_MS_LANGID_ARABIC_ALGERIA                    0x1401
#define TT_MS_LANGID_ARABIC_MOROCCO                    0x1801
#define TT_MS_LANGID_ARABIC_TUNISIA                    0x1c01
#define TT_MS_LANGID_ARABIC_OMAN                       0x2001
#define TT_MS_LANGID_ARABIC_YEMEN                      0x2401
#define TT_MS_LANGID_ARABIC_SYRIA                      0x2801
#define TT_MS_LANGID_ARABIC_JORDAN                     0x2c01
#define TT_MS_LANGID_ARABIC_LEBANON                    0x3001
#define TT_MS_LANGID_ARABIC_KUWAIT                     0x3401
#define TT_MS_LANGID_ARABIC_UAE                        0x3801
#define TT_MS_LANGID_ARABIC_BAHRAIN                    0x3c01
#define TT_MS_LANGID_ARABIC_QATAR                      0x4001
#define TT_MS_LANGID_BULGARIAN_BULGARIA                0x0402
#define TT_MS_LANGID_CATALAN_SPAIN                     0x0403
#define TT_MS_LANGID_CHINESE_GENERAL                   0x0004
#define TT_MS_LANGID_CHINESE_TAIWAN                    0x0404
#define TT_MS_LANGID_CHINESE_PRC                       0x0804
#define TT_MS_LANGID_CHINESE_HONG_KONG                 0x0c04
#define TT_MS_LANGID_CHINESE_SINGAPORE                 0x1004

#if 1  /* this looks like the correct value */
#define TT_MS_LANGID_CHINESE_MACAU                     0x1404
#else  /* but beware, Microsoft may change its mind...
          the most recent Word reference has the following: */
#define TT_MS_LANGID_CHINESE_MACAU  TT_MS_LANGID_CHINESE_HONG_KONG
#endif

#if 0  /* used only with .NET `cultures'; commented out */
#define TT_MS_LANGID_CHINESE_TRADITIONAL               0x7C04
#endif

#define TT_MS_LANGID_CZECH_CZECH_REPUBLIC              0x0405
#define TT_MS_LANGID_DANISH_DENMARK                    0x0406
#define TT_MS_LANGID_GERMAN_GERMANY                    0x0407
#define TT_MS_LANGID_GERMAN_SWITZERLAND                0x0807
#define TT_MS_LANGID_GERMAN_AUSTRIA                    0x0c07
#define TT_MS_LANGID_GERMAN_LUXEMBOURG                 0x1007
#define TT_MS_LANGID_GERMAN_LIECHTENSTEI               0x1407
#define TT_MS_LANGID_GREEK_GREECE                      0x0408

  /* don't ask what this one means... It is commented out currently. */
#if 0
#define TT_MS_LANGID_GREEK_GREECE2                     0x2008
#endif

#define TT_MS_LANGID_ENGLISH_GENERAL                   0x0009
#define TT_MS_LANGID_ENGLISH_UNITED_STATES             0x0409
#define TT_MS_LANGID_ENGLISH_UNITED_KINGDOM            0x0809
#define TT_MS_LANGID_ENGLISH_AUSTRALIA                 0x0c09
#define TT_MS_LANGID_ENGLISH_CANADA                    0x1009
#define TT_MS_LANGID_ENGLISH_NEW_ZEALAND               0x1409
#define TT_MS_LANGID_ENGLISH_IRELAND                   0x1809
#define TT_MS_LANGID_ENGLISH_SOUTH_AFRICA              0x1c09
#define TT_MS_LANGID_ENGLISH_JAMAICA                   0x2009
#define TT_MS_LANGID_ENGLISH_CARIBBEAN                 0x2409
#define TT_MS_LANGID_ENGLISH_BELIZE                    0x2809
#define TT_MS_LANGID_ENGLISH_TRINIDAD                  0x2c09
#define TT_MS_LANGID_ENGLISH_ZIMBABWE                  0x3009
#define TT_MS_LANGID_ENGLISH_PHILIPPINES               0x3409
#define TT_MS_LANGID_ENGLISH_INDONESIA                 0x3809
#define TT_MS_LANGID_ENGLISH_HONG_KONG                 0x3c09
#define TT_MS_LANGID_ENGLISH_INDIA                     0x4009
#define TT_MS_LANGID_ENGLISH_MALAYSIA                  0x4409
#define TT_MS_LANGID_ENGLISH_SINGAPORE                 0x4809
#define TT_MS_LANGID_SPANISH_SPAIN_TRADITIONAL_SORT    0x040a
#define TT_MS_LANGID_SPANISH_MEXICO                    0x080a
#define TT_MS_LANGID_SPANISH_SPAIN_INTERNATIONAL_SORT  0x0c0a
#define TT_MS_LANGID_SPANISH_GUATEMALA                 0x100a
#define TT_MS_LANGID_SPANISH_COSTA_RICA                0x140a
#define TT_MS_LANGID_SPANISH_PANAMA                    0x180a
#define TT_MS_LANGID_SPANISH_DOMINICAN_REPUBLIC        0x1c0a
#define TT_MS_LANGID_SPANISH_VENEZUELA                 0x200a
#define TT_MS_LANGID_SPANISH_COLOMBIA                  0x240a
#define TT_MS_LANGID_SPANISH_PERU                      0x280a
#define TT_MS_LANGID_SPANISH_ARGENTINA                 0x2c0a
#define TT_MS_LANGID_SPANISH_ECUADOR                   0x300a
#define TT_MS_LANGID_SPANISH_CHILE                     0x340a
#define TT_MS_LANGID_SPANISH_URUGUAY                   0x380a
#define TT_MS_LANGID_SPANISH_PARAGUAY                  0x3c0a
#define TT_MS_LANGID_SPANISH_BOLIVIA                   0x400a
#define TT_MS_LANGID_SPANISH_EL_SALVADOR               0x440a
#define TT_MS_LANGID_SPANISH_HONDURAS                  0x480a
#define TT_MS_LANGID_SPANISH_NICARAGUA                 0x4c0a
#define TT_MS_LANGID_SPANISH_PUERTO_RICO               0x500a
#define TT_MS_LANGID_SPANISH_UNITED_STATES             0x540a
  /* The following ID blatantly violate MS specs by using a */
  /* sublanguage > 0x1F.                                    */
#define TT_MS_LANGID_SPANISH_LATIN_AMERICA             0xE40aU
#define TT_MS_LANGID_FINNISH_FINLAND                   0x040b
#define TT_MS_LANGID_FRENCH_FRANCE                     0x040c
#define TT_MS_LANGID_FRENCH_BELGIUM                    0x080c
#define TT_MS_LANGID_FRENCH_CANADA                     0x0c0c
#define TT_MS_LANGID_FRENCH_SWITZERLAND                0x100c
#define TT_MS_LANGID_FRENCH_LUXEMBOURG                 0x140c
#define TT_MS_LANGID_FRENCH_MONACO                     0x180c
#define TT_MS_LANGID_FRENCH_WEST_INDIES                0x1c0c
#define TT_MS_LANGID_FRENCH_REUNION                    0x200c
#define TT_MS_LANGID_FRENCH_CONGO                      0x240c
  /* which was formerly: */
#define TT_MS_LANGID_FRENCH_ZAIRE  TT_MS_LANGID_FRENCH_CONGO
#define TT_MS_LANGID_FRENCH_SENEGAL                    0x280c
#define TT_MS_LANGID_FRENCH_CAMEROON                   0x2c0c
#define TT_MS_LANGID_FRENCH_COTE_D_IVOIRE              0x300c
#define TT_MS_LANGID_FRENCH_MALI                       0x340c
#define TT_MS_LANGID_FRENCH_MOROCCO                    0x380c
#define TT_MS_LANGID_FRENCH_HAITI                      0x3c0c
  /* and another violation of the spec (see 0xE40aU) */
#define TT_MS_LANGID_FRENCH_NORTH_AFRICA               0xE40cU
#define TT_MS_LANGID_HEBREW_ISRAEL                     0x040d
#define TT_MS_LANGID_HUNGARIAN_HUNGARY                 0x040e
#define TT_MS_LANGID_ICELANDIC_ICELAND                 0x040f
#define TT_MS_LANGID_ITALIAN_ITALY                     0x0410
#define TT_MS_LANGID_ITALIAN_SWITZERLAND               0x0810
#define TT_MS_LANGID_JAPANESE_JAPAN                    0x0411
#define TT_MS_LANGID_KOREAN_EXTENDED_WANSUNG_KOREA     0x0412
#define TT_MS_LANGID_KOREAN_JOHAB_KOREA                0x0812
#define TT_MS_LANGID_DUTCH_NETHERLANDS                 0x0413
#define TT_MS_LANGID_DUTCH_BELGIUM                     0x0813
#define TT_MS_LANGID_NORWEGIAN_NORWAY_BOKMAL           0x0414
#define TT_MS_LANGID_NORWEGIAN_NORWAY_NYNORSK          0x0814
#define TT_MS_LANGID_POLISH_POLAND                     0x0415
#define TT_MS_LANGID_PORTUGUESE_BRAZIL                 0x0416
#define TT_MS_LANGID_PORTUGUESE_PORTUGAL               0x0816
#define TT_MS_LANGID_RHAETO_ROMANIC_SWITZERLAND        0x0417
#define TT_MS_LANGID_ROMANIAN_ROMANIA                  0x0418
#define TT_MS_LANGID_MOLDAVIAN_MOLDAVIA                0x0818
#define TT_MS_LANGID_RUSSIAN_RUSSIA                    0x0419
#define TT_MS_LANGID_RUSSIAN_MOLDAVIA                  0x0819
#define TT_MS_LANGID_CROATIAN_CROATIA                  0x041a
#define TT_MS_LANGID_SERBIAN_SERBIA_LATIN              0x081a
#define TT_MS_LANGID_SERBIAN_SERBIA_CYRILLIC           0x0c1a

#if 0  /* this used to be this value, but it looks like we were wrong */
#define TT_MS_LANGID_BOSNIAN_BOSNIA_HERZEGOVINA        0x101a
#else  /* current sources say */
#define TT_MS_LANGID_CROATIAN_BOSNIA_HERZEGOVINA       0x101a
#define TT_MS_LANGID_BOSNIAN_BOSNIA_HERZEGOVINA        0x141a
       /* and XPsp2 Platform SDK added (2004-07-26) */
       /* Names are shortened to be significant within 40 chars. */
#define TT_MS_LANGID_SERBIAN_BOSNIA_HERZ_LATIN         0x181a
#define TT_MS_LANGID_SERBIAN_BOSNIA_HERZ_CYRILLIC      0x181a
#endif

#define TT_MS_LANGID_SLOVAK_SLOVAKIA                   0x041b
#define TT_MS_LANGID_ALBANIAN_ALBANIA                  0x041c
#define TT_MS_LANGID_SWEDISH_SWEDEN                    0x041d
#define TT_MS_LANGID_SWEDISH_FINLAND                   0x081d
#define TT_MS_LANGID_THAI_THAILAND                     0x041e
#define TT_MS_LANGID_TURKISH_TURKEY                    0x041f
#define TT_MS_LANGID_URDU_PAKISTAN                     0x0420
#define TT_MS_LANGID_URDU_INDIA                        0x0820
#define TT_MS_LANGID_INDONESIAN_INDONESIA              0x0421
#define TT_MS_LANGID_UKRAINIAN_UKRAINE                 0x0422
#define TT_MS_LANGID_BELARUSIAN_BELARUS                0x0423
#define TT_MS_LANGID_SLOVENE_SLOVENIA                  0x0424
#define TT_MS_LANGID_ESTONIAN_ESTONIA                  0x0425
#define TT_MS_LANGID_LATVIAN_LATVIA                    0x0426
#define TT_MS_LANGID_LITHUANIAN_LITHUANIA              0x0427
#define TT_MS_LANGID_CLASSIC_LITHUANIAN_LITHUANIA      0x0827
#define TT_MS_LANGID_TAJIK_TAJIKISTAN                  0x0428
#define TT_MS_LANGID_FARSI_IRAN                        0x0429
#define TT_MS_LANGID_VIETNAMESE_VIET_NAM               0x042a
#define TT_MS_LANGID_ARMENIAN_ARMENIA                  0x042b
#define TT_MS_LANGID_AZERI_AZERBAIJAN_LATIN            0x042c
#define TT_MS_LANGID_AZERI_AZERBAIJAN_CYRILLIC         0x082c
#define TT_MS_LANGID_BASQUE_SPAIN                      0x042d
#define TT_MS_LANGID_SORBIAN_GERMANY                   0x042e
#define TT_MS_LANGID_MACEDONIAN_MACEDONIA              0x042f
#define TT_MS_LANGID_SUTU_SOUTH_AFRICA                 0x0430
#define TT_MS_LANGID_TSONGA_SOUTH_AFRICA               0x0431
#define TT_MS_LANGID_TSWANA_SOUTH_AFRICA               0x0432
#define TT_MS_LANGID_VENDA_SOUTH_AFRICA                0x0433
#define TT_MS_LANGID_XHOSA_SOUTH_AFRICA                0x0434
#define TT_MS_LANGID_ZULU_SOUTH_AFRICA                 0x0435
#define TT_MS_LANGID_AFRIKAANS_SOUTH_AFRICA            0x0436
#define TT_MS_LANGID_GEORGIAN_GEORGIA                  0x0437
#define TT_MS_LANGID_FAEROESE_FAEROE_ISLANDS           0x0438
#define TT_MS_LANGID_HINDI_INDIA                       0x0439
#define TT_MS_LANGID_MALTESE_MALTA                     0x043a
  /* Added by XPsp2 Platform SDK (2004-07-26) */
#define TT_MS_LANGID_SAMI_NORTHERN_NORWAY              0x043b
#define TT_MS_LANGID_SAMI_NORTHERN_SWEDEN              0x083b
#define TT_MS_LANGID_SAMI_NORTHERN_FINLAND             0x0C3b
#define TT_MS_LANGID_SAMI_LULE_NORWAY                  0x103b
#define TT_MS_LANGID_SAMI_LULE_SWEDEN                  0x143b
#define TT_MS_LANGID_SAMI_SOUTHERN_NORWAY              0x183b
#define TT_MS_LANGID_SAMI_SOUTHERN_SWEDEN              0x1C3b
#define TT_MS_LANGID_SAMI_SKOLT_FINLAND                0x203b
#define TT_MS_LANGID_SAMI_INARI_FINLAND                0x243b
  /* ... and we also keep our old identifier... */
#define TT_MS_LANGID_SAAMI_LAPONIA                     0x043b

#if 0 /* this seems to be a previous inversion */
#define TT_MS_LANGID_IRISH_GAELIC_IRELAND              0x043c
#define TT_MS_LANGID_SCOTTISH_GAELIC_UNITED_KINGDOM    0x083c
#else
#define TT_MS_LANGID_SCOTTISH_GAELIC_UNITED_KINGDOM    0x083c
#define TT_MS_LANGID_IRISH_GAELIC_IRELAND              0x043c
#endif

#define TT_MS_LANGID_YIDDISH_GERMANY                   0x043d
#define TT_MS_LANGID_MALAY_MALAYSIA                    0x043e
#define TT_MS_LANGID_MALAY_BRUNEI_DARUSSALAM           0x083e
#define TT_MS_LANGID_KAZAK_KAZAKSTAN                   0x043f
#define TT_MS_LANGID_KIRGHIZ_KIRGHIZSTAN /* Cyrillic*/ 0x0440
  /* alias declared in Windows 2000 */
#define TT_MS_LANGID_KIRGHIZ_KIRGHIZ_REPUBLIC \
          TT_MS_LANGID_KIRGHIZ_KIRGHIZSTAN

#define TT_MS_LANGID_SWAHILI_KENYA                     0x0441
#define TT_MS_LANGID_TURKMEN_TURKMENISTAN              0x0442
#define TT_MS_LANGID_UZBEK_UZBEKISTAN_LATIN            0x0443
#define TT_MS_LANGID_UZBEK_UZBEKISTAN_CYRILLIC         0x0843
#define TT_MS_LANGID_TATAR_TATARSTAN                   0x0444
#define TT_MS_LANGID_BENGALI_INDIA                     0x0445
#define TT_MS_LANGID_BENGALI_BANGLADESH                0x0845
#define TT_MS_LANGID_PUNJABI_INDIA                     0x0446
#define TT_MS_LANGID_PUNJABI_ARABIC_PAKISTAN           0x0846
#define TT_MS_LANGID_GUJARATI_INDIA                    0x0447
#define TT_MS_LANGID_ORIYA_INDIA                       0x0448
#define TT_MS_LANGID_TAMIL_INDIA                       0x0449
#define TT_MS_LANGID_TELUGU_INDIA                      0x044a
#define TT_MS_LANGID_KANNADA_INDIA                     0x044b
#define TT_MS_LANGID_MALAYALAM_INDIA                   0x044c
#define TT_MS_LANGID_ASSAMESE_INDIA                    0x044d
#define TT_MS_LANGID_MARATHI_INDIA                     0x044e
#define TT_MS_LANGID_SANSKRIT_INDIA                    0x044f
#define TT_MS_LANGID_MONGOLIAN_MONGOLIA /* Cyrillic */ 0x0450
#define TT_MS_LANGID_MONGOLIAN_MONGOLIA_MONGOLIAN      0x0850
#define TT_MS_LANGID_TIBETAN_CHINA                     0x0451
  /* Don't use the next constant!  It has            */
  /*   (1) the wrong spelling (Dzonghka)             */
  /*   (2) Microsoft doesn't officially define it -- */
  /*       at least it is not in the List of Local   */
  /*       ID Values.                                */
  /*   (3) Dzongkha is not the same language as      */
  /*       Tibetan, so merging it is wrong anyway.   */
  /*                                                 */
  /* TT_MS_LANGID_TIBETAN_BHUTAN is correct, BTW.    */
#define TT_MS_LANGID_DZONGHKA_BHUTAN                   0x0851

#if 0
  /* the following used to be defined */
#define TT_MS_LANGID_TIBETAN_BHUTAN                    0x0451
  /* ... but it was changed; */
#else
  /* So we will continue to #define it, but with the correct value */
#define TT_MS_LANGID_TIBETAN_BHUTAN   TT_MS_LANGID_DZONGHKA_BHUTAN
#endif

#define TT_MS_LANGID_WELSH_WALES                       0x0452
#define TT_MS_LANGID_KHMER_CAMBODIA                    0x0453
#define TT_MS_LANGID_LAO_LAOS                          0x0454
#define TT_MS_LANGID_BURMESE_MYANMAR                   0x0455
#define TT_MS_LANGID_GALICIAN_SPAIN                    0x0456
#define TT_MS_LANGID_KONKANI_INDIA                     0x0457
#define TT_MS_LANGID_MANIPURI_INDIA  /* Bengali */     0x0458
#define TT_MS_LANGID_SINDHI_INDIA /* Arabic */         0x0459
#define TT_MS_LANGID_SINDHI_PAKISTAN                   0x0859
  /* Missing a LCID for Sindhi in Devanagari script */
#define TT_MS_LANGID_SYRIAC_SYRIA                      0x045a
#define TT_MS_LANGID_SINHALESE_SRI_LANKA               0x045b
#define TT_MS_LANGID_CHEROKEE_UNITED_STATES            0x045c
#define TT_MS_LANGID_INUKTITUT_CANADA                  0x045d
#define TT_MS_LANGID_AMHARIC_ETHIOPIA                  0x045e
#define TT_MS_LANGID_TAMAZIGHT_MOROCCO /* Arabic */    0x045f
#define TT_MS_LANGID_TAMAZIGHT_MOROCCO_LATIN           0x085f
  /* Missing a LCID for Tifinagh script */
#define TT_MS_LANGID_KASHMIRI_PAKISTAN /* Arabic */    0x0460
  /* Spelled this way by XPsp2 Platform SDK (2004-07-26) */
  /* script is yet unclear... might be Arabic, Nagari or Sharada */
#define TT_MS_LANGID_KASHMIRI_SASIA                    0x0860
  /* ... and aliased (by MS) for compatibility reasons. */
#define TT_MS_LANGID_KASHMIRI_INDIA TT_MS_LANGID_KASHMIRI_SASIA
#define TT_MS_LANGID_NEPALI_NEPAL                      0x0461
#define TT_MS_LANGID_NEPALI_INDIA                      0x0861
#define TT_MS_LANGID_FRISIAN_NETHERLANDS               0x0462
#define TT_MS_LANGID_PASHTO_AFGHANISTAN                0x0463
#define TT_MS_LANGID_FILIPINO_PHILIPPINES              0x0464
#define TT_MS_LANGID_DHIVEHI_MALDIVES                  0x0465
  /* alias declared in Windows 2000 */
#define TT_MS_LANGID_DIVEHI_MALDIVES  TT_MS_LANGID_DHIVEHI_MALDIVES
#define TT_MS_LANGID_EDO_NIGERIA                       0x0466
#define TT_MS_LANGID_FULFULDE_NIGERIA                  0x0467
#define TT_MS_LANGID_HAUSA_NIGERIA                     0x0468
#define TT_MS_LANGID_IBIBIO_NIGERIA                    0x0469
#define TT_MS_LANGID_YORUBA_NIGERIA                    0x046a
#define TT_MS_LANGID_QUECHUA_BOLIVIA                   0x046b
#define TT_MS_LANGID_QUECHUA_ECUADOR                   0x086b
#define TT_MS_LANGID_QUECHUA_PERU                      0x0c6b
#define TT_MS_LANGID_SEPEDI_SOUTH_AFRICA               0x046c
  /* Also spelled by XPsp2 Platform SDK (2004-07-26) */
#define TT_MS_LANGID_SOTHO_SOUTHERN_SOUTH_AFRICA \
          TT_MS_LANGID_SEPEDI_SOUTH_AFRICA
  /* language codes 0x046d, 0x046e and 0x046f are (still) unknown. */
#define TT_MS_LANGID_IGBO_NIGERIA                      0x0470
#define TT_MS_LANGID_KANURI_NIGERIA                    0x0471
#define TT_MS_LANGID_OROMO_ETHIOPIA                    0x0472
#define TT_MS_LANGID_TIGRIGNA_ETHIOPIA                 0x0473
#define TT_MS_LANGID_TIGRIGNA_ERYTHREA                 0x0873
  /* also spelled in the `Passport SDK' list as: */
#define TT_MS_LANGID_TIGRIGNA_ERYTREA  TT_MS_LANGID_TIGRIGNA_ERYTHREA
#define TT_MS_LANGID_GUARANI_PARAGUAY                  0x0474
#define TT_MS_LANGID_HAWAIIAN_UNITED_STATES            0x0475
#define TT_MS_LANGID_LATIN                             0x0476
#define TT_MS_LANGID_SOMALI_SOMALIA                    0x0477
  /* Note: Yi does not have a (proper) ISO 639-2 code, since it is mostly */
  /*       not written (but OTOH the peculiar writing system is worth     */
  /*       studying).                                                     */
#define TT_MS_LANGID_YI_CHINA                          0x0478
#define TT_MS_LANGID_PAPIAMENTU_NETHERLANDS_ANTILLES   0x0479
  /* language codes from 0x047a to 0x047f are (still) unknown. */
#define TT_MS_LANGID_UIGHUR_CHINA                      0x0480
#define TT_MS_LANGID_MAORI_NEW_ZEALAND                 0x0481

#if 0  /* not deemed useful for fonts */
#define TT_MS_LANGID_HUMAN_INTERFACE_DEVICE            0x04ff
#endif


  /*************************************************************************/
  /*                                                                       */
  /* Possible values of the `name' identifier field in the name records of */
  /* the TTF `name' table.  These values are platform independent.         */
  /*                                                                       */
#define TT_NAME_ID_COPYRIGHT            0
#define TT_NAME_ID_FONT_FAMILY          1
#define TT_NAME_ID_FONT_SUBFAMILY       2
#define TT_NAME_ID_UNIQUE_ID            3
#define TT_NAME_ID_FULL_NAME            4
#define TT_NAME_ID_VERSION_STRING       5
#define TT_NAME_ID_PS_NAME              6
#define TT_NAME_ID_TRADEMARK            7

  /* the following values are from the OpenType spec */
#define TT_NAME_ID_MANUFACTURER         8
#define TT_NAME_ID_DESIGNER             9
#define TT_NAME_ID_DESCRIPTION          10
#define TT_NAME_ID_VENDOR_URL           11
#define TT_NAME_ID_DESIGNER_URL         12
#define TT_NAME_ID_LICENSE              13
#define TT_NAME_ID_LICENSE_URL          14
  /* number 15 is reserved */
#define TT_NAME_ID_PREFERRED_FAMILY     16
#define TT_NAME_ID_PREFERRED_SUBFAMILY  17
#define TT_NAME_ID_MAC_FULL_NAME        18

  /* The following code is new as of 2000-01-21 */
#define TT_NAME_ID_SAMPLE_TEXT          19

  /* This is new in OpenType 1.3 */
#define TT_NAME_ID_CID_FINDFONT_NAME    20

  /* This is new in OpenType 1.5 */
#define TT_NAME_ID_WWS_FAMILY           21
#define TT_NAME_ID_WWS_SUBFAMILY        22


  /*************************************************************************/
  /*                                                                       */
  /* Bit mask values for the Unicode Ranges from the TTF `OS2 ' table.     */
  /*                                                                       */
  /* Updated 08-Nov-2008.                                                  */
  /*                                                                       */

  /* Bit  0   Basic Latin */
#define TT_UCR_BASIC_LATIN                     (1L <<  0) /* U+0020-U+007E */
  /* Bit  1   C1 Controls and Latin-1 Supplement */
#define TT_UCR_LATIN1_SUPPLEMENT               (1L <<  1) /* U+0080-U+00FF */
  /* Bit  2   Latin Extended-A */
#define TT_UCR_LATIN_EXTENDED_A                (1L <<  2) /* U+0100-U+017F */
  /* Bit  3   Latin Extended-B */
#define TT_UCR_LATIN_EXTENDED_B                (1L <<  3) /* U+0180-U+024F */
  /* Bit  4   IPA Extensions                 */
  /*          Phonetic Extensions            */
  /*          Phonetic Extensions Supplement */
#define TT_UCR_IPA_EXTENSIONS                  (1L <<  4) /* U+0250-U+02AF */
                                                          /* U+1D00-U+1D7F */
                                                          /* U+1D80-U+1DBF */
  /* Bit  5   Spacing Modifier Letters */
  /*          Modifier Tone Letters    */
#define TT_UCR_SPACING_MODIFIER                (1L <<  5) /* U+02B0-U+02FF */
                                                          /* U+A700-U+A71F */
  /* Bit  6   Combining Diacritical Marks            */
  /*          Combining Diacritical Marks Supplement */
#define TT_UCR_COMBINING_DIACRITICS            (1L <<  6) /* U+0300-U+036F */
                                                          /* U+1DC0-U+1DFF */
  /* Bit  7   Greek and Coptic */
#define TT_UCR_GREEK                           (1L <<  7) /* U+0370-U+03FF */
  /* Bit  8   Coptic */
#define TT_UCR_COPTIC                          (1L <<  8) /* U+2C80-U+2CFF */
  /* Bit  9   Cyrillic            */
  /*          Cyrillic Supplement */
  /*          Cyrillic Extended-A */
  /*          Cyrillic Extended-B */
#define TT_UCR_CYRILLIC                        (1L <<  9) /* U+0400-U+04FF */
                                                          /* U+0500-U+052F */
                                                          /* U+2DE0-U+2DFF */
                                                          /* U+A640-U+A69F */
  /* Bit 10   Armenian */
#define TT_UCR_ARMENIAN                        (1L << 10) /* U+0530-U+058F */
  /* Bit 11   Hebrew */
#define TT_UCR_HEBREW                          (1L << 11) /* U+0590-U+05FF */
  /* Bit 12   Vai */
#define TT_UCR_VAI                             (1L << 12) /* U+A500-U+A63F */
  /* Bit 13   Arabic            */
  /*          Arabic Supplement */
#define TT_UCR_ARABIC                          (1L << 13) /* U+0600-U+06FF */
                                                          /* U+0750-U+077F */
  /* Bit 14   NKo */
#define TT_UCR_NKO                             (1L << 14) /* U+07C0-U+07FF */
  /* Bit 15   Devanagari */
#define TT_UCR_DEVANAGARI                      (1L << 15) /* U+0900-U+097F */
  /* Bit 16   Bengali */
#define TT_UCR_BENGALI                         (1L << 16) /* U+0980-U+09FF */
  /* Bit 17   Gurmukhi */
#define TT_UCR_GURMUKHI                        (1L << 17) /* U+0A00-U+0A7F */
  /* Bit 18   Gujarati */
#define TT_UCR_GUJARATI                        (1L << 18) /* U+0A80-U+0AFF */
  /* Bit 19   Oriya */
#define TT_UCR_ORIYA                           (1L << 19) /* U+0B00-U+0B7F */
  /* Bit 20   Tamil */
#define TT_UCR_TAMIL                           (1L << 20) /* U+0B80-U+0BFF */
  /* Bit 21   Telugu */
#define TT_UCR_TELUGU                          (1L << 21) /* U+0C00-U+0C7F */
  /* Bit 22   Kannada */
#define TT_UCR_KANNADA                         (1L << 22) /* U+0C80-U+0CFF */
  /* Bit 23   Malayalam */
#define TT_UCR_MALAYALAM                       (1L << 23) /* U+0D00-U+0D7F */
  /* Bit 24   Thai */
#define TT_UCR_THAI                            (1L << 24) /* U+0E00-U+0E7F */
  /* Bit 25   Lao */
#define TT_UCR_LAO                             (1L << 25) /* U+0E80-U+0EFF */
  /* Bit 26   Georgian            */
  /*          Georgian Supplement */
#define TT_UCR_GEORGIAN                        (1L << 26) /* U+10A0-U+10FF */
                                                          /* U+2D00-U+2D2F */
  /* Bit 27   Balinese */
#define TT_UCR_BALINESE                        (1L << 27) /* U+1B00-U+1B7F */
  /* Bit 28   Hangul Jamo */
#define TT_UCR_HANGUL_JAMO                     (1L << 28) /* U+1100-U+11FF */
  /* Bit 29   Latin Extended Additional */
  /*          Latin Extended-C          */
  /*          Latin Extended-D          */
#define TT_UCR_LATIN_EXTENDED_ADDITIONAL       (1L << 29) /* U+1E00-U+1EFF */
                                                          /* U+2C60-U+2C7F */
                                                          /* U+A720-U+A7FF */
  /* Bit 30   Greek Extended */
#define TT_UCR_GREEK_EXTENDED                  (1L << 30) /* U+1F00-U+1FFF */
  /* Bit 31   General Punctuation      */
  /*          Supplemental Punctuation */
#define TT_UCR_GENERAL_PUNCTUATION             (1L << 31) /* U+2000-U+206F */
                                                          /* U+2E00-U+2E7F */
  /* Bit 32   Superscripts And Subscripts */
#define TT_UCR_SUPERSCRIPTS_SUBSCRIPTS         (1L <<  0) /* U+2070-U+209F */
  /* Bit 33   Currency Symbols */
#define TT_UCR_CURRENCY_SYMBOLS                (1L <<  1) /* U+20A0-U+20CF */
  /* Bit 34   Combining Diacritical Marks For Symbols */
#define TT_UCR_COMBINING_DIACRITICS_SYMB       (1L <<  2) /* U+20D0-U+20FF */
  /* Bit 35   Letterlike Symbols */
#define TT_UCR_LETTERLIKE_SYMBOLS              (1L <<  3) /* U+2100-U+214F */
  /* Bit 36   Number Forms */
#define TT_UCR_NUMBER_FORMS                    (1L <<  4) /* U+2150-U+218F */
  /* Bit 37   Arrows                           */
  /*          Supplemental Arrows-A            */
  /*          Supplemental Arrows-B            */
  /*          Miscellaneous Symbols and Arrows */
#define TT_UCR_ARROWS                          (1L <<  5) /* U+2190-U+21FF */
                                                          /* U+27F0-U+27FF */
                                                          /* U+2900-U+297F */
                                                          /* U+2B00-U+2BFF */
  /* Bit 38   Mathematical Operators               */
  /*          Supplemental Mathematical Operators  */
  /*          Miscellaneous Mathematical Symbols-A */
  /*          Miscellaneous Mathematical Symbols-B */
#define TT_UCR_MATHEMATICAL_OPERATORS          (1L <<  6) /* U+2200-U+22FF */
                                                          /* U+2A00-U+2AFF */
                                                          /* U+27C0-U+27EF */
                                                          /* U+2980-U+29FF */
  /* Bit 39 Miscellaneous Technical */
#define TT_UCR_MISCELLANEOUS_TECHNICAL         (1L <<  7) /* U+2300-U+23FF */
  /* Bit 40   Control Pictures */
#define TT_UCR_CONTROL_PICTURES                (1L <<  8) /* U+2400-U+243F */
  /* Bit 41   Optical Character Recognition */
#define TT_UCR_OCR                             (1L <<  9) /* U+2440-U+245F */
  /* Bit 42   Enclosed Alphanumerics */
#define TT_UCR_ENCLOSED_ALPHANUMERICS          (1L << 10) /* U+2460-U+24FF */
  /* Bit 43   Box Drawing */
#define TT_UCR_BOX_DRAWING                     (1L << 11) /* U+2500-U+257F */
  /* Bit 44   Block Elements */
#define TT_UCR_BLOCK_ELEMENTS                  (1L << 12) /* U+2580-U+259F */
  /* Bit 45   Geometric Shapes */
#define TT_UCR_GEOMETRIC_SHAPES                (1L << 13) /* U+25A0-U+25FF */
  /* Bit 46   Miscellaneous Symbols */
#define TT_UCR_MISCELLANEOUS_SYMBOLS           (1L << 14) /* U+2600-U+26FF */
  /* Bit 47   Dingbats */
#define TT_UCR_DINGBATS                        (1L << 15) /* U+2700-U+27BF */
  /* Bit 48   CJK Symbols and Punctuation */
#define TT_UCR_CJK_SYMBOLS                     (1L << 16) /* U+3000-U+303F */
  /* Bit 49   Hiragana */
#define TT_UCR_HIRAGANA                        (1L << 17) /* U+3040-U+309F */
  /* Bit 50   Katakana                     */
  /*          Katakana Phonetic Extensions */
#define TT_UCR_KATAKANA                        (1L << 18) /* U+30A0-U+30FF */
                                                          /* U+31F0-U+31FF */
  /* Bit 51   Bopomofo          */
  /*          Bopomofo Extended */
#define TT_UCR_BOPOMOFO                        (1L << 19) /* U+3100-U+312F */
                                                          /* U+31A0-U+31BF */
  /* Bit 52   Hangul Compatibility Jamo */
#define TT_UCR_HANGUL_COMPATIBILITY_JAMO       (1L << 20) /* U+3130-U+318F */
  /* Bit 53   Phags-Pa */
#define TT_UCR_CJK_MISC                        (1L << 21) /* U+A840-U+A87F */
#define TT_UCR_KANBUN  TT_UCR_CJK_MISC /* deprecated */
#define TT_UCR_PHAGSPA
  /* Bit 54   Enclosed CJK Letters and Months */
#define TT_UCR_ENCLOSED_CJK_LETTERS_MONTHS     (1L << 22) /* U+3200-U+32FF */
  /* Bit 55   CJK Compatibility */
#define TT_UCR_CJK_COMPATIBILITY               (1L << 23) /* U+3300-U+33FF */
  /* Bit 56   Hangul Syllables */
#define TT_UCR_HANGUL                          (1L << 24) /* U+AC00-U+D7A3 */
  /* Bit 57   High Surrogates              */
  /*          High Private Use Surrogates  */
  /*          Low Surrogates               */
  /*                                       */
  /* According to OpenType specs v.1.3+,   */
  /* setting bit 57 implies that there is  */
  /* at least one codepoint beyond the     */
  /* Basic Multilingual Plane that is      */
  /* supported by this font.  So it really */
  /* means >= U+10000                      */
#define TT_UCR_SURROGATES                      (1L << 25) /* U+D800-U+DB7F */
                                                          /* U+DB80-U+DBFF */
                                                          /* U+DC00-U+DFFF */
#define TT_UCR_NON_PLANE_0  TT_UCR_SURROGATES
  /* Bit 58  Phoenician */
#define TT_UCR_PHOENICIAN                      (1L << 26) /*U+10900-U+1091F*/
  /* Bit 59   CJK Unified Ideographs             */
  /*          CJK Radicals Supplement            */
  /*          Kangxi Radicals                    */
  /*          Ideographic Description Characters */
  /*          CJK Unified Ideographs Extension A */
  /*          CJK Unified Ideographs Extension B */
  /*          Kanbun                             */
#define TT_UCR_CJK_UNIFIED_IDEOGRAPHS          (1L << 27) /* U+4E00-U+9FFF */
                                                          /* U+2E80-U+2EFF */
                                                          /* U+2F00-U+2FDF */
                                                          /* U+2FF0-U+2FFF */
                                                          /* U+3400-U+4DB5 */
                                                          /*U+20000-U+2A6DF*/
                                                          /* U+3190-U+319F */
  /* Bit 60   Private Use */
#define TT_UCR_PRIVATE_USE                     (1L << 28) /* U+E000-U+F8FF */
  /* Bit 61   CJK Strokes                             */
  /*          CJK Compatibility Ideographs            */
  /*          CJK Compatibility Ideographs Supplement */
#define TT_UCR_CJK_COMPATIBILITY_IDEOGRAPHS    (1L << 29) /* U+31C0-U+31EF */
                                                          /* U+F900-U+FAFF */
                                                          /*U+2F800-U+2FA1F*/
  /* Bit 62   Alphabetic Presentation Forms */
#define TT_UCR_ALPHABETIC_PRESENTATION_FORMS   (1L << 30) /* U+FB00-U+FB4F */
  /* Bit 63   Arabic Presentation Forms-A */
#define TT_UCR_ARABIC_PRESENTATIONS_A          (1L << 31) /* U+FB50-U+FDFF */
  /* Bit 64   Combining Half Marks */
#define TT_UCR_COMBINING_HALF_MARKS            (1L <<  0) /* U+FE20-U+FE2F */
  /* Bit 65   Vertical forms          */
  /*          CJK Compatibility Forms */
#define TT_UCR_CJK_COMPATIBILITY_FORMS         (1L <<  1) /* U+FE10-U+FE1F */
                                                          /* U+FE30-U+FE4F */
  /* Bit 66   Small Form Variants */
#define TT_UCR_SMALL_FORM_VARIANTS             (1L <<  2) /* U+FE50-U+FE6F */
  /* Bit 67   Arabic Presentation Forms-B */
#define TT_UCR_ARABIC_PRESENTATIONS_B          (1L <<  3) /* U+FE70-U+FEFE */
  /* Bit 68   Halfwidth and Fullwidth Forms */
#define TT_UCR_HALFWIDTH_FULLWIDTH_FORMS       (1L <<  4) /* U+FF00-U+FFEF */
  /* Bit 69   Specials */
#define TT_UCR_SPECIALS                        (1L <<  5) /* U+FFF0-U+FFFD */
  /* Bit 70   Tibetan */
#define TT_UCR_TIBETAN                         (1L <<  6) /* U+0F00-U+0FFF */
  /* Bit 71   Syriac */
#define TT_UCR_SYRIAC                          (1L <<  7) /* U+0700-U+074F */
  /* Bit 72   Thaana */
#define TT_UCR_THAANA                          (1L <<  8) /* U+0780-U+07BF */
  /* Bit 73   Sinhala */
#define TT_UCR_SINHALA                         (1L <<  9) /* U+0D80-U+0DFF */
  /* Bit 74   Myanmar */
#define TT_UCR_MYANMAR                         (1L << 10) /* U+1000-U+109F */
  /* Bit 75   Ethiopic            */
  /*          Ethiopic Supplement */
  /*          Ethiopic Extended   */
#define TT_UCR_ETHIOPIC                        (1L << 11) /* U+1200-U+137F */
                                                          /* U+1380-U+139F */
                                                          /* U+2D80-U+2DDF */
  /* Bit 76   Cherokee */
#define TT_UCR_CHEROKEE                        (1L << 12) /* U+13A0-U+13FF */
  /* Bit 77   Unified Canadian Aboriginal Syllabics */
#define TT_UCR_CANADIAN_ABORIGINAL_SYLLABICS   (1L << 13) /* U+1400-U+167F */
  /* Bit 78   Ogham */
#define TT_UCR_OGHAM                           (1L << 14) /* U+1680-U+169F */
  /* Bit 79   Runic */
#define TT_UCR_RUNIC                           (1L << 15) /* U+16A0-U+16FF */
  /* Bit 80   Khmer         */
  /*          Khmer Symbols */
#define TT_UCR_KHMER                           (1L << 16) /* U+1780-U+17FF */
                                                          /* U+19E0-U+19FF */
  /* Bit 81   Mongolian */
#define TT_UCR_MONGOLIAN                       (1L << 17) /* U+1800-U+18AF */
  /* Bit 82   Braille Patterns */
#define TT_UCR_BRAILLE                         (1L << 18) /* U+2800-U+28FF */
  /* Bit 83   Yi Syllables */
  /*          Yi Radicals  */
#define TT_UCR_YI                              (1L << 19) /* U+A000-U+A48F */
                                                          /* U+A490-U+A4CF */
  /* Bit 84   Tagalog  */
  /*          Hanunoo  */
  /*          Buhid    */
  /*          Tagbanwa */
#define TT_UCR_PHILIPPINE                      (1L << 20) /* U+1700-U+171F */
                                                          /* U+1720-U+173F */
                                                          /* U+1740-U+175F */
                                                          /* U+1760-U+177F */
  /* Bit 85   Old Italic */
#define TT_UCR_OLD_ITALIC                      (1L << 21) /*U+10300-U+1032F*/
  /* Bit 86   Gothic */
#define TT_UCR_GOTHIC                          (1L << 22) /*U+10330-U+1034F*/
  /* Bit 87   Deseret */
#define TT_UCR_DESERET                         (1L << 23) /*U+10400-U+1044F*/
  /* Bit 88   Byzantine Musical Symbols      */
  /*          Musical Symbols                */
  /*          Ancient Greek Musical Notation */
#define TT_UCR_MUSICAL_SYMBOLS                 (1L << 24) /*U+1D000-U+1D0FF*/
                                                          /*U+1D100-U+1D1FF*/
                                                          /*U+1D200-U+1D24F*/
  /* Bit 89   Mathematical Alphanumeric Symbols */
#define TT_UCR_MATH_ALPHANUMERIC_SYMBOLS       (1L << 25) /*U+1D400-U+1D7FF*/
  /* Bit 90   Private Use (plane 15) */
  /*          Private Use (plane 16) */
#define TT_UCR_PRIVATE_USE_SUPPLEMENTARY       (1L << 26) /*U+F0000-U+FFFFD*/
                                                        /*U+100000-U+10FFFD*/
  /* Bit 91   Variation Selectors            */
  /*          Variation Selectors Supplement */
#define TT_UCR_VARIATION_SELECTORS             (1L << 27) /* U+FE00-U+FE0F */
                                                          /*U+E0100-U+E01EF*/
  /* Bit 92   Tags */
#define TT_UCR_TAGS                            (1L << 28) /*U+E0000-U+E007F*/
  /* Bit 93   Limbu */
#define TT_UCR_LIMBU                           (1L << 29) /* U+1900-U+194F */
  /* Bit 94   Tai Le */
#define TT_UCR_TAI_LE                          (1L << 30) /* U+1950-U+197F */
  /* Bit 95   New Tai Lue */
#define TT_UCR_NEW_TAI_LUE                     (1L << 31) /* U+1980-U+19DF */
  /* Bit 96   Buginese */
#define TT_UCR_BUGINESE                        (1L <<  0) /* U+1A00-U+1A1F */
  /* Bit 97   Glagolitic */
#define TT_UCR_GLAGOLITIC                      (1L <<  1) /* U+2C00-U+2C5F */
  /* Bit 98   Tifinagh */
#define TT_UCR_TIFINAGH                        (1L <<  2) /* U+2D30-U+2D7F */
  /* Bit 99   Yijing Hexagram Symbols */
#define TT_UCR_YIJING                          (1L <<  3) /* U+4DC0-U+4DFF */
  /* Bit 100  Syloti Nagri */
#define TT_UCR_SYLOTI_NAGRI                    (1L <<  4) /* U+A800-U+A82F */
  /* Bit 101  Linear B Syllabary */
  /*          Linear B Ideograms */
  /*          Aegean Numbers     */
#define TT_UCR_LINEAR_B                        (1L <<  5) /*U+10000-U+1007F*/
                                                          /*U+10080-U+100FF*/
                                                          /*U+10100-U+1013F*/
  /* Bit 102  Ancient Greek Numbers */
#define TT_UCR_ANCIENT_GREEK_NUMBERS           (1L <<  6) /*U+10140-U+1018F*/
  /* Bit 103  Ugaritic */
#define TT_UCR_UGARITIC                        (1L <<  7) /*U+10380-U+1039F*/
  /* Bit 104  Old Persian */
#define TT_UCR_OLD_PERSIAN                     (1L <<  8) /*U+103A0-U+103DF*/
  /* Bit 105  Shavian */
#define TT_UCR_SHAVIAN                         (1L <<  9) /*U+10450-U+1047F*/
  /* Bit 106  Osmanya */
#define TT_UCR_OSMANYA                         (1L << 10) /*U+10480-U+104AF*/
  /* Bit 107  Cypriot Syllabary */
#define TT_UCR_CYPRIOT_SYLLABARY               (1L << 11) /*U+10800-U+1083F*/
  /* Bit 108  Kharoshthi */
#define TT_UCR_KHAROSHTHI                      (1L << 12) /*U+10A00-U+10A5F*/
  /* Bit 109  Tai Xuan Jing Symbols */
#define TT_UCR_TAI_XUAN_JING                   (1L << 13) /*U+1D300-U+1D35F*/
  /* Bit 110  Cuneiform                         */
  /*          Cuneiform Numbers and Punctuation */
#define TT_UCR_CUNEIFORM                       (1L << 14) /*U+12000-U+123FF*/
                                                          /*U+12400-U+1247F*/
  /* Bit 111  Counting Rod Numerals */
#define TT_UCR_COUNTING_ROD_NUMERALS           (1L << 15) /*U+1D360-U+1D37F*/
  /* Bit 112  Sundanese */
#define TT_UCR_SUNDANESE                       (1L << 16) /* U+1B80-U+1BBF */
  /* Bit 113  Lepcha */
#define TT_UCR_LEPCHA                          (1L << 17) /* U+1C00-U+1C4F */
  /* Bit 114  Ol Chiki */
#define TT_UCR_OL_CHIKI                        (1L << 18) /* U+1C50-U+1C7F */
  /* Bit 115  Saurashtra */
#define TT_UCR_SAURASHTRA                      (1L << 19) /* U+A880-U+A8DF */
  /* Bit 116  Kayah Li */
#define TT_UCR_KAYAH_LI                        (1L << 20) /* U+A900-U+A92F */
  /* Bit 117  Rejang */
#define TT_UCR_REJANG                          (1L << 21) /* U+A930-U+A95F */
  /* Bit 118  Cham */
#define TT_UCR_CHAM                            (1L << 22) /* U+AA00-U+AA5F */
  /* Bit 119  Ancient Symbols */
#define TT_UCR_ANCIENT_SYMBOLS                 (1L << 23) /*U+10190-U+101CF*/
  /* Bit 120  Phaistos Disc */
#define TT_UCR_PHAISTOS_DISC                   (1L << 24) /*U+101D0-U+101FF*/
  /* Bit 121  Carian */
  /*          Lycian */
  /*          Lydian */
#define TT_UCR_OLD_ANATOLIAN                   (1L << 25) /*U+102A0-U+102DF*/
                                                          /*U+10280-U+1029F*/
                                                          /*U+10920-U+1093F*/
  /* Bit 122  Domino Tiles  */
  /*          Mahjong Tiles */
#define TT_UCR_GAME_TILES                      (1L << 26) /*U+1F030-U+1F09F*/
                                                          /*U+1F000-U+1F02F*/
  /* Bit 123-127 Reserved for process-internal usage */


  /*************************************************************************/
  /*                                                                       */
  /* Some compilers have a very limited length of identifiers.             */
  /*                                                                       */
#if defined( __TURBOC__ ) && __TURBOC__ < 0x0410 || defined( __PACIFIC__ )
#define HAVE_LIMIT_ON_IDENTS
#endif


#ifndef HAVE_LIMIT_ON_IDENTS


  /*************************************************************************/
  /*                                                                       */
  /* Here some alias #defines in order to be clearer.                      */
  /*                                                                       */
  /* These are not always #defined to stay within the 31~character limit   */
  /* which some compilers have.                                            */
  /*                                                                       */
  /* Credits go to Dave Hoo <dhoo@flash.net> for pointing out that modern  */
  /* Borland compilers (read: from BC++ 3.1 on) can increase this limit.   */
  /* If you get a warning with such a compiler, use the -i40 switch.       */
  /*                                                                       */
#define TT_UCR_ARABIC_PRESENTATION_FORMS_A      \
         TT_UCR_ARABIC_PRESENTATIONS_A
#define TT_UCR_ARABIC_PRESENTATION_FORMS_B      \
         TT_UCR_ARABIC_PRESENTATIONS_B

#define TT_UCR_COMBINING_DIACRITICAL_MARKS      \
         TT_UCR_COMBINING_DIACRITICS
#define TT_UCR_COMBINING_DIACRITICAL_MARKS_SYMB \
         TT_UCR_COMBINING_DIACRITICS_SYMB


#endif /* !HAVE_LIMIT_ON_IDENTS */


FT_END_HEADER

#endif /* __TTNAMEID_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  tttables.h                                                             */
/*                                                                         */
/*    Basic SFNT/TrueType tables definitions and interface                 */
/*    (specification only).                                                */
/*                                                                         */
/*  Copyright 1996-2001, 2002, 2003, 2004, 2005, 2008, 2009, 2010 by       */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __TTTABLES_H__
#define __TTTABLES_H__


#include <ft2build.h>
#include FT_FREETYPE_H

#ifdef FREETYPE_H
#error "freetype.h of FreeType 1 has been loaded!"
#error "Please fix the directory search order for header files"
#error "so that freetype.h of FreeType 2 is found first."
#endif


FT_BEGIN_HEADER

  /*************************************************************************/
  /*                                                                       */
  /* <Section>                                                             */
  /*    truetype_tables                                                    */
  /*                                                                       */
  /* <Title>                                                               */
  /*    TrueType Tables                                                    */
  /*                                                                       */
  /* <Abstract>                                                            */
  /*    TrueType specific table types and functions.                       */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This section contains the definition of TrueType-specific tables   */
  /*    as well as some routines used to access and process them.          */
  /*                                                                       */
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    TT_Header                                                          */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A structure used to model a TrueType font header table.  All       */
  /*    fields follow the TrueType specification.                          */
  /*                                                                       */
  typedef struct  TT_Header_
  {
    FT_Fixed   Table_Version;
    FT_Fixed   Font_Revision;

    FT_Long    CheckSum_Adjust;
    FT_Long    Magic_Number;

    FT_UShort  Flags;
    FT_UShort  Units_Per_EM;

    FT_Long    Created [2];
    FT_Long    Modified[2];

    FT_Short   xMin;
    FT_Short   yMin;
    FT_Short   xMax;
    FT_Short   yMax;

    FT_UShort  Mac_Style;
    FT_UShort  Lowest_Rec_PPEM;

    FT_Short   Font_Direction;
    FT_Short   Index_To_Loc_Format;
    FT_Short   Glyph_Data_Format;

  } TT_Header;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    TT_HoriHeader                                                      */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A structure used to model a TrueType horizontal header, the `hhea' */
  /*    table, as well as the corresponding horizontal metrics table,      */
  /*    i.e., the `hmtx' table.                                            */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    Version                :: The table version.                       */
  /*                                                                       */
  /*    Ascender               :: The font's ascender, i.e., the distance  */
  /*                              from the baseline to the top-most of all */
  /*                              glyph points found in the font.          */
  /*                                                                       */
  /*                              This value is invalid in many fonts, as  */
  /*                              it is usually set by the font designer,  */
  /*                              and often reflects only a portion of the */
  /*                              glyphs found in the font (maybe ASCII).  */
  /*                                                                       */
  /*                              You should use the `sTypoAscender' field */
  /*                              of the OS/2 table instead if you want    */
  /*                              the correct one.                         */
  /*                                                                       */
  /*    Descender              :: The font's descender, i.e., the distance */
  /*                              from the baseline to the bottom-most of  */
  /*                              all glyph points found in the font.  It  */
  /*                              is negative.                             */
  /*                                                                       */
  /*                              This value is invalid in many fonts, as  */
  /*                              it is usually set by the font designer,  */
  /*                              and often reflects only a portion of the */
  /*                              glyphs found in the font (maybe ASCII).  */
  /*                                                                       */
  /*                              You should use the `sTypoDescender'      */
  /*                              field of the OS/2 table instead if you   */
  /*                              want the correct one.                    */
  /*                                                                       */
  /*    Line_Gap               :: The font's line gap, i.e., the distance  */
  /*                              to add to the ascender and descender to  */
  /*                              get the BTB, i.e., the                   */
  /*                              baseline-to-baseline distance for the    */
  /*                              font.                                    */
  /*                                                                       */
  /*    advance_Width_Max      :: This field is the maximum of all advance */
  /*                              widths found in the font.  It can be     */
  /*                              used to compute the maximum width of an  */
  /*                              arbitrary string of text.                */
  /*                                                                       */
  /*    min_Left_Side_Bearing  :: The minimum left side bearing of all     */
  /*                              glyphs within the font.                  */
  /*                                                                       */
  /*    min_Right_Side_Bearing :: The minimum right side bearing of all    */
  /*                              glyphs within the font.                  */
  /*                                                                       */
  /*    xMax_Extent            :: The maximum horizontal extent (i.e., the */
  /*                              `width' of a glyph's bounding box) for   */
  /*                              all glyphs in the font.                  */
  /*                                                                       */
  /*    caret_Slope_Rise       :: The rise coefficient of the cursor's     */
  /*                              slope of the cursor (slope=rise/run).    */
  /*                                                                       */
  /*    caret_Slope_Run        :: The run coefficient of the cursor's      */
  /*                              slope.                                   */
  /*                                                                       */
  /*    Reserved               :: 8~reserved bytes.                        */
  /*                                                                       */
  /*    metric_Data_Format     :: Always~0.                                */
  /*                                                                       */
  /*    number_Of_HMetrics     :: Number of HMetrics entries in the `hmtx' */
  /*                              table -- this value can be smaller than  */
  /*                              the total number of glyphs in the font.  */
  /*                                                                       */
  /*    long_metrics           :: A pointer into the `hmtx' table.         */
  /*                                                                       */
  /*    short_metrics          :: A pointer into the `hmtx' table.         */
  /*                                                                       */
  /* <Note>                                                                */
  /*    IMPORTANT: The TT_HoriHeader and TT_VertHeader structures should   */
  /*               be identical except for the names of their fields which */
  /*               are different.                                          */
  /*                                                                       */
  /*               This ensures that a single function in the `ttload'     */
  /*               module is able to read both the horizontal and vertical */
  /*               headers.                                                */
  /*                                                                       */
  typedef struct  TT_HoriHeader_
  {
    FT_Fixed   Version;
    FT_Short   Ascender;
    FT_Short   Descender;
    FT_Short   Line_Gap;

    FT_UShort  advance_Width_Max;      /* advance width maximum */

    FT_Short   min_Left_Side_Bearing;  /* minimum left-sb       */
    FT_Short   min_Right_Side_Bearing; /* minimum right-sb      */
    FT_Short   xMax_Extent;            /* xmax extents          */
    FT_Short   caret_Slope_Rise;
    FT_Short   caret_Slope_Run;
    FT_Short   caret_Offset;

    FT_Short   Reserved[4];

    FT_Short   metric_Data_Format;
    FT_UShort  number_Of_HMetrics;

    /* The following fields are not defined by the TrueType specification */
    /* but they are used to connect the metrics header to the relevant    */
    /* `HMTX' table.                                                      */

    void*      long_metrics;
    void*      short_metrics;

  } TT_HoriHeader;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    TT_VertHeader                                                      */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A structure used to model a TrueType vertical header, the `vhea'   */
  /*    table, as well as the corresponding vertical metrics table, i.e.,  */
  /*    the `vmtx' table.                                                  */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    Version                 :: The table version.                      */
  /*                                                                       */
  /*    Ascender                :: The font's ascender, i.e., the distance */
  /*                               from the baseline to the top-most of    */
  /*                               all glyph points found in the font.     */
  /*                                                                       */
  /*                               This value is invalid in many fonts, as */
  /*                               it is usually set by the font designer, */
  /*                               and often reflects only a portion of    */
  /*                               the glyphs found in the font (maybe     */
  /*                               ASCII).                                 */
  /*                                                                       */
  /*                               You should use the `sTypoAscender'      */
  /*                               field of the OS/2 table instead if you  */
  /*                               want the correct one.                   */
  /*                                                                       */
  /*    Descender               :: The font's descender, i.e., the         */
  /*                               distance from the baseline to the       */
  /*                               bottom-most of all glyph points found   */
  /*                               in the font.  It is negative.           */
  /*                                                                       */
  /*                               This value is invalid in many fonts, as */
  /*                               it is usually set by the font designer, */
  /*                               and often reflects only a portion of    */
  /*                               the glyphs found in the font (maybe     */
  /*                               ASCII).                                 */
  /*                                                                       */
  /*                               You should use the `sTypoDescender'     */
  /*                               field of the OS/2 table instead if you  */
  /*                               want the correct one.                   */
  /*                                                                       */
  /*    Line_Gap                :: The font's line gap, i.e., the distance */
  /*                               to add to the ascender and descender to */
  /*                               get the BTB, i.e., the                  */
  /*                               baseline-to-baseline distance for the   */
  /*                               font.                                   */
  /*                                                                       */
  /*    advance_Height_Max      :: This field is the maximum of all        */
  /*                               advance heights found in the font.  It  */
  /*                               can be used to compute the maximum      */
  /*                               height of an arbitrary string of text.  */
  /*                                                                       */
  /*    min_Top_Side_Bearing    :: The minimum top side bearing of all     */
  /*                               glyphs within the font.                 */
  /*                                                                       */
  /*    min_Bottom_Side_Bearing :: The minimum bottom side bearing of all  */
  /*                               glyphs within the font.                 */
  /*                                                                       */
  /*    yMax_Extent             :: The maximum vertical extent (i.e., the  */
  /*                               `height' of a glyph's bounding box) for */
  /*                               all glyphs in the font.                 */
  /*                                                                       */
  /*    caret_Slope_Rise        :: The rise coefficient of the cursor's    */
  /*                               slope of the cursor (slope=rise/run).   */
  /*                                                                       */
  /*    caret_Slope_Run         :: The run coefficient of the cursor's     */
  /*                               slope.                                  */
  /*                                                                       */
  /*    caret_Offset            :: The cursor's offset for slanted fonts.  */
  /*                               This value is `reserved' in vmtx        */
  /*                               version 1.0.                            */
  /*                                                                       */
  /*    Reserved                :: 8~reserved bytes.                       */
  /*                                                                       */
  /*    metric_Data_Format      :: Always~0.                               */
  /*                                                                       */
  /*    number_Of_HMetrics      :: Number of VMetrics entries in the       */
  /*                               `vmtx' table -- this value can be       */
  /*                               smaller than the total number of glyphs */
  /*                               in the font.                            */
  /*                                                                       */
  /*    long_metrics           :: A pointer into the `vmtx' table.         */
  /*                                                                       */
  /*    short_metrics          :: A pointer into the `vmtx' table.         */
  /*                                                                       */
  /* <Note>                                                                */
  /*    IMPORTANT: The TT_HoriHeader and TT_VertHeader structures should   */
  /*               be identical except for the names of their fields which */
  /*               are different.                                          */
  /*                                                                       */
  /*               This ensures that a single function in the `ttload'     */
  /*               module is able to read both the horizontal and vertical */
  /*               headers.                                                */
  /*                                                                       */
  typedef struct  TT_VertHeader_
  {
    FT_Fixed   Version;
    FT_Short   Ascender;
    FT_Short   Descender;
    FT_Short   Line_Gap;

    FT_UShort  advance_Height_Max;      /* advance height maximum */

    FT_Short   min_Top_Side_Bearing;    /* minimum left-sb or top-sb       */
    FT_Short   min_Bottom_Side_Bearing; /* minimum right-sb or bottom-sb   */
    FT_Short   yMax_Extent;             /* xmax or ymax extents            */
    FT_Short   caret_Slope_Rise;
    FT_Short   caret_Slope_Run;
    FT_Short   caret_Offset;

    FT_Short   Reserved[4];

    FT_Short   metric_Data_Format;
    FT_UShort  number_Of_VMetrics;

    /* The following fields are not defined by the TrueType specification */
    /* but they're used to connect the metrics header to the relevant     */
    /* `HMTX' or `VMTX' table.                                            */

    void*      long_metrics;
    void*      short_metrics;

  } TT_VertHeader;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    TT_OS2                                                             */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A structure used to model a TrueType OS/2 table. This is the long  */
  /*    table version.  All fields comply to the TrueType specification.   */
  /*                                                                       */
  /*    Note that we now support old Mac fonts which do not include an     */
  /*    OS/2 table.  In this case, the `version' field is always set to    */
  /*    0xFFFF.                                                            */
  /*                                                                       */
  typedef struct  TT_OS2_
  {
    FT_UShort  version;                /* 0x0001 - more or 0xFFFF */
    FT_Short   xAvgCharWidth;
    FT_UShort  usWeightClass;
    FT_UShort  usWidthClass;
    FT_Short   fsType;
    FT_Short   ySubscriptXSize;
    FT_Short   ySubscriptYSize;
    FT_Short   ySubscriptXOffset;
    FT_Short   ySubscriptYOffset;
    FT_Short   ySuperscriptXSize;
    FT_Short   ySuperscriptYSize;
    FT_Short   ySuperscriptXOffset;
    FT_Short   ySuperscriptYOffset;
    FT_Short   yStrikeoutSize;
    FT_Short   yStrikeoutPosition;
    FT_Short   sFamilyClass;

    FT_Byte    panose[10];

    FT_ULong   ulUnicodeRange1;        /* Bits 0-31   */
    FT_ULong   ulUnicodeRange2;        /* Bits 32-63  */
    FT_ULong   ulUnicodeRange3;        /* Bits 64-95  */
    FT_ULong   ulUnicodeRange4;        /* Bits 96-127 */

    FT_Char    achVendID[4];

    FT_UShort  fsSelection;
    FT_UShort  usFirstCharIndex;
    FT_UShort  usLastCharIndex;
    FT_Short   sTypoAscender;
    FT_Short   sTypoDescender;
    FT_Short   sTypoLineGap;
    FT_UShort  usWinAscent;
    FT_UShort  usWinDescent;

    /* only version 1 tables: */

    FT_ULong   ulCodePageRange1;       /* Bits 0-31   */
    FT_ULong   ulCodePageRange2;       /* Bits 32-63  */

    /* only version 2 tables: */

    FT_Short   sxHeight;
    FT_Short   sCapHeight;
    FT_UShort  usDefaultChar;
    FT_UShort  usBreakChar;
    FT_UShort  usMaxContext;

  } TT_OS2;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    TT_Postscript                                                      */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A structure used to model a TrueType PostScript table.  All fields */
  /*    comply to the TrueType specification.  This structure does not     */
  /*    reference the PostScript glyph names, which can be nevertheless    */
  /*    accessed with the `ttpost' module.                                 */
  /*                                                                       */
  typedef struct  TT_Postscript_
  {
    FT_Fixed  FormatType;
    FT_Fixed  italicAngle;
    FT_Short  underlinePosition;
    FT_Short  underlineThickness;
    FT_ULong  isFixedPitch;
    FT_ULong  minMemType42;
    FT_ULong  maxMemType42;
    FT_ULong  minMemType1;
    FT_ULong  maxMemType1;

    /* Glyph names follow in the file, but we don't   */
    /* load them by default.  See the ttpost.c file.  */

  } TT_Postscript;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    TT_PCLT                                                            */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A structure used to model a TrueType PCLT table.  All fields       */
  /*    comply to the TrueType specification.                              */
  /*                                                                       */
  typedef struct  TT_PCLT_
  {
    FT_Fixed   Version;
    FT_ULong   FontNumber;
    FT_UShort  Pitch;
    FT_UShort  xHeight;
    FT_UShort  Style;
    FT_UShort  TypeFamily;
    FT_UShort  CapHeight;
    FT_UShort  SymbolSet;
    FT_Char    TypeFace[16];
    FT_Char    CharacterComplement[8];
    FT_Char    FileName[6];
    FT_Char    StrokeWeight;
    FT_Char    WidthType;
    FT_Byte    SerifStyle;
    FT_Byte    Reserved;

  } TT_PCLT;


  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    TT_MaxProfile                                                      */
  /*                                                                       */
  /* <Description>                                                         */
  /*    The maximum profile is a table containing many max values which    */
  /*    can be used to pre-allocate arrays.  This ensures that no memory   */
  /*    allocation occurs during a glyph load.                             */
  /*                                                                       */
  /* <Fields>                                                              */
  /*    version               :: The version number.                       */
  /*                                                                       */
  /*    numGlyphs             :: The number of glyphs in this TrueType     */
  /*                             font.                                     */
  /*                                                                       */
  /*    maxPoints             :: The maximum number of points in a         */
  /*                             non-composite TrueType glyph.  See also   */
  /*                             the structure element                     */
  /*                             `maxCompositePoints'.                     */
  /*                                                                       */
  /*    maxContours           :: The maximum number of contours in a       */
  /*                             non-composite TrueType glyph.  See also   */
  /*                             the structure element                     */
  /*                             `maxCompositeContours'.                   */
  /*                                                                       */
  /*    maxCompositePoints    :: The maximum number of points in a         */
  /*                             composite TrueType glyph.  See also the   */
  /*                             structure element `maxPoints'.            */
  /*                                                                       */
  /*    maxCompositeContours  :: The maximum number of contours in a       */
  /*                             composite TrueType glyph.  See also the   */
  /*                             structure element `maxContours'.          */
  /*                                                                       */
  /*    maxZones              :: The maximum number of zones used for      */
  /*                             glyph hinting.                            */
  /*                                                                       */
  /*    maxTwilightPoints     :: The maximum number of points in the       */
  /*                             twilight zone used for glyph hinting.     */
  /*                                                                       */
  /*    maxStorage            :: The maximum number of elements in the     */
  /*                             storage area used for glyph hinting.      */
  /*                                                                       */
  /*    maxFunctionDefs       :: The maximum number of function            */
  /*                             definitions in the TrueType bytecode for  */
  /*                             this font.                                */
  /*                                                                       */
  /*    maxInstructionDefs    :: The maximum number of instruction         */
  /*                             definitions in the TrueType bytecode for  */
  /*                             this font.                                */
  /*                                                                       */
  /*    maxStackElements      :: The maximum number of stack elements used */
  /*                             during bytecode interpretation.           */
  /*                                                                       */
  /*    maxSizeOfInstructions :: The maximum number of TrueType opcodes    */
  /*                             used for glyph hinting.                   */
  /*                                                                       */
  /*    maxComponentElements  :: The maximum number of simple (i.e., non-  */
  /*                             composite) glyphs in a composite glyph.   */
  /*                                                                       */
  /*    maxComponentDepth     :: The maximum nesting depth of composite    */
  /*                             glyphs.                                   */
  /*                                                                       */
  /* <Note>                                                                */
  /*    This structure is only used during font loading.                   */
  /*                                                                       */
  typedef struct  TT_MaxProfile_
  {
    FT_Fixed   version;
    FT_UShort  numGlyphs;
    FT_UShort  maxPoints;
    FT_UShort  maxContours;
    FT_UShort  maxCompositePoints;
    FT_UShort  maxCompositeContours;
    FT_UShort  maxZones;
    FT_UShort  maxTwilightPoints;
    FT_UShort  maxStorage;
    FT_UShort  maxFunctionDefs;
    FT_UShort  maxInstructionDefs;
    FT_UShort  maxStackElements;
    FT_UShort  maxSizeOfInstructions;
    FT_UShort  maxComponentElements;
    FT_UShort  maxComponentDepth;

  } TT_MaxProfile;


  /*************************************************************************/
  /*                                                                       */
  /* <Enum>                                                                */
  /*    FT_Sfnt_Tag                                                        */
  /*                                                                       */
  /* <Description>                                                         */
  /*    An enumeration used to specify the index of an SFNT table.         */
  /*    Used in the @FT_Get_Sfnt_Table API function.                       */
  /*                                                                       */
  typedef enum  FT_Sfnt_Tag_
  {
    ft_sfnt_head = 0,    /* TT_Header     */
    ft_sfnt_maxp = 1,    /* TT_MaxProfile */
    ft_sfnt_os2  = 2,    /* TT_OS2        */
    ft_sfnt_hhea = 3,    /* TT_HoriHeader */
    ft_sfnt_vhea = 4,    /* TT_VertHeader */
    ft_sfnt_post = 5,    /* TT_Postscript */
    ft_sfnt_pclt = 6,    /* TT_PCLT       */

    sfnt_max   /* internal end mark */

  } FT_Sfnt_Tag;

  /* */


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Get_Sfnt_Table                                                  */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Return a pointer to a given SFNT table within a face.              */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face :: A handle to the source.                                    */
  /*                                                                       */
  /*    tag  :: The index of the SFNT table.                               */
  /*                                                                       */
  /* <Return>                                                              */
  /*    A type-less pointer to the table.  This will be~0 in case of       */
  /*    error, or if the corresponding table was not found *OR* loaded     */
  /*    from the file.                                                     */
  /*                                                                       */
  /*    Use a typecast according to `tag' to access the structure          */
  /*    elements.                                                          */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The table is owned by the face object and disappears with it.      */
  /*                                                                       */
  /*    This function is only useful to access SFNT tables that are loaded */
  /*    by the sfnt, truetype, and opentype drivers.  See @FT_Sfnt_Tag for */
  /*    a list.                                                            */
  /*                                                                       */
  FT_EXPORT( void* )
  FT_Get_Sfnt_Table( FT_Face      face,
                     FT_Sfnt_Tag  tag );


 /**************************************************************************
  *
  * @function:
  *   FT_Load_Sfnt_Table
  *
  * @description:
  *   Load any font table into client memory.
  *
  * @input:
  *   face ::
  *     A handle to the source face.
  *
  *   tag ::
  *     The four-byte tag of the table to load.  Use the value~0 if you want
  *     to access the whole font file.  Otherwise, you can use one of the
  *     definitions found in the @FT_TRUETYPE_TAGS_H file, or forge a new
  *     one with @FT_MAKE_TAG.
  *
  *   offset ::
  *     The starting offset in the table (or file if tag == 0).
  *
  * @output:
  *   buffer ::
  *     The target buffer address.  The client must ensure that the memory
  *     array is big enough to hold the data.
  *
  * @inout:
  *   length ::
  *     If the `length' parameter is NULL, then try to load the whole table.
  *     Return an error code if it fails.
  *
  *     Else, if `*length' is~0, exit immediately while returning the
  *     table's (or file) full size in it.
  *
  *     Else the number of bytes to read from the table or file, from the
  *     starting offset.
  *
  * @return:
  *   FreeType error code.  0~means success.
  *
  * @note:
  *   If you need to determine the table's length you should first call this
  *   function with `*length' set to~0, as in the following example:
  *
  *     {
  *       FT_ULong  length = 0;
  *
  *
  *       error = FT_Load_Sfnt_Table( face, tag, 0, NULL, &length );
  *       if ( error ) { ... table does not exist ... }
  *
  *       buffer = malloc( length );
  *       if ( buffer == NULL ) { ... not enough memory ... }
  *
  *       error = FT_Load_Sfnt_Table( face, tag, 0, buffer, &length );
  *       if ( error ) { ... could not load table ... }
  *     }
  */
  FT_EXPORT( FT_Error )
  FT_Load_Sfnt_Table( FT_Face    face,
                      FT_ULong   tag,
                      FT_Long    offset,
                      FT_Byte*   buffer,
                      FT_ULong*  length );


 /**************************************************************************
  *
  * @function:
  *   FT_Sfnt_Table_Info
  *
  * @description:
  *   Return information on an SFNT table.
  *
  * @input:
  *   face ::
  *     A handle to the source face.
  *
  *   table_index ::
  *     The index of an SFNT table.  The function returns
  *     FT_Err_Table_Missing for an invalid value.
  *
  * @output:
  *   tag ::
  *     The name tag of the SFNT table.
  *
  *   length ::
  *     The length of the SFNT table.
  *
  * @return:
  *   FreeType error code.  0~means success.
  *
  * @note:
  *   SFNT tables with length zero are treated as missing.
  *
  */
  FT_EXPORT( FT_Error )
  FT_Sfnt_Table_Info( FT_Face    face,
                      FT_UInt    table_index,
                      FT_ULong  *tag,
                      FT_ULong  *length );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Get_CMap_Language_ID                                            */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Return TrueType/sfnt specific cmap language ID.  Definitions of    */
  /*    language ID values are in `freetype/ttnameid.h'.                   */
  /*                                                                       */
  /* <Input>                                                               */
  /*    charmap ::                                                         */
  /*      The target charmap.                                              */
  /*                                                                       */
  /* <Return>                                                              */
  /*    The language ID of `charmap'.  If `charmap' doesn't belong to a    */
  /*    TrueType/sfnt face, just return~0 as the default value.            */
  /*                                                                       */
  FT_EXPORT( FT_ULong )
  FT_Get_CMap_Language_ID( FT_CharMap  charmap );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Get_CMap_Format                                                 */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Return TrueType/sfnt specific cmap format.                         */
  /*                                                                       */
  /* <Input>                                                               */
  /*    charmap ::                                                         */
  /*      The target charmap.                                              */
  /*                                                                       */
  /* <Return>                                                              */
  /*    The format of `charmap'.  If `charmap' doesn't belong to a         */
  /*    TrueType/sfnt face, return -1.                                     */
  /*                                                                       */
  FT_EXPORT( FT_Long )
  FT_Get_CMap_Format( FT_CharMap  charmap );

  /* */


FT_END_HEADER

#endif /* __TTTABLES_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  tttags.h                                                               */
/*                                                                         */
/*    Tags for TrueType and OpenType tables (specification only).          */
/*                                                                         */
/*  Copyright 1996-2001, 2004, 2005, 2007, 2008 by                         */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __TTAGS_H__
#define __TTAGS_H__


#include <ft2build.h>
#include FT_FREETYPE_H

#ifdef FREETYPE_H
#error "freetype.h of FreeType 1 has been loaded!"
#error "Please fix the directory search order for header files"
#error "so that freetype.h of FreeType 2 is found first."
#endif


FT_BEGIN_HEADER


#define TTAG_avar  FT_MAKE_TAG( 'a', 'v', 'a', 'r' )
#define TTAG_BASE  FT_MAKE_TAG( 'B', 'A', 'S', 'E' )
#define TTAG_bdat  FT_MAKE_TAG( 'b', 'd', 'a', 't' )
#define TTAG_BDF   FT_MAKE_TAG( 'B', 'D', 'F', ' ' )
#define TTAG_bhed  FT_MAKE_TAG( 'b', 'h', 'e', 'd' )
#define TTAG_bloc  FT_MAKE_TAG( 'b', 'l', 'o', 'c' )
#define TTAG_bsln  FT_MAKE_TAG( 'b', 's', 'l', 'n' )
#define TTAG_CFF   FT_MAKE_TAG( 'C', 'F', 'F', ' ' )
#define TTAG_CID   FT_MAKE_TAG( 'C', 'I', 'D', ' ' )
#define TTAG_cmap  FT_MAKE_TAG( 'c', 'm', 'a', 'p' )
#define TTAG_cvar  FT_MAKE_TAG( 'c', 'v', 'a', 'r' )
#define TTAG_cvt   FT_MAKE_TAG( 'c', 'v', 't', ' ' )
#define TTAG_DSIG  FT_MAKE_TAG( 'D', 'S', 'I', 'G' )
#define TTAG_EBDT  FT_MAKE_TAG( 'E', 'B', 'D', 'T' )
#define TTAG_EBLC  FT_MAKE_TAG( 'E', 'B', 'L', 'C' )
#define TTAG_EBSC  FT_MAKE_TAG( 'E', 'B', 'S', 'C' )
#define TTAG_feat  FT_MAKE_TAG( 'f', 'e', 'a', 't' )
#define TTAG_FOND  FT_MAKE_TAG( 'F', 'O', 'N', 'D' )
#define TTAG_fpgm  FT_MAKE_TAG( 'f', 'p', 'g', 'm' )
#define TTAG_fvar  FT_MAKE_TAG( 'f', 'v', 'a', 'r' )
#define TTAG_gasp  FT_MAKE_TAG( 'g', 'a', 's', 'p' )
#define TTAG_GDEF  FT_MAKE_TAG( 'G', 'D', 'E', 'F' )
#define TTAG_glyf  FT_MAKE_TAG( 'g', 'l', 'y', 'f' )
#define TTAG_GPOS  FT_MAKE_TAG( 'G', 'P', 'O', 'S' )
#define TTAG_GSUB  FT_MAKE_TAG( 'G', 'S', 'U', 'B' )
#define TTAG_gvar  FT_MAKE_TAG( 'g', 'v', 'a', 'r' )
#define TTAG_hdmx  FT_MAKE_TAG( 'h', 'd', 'm', 'x' )
#define TTAG_head  FT_MAKE_TAG( 'h', 'e', 'a', 'd' )
#define TTAG_hhea  FT_MAKE_TAG( 'h', 'h', 'e', 'a' )
#define TTAG_hmtx  FT_MAKE_TAG( 'h', 'm', 't', 'x' )
#define TTAG_JSTF  FT_MAKE_TAG( 'J', 'S', 'T', 'F' )
#define TTAG_just  FT_MAKE_TAG( 'j', 'u', 's', 't' )
#define TTAG_kern  FT_MAKE_TAG( 'k', 'e', 'r', 'n' )
#define TTAG_lcar  FT_MAKE_TAG( 'l', 'c', 'a', 'r' )
#define TTAG_loca  FT_MAKE_TAG( 'l', 'o', 'c', 'a' )
#define TTAG_LTSH  FT_MAKE_TAG( 'L', 'T', 'S', 'H' )
#define TTAG_LWFN  FT_MAKE_TAG( 'L', 'W', 'F', 'N' )
#define TTAG_MATH  FT_MAKE_TAG( 'M', 'A', 'T', 'H' )
#define TTAG_maxp  FT_MAKE_TAG( 'm', 'a', 'x', 'p' )
#define TTAG_META  FT_MAKE_TAG( 'M', 'E', 'T', 'A' )
#define TTAG_MMFX  FT_MAKE_TAG( 'M', 'M', 'F', 'X' )
#define TTAG_MMSD  FT_MAKE_TAG( 'M', 'M', 'S', 'D' )
#define TTAG_mort  FT_MAKE_TAG( 'm', 'o', 'r', 't' )
#define TTAG_morx  FT_MAKE_TAG( 'm', 'o', 'r', 'x' )
#define TTAG_name  FT_MAKE_TAG( 'n', 'a', 'm', 'e' )
#define TTAG_opbd  FT_MAKE_TAG( 'o', 'p', 'b', 'd' )
#define TTAG_OS2   FT_MAKE_TAG( 'O', 'S', '/', '2' )
#define TTAG_OTTO  FT_MAKE_TAG( 'O', 'T', 'T', 'O' )
#define TTAG_PCLT  FT_MAKE_TAG( 'P', 'C', 'L', 'T' )
#define TTAG_POST  FT_MAKE_TAG( 'P', 'O', 'S', 'T' )
#define TTAG_post  FT_MAKE_TAG( 'p', 'o', 's', 't' )
#define TTAG_prep  FT_MAKE_TAG( 'p', 'r', 'e', 'p' )
#define TTAG_prop  FT_MAKE_TAG( 'p', 'r', 'o', 'p' )
#define TTAG_sfnt  FT_MAKE_TAG( 's', 'f', 'n', 't' )
#define TTAG_SING  FT_MAKE_TAG( 'S', 'I', 'N', 'G' )
#define TTAG_trak  FT_MAKE_TAG( 't', 'r', 'a', 'k' )
#define TTAG_true  FT_MAKE_TAG( 't', 'r', 'u', 'e' )
#define TTAG_ttc   FT_MAKE_TAG( 't', 't', 'c', ' ' )
#define TTAG_ttcf  FT_MAKE_TAG( 't', 't', 'c', 'f' )
#define TTAG_TYP1  FT_MAKE_TAG( 'T', 'Y', 'P', '1' )
#define TTAG_typ1  FT_MAKE_TAG( 't', 'y', 'p', '1' )
#define TTAG_VDMX  FT_MAKE_TAG( 'V', 'D', 'M', 'X' )
#define TTAG_vhea  FT_MAKE_TAG( 'v', 'h', 'e', 'a' )
#define TTAG_vmtx  FT_MAKE_TAG( 'v', 'm', 't', 'x' )


FT_END_HEADER

#endif /* __TTAGS_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  ttunpat.h                                                              */
/*                                                                         */
/*    Definitions for the unpatented TrueType hinting system               */
/*                                                                         */
/*  Copyright 2003, 2006 by                                                */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  Written by Graham Asher <graham.asher@btinternet.com>                  */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#ifndef __TTUNPAT_H__
#define __TTUNPAT_H__


#include <ft2build.h>
#include FT_FREETYPE_H

#ifdef FREETYPE_H
#error "freetype.h of FreeType 1 has been loaded!"
#error "Please fix the directory search order for header files"
#error "so that freetype.h of FreeType 2 is found first."
#endif


FT_BEGIN_HEADER


 /***************************************************************************
  *
  * @constant:
  *   FT_PARAM_TAG_UNPATENTED_HINTING
  *
  * @description:
  *   A constant used as the tag of an @FT_Parameter structure to indicate
  *   that unpatented methods only should be used by the TrueType bytecode
  *   interpreter for a typeface opened by @FT_Open_Face.
  *
  */
#define FT_PARAM_TAG_UNPATENTED_HINTING  FT_MAKE_TAG( 'u', 'n', 'p', 'a' )

 /* */

FT_END_HEADER


#endif /* __TTUNPAT_H__ */


/* END */
/***************************************************************************/
/*                                                                         */
/*  ft2build.h                                                             */
/*                                                                         */
/*    FreeType 2 build and setup macros.                                   */
/*    (Generic version)                                                    */
/*                                                                         */
/*  Copyright 1996-2001, 2006 by                                           */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* This file corresponds to the default `ft2build.h' file for            */
  /* FreeType 2.  It uses the `freetype' include root.                     */
  /*                                                                       */
  /* Note that specific platforms might use a different configuration.     */
  /* See builds/unix/ft2unix.h for an example.                             */
  /*                                                                       */
  /*************************************************************************/


#ifndef __FT2_BUILD_GENERIC_H__
#define __FT2_BUILD_GENERIC_H__

#include <freetype/config/ftheader.h>

#endif /* __FT2_BUILD_GENERIC_H__ */


/* END */
//========================================================================
//
// FixedPoint.cc
//
// Fixed point type, with C++ operators.
//
// Copyright 2004 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#if USE_FIXEDPOINT

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include "FixedPoint.h"

#define ln2 ((FixedPoint)0.69314718)

FixedPoint FixedPoint::sqrt(FixedPoint x) {
  FixedPoint y0, y1, z;

  if (x.val <= 0) {
    y1.val = 0;
  } else {
    y1.val = x.val == 1 ? 2 : x.val >> 1;
    do {
      y0.val = y1.val;
      z = x / y0;
      y1.val = (y0.val + z.val) >> 1;
    } while (::abs(y0.val - y1.val) > 1);
  }
  return y1;
}

FixedPoint FixedPoint::pow(FixedPoint x, FixedPoint y) {
  FixedPoint t, t2, lnx0, lnx, z0, z;
  int d, n, i;

  if (y.val <= 0) {
    z.val = 0;
  } else {
    // y * ln(x)
    t = (x - 1) / (x + 1);
    t2 = t * t;
    d = 1;
    lnx = 0;
    do {
      lnx0 = lnx;
      lnx += t / d;
      t *= t2;
      d += 2;
    } while (::abs(lnx.val - lnx0.val) > 2);
    lnx.val <<= 1;
    t = y * lnx;
    // exp(y * ln(x))
    n = floor(t / ln2);
    t -= ln2 * n;
    t2 = t;
    d = 1;
    i = 1;
    z = 1;
    do {
      z0 = z;
      z += t2 / d;
      t2 *= t;
      ++i;
      d *= i;
    } while (::abs(z.val - z0.val) > 2 && d < (1 << fixptShift));
    if (n >= 0) {
      z.val <<= n;
    } else if (n < 0) {
      z.val >>= -n;
    }
  }
  return z;
}

int FixedPoint::mul(int x, int y) {
#if 1 //~tmp
  return ((FixPtInt64)x * y) >> fixptShift;
#else
  int ah0, ah, bh, al, bl;
  ah0 = x & fixptMaskH;
  ah = x >> fixptShift;
  al = x - ah0;
  bh = y >> fixptShift;
  bl = y - (bh << fixptShift);
  return ah0 * bh + ah * bl + al * bh + ((al * bl) >> fixptShift);
#endif
}

int FixedPoint::div(int x, int y) {
#if 1 //~tmp
  return ((FixPtInt64)x << fixptShift) / y;
#else
#endif
}

GBool FixedPoint::divCheck(FixedPoint x, FixedPoint y, FixedPoint *result) {
#if 1 //~tmp
  FixPtInt64 z;

  z = ((FixPtInt64)x.val << fixptShift) / y.val;
  if ((z == 0 && x != 0) ||
      z >= ((FixPtInt64)1 << 31) || z < -((FixPtInt64)1 << 31)) {
    return gFalse;
  }
  result->val = z;
  return gTrue;
#else
#endif
}

#endif // USE_FIXEDPOINT
//========================================================================
//
// FixedPoint.h
//
// Fixed point type, with C++ operators.
//
// Copyright 2004 Glyph & Cog, LLC
//
//========================================================================

#ifndef FIXEDPOINT_H
#define FIXEDPOINT_H

#include <aconf.h>

#if USE_FIXEDPOINT

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include <stdio.h>
#include <stdlib.h>
#include "gtypes.h"

#define fixptShift 16
#define fixptMaskL ((1 << fixptShift) - 1)
#define fixptMaskH (~fixptMaskL)

typedef long long FixPtInt64;

class FixedPoint {
public:

  FixedPoint() { val = 0; }
  FixedPoint(const FixedPoint &x) { val = x.val; }
  FixedPoint(double x) { val = (int)(x * (1 << fixptShift) + 0.5); }
  FixedPoint(int x) { val = x << fixptShift; }
  FixedPoint(long x) { val = x << fixptShift; }

  operator float()
    { return (float) val * ((float)1 / (float)(1 << fixptShift)); }
  operator double()
    { return (double) val * (1.0 / (double)(1 << fixptShift)); }
  operator int()
    { return val >> fixptShift; }

  int getRaw() { return val; }

  FixedPoint operator =(FixedPoint x) { val = x.val; return *this; }

  int operator ==(FixedPoint x) { return val == x.val; }
  int operator ==(double x) { return *this == (FixedPoint)x; }
  int operator ==(int x) { return *this == (FixedPoint)x; }
  int operator ==(long x) { return *this == (FixedPoint)x; }

  int operator !=(FixedPoint x) { return val != x.val; }
  int operator !=(double x) { return *this != (FixedPoint)x; }
  int operator !=(int x) { return *this != (FixedPoint)x; }
  int operator !=(long x) { return *this != (FixedPoint)x; }

  int operator <(FixedPoint x) { return val < x.val; }
  int operator <(double x) { return *this < (FixedPoint)x; }
  int operator <(int x) { return *this < (FixedPoint)x; }
  int operator <(long x) { return *this < (FixedPoint)x; }

  int operator <=(FixedPoint x) { return val <= x.val; }
  int operator <=(double x) { return *this <= (FixedPoint)x; }
  int operator <=(int x) { return *this <= (FixedPoint)x; }
  int operator <=(long x) { return *this <= (FixedPoint)x; }

  int operator >(FixedPoint x) { return val > x.val; }
  int operator >(double x) { return *this > (FixedPoint)x; }
  int operator >(int x) { return *this > (FixedPoint)x; }
  int operator >(long x) { return *this > (FixedPoint)x; }

  int operator >=(FixedPoint x) { return val >= x.val; }
  int operator >=(double x) { return *this >= (FixedPoint)x; }
  int operator >=(int x) { return *this >= (FixedPoint)x; }
  int operator >=(long x) { return *this >= (FixedPoint)x; }

  FixedPoint operator -() { return make(-val); }

  FixedPoint operator +(FixedPoint x) { return make(val + x.val); }
  FixedPoint operator +(double x) { return *this + (FixedPoint)x; }
  FixedPoint operator +(int x) { return *this + (FixedPoint)x; }
  FixedPoint operator +(long x) { return *this + (FixedPoint)x; }

  FixedPoint operator +=(FixedPoint x) { val = val + x.val; return *this; }
  FixedPoint operator +=(double x) { return *this += (FixedPoint)x; }
  FixedPoint operator +=(int x) { return *this += (FixedPoint)x; }
  FixedPoint operator +=(long x) { return *this += (FixedPoint)x; }

  FixedPoint operator -(FixedPoint x) { return make(val - x.val); }
  FixedPoint operator -(double x) { return *this - (FixedPoint)x; }
  FixedPoint operator -(int x) { return *this - (FixedPoint)x; }
  FixedPoint operator -(long x) { return *this - (FixedPoint)x; }

  FixedPoint operator -=(FixedPoint x) { val = val - x.val; return *this; }
  FixedPoint operator -=(double x) { return *this -= (FixedPoint)x; }
  FixedPoint operator -=(int x) { return *this -= (FixedPoint)x; }
  FixedPoint operator -=(long x) { return *this -= (FixedPoint)x; }

  FixedPoint operator *(FixedPoint x) { return make(mul(val, x.val)); }
  FixedPoint operator *(double x) { return *this * (FixedPoint)x; }
  FixedPoint operator *(int x) { return *this * (FixedPoint)x; }
  FixedPoint operator *(long x) { return *this * (FixedPoint)x; }

  FixedPoint operator *=(FixedPoint x) { val = mul(val, x.val); return *this; }
  FixedPoint operator *=(double x) { return *this *= (FixedPoint)x; }
  FixedPoint operator *=(int x) { return *this *= (FixedPoint)x; }
  FixedPoint operator *=(long x) { return *this *= (FixedPoint)x; }

  FixedPoint operator /(FixedPoint x) { return make(div(val, x.val)); }
  FixedPoint operator /(double x) { return *this / (FixedPoint)x; }
  FixedPoint operator /(int x) { return *this / (FixedPoint)x; }
  FixedPoint operator /(long x) { return *this / (FixedPoint)x; }

  FixedPoint operator /=(FixedPoint x) { val = div(val, x.val); return *this; }
  FixedPoint operator /=(double x) { return *this /= (FixedPoint)x; }
  FixedPoint operator /=(int x) { return *this /= (FixedPoint)x; }
  FixedPoint operator /=(long x) { return *this /= (FixedPoint)x; }

  static FixedPoint abs(FixedPoint x) { return make(::abs(x.val)); }

  static int floor(FixedPoint x) { return x.val >> fixptShift; }

  static int ceil(FixedPoint x)
    { return (x.val & fixptMaskL) ? ((x.val >> fixptShift) + 1)
	                          : (x.val >> fixptShift); }

  static int round(FixedPoint x)
    { return (x.val + (1 << (fixptShift - 1))) >> fixptShift; }

  static FixedPoint sqrt(FixedPoint x);

  static FixedPoint pow(FixedPoint x, FixedPoint y);

  // Compute *result = x/y; return false if there is an underflow or
  // overflow.
  static GBool divCheck(FixedPoint x, FixedPoint y, FixedPoint *result);

private:

  static FixedPoint make(int valA) { FixedPoint x; x.val = valA; return x; }

  static int mul(int x, int y);
  static int div(int x, int y);

  int val;			// 16.16 fixed point
};

#endif // USE_FIXEDPOINT

#endif
//========================================================================
//
// GHash.cc
//
// Copyright 2001-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include "gmem.h"
#include "GString.h"
#include "GHash.h"

//------------------------------------------------------------------------

struct GHashBucket {
  GString *key;
  union {
    void *p;
    int i;
  } val;
  GHashBucket *next;
};

struct GHashIter {
  int h;
  GHashBucket *p;
};

//------------------------------------------------------------------------

GHash::GHash(GBool deleteKeysA) {
  int h;

  deleteKeys = deleteKeysA;
  size = 7;
  tab = (GHashBucket **)gmallocn(size, sizeof(GHashBucket *));
  for (h = 0; h < size; ++h) {
    tab[h] = NULL;
  }
  len = 0;
}

GHash::~GHash() {
  GHashBucket *p;
  int h;

  for (h = 0; h < size; ++h) {
    while (tab[h]) {
      p = tab[h];
      tab[h] = p->next;
      if (deleteKeys) {
	delete p->key;
      }
      delete p;
    }
  }
  gfree(tab);
}

void GHash::add(GString *key, void *val) {
  GHashBucket *p;
  int h;

  // expand the table if necessary
  if (len >= size) {
    expand();
  }

  // add the new symbol
  p = new GHashBucket;
  p->key = key;
  p->val.p = val;
  h = hash(key);
  p->next = tab[h];
  tab[h] = p;
  ++len;
}

void GHash::add(GString *key, int val) {
  GHashBucket *p;
  int h;

  // expand the table if necessary
  if (len >= size) {
    expand();
  }

  // add the new symbol
  p = new GHashBucket;
  p->key = key;
  p->val.i = val;
  h = hash(key);
  p->next = tab[h];
  tab[h] = p;
  ++len;
}

void GHash::replace(GString *key, void *val) {
  GHashBucket *p;
  int h;

  if ((p = find(key, &h))) {
    p->val.p = val;
    delete key;
  } else {
    add(key, val);
  }
}

void GHash::replace(GString *key, int val) {
  GHashBucket *p;
  int h;

  if ((p = find(key, &h))) {
    p->val.i = val;
    delete key;
  } else {
    add(key, val);
  }
}

void *GHash::lookup(GString *key) {
  GHashBucket *p;
  int h;

  if (!(p = find(key, &h))) {
    return NULL;
  }
  return p->val.p;
}

int GHash::lookupInt(GString *key) {
  GHashBucket *p;
  int h;

  if (!(p = find(key, &h))) {
    return 0;
  }
  return p->val.i;
}

void *GHash::lookup(char *key) {
  GHashBucket *p;
  int h;

  if (!(p = find(key, &h))) {
    return NULL;
  }
  return p->val.p;
}

int GHash::lookupInt(char *key) {
  GHashBucket *p;
  int h;

  if (!(p = find(key, &h))) {
    return 0;
  }
  return p->val.i;
}

void *GHash::remove(GString *key) {
  GHashBucket *p;
  GHashBucket **q;
  void *val;
  int h;

  if (!(p = find(key, &h))) {
    return NULL;
  }
  q = &tab[h];
  while (*q != p) {
    q = &((*q)->next);
  }
  *q = p->next;
  if (deleteKeys) {
    delete p->key;
  }
  val = p->val.p;
  delete p;
  --len;
  return val;
}

int GHash::removeInt(GString *key) {
  GHashBucket *p;
  GHashBucket **q;
  int val;
  int h;

  if (!(p = find(key, &h))) {
    return 0;
  }
  q = &tab[h];
  while (*q != p) {
    q = &((*q)->next);
  }
  *q = p->next;
  if (deleteKeys) {
    delete p->key;
  }
  val = p->val.i;
  delete p;
  --len;
  return val;
}

void *GHash::remove(char *key) {
  GHashBucket *p;
  GHashBucket **q;
  void *val;
  int h;

  if (!(p = find(key, &h))) {
    return NULL;
  }
  q = &tab[h];
  while (*q != p) {
    q = &((*q)->next);
  }
  *q = p->next;
  if (deleteKeys) {
    delete p->key;
  }
  val = p->val.p;
  delete p;
  --len;
  return val;
}

int GHash::removeInt(char *key) {
  GHashBucket *p;
  GHashBucket **q;
  int val;
  int h;

  if (!(p = find(key, &h))) {
    return 0;
  }
  q = &tab[h];
  while (*q != p) {
    q = &((*q)->next);
  }
  *q = p->next;
  if (deleteKeys) {
    delete p->key;
  }
  val = p->val.i;
  delete p;
  --len;
  return val;
}

void GHash::startIter(GHashIter **iter) {
  *iter = new GHashIter;
  (*iter)->h = -1;
  (*iter)->p = NULL;
}

GBool GHash::getNext(GHashIter **iter, GString **key, void **val) {
  if (!*iter) {
    return gFalse;
  }
  if ((*iter)->p) {
    (*iter)->p = (*iter)->p->next;
  }
  while (!(*iter)->p) {
    if (++(*iter)->h == size) {
      delete *iter;
      *iter = NULL;
      return gFalse;
    }
    (*iter)->p = tab[(*iter)->h];
  }
  *key = (*iter)->p->key;
  *val = (*iter)->p->val.p;
  return gTrue;
}

GBool GHash::getNext(GHashIter **iter, GString **key, int *val) {
  if (!*iter) {
    return gFalse;
  }
  if ((*iter)->p) {
    (*iter)->p = (*iter)->p->next;
  }
  while (!(*iter)->p) {
    if (++(*iter)->h == size) {
      delete *iter;
      *iter = NULL;
      return gFalse;
    }
    (*iter)->p = tab[(*iter)->h];
  }
  *key = (*iter)->p->key;
  *val = (*iter)->p->val.i;
  return gTrue;
}

void GHash::killIter(GHashIter **iter) {
  delete *iter;
  *iter = NULL;
}

void GHash::expand() {
  GHashBucket **oldTab;
  GHashBucket *p;
  int oldSize, h, i;

  oldSize = size;
  oldTab = tab;
  size = 2*size + 1;
  tab = (GHashBucket **)gmallocn(size, sizeof(GHashBucket *));
  for (h = 0; h < size; ++h) {
    tab[h] = NULL;
  }
  for (i = 0; i < oldSize; ++i) {
    while (oldTab[i]) {
      p = oldTab[i];
      oldTab[i] = oldTab[i]->next;
      h = hash(p->key);
      p->next = tab[h];
      tab[h] = p;
    }
  }
  gfree(oldTab);
}

GHashBucket *GHash::find(GString *key, int *h) {
  GHashBucket *p;

  *h = hash(key);
  for (p = tab[*h]; p; p = p->next) {
    if (!p->key->cmp(key)) {
      return p;
    }
  }
  return NULL;
}

GHashBucket *GHash::find(char *key, int *h) {
  GHashBucket *p;

  *h = hash(key);
  for (p = tab[*h]; p; p = p->next) {
    if (!p->key->cmp(key)) {
      return p;
    }
  }
  return NULL;
}

int GHash::hash(GString *key) {
  char *p;
  unsigned int h;
  int i;

  h = 0;
  for (p = key->getCString(), i = 0; i < key->getLength(); ++p, ++i) {
    h = 17 * h + (int)(*p & 0xff);
  }
  return (int)(h % size);
}

int GHash::hash(char *key) {
  char *p;
  unsigned int h;

  h = 0;
  for (p = key; *p; ++p) {
    h = 17 * h + (int)(*p & 0xff);
  }
  return (int)(h % size);
}
//========================================================================
//
// GHash.h
//
// Copyright 2001-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef GHASH_H
#define GHASH_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "gtypes.h"

class GString;
struct GHashBucket;
struct GHashIter;

//------------------------------------------------------------------------

class GHash {
public:

  GHash(GBool deleteKeysA = gFalse);
  ~GHash();
  void add(GString *key, void *val);
  void add(GString *key, int val);
  void replace(GString *key, void *val);
  void replace(GString *key, int val);
  void *lookup(GString *key);
  int lookupInt(GString *key);
  void *lookup(char *key);
  int lookupInt(char *key);
  void *remove(GString *key);
  int removeInt(GString *key);
  void *remove(char *key);
  int removeInt(char *key);
  int getLength() { return len; }
  void startIter(GHashIter **iter);
  GBool getNext(GHashIter **iter, GString **key, void **val);
  GBool getNext(GHashIter **iter, GString **key, int *val);
  void killIter(GHashIter **iter);

private:

  void expand();
  GHashBucket *find(GString *key, int *h);
  GHashBucket *find(char *key, int *h);
  int hash(GString *key);
  int hash(char *key);

  GBool deleteKeys;		// set if key strings should be deleted
  int size;			// number of buckets
  int len;			// number of entries
  GHashBucket **tab;
};

#define deleteGHash(hash, T)                       \
  do {                                             \
    GHash *_hash = (hash);                         \
    {                                              \
      GHashIter *_iter;                            \
      GString *_key;                               \
      void *_p;                                    \
      _hash->startIter(&_iter);                    \
      while (_hash->getNext(&_iter, &_key, &_p)) { \
        delete (T*)_p;                             \
      }                                            \
      delete _hash;                                \
    }                                              \
  } while(0)

#endif
//========================================================================
//
// GList.cc
//
// Copyright 2001-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stdlib.h>
#include <string.h>
#include "gmem.h"
#include "GList.h"

//------------------------------------------------------------------------
// GList
//------------------------------------------------------------------------

GList::GList() {
  size = 8;
  data = (void **)gmallocn(size, sizeof(void*));
  length = 0;
  inc = 0;
}

GList::GList(int sizeA) {
  size = sizeA;
  data = (void **)gmallocn(size, sizeof(void*));
  length = 0;
  inc = 0;
}

GList::~GList() {
  gfree(data);
}

void GList::append(void *p) {
  if (length >= size) {
    expand();
  }
  data[length++] = p;
}

void GList::append(GList *list) {
  int i;

  while (length + list->length > size) {
    expand();
  }
  for (i = 0; i < list->length; ++i) {
    data[length++] = list->data[i];
  }
}

void GList::insert(int i, void *p) {
  if (length >= size) {
    expand();
  }
  if (i < length) {
    memmove(data+i+1, data+i, (length - i) * sizeof(void *));
  }
  data[i] = p;
  ++length;
}

void *GList::del(int i) {
  void *p;

  p = data[i];
  if (i < length - 1) {
    memmove(data+i, data+i+1, (length - i - 1) * sizeof(void *));
  }
  --length;
  if (size - length >= ((inc > 0) ? inc : size/2)) {
    shrink();
  }
  return p;
}

void GList::sort(int (*cmp)(const void *obj1, const void *obj2)) {
  qsort(data, length, sizeof(void *), cmp);
}

void GList::expand() {
  size += (inc > 0) ? inc : size;
  data = (void **)greallocn(data, size, sizeof(void*));
}

void GList::shrink() {
  size -= (inc > 0) ? inc : size/2;
  data = (void **)greallocn(data, size, sizeof(void*));
}
//========================================================================
//
// GList.h
//
// Copyright 2001-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef GLIST_H
#define GLIST_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "gtypes.h"

//------------------------------------------------------------------------
// GList
//------------------------------------------------------------------------

class GList {
public:

  // Create an empty list.
  GList();

  // Create an empty list with space for <size1> elements.
  GList(int sizeA);

  // Destructor - does not free pointed-to objects.
  ~GList();

  //----- general

  // Get the number of elements.
  int getLength() { return length; }

  //----- ordered list support

  // Return the <i>th element.
  // Assumes 0 <= i < length.
  void *get(int i) { return data[i]; }

  // Append an element to the end of the list.
  void append(void *p);

  // Append another list to the end of this one.
  void append(GList *list);

  // Insert an element at index <i>.
  // Assumes 0 <= i <= length.
  void insert(int i, void *p);

  // Deletes and returns the element at index <i>.
  // Assumes 0 <= i < length.
  void *del(int i);

  // Sort the list accoring to the given comparison function.
  // NB: this sorts an array of pointers, so the pointer args need to
  // be double-dereferenced.
  void sort(int (*cmp)(const void *ptr1, const void *ptr2));

  //----- control

  // Set allocation increment to <inc>.  If inc > 0, that many
  // elements will be allocated every time the list is expanded.
  // If inc <= 0, the list will be doubled in size.
  void setAllocIncr(int incA) { inc = incA; }

private:

  void expand();
  void shrink();

  void **data;			// the list elements
  int size;			// size of data array
  int length;			// number of elements on list
  int inc;			// allocation increment
};

#define deleteGList(list, T)                        \
  do {                                              \
    GList *_list = (list);                          \
    {                                               \
      int _i;                                       \
      for (_i = 0; _i < _list->getLength(); ++_i) { \
        delete (T*)_list->get(_i);                  \
      }                                             \
      delete _list;                                 \
    }                                               \
  } while (0)

#endif
//========================================================================
//
// GMutex.h
//
// Portable mutex macros.
//
// Copyright 2002-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef GMUTEX_H
#define GMUTEX_H

// Usage:
//
// GMutex m;
// gInitMutex(&m);
// ...
// gLockMutex(&m);
//   ... critical section ...
// gUnlockMutex(&m);
// ...
// gDestroyMutex(&m);

#ifdef WIN32

#include <windows.h>

typedef CRITICAL_SECTION GMutex;

#define gInitMutex(m) InitializeCriticalSection(m)
#define gDestroyMutex(m) DeleteCriticalSection(m)
#define gLockMutex(m) EnterCriticalSection(m)
#define gUnlockMutex(m) LeaveCriticalSection(m)

#else // assume pthreads

#include <pthread.h>

typedef pthread_mutex_t GMutex;

#define gInitMutex(m) pthread_mutex_init(m, NULL)
#define gDestroyMutex(m) pthread_mutex_destroy(m)
#define gLockMutex(m) pthread_mutex_lock(m)
#define gUnlockMutex(m) pthread_mutex_unlock(m)

#endif

#endif
//========================================================================
//
// GString.cc
//
// Simple variable-length string type.
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include "gmem.h"
#include "GString.h"

//------------------------------------------------------------------------

union GStringFormatArg {
  int i;
  Guint ui;
  long l;
  Gulong ul;
  double f;
  char c;
  char *s;
  GString *gs;
};

enum GStringFormatType {
  fmtIntDecimal,
  fmtIntHex,
  fmtIntOctal,
  fmtIntBinary,
  fmtUIntDecimal,
  fmtUIntHex,
  fmtUIntOctal,
  fmtUIntBinary,
  fmtLongDecimal,
  fmtLongHex,
  fmtLongOctal,
  fmtLongBinary,
  fmtULongDecimal,
  fmtULongHex,
  fmtULongOctal,
  fmtULongBinary,
  fmtDouble,
  fmtDoubleTrim,
  fmtChar,
  fmtString,
  fmtGString,
  fmtSpace
};

static char *formatStrings[] = {
  "d", "x", "o", "b", "ud", "ux", "uo", "ub",
  "ld", "lx", "lo", "lb", "uld", "ulx", "ulo", "ulb",
  "f", "g",
  "c",
  "s",
  "t",
  "w",
  NULL
};

//------------------------------------------------------------------------

static inline int size(int len) {
  int delta;
  for (delta = 8; delta < len && delta < 0x100000; delta <<= 1) ;
  // this is ((len + 1) + (delta - 1)) & ~(delta - 1)
  return (len + delta) & ~(delta - 1);
}

inline void GString::resize(int length1) {
  char *s1;

  if (!s) {
    s = new char[size(length1)];
  } else if (size(length1) != size(length)) {
    s1 = new char[size(length1)];
    if (length1 < length) {
      memcpy(s1, s, length1);
      s1[length1] = '\0';
    } else {
      memcpy(s1, s, length + 1);
    }
    delete[] s;
    s = s1;
  }
}

GString::GString() {
  s = NULL;
  resize(length = 0);
  s[0] = '\0';
}

GString::GString(const char *sA) {
  int n = strlen(sA);

  s = NULL;
  resize(length = n);
  memcpy(s, sA, n + 1);
}

GString::GString(const char *sA, int lengthA) {
  s = NULL;
  resize(length = lengthA);
  memcpy(s, sA, length * sizeof(char));
  s[length] = '\0';
}

GString::GString(GString *str, int idx, int lengthA) {
  s = NULL;
  resize(length = lengthA);
  memcpy(s, str->getCString() + idx, length);
  s[length] = '\0';
}

GString::GString(GString *str) {
  s = NULL;
  resize(length = str->getLength());
  memcpy(s, str->getCString(), length + 1);
}

GString::GString(GString *str1, GString *str2) {
  int n1 = str1->getLength();
  int n2 = str2->getLength();

  s = NULL;
  resize(length = n1 + n2);
  memcpy(s, str1->getCString(), n1);
  memcpy(s + n1, str2->getCString(), n2 + 1);
}

GString *GString::fromInt(int x) {
  char buf[24]; // enough space for 64-bit ints plus a little extra
  char *p;
  int len;

  formatInt(x, buf, sizeof(buf), gFalse, 0, 10, &p, &len);
  return new GString(p, len);
}

GString *GString::format(char *fmt, ...) {
  va_list argList;
  GString *s;

  s = new GString();
  va_start(argList, fmt);
  s->appendfv(fmt, argList);
  va_end(argList);
  return s;
}

GString *GString::formatv(char *fmt, va_list argList) {
  GString *s;

  s = new GString();
  s->appendfv(fmt, argList);
  return s;
}

GString::~GString() {
  delete[] s;
}

GString *GString::clear() {
  s[length = 0] = '\0';
  resize(0);
  return this;
}

GString *GString::append(char c) {
  resize(length + 1);
  s[length++] = c;
  s[length] = '\0';
  return this;
}

GString *GString::append(GString *str) {
  int n = str->getLength();

  resize(length + n);
  memcpy(s + length, str->getCString(), n + 1);
  length += n;
  return this;
}

GString *GString::append(const char *str) {
  int n = strlen(str);

  resize(length + n);
  memcpy(s + length, str, n + 1);
  length += n;
  return this;
}

GString *GString::append(const char *str, int lengthA) {
  resize(length + lengthA);
  memcpy(s + length, str, lengthA);
  length += lengthA;
  s[length] = '\0';
  return this;
}

GString *GString::appendf(char *fmt, ...) {
  va_list argList;

  va_start(argList, fmt);
  appendfv(fmt, argList);
  va_end(argList);
  return this;
}

GString *GString::appendfv(char *fmt, va_list argList) {
  GStringFormatArg *args;
  int argsLen, argsSize;
  GStringFormatArg arg;
  int idx, width, prec;
  GBool reverseAlign, zeroFill;
  GStringFormatType ft;
  char buf[65];
  int len, i;
  char *p0, *p1, *str;

  argsLen = 0;
  argsSize = 8;
  args = (GStringFormatArg *)gmallocn(argsSize, sizeof(GStringFormatArg));

  p0 = fmt;
  while (*p0) {
    if (*p0 == '{') {
      ++p0;
      if (*p0 == '{') {
	++p0;
	append('{');
      } else {

	// parse the format string
	if (!(*p0 >= '0' && *p0 <= '9')) {
	  break;
	}
	idx = *p0 - '0';
	for (++p0; *p0 >= '0' && *p0 <= '9'; ++p0) {
	  idx = 10 * idx + (*p0 - '0');
	}
	if (*p0 != ':') {
	  break;
	}
	++p0;
	if (*p0 == '-') {
	  reverseAlign = gTrue;
	  ++p0;
	} else {
	  reverseAlign = gFalse;
	}
	width = 0;
	zeroFill = *p0 == '0';
	for (; *p0 >= '0' && *p0 <= '9'; ++p0) {
	  width = 10 * width + (*p0 - '0');
	}
	if (*p0 == '.') {
	  ++p0;
	  prec = 0;
	  for (; *p0 >= '0' && *p0 <= '9'; ++p0) {
	    prec = 10 * prec + (*p0 - '0');
	  }
	} else {
	  prec = 0;
	}
	for (ft = (GStringFormatType)0;
	     formatStrings[ft];
	     ft = (GStringFormatType)(ft + 1)) {
	  if (!strncmp(p0, formatStrings[ft], strlen(formatStrings[ft]))) {
	    break;
	  }
	}
	if (!formatStrings[ft]) {
	  break;
	}
	p0 += strlen(formatStrings[ft]);
	if (*p0 != '}') {
	  break;
	}
	++p0;

	// fetch the argument
	if (idx > argsLen) {
	  break;
	}
	if (idx == argsLen) {
	  if (argsLen == argsSize) {
	    argsSize *= 2;
	    args = (GStringFormatArg *)greallocn(args, argsSize,
						 sizeof(GStringFormatArg));
	  }
	  switch (ft) {
	  case fmtIntDecimal:
	  case fmtIntHex:
	  case fmtIntOctal:
	  case fmtIntBinary:
	  case fmtSpace:
	    args[argsLen].i = va_arg(argList, int);
	    break;
	  case fmtUIntDecimal:
	  case fmtUIntHex:
	  case fmtUIntOctal:
	  case fmtUIntBinary:
	    args[argsLen].ui = va_arg(argList, Guint);
	    break;
	  case fmtLongDecimal:
	  case fmtLongHex:
	  case fmtLongOctal:
	  case fmtLongBinary:
	    args[argsLen].l = va_arg(argList, long);
	    break;
	  case fmtULongDecimal:
	  case fmtULongHex:
	  case fmtULongOctal:
	  case fmtULongBinary:
	    args[argsLen].ul = va_arg(argList, Gulong);
	    break;
	  case fmtDouble:
	  case fmtDoubleTrim:
	    args[argsLen].f = va_arg(argList, double);
	    break;
	  case fmtChar:
	    args[argsLen].c = (char)va_arg(argList, int);
	    break;
	  case fmtString:
	    args[argsLen].s = va_arg(argList, char *);
	    break;
	  case fmtGString:
	    args[argsLen].gs = va_arg(argList, GString *);
	    break;
	  }
	  ++argsLen;
	}

	// format the argument
	arg = args[idx];
	switch (ft) {
	case fmtIntDecimal:
	  formatInt(arg.i, buf, sizeof(buf), zeroFill, width, 10, &str, &len);
	  break;
	case fmtIntHex:
	  formatInt(arg.i, buf, sizeof(buf), zeroFill, width, 16, &str, &len);
	  break;
	case fmtIntOctal:
	  formatInt(arg.i, buf, sizeof(buf), zeroFill, width, 8, &str, &len);
	  break;
	case fmtIntBinary:
	  formatInt(arg.i, buf, sizeof(buf), zeroFill, width, 2, &str, &len);
	  break;
	case fmtUIntDecimal:
	  formatUInt(arg.ui, buf, sizeof(buf), zeroFill, width, 10,
		     &str, &len);
	  break;
	case fmtUIntHex:
	  formatUInt(arg.ui, buf, sizeof(buf), zeroFill, width, 16,
		     &str, &len);
	  break;
	case fmtUIntOctal:
	  formatUInt(arg.ui, buf, sizeof(buf), zeroFill, width, 8, &str, &len);
	  break;
	case fmtUIntBinary:
	  formatUInt(arg.ui, buf, sizeof(buf), zeroFill, width, 2, &str, &len);
	  break;
	case fmtLongDecimal:
	  formatInt(arg.l, buf, sizeof(buf), zeroFill, width, 10, &str, &len);
	  break;
	case fmtLongHex:
	  formatInt(arg.l, buf, sizeof(buf), zeroFill, width, 16, &str, &len);
	  break;
	case fmtLongOctal:
	  formatInt(arg.l, buf, sizeof(buf), zeroFill, width, 8, &str, &len);
	  break;
	case fmtLongBinary:
	  formatInt(arg.l, buf, sizeof(buf), zeroFill, width, 2, &str, &len);
	  break;
	case fmtULongDecimal:
	  formatUInt(arg.ul, buf, sizeof(buf), zeroFill, width, 10,
		     &str, &len);
	  break;
	case fmtULongHex:
	  formatUInt(arg.ul, buf, sizeof(buf), zeroFill, width, 16,
		     &str, &len);
	  break;
	case fmtULongOctal:
	  formatUInt(arg.ul, buf, sizeof(buf), zeroFill, width, 8, &str, &len);
	  break;
	case fmtULongBinary:
	  formatUInt(arg.ul, buf, sizeof(buf), zeroFill, width, 2, &str, &len);
	  break;
	case fmtDouble:
	  formatDouble(arg.f, buf, sizeof(buf), prec, gFalse, &str, &len);
	  break;
	case fmtDoubleTrim:
	  formatDouble(arg.f, buf, sizeof(buf), prec, gTrue, &str, &len);
	  break;
	case fmtChar:
	  buf[0] = arg.c;
	  str = buf;
	  len = 1;
	  reverseAlign = !reverseAlign;
	  break;
	case fmtString:
	  str = arg.s;
	  len = strlen(str);
	  reverseAlign = !reverseAlign;
	  break;
	case fmtGString:
	  str = arg.gs->getCString();
	  len = arg.gs->getLength();
	  reverseAlign = !reverseAlign;
	  break;
	case fmtSpace:
	  str = buf;
	  len = 0;
	  width = arg.i;
	  break;
	}

	// append the formatted arg, handling width and alignment
	if (!reverseAlign && len < width) {
	  for (i = len; i < width; ++i) {
	    append(' ');
	  }
	}
	append(str, len);
	if (reverseAlign && len < width) {
	  for (i = len; i < width; ++i) {
	    append(' ');
	  }
	}
      }

    } else if (*p0 == '}') {
      ++p0;
      if (*p0 == '}') {
	++p0;
      }
      append('}');
      
    } else {
      for (p1 = p0 + 1; *p1 && *p1 != '{' && *p1 != '}'; ++p1) ;
      append(p0, p1 - p0);
      p0 = p1;
    }
  }

  gfree(args);
  return this;
}

void GString::formatInt(long x, char *buf, int bufSize,
			GBool zeroFill, int width, int base,
			char **p, int *len) {
  static char vals[17] = "0123456789abcdef";
  GBool neg;
  int start, i, j;

  i = bufSize;
  if ((neg = x < 0)) {
    x = -x;
  }
  start = neg ? 1 : 0;
  if (x == 0) {
    buf[--i] = '0';
  } else {
    while (i > start && x) {
      buf[--i] = vals[x % base];
      x /= base;
    }
  }
  if (zeroFill) {
    for (j = bufSize - i; i > start && j < width - start; ++j) {
      buf[--i] = '0';
    }
  }
  if (neg) {
    buf[--i] = '-';
  }
  *p = buf + i;
  *len = bufSize - i;
}

void GString::formatUInt(Gulong x, char *buf, int bufSize,
			 GBool zeroFill, int width, int base,
			 char **p, int *len) {
  static char vals[17] = "0123456789abcdef";
  int i, j;

  i = bufSize;
  if (x == 0) {
    buf[--i] = '0';
  } else {
    while (i > 0 && x) {
      buf[--i] = vals[x % base];
      x /= base;
    }
  }
  if (zeroFill) {
    for (j = bufSize - i; i > 0 && j < width; ++j) {
      buf[--i] = '0';
    }
  }
  *p = buf + i;
  *len = bufSize - i;
}

void GString::formatDouble(double x, char *buf, int bufSize, int prec,
			   GBool trim, char **p, int *len) {
  GBool neg, started;
  double x2;
  int d, i, j;

  if ((neg = x < 0)) {
    x = -x;
  }
  
  #ifdef WIN32
  x = floor(x * pow((double)10, prec) + 0.5);
  #else
  x = floor(x * pow(10, prec) + 0.5);
  #endif
  
  i = bufSize;
  started = !trim;
  for (j = 0; j < prec && i > 1; ++j) {
    x2 = floor(0.1 * (x + 0.5));
    d = (int)floor(x - 10 * x2 + 0.5);
    if (started || d != 0) {
      buf[--i] = '0' + d;
      started = gTrue;
    }
    x = x2;
  }
  if (i > 1 && started) {
    buf[--i] = '.';
  }
  if (i > 1) {
    do {
      x2 = floor(0.1 * (x + 0.5));
      d = (int)floor(x - 10 * x2 + 0.5);
      buf[--i] = '0' + d;
      x = x2;
    } while (i > 1 && x);
  }
  if (neg) {
    buf[--i] = '-';
  }
  *p = buf + i;
  *len = bufSize - i;
}

GString *GString::insert(int i, char c) {
  int j;

  resize(length + 1);
  for (j = length + 1; j > i; --j)
    s[j] = s[j-1];
  s[i] = c;
  ++length;
  return this;
}

GString *GString::insert(int i, GString *str) {
  int n = str->getLength();
  int j;

  resize(length + n);
  for (j = length; j >= i; --j)
    s[j+n] = s[j];
  memcpy(s+i, str->getCString(), n);
  length += n;
  return this;
}

GString *GString::insert(int i, const char *str) {
  int n = strlen(str);
  int j;

  resize(length + n);
  for (j = length; j >= i; --j)
    s[j+n] = s[j];
  memcpy(s+i, str, n);
  length += n;
  return this;
}

GString *GString::insert(int i, const char *str, int lengthA) {
  int j;

  resize(length + lengthA);
  for (j = length; j >= i; --j)
    s[j+lengthA] = s[j];
  memcpy(s+i, str, lengthA);
  length += lengthA;
  return this;
}

GString *GString::del(int i, int n) {
  int j;

  if (n > 0) {
    if (i + n > length) {
      n = length - i;
    }
    for (j = i; j <= length - n; ++j) {
      s[j] = s[j + n];
    }
    resize(length -= n);
  }
  return this;
}

GString *GString::upperCase() {
  int i;

  for (i = 0; i < length; ++i) {
    if (islower(s[i]))
      s[i] = toupper(s[i]);
  }
  return this;
}

GString *GString::lowerCase() {
  int i;

  for (i = 0; i < length; ++i) {
    if (isupper(s[i]))
      s[i] = tolower(s[i]);
  }
  return this;
}

int GString::cmp(GString *str) {
  int n1, n2, i, x;
  char *p1, *p2;

  n1 = length;
  n2 = str->length;
  for (i = 0, p1 = s, p2 = str->s; i < n1 && i < n2; ++i, ++p1, ++p2) {
    x = *p1 - *p2;
    if (x != 0) {
      return x;
    }
  }
  return n1 - n2;
}

int GString::cmpN(GString *str, int n) {
  int n1, n2, i, x;
  char *p1, *p2;

  n1 = length;
  n2 = str->length;
  for (i = 0, p1 = s, p2 = str->s;
       i < n1 && i < n2 && i < n;
       ++i, ++p1, ++p2) {
    x = *p1 - *p2;
    if (x != 0) {
      return x;
    }
  }
  if (i == n) {
    return 0;
  }
  return n1 - n2;
}

int GString::cmp(const char *sA) {
  int n1, i, x;
  const char *p1, *p2;

  n1 = length;
  for (i = 0, p1 = s, p2 = sA; i < n1 && *p2; ++i, ++p1, ++p2) {
    x = *p1 - *p2;
    if (x != 0) {
      return x;
    }
  }
  if (i < n1) {
    return 1;
  }
  if (*p2) {
    return -1;
  }
  return 0;
}

int GString::cmpN(const char *sA, int n) {
  int n1, i, x;
  const char *p1, *p2;

  n1 = length;
  for (i = 0, p1 = s, p2 = sA; i < n1 && *p2 && i < n; ++i, ++p1, ++p2) {
    x = *p1 - *p2;
    if (x != 0) {
      return x;
    }
  }
  if (i == n) {
    return 0;
  }
  if (i < n1) {
    return 1;
  }
  if (*p2) {
    return -1;
  }
  return 0;
}
//========================================================================
//
// GString.cc
//
// Simple variable-length string type.
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include "gmem.h"
#include "GString.h"

//------------------------------------------------------------------------

union GStringFormatArg {
  int i;
  Guint ui;
  long l;
  Gulong ul;
  double f;
  char c;
  char *s;
  GString *gs;
};

enum GStringFormatType {
  fmtIntDecimal,
  fmtIntHex,
  fmtIntOctal,
  fmtIntBinary,
  fmtUIntDecimal,
  fmtUIntHex,
  fmtUIntOctal,
  fmtUIntBinary,
  fmtLongDecimal,
  fmtLongHex,
  fmtLongOctal,
  fmtLongBinary,
  fmtULongDecimal,
  fmtULongHex,
  fmtULongOctal,
  fmtULongBinary,
  fmtDouble,
  fmtDoubleTrim,
  fmtChar,
  fmtString,
  fmtGString,
  fmtSpace
};

static char *formatStrings[] = {
  "d", "x", "o", "b", "ud", "ux", "uo", "ub",
  "ld", "lx", "lo", "lb", "uld", "ulx", "ulo", "ulb",
  "f", "g",
  "c",
  "s",
  "t",
  "w",
  NULL
};

//------------------------------------------------------------------------

static inline int size(int len) {
  int delta;
  for (delta = 8; delta < len && delta < 0x100000; delta <<= 1) ;
  // this is ((len + 1) + (delta - 1)) & ~(delta - 1)
  return (len + delta) & ~(delta - 1);
}

inline void GString::resize(int length1) {
  char *s1;

  if (!s) {
    s = new char[size(length1)];
  } else if (size(length1) != size(length)) {
    s1 = new char[size(length1)];
    if (length1 < length) {
      memcpy(s1, s, length1);
      s1[length1] = '\0';
    } else {
      memcpy(s1, s, length + 1);
    }
    delete[] s;
    s = s1;
  }
}

GString::GString() {
  s = NULL;
  resize(length = 0);
  s[0] = '\0';
}

GString::GString(const char *sA) {
  int n = strlen(sA);

  s = NULL;
  resize(length = n);
  memcpy(s, sA, n + 1);
}

GString::GString(const char *sA, int lengthA) {
  s = NULL;
  resize(length = lengthA);
  memcpy(s, sA, length * sizeof(char));
  s[length] = '\0';
}

GString::GString(GString *str, int idx, int lengthA) {
  s = NULL;
  resize(length = lengthA);
  memcpy(s, str->getCString() + idx, length);
  s[length] = '\0';
}

GString::GString(GString *str) {
  s = NULL;
  resize(length = str->getLength());
  memcpy(s, str->getCString(), length + 1);
}

GString::GString(GString *str1, GString *str2) {
  int n1 = str1->getLength();
  int n2 = str2->getLength();

  s = NULL;
  resize(length = n1 + n2);
  memcpy(s, str1->getCString(), n1);
  memcpy(s + n1, str2->getCString(), n2 + 1);
}

GString *GString::fromInt(int x) {
  char buf[24]; // enough space for 64-bit ints plus a little extra
  char *p;
  int len;

  formatInt(x, buf, sizeof(buf), gFalse, 0, 10, &p, &len);
  return new GString(p, len);
}

GString *GString::format(char *fmt, ...) {
  va_list argList;
  GString *s;

  s = new GString();
  va_start(argList, fmt);
  s->appendfv(fmt, argList);
  va_end(argList);
  return s;
}

GString *GString::formatv(char *fmt, va_list argList) {
  GString *s;

  s = new GString();
  s->appendfv(fmt, argList);
  return s;
}

GString::~GString() {
  delete[] s;
}

GString *GString::clear() {
  s[length = 0] = '\0';
  resize(0);
  return this;
}

GString *GString::append(char c) {
  resize(length + 1);
  s[length++] = c;
  s[length] = '\0';
  return this;
}

GString *GString::append(GString *str) {
  int n = str->getLength();

  resize(length + n);
  memcpy(s + length, str->getCString(), n + 1);
  length += n;
  return this;
}

GString *GString::append(const char *str) {
  int n = strlen(str);

  resize(length + n);
  memcpy(s + length, str, n + 1);
  length += n;
  return this;
}

GString *GString::append(const char *str, int lengthA) {
  resize(length + lengthA);
  memcpy(s + length, str, lengthA);
  length += lengthA;
  s[length] = '\0';
  return this;
}

GString *GString::appendf(char *fmt, ...) {
  va_list argList;

  va_start(argList, fmt);
  appendfv(fmt, argList);
  va_end(argList);
  return this;
}

GString *GString::appendfv(char *fmt, va_list argList) {
  GStringFormatArg *args;
  int argsLen, argsSize;
  GStringFormatArg arg;
  int idx, width, prec;
  GBool reverseAlign, zeroFill;
  GStringFormatType ft;
  char buf[65];
  int len, i;
  char *p0, *p1, *str;

  argsLen = 0;
  argsSize = 8;
  args = (GStringFormatArg *)gmallocn(argsSize, sizeof(GStringFormatArg));

  p0 = fmt;
  while (*p0) {
    if (*p0 == '{') {
      ++p0;
      if (*p0 == '{') {
	++p0;
	append('{');
      } else {

	// parse the format string
	if (!(*p0 >= '0' && *p0 <= '9')) {
	  break;
	}
	idx = *p0 - '0';
	for (++p0; *p0 >= '0' && *p0 <= '9'; ++p0) {
	  idx = 10 * idx + (*p0 - '0');
	}
	if (*p0 != ':') {
	  break;
	}
	++p0;
	if (*p0 == '-') {
	  reverseAlign = gTrue;
	  ++p0;
	} else {
	  reverseAlign = gFalse;
	}
	width = 0;
	zeroFill = *p0 == '0';
	for (; *p0 >= '0' && *p0 <= '9'; ++p0) {
	  width = 10 * width + (*p0 - '0');
	}
	if (*p0 == '.') {
	  ++p0;
	  prec = 0;
	  for (; *p0 >= '0' && *p0 <= '9'; ++p0) {
	    prec = 10 * prec + (*p0 - '0');
	  }
	} else {
	  prec = 0;
	}
	for (ft = (GStringFormatType)0;
	     formatStrings[ft];
	     ft = (GStringFormatType)(ft + 1)) {
	  if (!strncmp(p0, formatStrings[ft], strlen(formatStrings[ft]))) {
	    break;
	  }
	}
	if (!formatStrings[ft]) {
	  break;
	}
	p0 += strlen(formatStrings[ft]);
	if (*p0 != '}') {
	  break;
	}
	++p0;

	// fetch the argument
	if (idx > argsLen) {
	  break;
	}
	if (idx == argsLen) {
	  if (argsLen == argsSize) {
	    argsSize *= 2;
	    args = (GStringFormatArg *)greallocn(args, argsSize,
						 sizeof(GStringFormatArg));
	  }
	  switch (ft) {
	  case fmtIntDecimal:
	  case fmtIntHex:
	  case fmtIntOctal:
	  case fmtIntBinary:
	  case fmtSpace:
	    args[argsLen].i = va_arg(argList, int);
	    break;
	  case fmtUIntDecimal:
	  case fmtUIntHex:
	  case fmtUIntOctal:
	  case fmtUIntBinary:
	    args[argsLen].ui = va_arg(argList, Guint);
	    break;
	  case fmtLongDecimal:
	  case fmtLongHex:
	  case fmtLongOctal:
	  case fmtLongBinary:
	    args[argsLen].l = va_arg(argList, long);
	    break;
	  case fmtULongDecimal:
	  case fmtULongHex:
	  case fmtULongOctal:
	  case fmtULongBinary:
	    args[argsLen].ul = va_arg(argList, Gulong);
	    break;
	  case fmtDouble:
	  case fmtDoubleTrim:
	    args[argsLen].f = va_arg(argList, double);
	    break;
	  case fmtChar:
	    args[argsLen].c = (char)va_arg(argList, int);
	    break;
	  case fmtString:
	    args[argsLen].s = va_arg(argList, char *);
	    break;
	  case fmtGString:
	    args[argsLen].gs = va_arg(argList, GString *);
	    break;
	  }
	  ++argsLen;
	}

	// format the argument
	arg = args[idx];
	switch (ft) {
	case fmtIntDecimal:
	  formatInt(arg.i, buf, sizeof(buf), zeroFill, width, 10, &str, &len);
	  break;
	case fmtIntHex:
	  formatInt(arg.i, buf, sizeof(buf), zeroFill, width, 16, &str, &len);
	  break;
	case fmtIntOctal:
	  formatInt(arg.i, buf, sizeof(buf), zeroFill, width, 8, &str, &len);
	  break;
	case fmtIntBinary:
	  formatInt(arg.i, buf, sizeof(buf), zeroFill, width, 2, &str, &len);
	  break;
	case fmtUIntDecimal:
	  formatUInt(arg.ui, buf, sizeof(buf), zeroFill, width, 10,
		     &str, &len);
	  break;
	case fmtUIntHex:
	  formatUInt(arg.ui, buf, sizeof(buf), zeroFill, width, 16,
		     &str, &len);
	  break;
	case fmtUIntOctal:
	  formatUInt(arg.ui, buf, sizeof(buf), zeroFill, width, 8, &str, &len);
	  break;
	case fmtUIntBinary:
	  formatUInt(arg.ui, buf, sizeof(buf), zeroFill, width, 2, &str, &len);
	  break;
	case fmtLongDecimal:
	  formatInt(arg.l, buf, sizeof(buf), zeroFill, width, 10, &str, &len);
	  break;
	case fmtLongHex:
	  formatInt(arg.l, buf, sizeof(buf), zeroFill, width, 16, &str, &len);
	  break;
	case fmtLongOctal:
	  formatInt(arg.l, buf, sizeof(buf), zeroFill, width, 8, &str, &len);
	  break;
	case fmtLongBinary:
	  formatInt(arg.l, buf, sizeof(buf), zeroFill, width, 2, &str, &len);
	  break;
	case fmtULongDecimal:
	  formatUInt(arg.ul, buf, sizeof(buf), zeroFill, width, 10,
		     &str, &len);
	  break;
	case fmtULongHex:
	  formatUInt(arg.ul, buf, sizeof(buf), zeroFill, width, 16,
		     &str, &len);
	  break;
	case fmtULongOctal:
	  formatUInt(arg.ul, buf, sizeof(buf), zeroFill, width, 8, &str, &len);
	  break;
	case fmtULongBinary:
	  formatUInt(arg.ul, buf, sizeof(buf), zeroFill, width, 2, &str, &len);
	  break;
	case fmtDouble:
	  formatDouble(arg.f, buf, sizeof(buf), prec, gFalse, &str, &len);
	  break;
	case fmtDoubleTrim:
	  formatDouble(arg.f, buf, sizeof(buf), prec, gTrue, &str, &len);
	  break;
	case fmtChar:
	  buf[0] = arg.c;
	  str = buf;
	  len = 1;
	  reverseAlign = !reverseAlign;
	  break;
	case fmtString:
	  str = arg.s;
	  len = strlen(str);
	  reverseAlign = !reverseAlign;
	  break;
	case fmtGString:
	  str = arg.gs->getCString();
	  len = arg.gs->getLength();
	  reverseAlign = !reverseAlign;
	  break;
	case fmtSpace:
	  str = buf;
	  len = 0;
	  width = arg.i;
	  break;
	}

	// append the formatted arg, handling width and alignment
	if (!reverseAlign && len < width) {
	  for (i = len; i < width; ++i) {
	    append(' ');
	  }
	}
	append(str, len);
	if (reverseAlign && len < width) {
	  for (i = len; i < width; ++i) {
	    append(' ');
	  }
	}
      }

    } else if (*p0 == '}') {
      ++p0;
      if (*p0 == '}') {
	++p0;
      }
      append('}');
      
    } else {
      for (p1 = p0 + 1; *p1 && *p1 != '{' && *p1 != '}'; ++p1) ;
      append(p0, p1 - p0);
      p0 = p1;
    }
  }

  gfree(args);
  return this;
}

void GString::formatInt(long x, char *buf, int bufSize,
			GBool zeroFill, int width, int base,
			char **p, int *len) {
  static char vals[17] = "0123456789abcdef";
  GBool neg;
  int start, i, j;

  i = bufSize;
  if ((neg = x < 0)) {
    x = -x;
  }
  start = neg ? 1 : 0;
  if (x == 0) {
    buf[--i] = '0';
  } else {
    while (i > start && x) {
      buf[--i] = vals[x % base];
      x /= base;
    }
  }
  if (zeroFill) {
    for (j = bufSize - i; i > start && j < width - start; ++j) {
      buf[--i] = '0';
    }
  }
  if (neg) {
    buf[--i] = '-';
  }
  *p = buf + i;
  *len = bufSize - i;
}

void GString::formatUInt(Gulong x, char *buf, int bufSize,
			 GBool zeroFill, int width, int base,
			 char **p, int *len) {
  static char vals[17] = "0123456789abcdef";
  int i, j;

  i = bufSize;
  if (x == 0) {
    buf[--i] = '0';
  } else {
    while (i > 0 && x) {
      buf[--i] = vals[x % base];
      x /= base;
    }
  }
  if (zeroFill) {
    for (j = bufSize - i; i > 0 && j < width; ++j) {
      buf[--i] = '0';
    }
  }
  *p = buf + i;
  *len = bufSize - i;
}

void GString::formatDouble(double x, char *buf, int bufSize, int prec,
			   GBool trim, char **p, int *len) {
  GBool neg, started;
  double x2;
  int d, i, j;

  if ((neg = x < 0)) {
    x = -x;
  }
  x = floor(x * pow((double)10, prec) + 0.5);
  i = bufSize;
  started = !trim;
  for (j = 0; j < prec && i > 1; ++j) {
    x2 = floor(0.1 * (x + 0.5));
    d = (int)floor(x - 10 * x2 + 0.5);
    if (started || d != 0) {
      buf[--i] = '0' + d;
      started = gTrue;
    }
    x = x2;
  }
  if (i > 1 && started) {
    buf[--i] = '.';
  }
  if (i > 1) {
    do {
      x2 = floor(0.1 * (x + 0.5));
      d = (int)floor(x - 10 * x2 + 0.5);
      buf[--i] = '0' + d;
      x = x2;
    } while (i > 1 && x);
  }
  if (neg) {
    buf[--i] = '-';
  }
  *p = buf + i;
  *len = bufSize - i;
}

GString *GString::insert(int i, char c) {
  int j;

  resize(length + 1);
  for (j = length + 1; j > i; --j)
    s[j] = s[j-1];
  s[i] = c;
  ++length;
  return this;
}

GString *GString::insert(int i, GString *str) {
  int n = str->getLength();
  int j;

  resize(length + n);
  for (j = length; j >= i; --j)
    s[j+n] = s[j];
  memcpy(s+i, str->getCString(), n);
  length += n;
  return this;
}

GString *GString::insert(int i, const char *str) {
  int n = strlen(str);
  int j;

  resize(length + n);
  for (j = length; j >= i; --j)
    s[j+n] = s[j];
  memcpy(s+i, str, n);
  length += n;
  return this;
}

GString *GString::insert(int i, const char *str, int lengthA) {
  int j;

  resize(length + lengthA);
  for (j = length; j >= i; --j)
    s[j+lengthA] = s[j];
  memcpy(s+i, str, lengthA);
  length += lengthA;
  return this;
}

GString *GString::del(int i, int n) {
  int j;

  if (n > 0) {
    if (i + n > length) {
      n = length - i;
    }
    for (j = i; j <= length - n; ++j) {
      s[j] = s[j + n];
    }
    resize(length -= n);
  }
  return this;
}

GString *GString::upperCase() {
  int i;

  for (i = 0; i < length; ++i) {
    if (islower(s[i]))
      s[i] = toupper(s[i]);
  }
  return this;
}

GString *GString::lowerCase() {
  int i;

  for (i = 0; i < length; ++i) {
    if (isupper(s[i]))
      s[i] = tolower(s[i]);
  }
  return this;
}

int GString::cmp(GString *str) {
  int n1, n2, i, x;
  char *p1, *p2;

  n1 = length;
  n2 = str->length;
  for (i = 0, p1 = s, p2 = str->s; i < n1 && i < n2; ++i, ++p1, ++p2) {
    x = *p1 - *p2;
    if (x != 0) {
      return x;
    }
  }
  return n1 - n2;
}

int GString::cmpN(GString *str, int n) {
  int n1, n2, i, x;
  char *p1, *p2;

  n1 = length;
  n2 = str->length;
  for (i = 0, p1 = s, p2 = str->s;
       i < n1 && i < n2 && i < n;
       ++i, ++p1, ++p2) {
    x = *p1 - *p2;
    if (x != 0) {
      return x;
    }
  }
  if (i == n) {
    return 0;
  }
  return n1 - n2;
}

int GString::cmp(const char *sA) {
  int n1, i, x;
  const char *p1, *p2;

  n1 = length;
  for (i = 0, p1 = s, p2 = sA; i < n1 && *p2; ++i, ++p1, ++p2) {
    x = *p1 - *p2;
    if (x != 0) {
      return x;
    }
  }
  if (i < n1) {
    return 1;
  }
  if (*p2) {
    return -1;
  }
  return 0;
}

int GString::cmpN(const char *sA, int n) {
  int n1, i, x;
  const char *p1, *p2;

  n1 = length;
  for (i = 0, p1 = s, p2 = sA; i < n1 && *p2 && i < n; ++i, ++p1, ++p2) {
    x = *p1 - *p2;
    if (x != 0) {
      return x;
    }
  }
  if (i == n) {
    return 0;
  }
  if (i < n1) {
    return 1;
  }
  if (*p2) {
    return -1;
  }
  return 0;
}
//========================================================================
//
// GString.h
//
// Simple variable-length string type.
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef GSTRING_H
#define GSTRING_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include <stdarg.h>
#include "gtypes.h"

class GString {
public:

  // Create an empty string.
  GString();

  // Create a string from a C string.
  GString(const char *sA);

  // Create a string from <lengthA> chars at <sA>.  This string
  // can contain null characters.
  GString(const char *sA, int lengthA);

  // Create a string from <lengthA> chars at <idx> in <str>.
  GString(GString *str, int idx, int lengthA);

  // Copy a string.
  GString(GString *str);
  GString *copy() { return new GString(this); }

  // Concatenate two strings.
  GString(GString *str1, GString *str2);

  // Convert an integer to a string.
  static GString *fromInt(int x);

  // Create a formatted string.  Similar to printf, but without the
  // string overflow issues.  Formatting elements consist of:
  //     {<arg>:[<width>][.<precision>]<type>}
  // where:
  // - <arg> is the argument number (arg 0 is the first argument
  //   following the format string) -- NB: args must be first used in
  //   order; they can be reused in any order
  // - <width> is the field width -- negative to reverse the alignment;
  //   starting with a leading zero to zero-fill (for integers)
  // - <precision> is the number of digits to the right of the decimal
  //   point (for floating point numbers)
  // - <type> is one of:
  //     d, x, o, b -- int in decimal, hex, octal, binary
  //     ud, ux, uo, ub -- unsigned int
  //     ld, lx, lo, lb, uld, ulx, ulo, ulb -- long, unsigned long
  //     f, g -- double
  //     c -- char
  //     s -- string (char *)
  //     t -- GString *
  //     w -- blank space; arg determines width
  // To get literal curly braces, use {{ or }}.
  static GString *format(char *fmt, ...);
  static GString *formatv(char *fmt, va_list argList);

  // Destructor.
  ~GString();

  // Get length.
  int getLength() { return length; }

  // Get C string.
  char *getCString() { return s; }

  // Get <i>th character.
  char getChar(int i) { return s[i]; }

  // Change <i>th character.
  void setChar(int i, char c) { s[i] = c; }

  // Clear string to zero length.
  GString *clear();

  // Append a character or string.
  GString *append(char c);
  GString *append(GString *str);
  GString *append(const char *str);
  GString *append(const char *str, int lengthA);

  // Append a formatted string.
  GString *appendf(char *fmt, ...);
  GString *appendfv(char *fmt, va_list argList);

  // Insert a character or string.
  GString *insert(int i, char c);
  GString *insert(int i, GString *str);
  GString *insert(int i, const char *str);
  GString *insert(int i, const char *str, int lengthA);

  // Delete a character or range of characters.
  GString *del(int i, int n = 1);

  // Convert string to all-upper/all-lower case.
  GString *upperCase();
  GString *lowerCase();

  // Compare two strings:  -1:<  0:=  +1:>
  int cmp(GString *str);
  int cmpN(GString *str, int n);
  int cmp(const char *sA);
  int cmpN(const char *sA, int n);

private:

  int length;
  char *s;

  void resize(int length1);
  static void formatInt(long x, char *buf, int bufSize,
			GBool zeroFill, int width, int base,
			char **p, int *len);
  static void formatUInt(Gulong x, char *buf, int bufSize,
			 GBool zeroFill, int width, int base,
			 char **p, int *len);
  static void formatDouble(double x, char *buf, int bufSize, int prec,
			   GBool trim, char **p, int *len);
};

#endif
#========================================================================
#
# Goo library Makefile
#
# Copyright 1996-2003 Glyph & Cog, LLC
#
#========================================================================

SHELL = /bin/sh

srcdir = @srcdir@
VPATH = @srcdir@

CFLAGS = @CFLAGS@ @DEFS@ -I.. -I$(srcdir)
CXXFLAGS = -I/usr/local/include @CXXFLAGS@ @DEFS@ -I.. -I$(srcdir)

CC = @CC@
CXX = @CXX@
AR = ar rc
RANLIB = @RANLIB@

LIBPREFIX = lib

#------------------------------------------------------------------------

.SUFFIXES: .cc

.cc.o:
	$(CXX) $(CXXFLAGS) -c $<

#------------------------------------------------------------------------

CXX_SRC = \
	$(srcdir)/GHash.cc \
	$(srcdir)/GList.cc \
	$(srcdir)/GString.cc \
	$(srcdir)/gmem.cc \
	$(srcdir)/gmempp.cc \
	$(srcdir)/gfile.cc \
	$(srcdir)/FixedPoint.cc

C_SRC = \
	$(srcdir)/parseargs.c

#------------------------------------------------------------------------

all: $(LIBPREFIX)Goo.a

#------------------------------------------------------------------------

GOO_CXX_OBJS = GHash.o GList.o GString.o gmem.o gmempp.o gfile.o FixedPoint.o
GOO_C_OBJS = parseargs.o
GOO_OBJS = $(GOO_CXX_OBJS) $(GOO_C_OBJS)
GOO_WINOBJS = libGoo.lib

$(LIBPREFIX)Goo.a: $(GOO_OBJS)
	rm -f $(LIBPREFIX)Goo.a
	$(AR) $(LIBPREFIX)Goo.a $(GOO_OBJS)
	$(RANLIB) $(LIBPREFIX)Goo.a

#------------------------------------------------------------------------

clean:
	rm -f $(GOO_OBJS) $(LIBPREFIX)Goo.a $(GOO_WINOBJS)

#------------------------------------------------------------------------

depend:
	$(CXX) $(CXXFLAGS) -MM $(CXX_SRC) >Makefile.dep
	$(CC) $(CFLAGS) -MM $(C_SRC) >>Makefile.dep

include Makefile.dep
//========================================================================
//
// gfile.cc
//
// Miscellaneous file and directory name manipulation.
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef WIN32
#  include <time.h>
#else
#  if defined(MACOS)
#    include <sys/stat.h>
#  elif !defined(ACORN)
#    include <sys/types.h>
#    include <sys/stat.h>
#    include <fcntl.h>
#  endif
#  include <limits.h>
#  include <string.h>
#  if !defined(VMS) && !defined(ACORN) && !defined(MACOS)
#    include <pwd.h>
#  endif
#  if defined(VMS) && (__DECCXX_VER < 50200000)
#    include <unixlib.h>
#  endif
#endif // WIN32
#include "GString.h"
#include "gfile.h"

// Some systems don't define this, so just make it something reasonably
// large.
#ifndef PATH_MAX
#define PATH_MAX 1024
#endif

//------------------------------------------------------------------------

GString *getHomeDir() {
#ifdef VMS
  //---------- VMS ----------
  return new GString("SYS$LOGIN:");

#elif defined(__EMX__) || defined(WIN32)
  //---------- OS/2+EMX and Win32 ----------
  char *s;
  GString *ret;

  if ((s = getenv("HOME")))
    ret = new GString(s);
  else
    ret = new GString(".");
  return ret;

#elif defined(ACORN)
  //---------- RISCOS ----------
  return new GString("@");

#elif defined(MACOS)
  //---------- MacOS ----------
  return new GString(":");

#else
  //---------- Unix ----------
  char *s;
  struct passwd *pw;
  GString *ret;

  if ((s = getenv("HOME"))) {
    ret = new GString(s);
  } else {
    if ((s = getenv("USER")))
      pw = getpwnam(s);
    else
      pw = getpwuid(getuid());
    if (pw)
      ret = new GString(pw->pw_dir);
    else
      ret = new GString(".");
  }
  return ret;
#endif
}

GString *getCurrentDir() {
  char buf[PATH_MAX+1];

#if defined(__EMX__)
  if (_getcwd2(buf, sizeof(buf)))
#elif defined(WIN32)
  if (GetCurrentDirectory(sizeof(buf), buf))
#elif defined(ACORN)
  if (strcpy(buf, "@"))
#elif defined(MACOS)
  if (strcpy(buf, ":"))
#else
  if (getcwd(buf, sizeof(buf)))
#endif
    return new GString(buf);
  return new GString();
}

GString *appendToPath(GString *path, char *fileName) {
#if defined(VMS)
  //---------- VMS ----------
  //~ this should handle everything necessary for file
  //~ requesters, but it's certainly not complete
  char *p0, *p1, *p2;
  char *q1;

  p0 = path->getCString();
  p1 = p0 + path->getLength() - 1;
  if (!strcmp(fileName, "-")) {
    if (*p1 == ']') {
      for (p2 = p1; p2 > p0 && *p2 != '.' && *p2 != '['; --p2) ;
      if (*p2 == '[')
	++p2;
      path->del(p2 - p0, p1 - p2);
    } else if (*p1 == ':') {
      path->append("[-]");
    } else {
      path->clear();
      path->append("[-]");
    }
  } else if ((q1 = strrchr(fileName, '.')) && !strncmp(q1, ".DIR;", 5)) {
    if (*p1 == ']') {
      path->insert(p1 - p0, '.');
      path->insert(p1 - p0 + 1, fileName, q1 - fileName);
    } else if (*p1 == ':') {
      path->append('[');
      path->append(']');
      path->append(fileName, q1 - fileName);
    } else {
      path->clear();
      path->append(fileName, q1 - fileName);
    }
  } else {
    if (*p1 != ']' && *p1 != ':')
      path->clear();
    path->append(fileName);
  }
  return path;

#elif defined(WIN32)
  //---------- Win32 ----------
  GString *tmp;
  char buf[256];
  char *fp;

  tmp = new GString(path);
  tmp->append('/');
  tmp->append(fileName);
  GetFullPathName(tmp->getCString(), sizeof(buf), buf, &fp);
  delete tmp;
  path->clear();
  path->append(buf);
  return path;

#elif defined(ACORN)
  //---------- RISCOS ----------
  char *p;
  int i;

  path->append(".");
  i = path->getLength();
  path->append(fileName);
  for (p = path->getCString() + i; *p; ++p) {
    if (*p == '/') {
      *p = '.';
    } else if (*p == '.') {
      *p = '/';
    }
  }
  return path;

#elif defined(MACOS)
  //---------- MacOS ----------
  char *p;
  int i;

  path->append(":");
  i = path->getLength();
  path->append(fileName);
  for (p = path->getCString() + i; *p; ++p) {
    if (*p == '/') {
      *p = ':';
    } else if (*p == '.') {
      *p = ':';
    }
  }
  return path;

#elif defined(__EMX__)
  //---------- OS/2+EMX ----------
  int i;

  // appending "." does nothing
  if (!strcmp(fileName, "."))
    return path;

  // appending ".." goes up one directory
  if (!strcmp(fileName, "..")) {
    for (i = path->getLength() - 2; i >= 0; --i) {
      if (path->getChar(i) == '/' || path->getChar(i) == '\\' ||
	  path->getChar(i) == ':')
	break;
    }
    if (i <= 0) {
      if (path->getChar(0) == '/' || path->getChar(0) == '\\') {
	path->del(1, path->getLength() - 1);
      } else if (path->getLength() >= 2 && path->getChar(1) == ':') {
	path->del(2, path->getLength() - 2);
      } else {
	path->clear();
	path->append("..");
      }
    } else {
      if (path->getChar(i-1) == ':')
	++i;
      path->del(i, path->getLength() - i);
    }
    return path;
  }

  // otherwise, append "/" and new path component
  if (path->getLength() > 0 &&
      path->getChar(path->getLength() - 1) != '/' &&
      path->getChar(path->getLength() - 1) != '\\')
    path->append('/');
  path->append(fileName);
  return path;

#else
  //---------- Unix ----------
  int i;

  // appending "." does nothing
  if (!strcmp(fileName, "."))
    return path;

  // appending ".." goes up one directory
  if (!strcmp(fileName, "..")) {
    for (i = path->getLength() - 2; i >= 0; --i) {
      if (path->getChar(i) == '/')
	break;
    }
    if (i <= 0) {
      if (path->getChar(0) == '/') {
	path->del(1, path->getLength() - 1);
      } else {
	path->clear();
	path->append("..");
      }
    } else {
      path->del(i, path->getLength() - i);
    }
    return path;
  }

  // otherwise, append "/" and new path component
  if (path->getLength() > 0 &&
      path->getChar(path->getLength() - 1) != '/')
    path->append('/');
  path->append(fileName);
  return path;
#endif
}

GString *grabPath(char *fileName) {
#ifdef VMS
  //---------- VMS ----------
  char *p;

  if ((p = strrchr(fileName, ']')))
    return new GString(fileName, p + 1 - fileName);
  if ((p = strrchr(fileName, ':')))
    return new GString(fileName, p + 1 - fileName);
  return new GString();

#elif defined(__EMX__) || defined(WIN32)
  //---------- OS/2+EMX and Win32 ----------
  char *p;

  if ((p = strrchr(fileName, '/')))
    return new GString(fileName, p - fileName);
  if ((p = strrchr(fileName, '\\')))
    return new GString(fileName, p - fileName);
  if ((p = strrchr(fileName, ':')))
    return new GString(fileName, p + 1 - fileName);
  return new GString();

#elif defined(ACORN)
  //---------- RISCOS ----------
  char *p;

  if ((p = strrchr(fileName, '.')))
    return new GString(fileName, p - fileName);
  return new GString();

#elif defined(MACOS)
  //---------- MacOS ----------
  char *p;

  if ((p = strrchr(fileName, ':')))
    return new GString(fileName, p - fileName);
  return new GString();

#else
  //---------- Unix ----------
  char *p;

  if ((p = strrchr(fileName, '/')))
    return new GString(fileName, p - fileName);
  return new GString();
#endif
}

GBool isAbsolutePath(char *path) {
#ifdef VMS
  //---------- VMS ----------
  return strchr(path, ':') ||
	 (path[0] == '[' && path[1] != '.' && path[1] != '-');

#elif defined(__EMX__) || defined(WIN32)
  //---------- OS/2+EMX and Win32 ----------
  return path[0] == '/' || path[0] == '\\' || path[1] == ':';

#elif defined(ACORN)
  //---------- RISCOS ----------
  return path[0] == '$';

#elif defined(MACOS)
  //---------- MacOS ----------
  return path[0] != ':';

#else
  //---------- Unix ----------
  return path[0] == '/';
#endif
}

GString *makePathAbsolute(GString *path) {
#ifdef VMS
  //---------- VMS ----------
  char buf[PATH_MAX+1];

  if (!isAbsolutePath(path->getCString())) {
    if (getcwd(buf, sizeof(buf))) {
      path->insert(0, buf);
    }
  }
  return path;

#elif defined(WIN32)
  //---------- Win32 ----------
  char buf[_MAX_PATH];
  char *fp;

  buf[0] = '\0';
  if (!GetFullPathName(path->getCString(), _MAX_PATH, buf, &fp)) {
    path->clear();
    return path;
  }
  path->clear();
  path->append(buf);
  return path;

#elif defined(ACORN)
  //---------- RISCOS ----------
  path->insert(0, '@');
  return path;

#elif defined(MACOS)
  //---------- MacOS ----------
  path->del(0, 1);
  return path;

#else
  //---------- Unix and OS/2+EMX ----------
  struct passwd *pw;
  char buf[PATH_MAX+1];
  GString *s;
  char *p1, *p2;
  int n;

  if (path->getChar(0) == '~') {
    if (path->getChar(1) == '/' ||
#ifdef __EMX__
	path->getChar(1) == '\\' ||
#endif
	path->getLength() == 1) {
      path->del(0, 1);
      s = getHomeDir();
      path->insert(0, s);
      delete s;
    } else {
      p1 = path->getCString() + 1;
#ifdef __EMX__
      for (p2 = p1; *p2 && *p2 != '/' && *p2 != '\\'; ++p2) ;
#else
      for (p2 = p1; *p2 && *p2 != '/'; ++p2) ;
#endif
      if ((n = p2 - p1) > PATH_MAX)
	n = PATH_MAX;
      strncpy(buf, p1, n);
      buf[n] = '\0';
      if ((pw = getpwnam(buf))) {
	path->del(0, p2 - p1 + 1);
	path->insert(0, pw->pw_dir);
      }
    }
  } else if (!isAbsolutePath(path->getCString())) {
    if (getcwd(buf, sizeof(buf))) {
#ifndef __EMX__
      path->insert(0, '/');
#endif
      path->insert(0, buf);
    }
  }
  return path;
#endif
}

time_t getModTime(char *fileName) {
#ifdef WIN32
  //~ should implement this, but it's (currently) only used in xpdf
  return 0;
#else
  struct stat statBuf;

  if (stat(fileName, &statBuf)) {
    return 0;
  }
  return statBuf.st_mtime;
#endif
}

GBool openTempFile(GString **name, FILE **f, char *mode, char *ext) {
#if defined(WIN32)
  //---------- Win32 ----------
  char *tempDir;
  GString *s, *s2;
  char buf[32];
  FILE *f2;
  int t, i;

  // this has the standard race condition problem, but I haven't found
  // a better way to generate temp file names with extensions on
  // Windows
  if ((tempDir = getenv("TEMP"))) {
    s = new GString(tempDir);
    s->append('\\');
  } else {
    s = new GString();
  }
  s->append("x");
  t = (int)time(NULL);
  for (i = 0; i < 1000; ++i) {
    sprintf(buf, "%d", t + i);
    s2 = s->copy()->append(buf);
    if (ext) {
      s2->append(ext);
    }
    if (!(f2 = fopen(s2->getCString(), "r"))) {
      if (!(f2 = fopen(s2->getCString(), mode))) {
	delete s2;
	delete s;
	return gFalse;
      }
      *name = s2;
      *f = f2;
      delete s;
      return gTrue;
    }
    fclose(f2);
    delete s2;
  }
  delete s;
  return gFalse;
#elif defined(VMS) || defined(__EMX__) || defined(ACORN) || defined(MACOS)
  //---------- non-Unix ----------
  char *s;

  // There is a security hole here: an attacker can create a symlink
  // with this file name after the tmpnam call and before the fopen
  // call.  I will happily accept fixes to this function for non-Unix
  // OSs.
  if (!(s = tmpnam(NULL))) {
    return gFalse;
  }
  *name = new GString(s);
  if (ext) {
    (*name)->append(ext);
  }
  if (!(*f = fopen((*name)->getCString(), mode))) {
    delete (*name);
    return gFalse;
  }
  return gTrue;
#else
  //---------- Unix ----------
  char *s;
  int fd;

  if (ext) {
#if HAVE_MKSTEMPS
    if ((s = getenv("TMPDIR"))) {
      *name = new GString(s);
    } else {
      *name = new GString("/tmp");
    }
    (*name)->append("/XXXXXX")->append(ext);
    fd = mkstemps((*name)->getCString(), strlen(ext));
#else
    if (!(s = tmpnam(NULL))) {
      return gFalse;
    }
    *name = new GString(s);
    (*name)->append(ext);
    fd = open((*name)->getCString(), O_WRONLY | O_CREAT | O_EXCL, 0600);
#endif
  } else {
#if HAVE_MKSTEMP
    if ((s = getenv("TMPDIR"))) {
      *name = new GString(s);
    } else {
      *name = new GString("/tmp");
    }
    (*name)->append("/XXXXXX");
    fd = mkstemp((*name)->getCString());
#else // HAVE_MKSTEMP
    if (!(s = tmpnam(NULL))) {
      return gFalse;
    }
    *name = new GString(s);
    fd = open((*name)->getCString(), O_WRONLY | O_CREAT | O_EXCL, 0600);
#endif // HAVE_MKSTEMP
  }
  if (fd < 0 || !(*f = fdopen(fd, mode))) {
    delete *name;
    return gFalse;
  }
  return gTrue;
#endif
}

GBool executeCommand(char *cmd) {
#ifdef VMS
  return system(cmd) ? gTrue : gFalse;
#else
  return system(cmd) ? gFalse : gTrue;
#endif
}

char *getLine(char *buf, int size, FILE *f) {
  int c, i;

  i = 0;
  while (i < size - 1) {
    if ((c = fgetc(f)) == EOF) {
      break;
    }
    buf[i++] = (char)c;
    if (c == '\x0a') {
      break;
    }
    if (c == '\x0d') {
      c = fgetc(f);
      if (c == '\x0a' && i < size - 1) {
	buf[i++] = (char)c;
      } else if (c != EOF) {
	ungetc(c, f);
      }
      break;
    }
  }
  buf[i] = '\0';
  if (i == 0) {
    return NULL;
  }
  return buf;
}

//------------------------------------------------------------------------
// GDir and GDirEntry
//------------------------------------------------------------------------

GDirEntry::GDirEntry(char *dirPath, char *nameA, GBool doStat) {
#ifdef VMS
  char *p;
#elif defined(WIN32)
  int fa;
  GString *s;
#elif defined(ACORN)
#else
  struct stat st;
  GString *s;
#endif

  name = new GString(nameA);
  dir = gFalse;
  if (doStat) {
#ifdef VMS
    if (!strcmp(nameA, "-") ||
	((p = strrchr(nameA, '.')) && !strncmp(p, ".DIR;", 5)))
      dir = gTrue;
#elif defined(ACORN)
#else
    s = new GString(dirPath);
    appendToPath(s, nameA);
#ifdef WIN32
    fa = GetFileAttributes(s->getCString());
    dir = (fa != 0xFFFFFFFF && (fa & FILE_ATTRIBUTE_DIRECTORY));
#else
    if (stat(s->getCString(), &st) == 0)
      dir = S_ISDIR(st.st_mode);
#endif
    delete s;
#endif
  }
}

GDirEntry::~GDirEntry() {
  delete name;
}

GDir::GDir(char *name, GBool doStatA) {
  path = new GString(name);
  doStat = doStatA;
#if defined(WIN32)
  GString *tmp;

  tmp = path->copy();
  tmp->append("/*.*");
  hnd = FindFirstFile(tmp->getCString(), &ffd);
  delete tmp;
#elif defined(ACORN)
#elif defined(MACOS)
#else
  dir = opendir(name);
#ifdef VMS
  needParent = strchr(name, '[') != NULL;
#endif
#endif
}

GDir::~GDir() {
  delete path;
#if defined(WIN32)
  if (hnd) {
    FindClose(hnd);
    hnd = NULL;
  }
#elif defined(ACORN)
#elif defined(MACOS)
#else
  if (dir)
    closedir(dir);
#endif
}

GDirEntry *GDir::getNextEntry() {
  GDirEntry *e;

#if defined(WIN32)
  if (hnd) {
    e = new GDirEntry(path->getCString(), ffd.cFileName, doStat);
    if (hnd  && !FindNextFile(hnd, &ffd)) {
      FindClose(hnd);
      hnd = NULL;
    }
  } else {
    e = NULL;
  }
#elif defined(ACORN)
#elif defined(MACOS)
#elif defined(VMS)
  struct dirent *ent;
  e = NULL;
  if (dir) {
    if (needParent) {
      e = new GDirEntry(path->getCString(), "-", doStat);
      needParent = gFalse;
      return e;
    }
    ent = readdir(dir);
    if (ent) {
      e = new GDirEntry(path->getCString(), ent->d_name, doStat);
    }
  }
#else
  struct dirent *ent;
  e = NULL;
  if (dir) {
    ent = (struct dirent *)readdir(dir);
    if (ent && !strcmp(ent->d_name, ".")) {
      ent = (struct dirent *)readdir(dir);
    }
    if (ent) {
      e = new GDirEntry(path->getCString(), ent->d_name, doStat);
    }
  }
#endif

  return e;
}

void GDir::rewind() {
#ifdef WIN32
  GString *tmp;

  if (hnd)
    FindClose(hnd);
  tmp = path->copy();
  tmp->append("/*.*");
  hnd = FindFirstFile(tmp->getCString(), &ffd);
  delete tmp;
#elif defined(ACORN)
#elif defined(MACOS)
#else
  if (dir)
    rewinddir(dir);
#ifdef VMS
  needParent = strchr(path->getCString(), '[') != NULL;
#endif
#endif
}
//========================================================================
//
// gfile.h
//
// Miscellaneous file and directory name manipulation.
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef GFILE_H
#define GFILE_H

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#if defined(WIN32)
#  include <sys/stat.h>
#  ifdef FPTEX
#    include <win32lib.h>
#  else
#    include <windows.h>
#  endif
#elif defined(ACORN)
#elif defined(MACOS)
#  include <ctime.h>
#else
#  include <unistd.h>
#  include <sys/types.h>
#  ifdef VMS
#    include "vms_dirent.h"
#  elif HAVE_DIRENT_H
#    include <dirent.h>
#    define NAMLEN(d) strlen((d)->d_name)
#  else
#    define dirent direct
#    define NAMLEN(d) (d)->d_namlen
#    if HAVE_SYS_NDIR_H
#      include <sys/ndir.h>
#    endif
#    if HAVE_SYS_DIR_H
#      include <sys/dir.h>
#    endif
#    if HAVE_NDIR_H
#      include <ndir.h>
#    endif
#  endif
#endif
#include "gtypes.h"

class GString;

//------------------------------------------------------------------------

// Get home directory path.
extern GString *getHomeDir();

// Get current directory.
extern GString *getCurrentDir();

// Append a file name to a path string.  <path> may be an empty
// string, denoting the current directory).  Returns <path>.
extern GString *appendToPath(GString *path, char *fileName);

// Grab the path from the front of the file name.  If there is no
// directory component in <fileName>, returns an empty string.
extern GString *grabPath(char *fileName);

// Is this an absolute path or file name?
extern GBool isAbsolutePath(char *path);

// Make this path absolute by prepending current directory (if path is
// relative) or prepending user's directory (if path starts with '~').
extern GString *makePathAbsolute(GString *path);

// Get the modification time for <fileName>.  Returns 0 if there is an
// error.
extern time_t getModTime(char *fileName);

// Create a temporary file and open it for writing.  If <ext> is not
// NULL, it will be used as the file name extension.  Returns both the
// name and the file pointer.  For security reasons, all writing
// should be done to the returned file pointer; the file may be
// reopened later for reading, but not for writing.  The <mode> string
// should be "w" or "wb".  Returns true on success.
extern GBool openTempFile(GString **name, FILE **f, char *mode, char *ext);

// Execute <command>.  Returns true on success.
extern GBool executeCommand(char *cmd);

// Just like fgets, but handles Unix, Mac, and/or DOS end-of-line
// conventions.
extern char *getLine(char *buf, int size, FILE *f);

//------------------------------------------------------------------------
// GDir and GDirEntry
//------------------------------------------------------------------------

class GDirEntry {
public:

  GDirEntry(char *dirPath, char *nameA, GBool doStat);
  ~GDirEntry();
  GString *getName() { return name; }
  GBool isDir() { return dir; }

private:

  GString *name;		// dir/file name
  GBool dir;			// is it a directory?
};

class GDir {
public:

  GDir(char *name, GBool doStatA = gTrue);
  ~GDir();
  GDirEntry *getNextEntry();
  void rewind();

private:

  GString *path;		// directory path
  GBool doStat;			// call stat() for each entry?
#if defined(WIN32)
  WIN32_FIND_DATA ffd;
  HANDLE hnd;
#elif defined(ACORN)
#elif defined(MACOS)
#else
  DIR *dir;			// the DIR structure from opendir()
#ifdef VMS
  GBool needParent;		// need to return an entry for [-]
#endif
#endif
};

#endif
/*
 * gmem.c
 *
 * Memory routines with out-of-memory checking.
 *
 * Copyright 1996-2003 Glyph & Cog, LLC
 */

#include <aconf.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <limits.h>
#include "gmem.h"

#ifdef DEBUG_MEM

typedef struct _GMemHdr {
  unsigned int magic;
  int size;
  int index;
  struct _GMemHdr *next, *prev;
} GMemHdr;

#define gMemHdrSize ((sizeof(GMemHdr) + 7) & ~7)
#define gMemTrlSize (sizeof(long))

#define gMemMagic 0xabcd9999

#if gmemTrlSize==8
#define gMemDeadVal 0xdeadbeefdeadbeefUL
#else
#define gMemDeadVal 0xdeadbeefUL
#endif

/* round data size so trailer will be aligned */
#define gMemDataSize(size) \
  ((((size) + gMemTrlSize - 1) / gMemTrlSize) * gMemTrlSize)

static GMemHdr *gMemHead = NULL;
static GMemHdr *gMemTail = NULL;

static int gMemIndex = 0;
static int gMemAlloc = 0;
static int gMemInUse = 0;

#endif /* DEBUG_MEM */

void *gmalloc(int size) GMEM_EXCEP {
#ifdef DEBUG_MEM
  int size1;
  char *mem;
  GMemHdr *hdr;
  void *data;
  unsigned long *trl, *p;

  if (size <= 0) {
    return NULL;
  }
  size1 = gMemDataSize(size);
  if (!(mem = (char *)malloc(size1 + gMemHdrSize + gMemTrlSize))) {
#if USE_EXCEPTIONS
    throw GMemException();
#else
    fprintf(stderr, "Out of memory\n");
    exit(1);
#endif
  }
  hdr = (GMemHdr *)mem;
  data = (void *)(mem + gMemHdrSize);
  trl = (unsigned long *)(mem + gMemHdrSize + size1);
  hdr->magic = gMemMagic;
  hdr->size = size;
  hdr->index = gMemIndex++;
  if (gMemTail) {
    gMemTail->next = hdr;
    hdr->prev = gMemTail;
    gMemTail = hdr;
  } else {
    hdr->prev = NULL;
    gMemHead = gMemTail = hdr;
  }
  hdr->next = NULL;
  ++gMemAlloc;
  gMemInUse += size;
  for (p = (unsigned long *)data; p <= trl; ++p) {
    *p = gMemDeadVal;
  }
  return data;
#else
  void *p;

  if (size <= 0) {
    return NULL;
  }
  if (!(p = malloc(size))) {
#if USE_EXCEPTIONS
    throw GMemException();
#else
    fprintf(stderr, "Out of memory\n");
    exit(1);
#endif
  }
  return p;
#endif
}

void *grealloc(void *p, int size) GMEM_EXCEP {
#ifdef DEBUG_MEM
  GMemHdr *hdr;
  void *q;
  int oldSize;

  if (size <= 0) {
    if (p) {
      gfree(p);
    }
    return NULL;
  }
  if (p) {
    hdr = (GMemHdr *)((char *)p - gMemHdrSize);
    oldSize = hdr->size;
    q = gmalloc(size);
    memcpy(q, p, size < oldSize ? size : oldSize);
    gfree(p);
  } else {
    q = gmalloc(size);
  }
  return q;
#else
  void *q;

  if (size <= 0) {
    if (p) {
      free(p);
    }
    return NULL;
  }
  if (p) {
    q = realloc(p, size);
  } else {
    q = malloc(size);
  }
  if (!q) {
#if USE_EXCEPTIONS
    throw GMemException();
#else
    fprintf(stderr, "Out of memory\n");
    exit(1);
#endif
  }
  return q;
#endif
}

void *gmallocn(int nObjs, int objSize) GMEM_EXCEP {
  int n;

  if (nObjs == 0) {
    return NULL;
  }
  n = nObjs * objSize;
  if (objSize <= 0 || nObjs < 0 || nObjs >= INT_MAX / objSize) {
#if USE_EXCEPTIONS
    throw GMemException();
#else
    fprintf(stderr, "Bogus memory allocation size\n");
    exit(1);
#endif
  }
  return gmalloc(n);
}

void *greallocn(void *p, int nObjs, int objSize) GMEM_EXCEP {
  int n;

  if (nObjs == 0) {
    if (p) {
      gfree(p);
    }
    return NULL;
  }
  n = nObjs * objSize;
  if (objSize <= 0 || nObjs < 0 || nObjs >= INT_MAX / objSize) {
#if USE_EXCEPTIONS
    throw GMemException();
#else
    fprintf(stderr, "Bogus memory allocation size\n");
    exit(1);
#endif
  }
  return grealloc(p, n);
}

void gfree(void *p) {
#ifdef DEBUG_MEM
  int size;
  GMemHdr *hdr;
  unsigned long *trl, *clr;

  if (p) {
    hdr = (GMemHdr *)((char *)p - gMemHdrSize);
    if (hdr->magic == gMemMagic &&
	((hdr->prev == NULL) == (hdr == gMemHead)) &&
	((hdr->next == NULL) == (hdr == gMemTail))) {
      if (hdr->prev) {
	hdr->prev->next = hdr->next;
      } else {
	gMemHead = hdr->next;
      }
      if (hdr->next) {
	hdr->next->prev = hdr->prev;
      } else {
	gMemTail = hdr->prev;
      }
      --gMemAlloc;
      gMemInUse -= hdr->size;
      size = gMemDataSize(hdr->size);
      trl = (unsigned long *)((char *)hdr + gMemHdrSize + size);
      if (*trl != gMemDeadVal) {
	fprintf(stderr, "Overwrite past end of block %d at address %p\n",
		hdr->index, p);
      }
      for (clr = (unsigned long *)hdr; clr <= trl; ++clr) {
	*clr = gMemDeadVal;
      }
      free(hdr);
    } else {
      fprintf(stderr, "Attempted to free bad address %p\n", p);
    }
  }
#else
  if (p) {
    free(p);
  }
#endif
}

#ifdef DEBUG_MEM
void gMemReport(FILE *f) {
  GMemHdr *p;

  fprintf(f, "%d memory allocations in all\n", gMemIndex);
  if (gMemAlloc > 0) {
    fprintf(f, "%d memory blocks left allocated:\n", gMemAlloc);
    fprintf(f, " index     size\n");
    fprintf(f, "-------- --------\n");
    for (p = gMemHead; p; p = p->next) {
      fprintf(f, "%8d %8d\n", p->index, p->size);
    }
  } else {
    fprintf(f, "No memory blocks left allocated\n");
  }
}
#endif

char *copyString(char *s) {
  char *s1;

  s1 = (char *)gmalloc(strlen(s) + 1);
  strcpy(s1, s);
  return s1;
}
/*
 * gmem.h
 *
 * Memory routines with out-of-memory checking.
 *
 * Copyright 1996-2003 Glyph & Cog, LLC
 */

#ifndef GMEM_H
#define GMEM_H

#include <stdio.h>
#include <aconf.h>

#if USE_EXCEPTIONS

class GMemException {
public:
  GMemException() {}
  ~GMemException() {}
};

#define GMEM_EXCEP throw(GMemException)

#else // USE_EXCEPTIONS

#define GMEM_EXCEP

#endif // USE_EXCEPTIONS

#ifdef __cplusplus
extern "C" {
#endif

/*
 * Same as malloc, but prints error message and exits if malloc()
 * returns NULL.
 */
extern void *gmalloc(int size) GMEM_EXCEP;

/*
 * Same as realloc, but prints error message and exits if realloc()
 * returns NULL.  If <p> is NULL, calls malloc instead of realloc().
 */
extern void *grealloc(void *p, int size) GMEM_EXCEP;

/*
 * These are similar to gmalloc and grealloc, but take an object count
 * and size.  The result is similar to allocating nObjs * objSize
 * bytes, but there is an additional error check that the total size
 * doesn't overflow an int.
 */
extern void *gmallocn(int nObjs, int objSize) GMEM_EXCEP;
extern void *greallocn(void *p, int nObjs, int objSize) GMEM_EXCEP;

/*
 * Same as free, but checks for and ignores NULL pointers.
 */
extern void gfree(void *p);

#ifdef DEBUG_MEM
/*
 * Report on unfreed memory.
 */
extern void gMemReport(FILE *f);
#else
#define gMemReport(f)
#endif

/*
 * Allocate memory and copy a string into it.
 */
extern char *copyString(char *s);

#ifdef __cplusplus
}
#endif

#endif
//========================================================================
//
// gmempp.cc
//
// Use gmalloc/gfree for C++ new/delete operators.
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>
#include "gmem.h"

#ifdef DEBUG_MEM

void *operator new(size_t size) {
  return gmalloc((int)size);
}

void *operator new[](size_t size) {
  return gmalloc((int)size);
}

void operator delete(void *p) {
  gfree(p);
}

void operator delete[](void *p) {
  gfree(p);
}

#endif
/*
 * gtypes.h
 *
 * Some useful simple types.
 *
 * Copyright 1996-2003 Glyph & Cog, LLC
 */

#ifndef GTYPES_H
#define GTYPES_H

/*
 * These have stupid names to avoid conflicts with some (but not all)
 * C++ compilers which define them.
 */
typedef int GBool;
#define gTrue 1
#define gFalse 0

/*
 * These have stupid names to avoid conflicts with <sys/types.h>,
 * which on various systems defines some random subset of these.
 */
typedef unsigned char Guchar;
typedef unsigned short Gushort;
typedef unsigned int Guint;
typedef unsigned long Gulong;

#endif
/*
 * parseargs.h
 *
 * Command line argument parser.
 *
 * Copyright 1996-2003 Glyph & Cog, LLC
 */

#include <stdio.h>
#include <stddef.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "parseargs.h"

static ArgDesc *findArg(ArgDesc *args, char *arg);
static GBool grabArg(ArgDesc *arg, int i, int *argc, char *argv[]);

GBool parseArgs(ArgDesc *args, int *argc, char *argv[]) {
  ArgDesc *arg;
  int i, j;
  GBool ok;

  ok = gTrue;
  i = 1;
  while (i < *argc) {
    if (!strcmp(argv[i], "--")) {
      --*argc;
      for (j = i; j < *argc; ++j)
	argv[j] = argv[j+1];
      break;
    } else if ((arg = findArg(args, argv[i]))) {
      if (!grabArg(arg, i, argc, argv))
	ok = gFalse;
    } else {
      ++i;
    }
  }
  return ok;
}

void printUsage(char *program, char *otherArgs, ArgDesc *args) {
  ArgDesc *arg;
  char *typ;
  int w, w1;

  w = 0;
  for (arg = args; arg->arg; ++arg) {
    if ((w1 = strlen(arg->arg)) > w)
      w = w1;
  }

  fprintf(stderr, "Usage: %s [options]", program);
  if (otherArgs)
    fprintf(stderr, " %s", otherArgs);
  fprintf(stderr, "\n");

  for (arg = args; arg->arg; ++arg) {
    fprintf(stderr, "  %s", arg->arg);
    w1 = 9 + w - strlen(arg->arg);
    switch (arg->kind) {
    case argInt:
    case argIntDummy:
      typ = " <int>";
      break;
    case argFP:
    case argFPDummy:
      typ = " <fp>";
      break;
    case argString:
    case argStringDummy:
      typ = " <string>";
      break;
    case argFlag:
    case argFlagDummy:
    default:
      typ = "";
      break;
    }
    fprintf(stderr, "%-*s", w1, typ);
    if (arg->usage)
      fprintf(stderr, ": %s", arg->usage);
    fprintf(stderr, "\n");
  }
}

static ArgDesc *findArg(ArgDesc *args, char *arg) {
  ArgDesc *p;

  for (p = args; p->arg; ++p) {
    if (p->kind < argFlagDummy && !strcmp(p->arg, arg))
      return p;
  }
  return NULL;
}

static GBool grabArg(ArgDesc *arg, int i, int *argc, char *argv[]) {
  int n;
  int j;
  GBool ok;

  ok = gTrue;
  n = 0;
  switch (arg->kind) {
  case argFlag:
    *(GBool *)arg->val = gTrue;
    n = 1;
    break;
  case argInt:
    if (i + 1 < *argc && isInt(argv[i+1])) {
      *(int *)arg->val = atoi(argv[i+1]);
      n = 2;
    } else {
      ok = gFalse;
      n = 1;
    }
    break;
  case argFP:
    if (i + 1 < *argc && isFP(argv[i+1])) {
      *(double *)arg->val = atof(argv[i+1]);
      n = 2;
    } else {
      ok = gFalse;
      n = 1;
    }
    break;
  case argString:
    if (i + 1 < *argc) {
      strncpy((char *)arg->val, argv[i+1], arg->size - 1);
      ((char *)arg->val)[arg->size - 1] = '\0';
      n = 2;
    } else {
      ok = gFalse;
      n = 1;
    }
    break;
  default:
    fprintf(stderr, "Internal error in arg table\n");
    n = 1;
    break;
  }
  if (n > 0) {
    *argc -= n;
    for (j = i; j < *argc; ++j)
      argv[j] = argv[j+n];
  }
  return ok;
}

GBool isInt(char *s) {
  if (*s == '-' || *s == '+')
    ++s;
  while (isdigit(*s & 0xff))
    ++s;
  if (*s)
    return gFalse;
  return gTrue;
}

GBool isFP(char *s) {
  int n;

  if (*s == '-' || *s == '+')
    ++s;
  n = 0;
  while (isdigit(*s & 0xff)) {
    ++s;
    ++n;
  }
  if (*s == '.')
    ++s;
  while (isdigit(*s & 0xff)) {
    ++s;
    ++n;
  }
  if (n > 0 && (*s == 'e' || *s == 'E')) {
    ++s;
    if (*s == '-' || *s == '+')
      ++s;
    n = 0;
    if (!isdigit(*s & 0xff))
      return gFalse;
    do {
      ++s;
    } while (isdigit(*s & 0xff));
  }
  if (*s)
    return gFalse;
  return gTrue;
}
/*
 * parseargs.h
 *
 * Command line argument parser.
 *
 * Copyright 1996-2003 Glyph & Cog, LLC
 */

#ifndef PARSEARGS_H
#define PARSEARGS_H

#ifdef __cplusplus
extern "C" {
#endif

#include "gtypes.h"

/*
 * Argument kinds.
 */
typedef enum {
  argFlag,			/* flag (present / not-present) */
				/*   [val: GBool *]             */
  argInt,			/* integer arg    */
				/*   [val: int *] */
  argFP,			/* floating point arg */
				/*   [val: double *]  */
  argString,			/* string arg      */
				/*   [val: char *] */
  /* dummy entries -- these show up in the usage listing only; */
  /* useful for X args, for example                            */
  argFlagDummy,
  argIntDummy,
  argFPDummy,
  argStringDummy
} ArgKind;

/*
 * Argument descriptor.
 */
typedef struct {
  char *arg;			/* the command line switch */
  ArgKind kind;			/* kind of arg */
  void *val;			/* place to store value */
  int size;			/* for argString: size of string */
  char *usage;			/* usage string */
} ArgDesc;

/*
 * Parse command line.  Removes all args which are found in the arg
 * descriptor list <args>.  Stops parsing if "--" is found (and removes
 * it).  Returns gFalse if there was an error.
 */
extern GBool parseArgs(ArgDesc *args, int *argc, char *argv[]);

/*
 * Print usage message, based on arg descriptor list.
 */
extern void printUsage(char *program, char *otherArgs, ArgDesc *args);

/*
 * Check if a string is a valid integer or floating point number.
 */
extern GBool isInt(char *s);
extern GBool isFP(char *s);

#ifdef __cplusplus
}
#endif

#endif
/*
 *	DIRECTORY.C - VMS emulation routines for UNIX Directory
 *	              callable routines
 *
 *	Author:		Patrick L. Mahan
 *	Location:	TGV, Inc
 *	Date:		19-November-1991
 *
 *	Purpose:	Provides emulation of the BSD directory routines
 *			which are used by some of the X11 R4 release
 *			software.
 *
 *	Side effects:	This is only a partial emulation.  Not all of
 *			the required information is passed to the user.
 *
 *	Modification History
 *
 *	Date        | Who	| Version	| History
 *	------------+-----------+---------------+----------------------------
 *	19-Nov-1991 | PLM	| 1.0		| First Write
 *	20-Apr-1992 | PLM	| 1.1		| Added validation check for
 *	            |		|		| for the directory
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <rmsdef.h>
#include <descrip.h>
#include <lib$routines.h>
#include "vms_dirent.h"

#define	NOWILD		0x00000001
#define MULTIPLE	0x00000002

static unsigned long context = 0;

static struct dsc$descriptor_s *create_descriptor ( name )
char *name;
{
   struct dsc$descriptor_s *retdescrip;

   retdescrip = (struct dsc$descriptor_s *)calloc(1, sizeof(struct dsc$descriptor_s));

   if (retdescrip == NULL) return ((struct dsc$descriptor_s *)NULL);

   retdescrip->dsc$b_dtype	= DSC$K_DTYPE_T;
   retdescrip->dsc$b_class	= DSC$K_CLASS_S;
   retdescrip->dsc$w_length	= strlen(name);
   retdescrip->dsc$a_pointer	= name;

   return (retdescrip);
}

static int Check_Directory( dirname )
char *dirname;
{
	static char *tmpdir, *cp;
	FILE *tfp;
	int status;

	status = 1;

	tmpdir = calloc(strlen(dirname)+15,sizeof(char));

	strcpy(tmpdir, dirname);

	cp = strrchr(tmpdir, '.');

	if (cp != NULL) {
		*cp = ']';
		cp = strrchr(tmpdir, ']');
		*cp = '.';
		strcat(tmpdir, "dir");
	}
	else {
		char *tmp1;
		tmp1 = calloc(strlen(dirname)+1,sizeof(char));
		cp = strchr(tmpdir, '[');
		cp++;
		strcpy(tmp1, cp);
		cp = strrchr(tmp1, ']');
		*cp = '\0';
		cp = strchr(tmpdir, '[');
		cp++;
		*cp = '\0';
		strcat(tmpdir, "000000]");
		strcat(tmpdir, tmp1);
		strcat(tmpdir, ".dir");
	}

	tfp = fopen(tmpdir, "r");

	if (tfp == NULL) status = 0;

	fclose(tfp);

	return (status);
}

DIR *opendir( dirname )
char *dirname;
{
   DIR *retdir;
   struct dsc$descriptor_s filedescriptor;
   char *filepathname;

   retdir = (DIR *) calloc(1, sizeof(DIR));

   if (retdir == NULL) return ((DIR *)NULL);

   if (!Check_Directory(dirname)) return ((DIR *)NULL);

   filepathname = (char *)calloc(256, sizeof(char));

   strcpy(filepathname, dirname);
   strcat(filepathname, "*.*.*");

   retdir->dd_fd = (unsigned long) create_descriptor(filepathname);
   retdir->dd_loc = 0;
   retdir->dd_size = strlen(filepathname);
   retdir->dd_bsize = 0;
   retdir->dd_off = 0;
   retdir->dd_buf = filepathname;

   return (retdir);
}

struct dirent *readdir( dirp )
DIR *dirp;
{
   static struct dirent *retdirent;
   struct dsc$descriptor_s retfilenamedesc;
   struct dsc$descriptor_s searchpathdesc = *((struct dsc$descriptor_s *)dirp->dd_fd);
   char retfilename[256];
   char *sp;
   unsigned long istatus;
   unsigned long rms_status;
   unsigned long flags;

   retdirent = (struct dirent *)NULL;

   flags = MULTIPLE;

   retfilenamedesc.dsc$b_dtype	= DSC$K_DTYPE_T;
   retfilenamedesc.dsc$b_class	= DSC$K_CLASS_S;
   retfilenamedesc.dsc$w_length	= 255;
   retfilenamedesc.dsc$a_pointer= retfilename;

   istatus = lib$find_file (&searchpathdesc,
                            &retfilenamedesc,
                            &dirp->dd_loc,
                            0, 0,
                            &rms_status,
                            &flags);

   if (!(istatus & 1) && (istatus != RMS$_NMF) && (istatus != RMS$_FNF))
   {
      lib$signal (istatus);
      return (retdirent);
   }
   else if ((istatus == RMS$_NMF) || (istatus == RMS$_FNF))
      return (retdirent);

   retfilename[retfilenamedesc.dsc$w_length] = '\0';

   sp = strchr(retfilename, ' ');
   if (sp != NULL) *sp = '\0';

   sp = strrchr(retfilename, ']');
   if (sp != NULL)
      sp++;
   else
      sp = retfilename;

   retdirent = (struct dirent *)calloc(1, sizeof(struct dirent));

   strcpy(retdirent->d_name, sp);
   retdirent->d_namlen = strlen(sp);
   retdirent->d_fileno = 0;
   retdirent->d_off = 0;
   retdirent->d_reclen = DIRSIZ(retdirent);

   return (retdirent);
}

long telldir( dirp )
DIR *dirp;
{
   return(0);
}

void seekdir( dirp, loc )
DIR *dirp;
int loc;
{
   return;
}

void rewinddir( dirp )
DIR *dirp;
{
   lib$find_file_end (&dirp->dd_loc);
}

void closedir( dirp )
DIR *dirp;
{
   lib$find_file_end (&dirp->dd_loc);

   cfree ((void *) dirp->dd_fd);
   cfree (dirp->dd_buf);
   cfree (dirp);
}
/*	@(#)dirent.h 1.7 89/06/25 SMI 	*/

/*
 * Filesystem-independent directory information.
 */

#ifndef	__dirent_h
#define	__dirent_h

#ifdef __cplusplus
extern "C" {
#endif

/* Make sure we don't get the V7 RTL dirent functions. These are broken. */

#ifndef __CRTL_VER
#   define __CRTL_VER __VMS_VER
#endif
#if __CRTL_VER >= 70000000
#include <dirent.h>
#endif

#include <types.h>

#define opendir   goo_opendir
#define readdir   goo_readdir
#define closedir  goo_closedir
#define seekdir   goo_seekdir
#define telldir   goo_telldir
#define rewinddir goo_rewindir
#define DIR       GOO_DIR

#ifndef	_POSIX_SOURCE
#define	d_ino	d_fileno	/* compatability */
#ifndef	NULL
#define	NULL	0
#endif
#endif	/* !_POSIX_SOURCE */

/*
 * Definitions for library routines operating on directories.
 */
typedef	struct __dirdesc {
	unsigned long dd_fd;	/* file descriptor */
	long	dd_loc;		/* buf offset of entry from last readddir() */
	long	dd_size;	/* amount of valid data in buffer */
	long	dd_bsize;	/* amount of entries read at a time */
	long	dd_off;		/* Current offset in dir (for telldir) */
	char	*dd_buf;	/* directory data buffer */
} DIR;

#include "vms_sys_dirent.h"

extern	DIR *opendir(char *dirname);
extern	struct dirent *readdir(DIR *dirp);
extern	void closedir(DIR *dirp);
#ifndef	_POSIX_SOURCE
extern	void seekdir(DIR *dirp, int loc);
extern	long telldir(DIR *dirp);
#endif	/* POSIX_SOURCE */
extern	void rewinddir(DIR *dirp);

#ifdef __cplusplus
}
#endif

#endif	/* !__dirent_h */
$!========================================================================
$!
$! Goo library compile script for VMS.
$!
$! Written by Patrick Moreau, Martin P.J. Zinser.
$!
$! Copyright 1996-2003 Glyph & Cog, LLC
$!
$!========================================================================
$!
$ GOO_CXXOBJS = "GString.obj,gmempp.obj,gfile.obj,ghash.obj,glist.obj"
$ GOO_CCOBJS  = "gmem.obj,parseargs.obj,vms_directory.obj,vms_unix_times.obj"
$!
$ if f$extract(1,3,f$getsyi("Version")) .lts. "7.0"
$  then
$   GOO_CCOBJS = GOO_CCOBJS + ",vms_unlink.obj"
$ endif
$!
$ i = 0
$ j = 0
$COMPILE_CXX_LOOP:
$ file = f$element(i, ",",GOO_CXXOBJS)
$ if file .eqs. "," then goto COMPILE_CC_LOOP
$ i = i + 1
$ name = f$parse(file,,,"NAME")
$ call make 'file "CXXCOMP ''name'.cc" - 
       'name'.cc
$ goto COMPILE_CXX_LOOP
$!
$COMPILE_CC_LOOP:
$ file = f$element(j, ",",GOO_CCOBJS)
$ if file .eqs. "," then goto COMPILE_END
$ j = j + 1
$ name = f$parse(file,,,"NAME")
$ call make 'file "CCOMP ''name'.c" - 
       'name'.c
$ goto COMPILE_CC_LOOP
$!
$COMPILE_END:
$ call make libgoo.olb "lib/cre libgoo.olb ''GOO_CXXOBJS',''GOO_CCOBJS'" *.obj
$!
$ exit
$!
$MAKE: SUBROUTINE   !SUBROUTINE TO CHECK DEPENDENCIES
$ V = 'F$Verify(0)
$! P1 = What we are trying to make
$! P2 = Command to make it
$! P3 - P8  What it depends on
$
$ If F$Search(P1) .Eqs. "" Then Goto Makeit
$ Time = F$CvTime(F$File(P1,"RDT"))
$arg=3
$Loop:
$       Argument = P'arg
$       If Argument .Eqs. "" Then Goto Exit
$       El=0
$Loop2:
$       File = F$Element(El," ",Argument)
$       If File .Eqs. " " Then Goto Endl
$       AFile = ""
$Loop3:
$       OFile = AFile
$       AFile = F$Search(File)
$       If AFile .Eqs. "" .Or. AFile .Eqs. OFile Then Goto NextEl
$       If F$CvTime(F$File(AFile,"RDT")) .Ges. Time Then Goto Makeit
$       Goto Loop3
$NextEL:
$       El = El + 1
$       Goto Loop2
$EndL:
$ arg=arg+1
$ If arg .Le. 8 Then Goto Loop
$ Goto Exit
$
$Makeit:
$ VV=F$VERIFY(0)
$ write sys$output P2
$ 'P2
$ VV='F$Verify(VV)
$Exit:
$ If V Then Set Verify
$ENDSUBROUTINE
/*	@(#)dirent.h 1.4 89/06/16 SMI 	*/

/*
 * Filesystem-independent directory information.
 * Directory entry structures are of variable length.
 * Each directory entry is a struct dirent containing its file number, the
 * offset of the next entry (a cookie interpretable only the filesystem
 * type that generated it), the length of the entry, and the length of the
 * name contained in the entry.  These are followed by the name. The
 * entire entry is padded with null bytes to a 4 byte boundary. All names
 * are guaranteed null terminated. The maximum length of a name in a
 * directory is MAXNAMLEN, plus a null byte.
 */

#ifndef	__sys_dirent_h
#define	__sys_dirent_h

#ifdef __cplusplus
extern "C" {
#endif

#define dirent GOO_dirent

struct	dirent {
	long		d_off;		/* offset of next disk dir entry */
	unsigned long	d_fileno;	/* file number of entry */
	unsigned short	d_reclen;	/* length of this record */
	unsigned short	d_namlen;	/* length of string in d_name */
	char		d_name[255+1];	/* name (up to MAXNAMLEN + 1) */
};

#ifndef	_POSIX_SOURCE
/*
 * It's unlikely to change, but make sure that sizeof d_name above is
 * at least MAXNAMLEN + 1 (more may be added for padding).
 */
#define	MAXNAMLEN	255
/*
 * The macro DIRSIZ(dp) gives the minimum amount of space required to represent
 * a directory entry.  For any directory entry dp->d_reclen >= DIRSIZ(dp).
 * Specific filesystem types may use this macro to construct the value
 * for d_reclen.
 */
#undef	DIRSIZ
#define	DIRSIZ(dp) \
	(((sizeof(struct dirent) - (MAXNAMLEN+1) + ((dp)->d_namlen+1)) +3) & ~3)

#endif	/* !_POSIX_SOURCE */

#ifdef __cplusplus
}
#endif

#endif	/* !__sys_dirent_h */
/*	@(#)time.h 2.9 87/01/17 SMI; from UCB 7.1 6/4/86	*/

/*
	Definitions of various structures used on UNIX for
	time-related syscalls.
*/

/*
 * Copyright (c) 1982, 1986 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef _UNIX_TIME_
#define _UNIX_TIME_

#ifdef __cplusplus
extern "C" {
#endif

/*
 * Structure returned by gettimeofday(2) system call,
 * and used in other calls.
 */
#ifndef __DECC
struct timeval
{
	long	tv_sec;		/* seconds */
	long	tv_usec;	/* and microseconds */
};
#else
#if (__DECC_VER < 50200000) && (__VMS_VER < 70000000)
struct timeval
{
	long	tv_sec;		/* seconds */
	long	tv_usec;	/* and microseconds */
};
#endif /* __DECC_VER */
#endif /* __DECC */
struct timezone
{
	int	tz_minuteswest;	/* minutes west of Greenwich */
	int	tz_dsttime;	/* type of dst correction */
};

#define	DST_NONE	0	/* not on dst */
#define	DST_USA		1	/* USA style dst */
#define	DST_AUST	2	/* Australian style dst */
#define	DST_WET		3	/* Western European dst */
#define	DST_MET		4	/* Middle European dst */
#define	DST_EET		5	/* Eastern European dst */
#define	DST_CAN		6	/* Canada */
#define	DST_GB		7	/* Great Britain and Eire */
#define	DST_RUM		8	/* Rumania */
#define	DST_TUR		9	/* Turkey */
#define	DST_AUSTALT	10	/* Australian style with shift in 1986 */

/*
 * Operations on timevals.
 *
 * NB: timercmp does not work for >= or <=.
 */
#define	timerisset(tvp)		((tvp)->tv_sec || (tvp)->tv_usec)
#define	timercmp(tvp, uvp, cmp)	\
	((tvp)->tv_sec cmp (uvp)->tv_sec || \
	 (tvp)->tv_sec == (uvp)->tv_sec && (tvp)->tv_usec cmp (uvp)->tv_usec)
#define	timerclear(tvp)		(tvp)->tv_sec = (tvp)->tv_usec = 0

/*
 * Names of the interval timers, and structure
 * defining a timer setting.
 */
#define	ITIMER_REAL	0
#define	ITIMER_VIRTUAL	1
#define	ITIMER_PROF	2

#ifndef __DECC
struct	itimerval
{
	struct	timeval it_interval;	/* timer interval */
	struct	timeval it_value;	/* current value */
};
#else
#if (__DECC_VER < 50200000) && (__VMS_VER < 70000000)
struct	itimerval
{
	struct	timeval it_interval;	/* timer interval */
	struct	timeval it_value;	/* current value */
};
#endif /* __DECC_VER */
#endif /* __DECC */

#ifndef KERNEL
#include <time.h>
#endif

#ifdef __cplusplus
}
#endif

#endif /*!_UNIX_TIME_*/

/*
 *	UNIX-style Time Functions
 *
 */
#include <stdio.h>
#include <signal.h>
#include <time.h>
#include "vms_unix_time.h"

/*
 *	gettimeofday(2) - Returns the current time
 *
 *	NOTE: The timezone portion is useless on VMS.
 *	Even on UNIX, it is only provided for backwards
 *	compatibilty and is not guaranteed to be correct.
 */

#if (__VMS_VER < 70000000)
int gettimeofday(tv, tz)
struct timeval  *tv;
struct timezone *tz;
{
    timeb_t tmp_time;

    ftime(&tmp_time);

    if (tv != NULL)
    {
	tv->tv_sec  = tmp_time.time;
	tv->tv_usec = tmp_time.millitm * 1000;
    }

    if (tz != NULL)
    {
	tz->tz_minuteswest = tmp_time.timezone;
	tz->tz_dsttime = tmp_time.dstflag;
    }

    return (0);

} /*** End gettimeofday() ***/
#endif
/*
 * vms_unlink.c
 *
 * A UNIX-style unlink() function for VMS.
 *
 * Thanks to Patrick Moreau (pmoreau@cena.dgac.fr).
 */

#include <descrip.h>
#include <string.h>
#include <lib$routines.h>

int unlink(char *filename) {
  static struct dsc$descriptor_s file_desc;

  file_desc.dsc$w_length = strlen(filename);
  file_desc.dsc$b_dtype  = DSC$K_DTYPE_T;
  file_desc.dsc$b_class  = DSC$K_CLASS_S;
  file_desc.dsc$a_pointer= filename;

  return (lib$delete_file(&file_desc));
}
set CC=cl
set FPFLAGS=/I..\freetype\include\freetype.win32 /I..\freetype.win32\include\ /I..\freetype.win32\lib
set CFLAGS=/DWIN32 %FPFLAGS% /I.. /I..\splash /I..\goo /I..\fofi /I..\xpdf /O2 /nologo

set CXX=cl

set CXXFLAGS=%CFLAGS% /TP

set LIBPROG=lib
set FTLIBS=-lfreetype


copy aconf-win32.h aconf.h



cd goo

%CXX% %CXXFLAGS% /c GHash.cc

%CXX% %CXXFLAGS% /c GList.cc

%CXX% %CXXFLAGS% /c GString.cc

%CXX% %CXXFLAGS% /c gmempp.cc

%CXX% %CXXFLAGS% /c gfile.cc

%CC% %CFLAGS% /c gmem.cc

%CC% %CFLAGS% /c parseargs.c

%LIBPROG% /nologo /out:libGoo.lib GHash.obj GList.obj GString.obj gmempp.obj gfile.obj gmem.obj parseargs.obj



cd ..\splash

%CXX% %CXXFLAGS% /c Splash.cc 

%CXX% %CXXFLAGS% /c SplashBitmap.cc 

%CXX% %CXXFLAGS% /c SplashClip.cc 

%CXX% %CXXFLAGS% /c SplashFTFont.cc 

%CXX% %CXXFLAGS% /c SplashFTFontEngine.cc 

%CXX% %CXXFLAGS% /c SplashFTFontFile.cc 

%CXX% %CXXFLAGS% /c SplashFont.cc 

%CXX% %CXXFLAGS% /c SplashFontEngine.cc 

%CXX% %CXXFLAGS% /c SplashFontFile.cc 

%CXX% %CXXFLAGS% /c SplashFontFileID.cc 

%CXX% %CXXFLAGS% /c SplashPath.cc 

%CXX% %CXXFLAGS% /c SplashPattern.cc 

%CXX% %CXXFLAGS% /c SplashScreen.cc 

%CXX% %CXXFLAGS% /c SplashState.cc 

%CXX% %CXXFLAGS% /c SplashT1Font.cc 

%CXX% %CXXFLAGS% /c SplashT1FontEngine.cc 

%CXX% %CXXFLAGS% /c SplashT1FontFile.cc 

%CXX% %CXXFLAGS% /c SplashXPath.cc 

%CXX% %CXXFLAGS% /c SplashXPathScanner.cc

%LIBPROG% /nologo /out:libsplash.lib Splash.obj SplashBitmap.obj SplashClip.obj SplashFTFont.obj SplashFTFontEngine.obj SplashFTFontFile.obj SplashFont.obj SplashFontEngine.obj SplashFontFile.obj SplashFontFileID.obj SplashPath.obj SplashPattern.obj SplashScreen.obj SplashState.obj SplashT1Font.obj SplashT1FontEngine.obj SplashT1FontFile.obj SplashXPath.obj SplashXPathScanner.obj



cd ..\fofi

%CXX% %CXXFLAGS% /c FoFiBase.cc

%CXX% %CXXFLAGS% /c FoFiEncodings.cc

%CXX% %CXXFLAGS% /c FoFiTrueType.cc 

%CXX% %CXXFLAGS% /c FoFiType1.cc

%CXX% %CXXFLAGS% /c FoFiType1C.cc

%LIBPROG% /nologo /out:libfofi.lib FoFiBase.obj FoFiEncodings.obj FoFiTrueType.obj FoFiType1.obj FoFiType1C.obj



cd ..\xpdf

%CXX% %CXXFLAGS% /c Annot.cc

%CXX% %CXXFLAGS% /c Array.cc

%CXX% %CXXFLAGS% /c BuiltinFont.cc

%CXX% %CXXFLAGS% /c BuiltinFontTables.cc

%CXX% %CXXFLAGS% /c CMap.cc

%CXX% %CXXFLAGS% /c Catalog.cc

%CXX% %CXXFLAGS% /c CharCodeToUnicode.cc

%CXX% %CXXFLAGS% /c Decrypt.cc

%CXX% %CXXFLAGS% /c Dict.cc

%CXX% %CXXFLAGS% /c Error.cc

%CXX% %CXXFLAGS% /c FontEncodingTables.cc

%CXX% %CXXFLAGS% /c Function.cc

%CXX% %CXXFLAGS% /c Gfx.cc

%CXX% %CXXFLAGS% /c GfxFont.cc

%CXX% %CXXFLAGS% /c GfxState.cc

%CXX% %CXXFLAGS% /c GlobalParams.cc

%CXX% %CXXFLAGS% /c JArithmeticDecoder.cc

%CXX% %CXXFLAGS% /c JBIG2Stream.cc

%CXX% %CXXFLAGS% /c JPXStream.cc

%CXX% %CXXFLAGS% /c Lexer.cc

%CXX% %CXXFLAGS% /c Link.cc

%CXX% %CXXFLAGS% /c PSTokenizer.cc

%CXX% %CXXFLAGS% /c NameToCharCode.cc

%CXX% %CXXFLAGS% /c Object.cc

%CXX% %CXXFLAGS% /c Outline.cc

%CXX% %CXXFLAGS% /c OutputDev.cc

%CXX% %CXXFLAGS% /c PDFDoc.cc

%CXX% %CXXFLAGS% /c PDFDocEncoding.cc

%CXX% %CXXFLAGS% /c PSOutputDev.cc

%CXX% %CXXFLAGS% /c PSTokenizer.cc

%CXX% %CXXFLAGS% /c Page.cc

%CXX% %CXXFLAGS% /c Parser.cc

%CXX% %CXXFLAGS% /c PreScanOutputDev.cc

%CXX% %CXXFLAGS% /c Stream.cc

%CXX% %CXXFLAGS% /c SplashOutputDev.cc

%CXX% %CXXFLAGS% /c UnicodeMap.cc

%CXX% %CXXFLAGS% /c SecurityHandler.cc

%CXX% %CXXFLAGS% /c UnicodeTypeTable.cc

%CXX% %CXXFLAGS% /c XRef.cc



%LIBPROG% /nologo /out:libxpdf.lib Annot.obj Array.obj BuiltinFont.obj BuiltinFontTables.obj Catalog.obj CharCodeToUnicode.obj CMap.obj Decrypt.obj Dict.obj Error.obj FontEncodingTables.obj Function.obj Gfx.obj GfxFont.obj GfxState.obj GlobalParams.obj JArithmeticDecoder.obj JBIG2Stream.obj JPXStream.obj Lexer.obj Link.obj NameToCharCode.obj Object.obj Outline.obj OutputDev.obj Page.obj Parser.obj PDFDoc.obj PDFDocEncoding.obj PreScanOutputDev.obj PSOutputDev.obj PSTokenizer.obj Stream.obj SplashOutputDev.obj UnicodeMap.obj SecurityHandler.obj UnicodeTypeTable.obj XRef.obj 



cd ..\src

%CXX% %CXXFLAGS% /c XmlFonts.cc

%CXX% %CXXFLAGS% /c XmlLinks.cc

%CXX% %CXXFLAGS% /c ImgOutPutDev.cc

%CXX% %CXXFLAGS% /c pdf2json.cc



%CXX% %FPFLAGS% /nologo /Fepdf2json.exe XmlFonts.obj XmlLinks.obj ImgOutPutDev.obj pdf2json.obj ..\goo\libGoo.lib ..\xpdf\libxpdf.lib ..\fofi\libfofi.lib ..\splash\libsplash.lib advapi32.lib Gdi32.lib User32.lib ..\freetype.win32\lib\freetype_a.lib



cd ..
#========================================================================
#
# Splash library Makefile
#
# Copyright 2003 Glyph & Cog, LLC
#
#========================================================================

SHELL = /bin/sh

srcdir = @srcdir@
VPATH = @srcdir@

GOOSRCDIR = $(srcdir)/../goo
GOOLIBDIR = ../goo
FOFISRCDIR = $(srcdir)/../fofi
FOFILIBDIR = ../fofi

CXXFLAGS = -I/usr/local/include @CXXFLAGS@ @DEFS@ -I.. -I$(GOOSRCDIR) -I$(FOFISRCDIR) -I$(srcdir)

AR = ar rc
RANLIB = ranlib

LIBPREFIX = lib

#------------------------------------------------------------------------

.SUFFIXES: .cc

.cc.o:
	$(CXX) $(CXXFLAGS) -c $<
	
#------------------------------------------------------------------------

CXX_SRC = \
	$(srcdir)/Splash.cc \
	$(srcdir)/SplashBitmap.cc \
	$(srcdir)/SplashClip.cc \
	$(srcdir)/SplashFTFont.cc \
	$(srcdir)/SplashFTFontEngine.cc \
	$(srcdir)/SplashFTFontFile.cc \
	$(srcdir)/SplashFont.cc \
	$(srcdir)/SplashFontEngine.cc \
	$(srcdir)/SplashFontFile.cc \
	$(srcdir)/SplashFontFileID.cc \
	$(srcdir)/SplashPath.cc \
	$(srcdir)/SplashPattern.cc \
	$(srcdir)/SplashScreen.cc \
	$(srcdir)/SplashState.cc \
	$(srcdir)/SplashT1Font.cc \
	$(srcdir)/SplashT1FontEngine.cc \
	$(srcdir)/SplashT1FontFile.cc \
	$(srcdir)/SplashXPath.cc \
	$(srcdir)/SplashXPathScanner.cc

#------------------------------------------------------------------------

all: $(LIBPREFIX)splash.a

#------------------------------------------------------------------------

SPLASH_OBJS = \
	Splash.o \
	SplashBitmap.o \
	SplashClip.o \
	SplashFTFont.o \
	SplashFTFontEngine.o \
	SplashFTFontFile.o \
	SplashFont.o \
	SplashFontEngine.o \
	SplashFontFile.o \
	SplashFontFileID.o \
	SplashPath.o \
	SplashPattern.o \
	SplashScreen.o \
	SplashState.o \
	SplashT1Font.o \
	SplashT1FontEngine.o \
	SplashT1FontFile.o \
	SplashXPath.o \
	SplashXPathScanner.o

$(LIBPREFIX)splash.a: $(SPLASH_OBJS)
	rm -f $(LIBPREFIX)splash.a
	$(AR) $(LIBPREFIX)splash.a $(SPLASH_OBJS)
	$(RANLIB) $(LIBPREFIX)splash.a

#------------------------------------------------------------------------
SPLASH_WINOBJS = libsplash.lib SplashBitmap.obj SplashFontFile.obj SplashFTFont.obj SplashScreen.obj SplashT1Font.obj \
SplashClip.obj SplashFont.obj Splash.obj SplashState.obj SplashXPath.obj \
SplashFontEngine.obj  SplashFTFontEngine.obj  SplashPath.obj     SplashT1FontEngine.obj  SplashXPathScanner.obj \
SplashFontFileID.obj  SplashFTFontFile.obj    SplashPattern.obj  SplashT1FontFile.obj
 
clean:
	rm -f $(SPLASH_OBJS) $(LIBPREFIX)splash.a $(SPLASH_WINOBJS)

#------------------------------------------------------------------------

depend:
	$(CXX) $(CXXFLAGS) -MM $(CXX_SRC) >Makefile.dep

include Makefile.dep
//========================================================================
//
// Splash.cc
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stdlib.h>
#include <string.h>
#include "gmem.h"
#include "SplashErrorCodes.h"
#include "SplashMath.h"
#include "SplashBitmap.h"
#include "SplashState.h"
#include "SplashPath.h"
#include "SplashXPath.h"
#include "SplashXPathScanner.h"
#include "SplashPattern.h"
#include "SplashScreen.h"
#include "SplashFont.h"
#include "SplashGlyphBitmap.h"
#include "Splash.h"

//------------------------------------------------------------------------

// distance of Bezier control point from center for circle approximation
// = (4 * (sqrt(2) - 1) / 3) * r
#define bezierCircle ((SplashCoord)0.55228475)
#define bezierCircle2 ((SplashCoord)(0.5 * 0.55228475))

// Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result.
static inline Guchar div255(int x) {
  return (Guchar)((x + (x >> 8) + 0x80) >> 8);
}

//------------------------------------------------------------------------
// SplashPipe
//------------------------------------------------------------------------

#define splashPipeMaxStages 9

struct SplashPipe {
  // pixel coordinates
  int x, y;

  // source pattern
  SplashPattern *pattern;

  // source alpha and color
  SplashCoord aInput;
  GBool usesShape;
  Guchar aSrc;
  SplashColorPtr cSrc;
  SplashColor cSrcVal;

  // non-isolated group alpha0
  Guchar *alpha0Ptr;

  // soft mask
  SplashColorPtr softMaskPtr;

  // destination alpha and color
  SplashColorPtr destColorPtr;
  int destColorMask;
  Guchar *destAlphaPtr;

  // shape
  SplashCoord shape;

  // result alpha and color
  GBool noTransparency;
  SplashPipeResultColorCtrl resultColorCtrl;

  // non-isolated group correction
  int nonIsolatedGroup;
};

SplashPipeResultColorCtrl Splash::pipeResultColorNoAlphaBlend[] = {
  splashPipeResultColorNoAlphaBlendMono,
  splashPipeResultColorNoAlphaBlendMono,
  splashPipeResultColorNoAlphaBlendRGB,
  splashPipeResultColorNoAlphaBlendRGB
#if SPLASH_CMYK
  ,
  splashPipeResultColorNoAlphaBlendCMYK
#endif
};

SplashPipeResultColorCtrl Splash::pipeResultColorAlphaNoBlend[] = {
  splashPipeResultColorAlphaNoBlendMono,
  splashPipeResultColorAlphaNoBlendMono,
  splashPipeResultColorAlphaNoBlendRGB,
  splashPipeResultColorAlphaNoBlendRGB
#if SPLASH_CMYK
  ,
  splashPipeResultColorAlphaNoBlendCMYK
#endif
};

SplashPipeResultColorCtrl Splash::pipeResultColorAlphaBlend[] = {
  splashPipeResultColorAlphaBlendMono,
  splashPipeResultColorAlphaBlendMono,
  splashPipeResultColorAlphaBlendRGB,
  splashPipeResultColorAlphaBlendRGB
#if SPLASH_CMYK
  ,
  splashPipeResultColorAlphaBlendCMYK
#endif
};

//------------------------------------------------------------------------

static void blendXor(SplashColorPtr src, SplashColorPtr dest,
		     SplashColorPtr blend, SplashColorMode cm) {
  int i;

  for (i = 0; i < splashColorModeNComps[cm]; ++i) {
    blend[i] = src[i] ^ dest[i];
  }
}

//------------------------------------------------------------------------
// modified region
//------------------------------------------------------------------------

void Splash::clearModRegion() {
  modXMin = bitmap->getWidth();
  modYMin = bitmap->getHeight();
  modXMax = -1;
  modYMax = -1;
}

inline void Splash::updateModX(int x) {
  if (x < modXMin) {
    modXMin = x;
  }
  if (x > modXMax) {
    modXMax = x;
  }
}

inline void Splash::updateModY(int y) {
  if (y < modYMin) {
    modYMin = y;
  }
  if (y > modYMax) {
    modYMax = y;
  }
}

//------------------------------------------------------------------------
// pipeline
//------------------------------------------------------------------------

inline void Splash::pipeInit(SplashPipe *pipe, int x, int y,
			     SplashPattern *pattern, SplashColorPtr cSrc,
			     SplashCoord aInput, GBool usesShape,
			     GBool nonIsolatedGroup) {
  pipeSetXY(pipe, x, y);
  pipe->pattern = NULL;

  // source color
  if (pattern) {
    if (pattern->isStatic()) {
      pattern->getColor(x, y, pipe->cSrcVal);
    } else {
      pipe->pattern = pattern;
    }
    pipe->cSrc = pipe->cSrcVal;
  } else {
    pipe->cSrc = cSrc;
  }

  // source alpha
  pipe->aInput = aInput;
  if (!state->softMask) {
    if (usesShape) {
      pipe->aInput *= 255;
    } else {
      pipe->aSrc = (Guchar)splashRound(pipe->aInput * 255);
    }
  }
  pipe->usesShape = usesShape;

  // result alpha
  if (aInput == 1 && !state->softMask && !usesShape &&
      !state->inNonIsolatedGroup) {
    pipe->noTransparency = gTrue;
  } else {
    pipe->noTransparency = gFalse;
  }

  // result color
  if (pipe->noTransparency) {
    // the !state->blendFunc case is handled separately in pipeRun
    pipe->resultColorCtrl = pipeResultColorNoAlphaBlend[bitmap->mode];
  } else if (!state->blendFunc) {
    pipe->resultColorCtrl = pipeResultColorAlphaNoBlend[bitmap->mode];
  } else {
    pipe->resultColorCtrl = pipeResultColorAlphaBlend[bitmap->mode];
  }

  // non-isolated group correction
  if (nonIsolatedGroup) {
    pipe->nonIsolatedGroup = splashColorModeNComps[bitmap->mode];
  } else {
    pipe->nonIsolatedGroup = 0;
  }
}

inline void Splash::pipeRun(SplashPipe *pipe) {
  Guchar aSrc, aDest, alpha2, alpha0, aResult;
  SplashColor cDest, cBlend;
  Guchar cResult0, cResult1, cResult2, cResult3;

  //----- source color

  // static pattern: handled in pipeInit
  // fixed color: handled in pipeInit

  // dynamic pattern
  if (pipe->pattern) {
    pipe->pattern->getColor(pipe->x, pipe->y, pipe->cSrcVal);
  }

  if (pipe->noTransparency && !state->blendFunc) {

    //----- write destination pixel

    switch (bitmap->mode) {
    case splashModeMono1:
      cResult0 = pipe->cSrc[0];
      if (state->screen->test(pipe->x, pipe->y, cResult0)) {
	*pipe->destColorPtr |= pipe->destColorMask;
      } else {
	*pipe->destColorPtr &= ~pipe->destColorMask;
      }
      if (!(pipe->destColorMask >>= 1)) {
	pipe->destColorMask = 0x80;
	++pipe->destColorPtr;
      }
      break;
    case splashModeMono8:
      *pipe->destColorPtr++ = pipe->cSrc[0];
      break;
    case splashModeRGB8:
      *pipe->destColorPtr++ = pipe->cSrc[0];
      *pipe->destColorPtr++ = pipe->cSrc[1];
      *pipe->destColorPtr++ = pipe->cSrc[2];
      break;
    case splashModeBGR8:
      *pipe->destColorPtr++ = pipe->cSrc[2];
      *pipe->destColorPtr++ = pipe->cSrc[1];
      *pipe->destColorPtr++ = pipe->cSrc[0];
      break;
#if SPLASH_CMYK
    case splashModeCMYK8:
      *pipe->destColorPtr++ = pipe->cSrc[0];
      *pipe->destColorPtr++ = pipe->cSrc[1];
      *pipe->destColorPtr++ = pipe->cSrc[2];
      *pipe->destColorPtr++ = pipe->cSrc[3];
      break;
#endif
    }
    if (pipe->destAlphaPtr) {
      *pipe->destAlphaPtr++ = 255;
    }

  } else {

    //----- read destination pixel

    switch (bitmap->mode) {
    case splashModeMono1:
      cDest[0] = (*pipe->destColorPtr & pipe->destColorMask) ? 0xff : 0x00;
      break;
    case splashModeMono8:
      cDest[0] = *pipe->destColorPtr;
      break;
    case splashModeRGB8:
      cDest[0] = pipe->destColorPtr[0];
      cDest[1] = pipe->destColorPtr[1];
      cDest[2] = pipe->destColorPtr[2];
      break;
    case splashModeBGR8:
      cDest[0] = pipe->destColorPtr[2];
      cDest[1] = pipe->destColorPtr[1];
      cDest[2] = pipe->destColorPtr[0];
      break;
#if SPLASH_CMYK
    case splashModeCMYK8:
      cDest[0] = pipe->destColorPtr[0];
      cDest[1] = pipe->destColorPtr[1];
      cDest[2] = pipe->destColorPtr[2];
      cDest[3] = pipe->destColorPtr[3];
      break;
#endif
    }
    if (pipe->destAlphaPtr) {
      aDest = *pipe->destAlphaPtr;
    } else {
      aDest = 0xff;
    }

    //----- blend function

    if (state->blendFunc) {
      (*state->blendFunc)(pipe->cSrc, cDest, cBlend, bitmap->mode);
    }

    //----- source alpha

    if (state->softMask) {
      if (pipe->usesShape) {
	aSrc = (Guchar)splashRound(pipe->aInput * *pipe->softMaskPtr++
				   * pipe->shape);
      } else {
	aSrc = (Guchar)splashRound(pipe->aInput * *pipe->softMaskPtr++);
      }
    } else if (pipe->usesShape) {
      // pipe->aInput is premultiplied by 255 in pipeInit
      aSrc = (Guchar)splashRound(pipe->aInput * pipe->shape);
    } else {
      // precomputed in pipeInit
      aSrc = pipe->aSrc;
    }

    //----- result alpha and non-isolated group element correction

    if (pipe->noTransparency) {
      alpha2 = aResult = 255;
    } else {
      aResult = aSrc + aDest - div255(aSrc * aDest);

      if (pipe->alpha0Ptr) {
	alpha0 = *pipe->alpha0Ptr++;
	alpha2 = aResult + alpha0 - div255(aResult * alpha0);
      } else {
	alpha2 = aResult;
      }
    }

    //----- result color

    cResult0 = cResult1 = cResult2 = cResult3 = 0; // make gcc happy

    switch (pipe->resultColorCtrl) {

#if SPLASH_CMYK
    case splashPipeResultColorNoAlphaBlendCMYK:
      cResult3 = div255((255 - aDest) * pipe->cSrc[3] + aDest * cBlend[3]);
#endif
    case splashPipeResultColorNoAlphaBlendRGB:
      cResult2 = div255((255 - aDest) * pipe->cSrc[2] + aDest * cBlend[2]);
      cResult1 = div255((255 - aDest) * pipe->cSrc[1] + aDest * cBlend[1]);
    case splashPipeResultColorNoAlphaBlendMono:
      cResult0 = div255((255 - aDest) * pipe->cSrc[0] + aDest * cBlend[0]);
      break;

    case splashPipeResultColorAlphaNoBlendMono:
      if (alpha2 == 0) {
	cResult0 = 0;
      } else {
	cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] +
			     aSrc * pipe->cSrc[0]) / alpha2);
      }
      break;
    case splashPipeResultColorAlphaNoBlendRGB:
      if (alpha2 == 0) {
	cResult0 = 0;
	cResult1 = 0;
	cResult2 = 0;
      } else {
	cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] +
			     aSrc * pipe->cSrc[0]) / alpha2);
	cResult1 = (Guchar)(((alpha2 - aSrc) * cDest[1] +
			     aSrc * pipe->cSrc[1]) / alpha2);
	cResult2 = (Guchar)(((alpha2 - aSrc) * cDest[2] +
			     aSrc * pipe->cSrc[2]) / alpha2);
      }
      break;
#if SPLASH_CMYK
    case splashPipeResultColorAlphaNoBlendCMYK:
      if (alpha2 == 0) {
	cResult0 = 0;
	cResult1 = 0;
	cResult2 = 0;
	cResult3 = 0;
      } else {
	cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] +
			     aSrc * pipe->cSrc[0]) / alpha2);
	cResult1 = (Guchar)(((alpha2 - aSrc) * cDest[1] +
			     aSrc * pipe->cSrc[1]) / alpha2);
	cResult2 = (Guchar)(((alpha2 - aSrc) * cDest[2] +
			     aSrc * pipe->cSrc[2]) / alpha2);
	cResult3 = (Guchar)(((alpha2 - aSrc) * cDest[3] +
			     aSrc * pipe->cSrc[3]) / alpha2);
      }
      break;
#endif

    case splashPipeResultColorAlphaBlendMono:
      if (alpha2 == 0) {
	cResult0 = 0;
      } else {
	cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] +
			     aSrc * ((255 - aDest) * pipe->cSrc[0] +
				     aDest * cBlend[0]) / 255) /
			    alpha2);
      }
      break;
    case splashPipeResultColorAlphaBlendRGB:
      if (alpha2 == 0) {
	cResult0 = 0;
	cResult1 = 0;
	cResult2 = 0;
      } else {
	cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] +
			     aSrc * ((255 - aDest) * pipe->cSrc[0] +
				     aDest * cBlend[0]) / 255) /
			    alpha2);
	cResult1 = (Guchar)(((alpha2 - aSrc) * cDest[1] +
			     aSrc * ((255 - aDest) * pipe->cSrc[1] +
				     aDest * cBlend[1]) / 255) /
			    alpha2);
	cResult2 = (Guchar)(((alpha2 - aSrc) * cDest[2] +
			     aSrc * ((255 - aDest) * pipe->cSrc[2] +
				     aDest * cBlend[2]) / 255) /
			    alpha2);
      }
      break;
#if SPLASH_CMYK
    case splashPipeResultColorAlphaBlendCMYK:
      if (alpha2 == 0) {
	cResult0 = 0;
	cResult1 = 0;
	cResult2 = 0;
	cResult3 = 0;
      } else {
	cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] +
			     aSrc * ((255 - aDest) * pipe->cSrc[0] +
				     aDest * cBlend[0]) / 255) /
			    alpha2);
	cResult1 = (Guchar)(((alpha2 - aSrc) * cDest[1] +
			     aSrc * ((255 - aDest) * pipe->cSrc[1] +
				     aDest * cBlend[1]) / 255) /
			    alpha2);
	cResult2 = (Guchar)(((alpha2 - aSrc) * cDest[2] +
			     aSrc * ((255 - aDest) * pipe->cSrc[2] +
				     aDest * cBlend[2]) / 255) /
			    alpha2);
	cResult3 = (Guchar)(((alpha2 - aSrc) * cDest[3] +
			     aSrc * ((255 - aDest) * pipe->cSrc[3] +
				     aDest * cBlend[3]) / 255) /
			    alpha2);
      }
      break;
#endif
    }

    //----- non-isolated group correction

    if (aResult != 0) {
      switch (pipe->nonIsolatedGroup) {
#if SPLASH_CMYK
      case 4:
	cResult3 += (cResult3 - cDest[3]) * aDest *
	            (255 - aResult) / (255 * aResult);
#endif
      case 3:
	cResult2 += (cResult2 - cDest[2]) * aDest *
	            (255 - aResult) / (255 * aResult);
	cResult1 += (cResult1 - cDest[1]) * aDest *
	            (255 - aResult) / (255 * aResult);
      case 1:
	cResult0 += (cResult0 - cDest[0]) * aDest *
	            (255 - aResult) / (255 * aResult);
      case 0:
	break;
      }
    }

    //----- write destination pixel

    switch (bitmap->mode) {
    case splashModeMono1:
      if (state->screen->test(pipe->x, pipe->y, cResult0)) {
	*pipe->destColorPtr |= pipe->destColorMask;
      } else {
	*pipe->destColorPtr &= ~pipe->destColorMask;
      }
      if (!(pipe->destColorMask >>= 1)) {
	pipe->destColorMask = 0x80;
	++pipe->destColorPtr;
      }
      break;
    case splashModeMono8:
      *pipe->destColorPtr++ = cResult0;
      break;
    case splashModeRGB8:
      *pipe->destColorPtr++ = cResult0;
      *pipe->destColorPtr++ = cResult1;
      *pipe->destColorPtr++ = cResult2;
      break;
    case splashModeBGR8:
      *pipe->destColorPtr++ = cResult2;
      *pipe->destColorPtr++ = cResult1;
      *pipe->destColorPtr++ = cResult0;
      break;
#if SPLASH_CMYK
    case splashModeCMYK8:
      *pipe->destColorPtr++ = cResult0;
      *pipe->destColorPtr++ = cResult1;
      *pipe->destColorPtr++ = cResult2;
      *pipe->destColorPtr++ = cResult3;
      break;
#endif
    }
    if (pipe->destAlphaPtr) {
      *pipe->destAlphaPtr++ = aResult;
    }

  }

  ++pipe->x;
}

inline void Splash::pipeSetXY(SplashPipe *pipe, int x, int y) {
  pipe->x = x;
  pipe->y = y;
  if (state->softMask) {
    pipe->softMaskPtr =
        &state->softMask->data[y * state->softMask->rowSize + x];
  }
  switch (bitmap->mode) {
  case splashModeMono1:
    pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + (x >> 3)];
    pipe->destColorMask = 0x80 >> (x & 7);
    break;
  case splashModeMono8:
    pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + x];
    break;
  case splashModeRGB8:
  case splashModeBGR8:
    pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x];
    break;
#if SPLASH_CMYK
  case splashModeCMYK8:
    pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x];
    break;
#endif
  }
  if (bitmap->alpha) {
    pipe->destAlphaPtr = &bitmap->alpha[y * bitmap->width + x];
  } else {
    pipe->destAlphaPtr = NULL;
  }
  if (state->inNonIsolatedGroup && alpha0Bitmap->alpha) {
    pipe->alpha0Ptr =
        &alpha0Bitmap->alpha[(alpha0Y + y) * alpha0Bitmap->width +
			     (alpha0X + x)];
  } else {
    pipe->alpha0Ptr = NULL;
  }
}

inline void Splash::pipeIncX(SplashPipe *pipe) {
  ++pipe->x;
  if (state->softMask) {
    ++pipe->softMaskPtr;
  }
  switch (bitmap->mode) {
  case splashModeMono1:
    if (!(pipe->destColorMask >>= 1)) {
      pipe->destColorMask = 0x80;
      ++pipe->destColorPtr;
    }
    break;
  case splashModeMono8:
    ++pipe->destColorPtr;
    break;
  case splashModeRGB8:
  case splashModeBGR8:
    pipe->destColorPtr += 3;
    break;
#if SPLASH_CMYK
  case splashModeCMYK8:
    pipe->destColorPtr += 4;
    break;
#endif
  }
  if (pipe->destAlphaPtr) {
    ++pipe->destAlphaPtr;
  }
  if (pipe->alpha0Ptr) {
    ++pipe->alpha0Ptr;
  }
}

inline void Splash::drawPixel(SplashPipe *pipe, int x, int y, GBool noClip) {
  if (noClip || state->clip->test(x, y)) {
    pipeSetXY(pipe, x, y);
    pipeRun(pipe);
    updateModX(x);
    updateModY(y);
  }
}

inline void Splash::drawAAPixelInit() {
  aaBufY = -1;
}

inline void Splash::drawAAPixel(SplashPipe *pipe, int x, int y) {
#if splashAASize == 4
  static int bitCount4[16] = { 0, 1, 1, 2, 1, 2, 2, 3,
			       1, 2, 2, 3, 2, 3, 3, 4 };
  int w;
#else
  int xx, yy;
#endif
  SplashColorPtr p;
  int x0, x1, t;

  if (x < 0 || x >= bitmap->width ||
      y < state->clip->getYMinI() || y > state->clip->getYMaxI()) {
    return;
  }

  // update aaBuf
  if (y != aaBufY) {
    memset(aaBuf->getDataPtr(), 0xff,
	   aaBuf->getRowSize() * aaBuf->getHeight());
    x0 = 0;
    x1 = bitmap->width - 1;
    state->clip->clipAALine(aaBuf, &x0, &x1, y);
    aaBufY = y;
  }

  // compute the shape value
#if splashAASize == 4
  p = aaBuf->getDataPtr() + (x >> 1);
  w = aaBuf->getRowSize();
  if (x & 1) {
    t = bitCount4[*p & 0x0f] + bitCount4[p[w] & 0x0f] +
        bitCount4[p[2*w] & 0x0f] + bitCount4[p[3*w] & 0x0f];
  } else {
    t = bitCount4[*p >> 4] + bitCount4[p[w] >> 4] +
        bitCount4[p[2*w] >> 4] + bitCount4[p[3*w] >> 4];
  }
#else
  t = 0;
  for (yy = 0; yy < splashAASize; ++yy) {
    for (xx = 0; xx < splashAASize; ++xx) {
      p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() +
	  ((x * splashAASize + xx) >> 3);
      t += (*p >> (7 - ((x * splashAASize + xx) & 7))) & 1;
    }
  }
#endif

  // draw the pixel
  if (t != 0) {
    pipeSetXY(pipe, x, y);
    pipe->shape *= aaGamma[t];
    pipeRun(pipe);
    updateModX(x);
    updateModY(y);
  }
}

inline void Splash::drawSpan(SplashPipe *pipe, int x0, int x1, int y,
			     GBool noClip) {
  int x;

  pipeSetXY(pipe, x0, y);
  if (noClip) {
    for (x = x0; x <= x1; ++x) {
      pipeRun(pipe);
    }
    updateModX(x0);
    updateModX(x1);
    updateModY(y);
  } else {
    for (x = x0; x <= x1; ++x) {
      if (state->clip->test(x, y)) {
	pipeRun(pipe);
	updateModX(x);
	updateModY(y);
      } else {
	pipeIncX(pipe);
      }
    }
  }
}

inline void Splash::drawAALine(SplashPipe *pipe, int x0, int x1, int y) {
#if splashAASize == 4
  static int bitCount4[16] = { 0, 1, 1, 2, 1, 2, 2, 3,
			       1, 2, 2, 3, 2, 3, 3, 4 };
  SplashColorPtr p0, p1, p2, p3;
  int t;
#else
  SplashColorPtr p;
  int xx, yy, t;
#endif
  int x;

#if splashAASize == 4
  p0 = aaBuf->getDataPtr() + (x0 >> 1);
  p1 = p0 + aaBuf->getRowSize();
  p2 = p1 + aaBuf->getRowSize();
  p3 = p2 + aaBuf->getRowSize();
#endif
  pipeSetXY(pipe, x0, y);
  for (x = x0; x <= x1; ++x) {

    // compute the shape value
#if splashAASize == 4
    if (x & 1) {
      t = bitCount4[*p0 & 0x0f] + bitCount4[*p1 & 0x0f] +
	  bitCount4[*p2 & 0x0f] + bitCount4[*p3 & 0x0f];
      ++p0; ++p1; ++p2; ++p3;
    } else {
      t = bitCount4[*p0 >> 4] + bitCount4[*p1 >> 4] +
	  bitCount4[*p2 >> 4] + bitCount4[*p3 >> 4];
    }
#else
    t = 0;
    for (yy = 0; yy < splashAASize; ++yy) {
      for (xx = 0; xx < splashAASize; ++xx) {
	p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() +
	    ((x * splashAASize + xx) >> 3);
	t += (*p >> (7 - ((x * splashAASize + xx) & 7))) & 1;
      }
    }
#endif

    if (t != 0) {
      pipe->shape = aaGamma[t];
      pipeRun(pipe);
      updateModX(x);
      updateModY(y);
    } else {
      pipeIncX(pipe);
    }
  }
}

//------------------------------------------------------------------------

// Transform a point from user space to device space.
inline void Splash::transform(SplashCoord *matrix,
			      SplashCoord xi, SplashCoord yi,
			      SplashCoord *xo, SplashCoord *yo) {
  //                          [ m[0] m[1] 0 ]
  // [xo yo 1] = [xi yi 1] *  [ m[2] m[3] 0 ]
  //                          [ m[4] m[5] 1 ]
  *xo = xi * matrix[0] + yi * matrix[2] + matrix[4];
  *yo = xi * matrix[1] + yi * matrix[3] + matrix[5];
}

//------------------------------------------------------------------------
// Splash
//------------------------------------------------------------------------

Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA,
	       SplashScreenParams *screenParams) {
  int i;

  bitmap = bitmapA;
  vectorAntialias = vectorAntialiasA;
  state = new SplashState(bitmap->width, bitmap->height, vectorAntialias,
			  screenParams);
  if (vectorAntialias) {
    aaBuf = new SplashBitmap(splashAASize * bitmap->width, splashAASize,
			     1, splashModeMono1, gFalse);
    for (i = 0; i <= splashAASize * splashAASize; ++i) {
      aaGamma[i] = splashPow((SplashCoord)i /
			       (SplashCoord)(splashAASize * splashAASize),
			     1.5);
    }
  } else {
    aaBuf = NULL;
  }
  clearModRegion();
  debugMode = gFalse;
}

Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA,
	       SplashScreen *screenA) {
  int i;

  bitmap = bitmapA;
  vectorAntialias = vectorAntialiasA;
  state = new SplashState(bitmap->width, bitmap->height, vectorAntialias,
			  screenA);
  if (vectorAntialias) {
    aaBuf = new SplashBitmap(splashAASize * bitmap->width, splashAASize,
			     1, splashModeMono1, gFalse);
    for (i = 0; i <= splashAASize * splashAASize; ++i) {
      aaGamma[i] = splashPow((SplashCoord)i /
			       (SplashCoord)(splashAASize * splashAASize),
			     1.5);
    }
  } else {
    aaBuf = NULL;
  }
  clearModRegion();
  debugMode = gFalse;
}

Splash::~Splash() {
  while (state->next) {
    restoreState();
  }
  delete state;
  if (vectorAntialias) {
    delete aaBuf;
  }
}

//------------------------------------------------------------------------
// state read
//------------------------------------------------------------------------

SplashCoord *Splash::getMatrix() {
  return state->matrix;
}

SplashPattern *Splash::getStrokePattern() {
  return state->strokePattern;
}

SplashPattern *Splash::getFillPattern() {
  return state->fillPattern;
}

SplashScreen *Splash::getScreen() {
  return state->screen;
}

SplashBlendFunc Splash::getBlendFunc() {
  return state->blendFunc;
}

SplashCoord Splash::getStrokeAlpha() {
  return state->strokeAlpha;
}

SplashCoord Splash::getFillAlpha() {
  return state->fillAlpha;
}

SplashCoord Splash::getLineWidth() {
  return state->lineWidth;
}

int Splash::getLineCap() {
  return state->lineCap;
}

int Splash::getLineJoin() {
  return state->lineJoin;
}

SplashCoord Splash::getMiterLimit() {
  return state->miterLimit;
}

SplashCoord Splash::getFlatness() {
  return state->flatness;
}

SplashCoord *Splash::getLineDash() {
  return state->lineDash;
}

int Splash::getLineDashLength() {
  return state->lineDashLength;
}

SplashCoord Splash::getLineDashPhase() {
  return state->lineDashPhase;
}

SplashClip *Splash::getClip() {
  return state->clip;
}

SplashBitmap *Splash::getSoftMask() {
  return state->softMask;
}

GBool Splash::getInNonIsolatedGroup() {
  return state->inNonIsolatedGroup;
}

//------------------------------------------------------------------------
// state write
//------------------------------------------------------------------------

void Splash::setMatrix(SplashCoord *matrix) {
  memcpy(state->matrix, matrix, 6 * sizeof(SplashCoord));
}

void Splash::setStrokePattern(SplashPattern *strokePattern) {
  state->setStrokePattern(strokePattern);
}

void Splash::setFillPattern(SplashPattern *fillPattern) {
  state->setFillPattern(fillPattern);
}

void Splash::setScreen(SplashScreen *screen) {
  state->setScreen(screen);
}

void Splash::setBlendFunc(SplashBlendFunc func) {
  state->blendFunc = func;
}

void Splash::setStrokeAlpha(SplashCoord alpha) {
  state->strokeAlpha = alpha;
}

void Splash::setFillAlpha(SplashCoord alpha) {
  state->fillAlpha = alpha;
}

void Splash::setLineWidth(SplashCoord lineWidth) {
  state->lineWidth = lineWidth;
}

void Splash::setLineCap(int lineCap) {
  state->lineCap = lineCap;
}

void Splash::setLineJoin(int lineJoin) {
  state->lineJoin = lineJoin;
}

void Splash::setMiterLimit(SplashCoord miterLimit) {
  state->miterLimit = miterLimit;
}

void Splash::setFlatness(SplashCoord flatness) {
  if (flatness < 1) {
    state->flatness = 1;
  } else {
    state->flatness = flatness;
  }
}

void Splash::setLineDash(SplashCoord *lineDash, int lineDashLength,
			 SplashCoord lineDashPhase) {
  state->setLineDash(lineDash, lineDashLength, lineDashPhase);
}

void Splash::setStrokeAdjust(GBool strokeAdjust) {
  state->strokeAdjust = strokeAdjust;
}

void Splash::clipResetToRect(SplashCoord x0, SplashCoord y0,
			     SplashCoord x1, SplashCoord y1) {
  state->clip->resetToRect(x0, y0, x1, y1);
}

SplashError Splash::clipToRect(SplashCoord x0, SplashCoord y0,
			       SplashCoord x1, SplashCoord y1) {
  return state->clip->clipToRect(x0, y0, x1, y1);
}

SplashError Splash::clipToPath(SplashPath *path, GBool eo) {
  return state->clip->clipToPath(path, state->matrix, state->flatness, eo);
}

void Splash::setSoftMask(SplashBitmap *softMask) {
  state->setSoftMask(softMask);
}

void Splash::setInNonIsolatedGroup(SplashBitmap *alpha0BitmapA,
				   int alpha0XA, int alpha0YA) {
  alpha0Bitmap = alpha0BitmapA;
  alpha0X = alpha0XA;
  alpha0Y = alpha0YA;
  state->inNonIsolatedGroup = gTrue;
}

//------------------------------------------------------------------------
// state save/restore
//------------------------------------------------------------------------

void Splash::saveState() {
  SplashState *newState;

  newState = state->copy();
  newState->next = state;
  state = newState;
}

SplashError Splash::restoreState() {
  SplashState *oldState;

  if (!state->next) {
    return splashErrNoSave;
  }
  oldState = state;
  state = state->next;
  delete oldState;
  return splashOk;
}

//------------------------------------------------------------------------
// drawing operations
//------------------------------------------------------------------------

void Splash::clear(SplashColorPtr color, Guchar alpha) {
  SplashColorPtr row, p;
  Guchar mono;
  int x, y;

  switch (bitmap->mode) {
  case splashModeMono1:
    mono = (color[0] & 0x80) ? 0xff : 0x00;
    if (bitmap->rowSize < 0) {
      memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
	     mono, -bitmap->rowSize * bitmap->height);
    } else {
      memset(bitmap->data, mono, bitmap->rowSize * bitmap->height);
    }
    break;
  case splashModeMono8:
    if (bitmap->rowSize < 0) {
      memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
	     color[0], -bitmap->rowSize * bitmap->height);
    } else {
      memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
    }
    break;
  case splashModeRGB8:
    if (color[0] == color[1] && color[1] == color[2]) {
      if (bitmap->rowSize < 0) {
	memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
	       color[0], -bitmap->rowSize * bitmap->height);
      } else {
	memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
      }
    } else {
      row = bitmap->data;
      for (y = 0; y < bitmap->height; ++y) {
	p = row;
	for (x = 0; x < bitmap->width; ++x) {
	  *p++ = color[2];
	  *p++ = color[1];
	  *p++ = color[0];
	}
	row += bitmap->rowSize;
      }
    }
    break;
  case splashModeBGR8:
    if (color[0] == color[1] && color[1] == color[2]) {
      if (bitmap->rowSize < 0) {
	memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
	       color[0], -bitmap->rowSize * bitmap->height);
      } else {
	memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
      }
    } else {
      row = bitmap->data;
      for (y = 0; y < bitmap->height; ++y) {
	p = row;
	for (x = 0; x < bitmap->width; ++x) {
	  *p++ = color[0];
	  *p++ = color[1];
	  *p++ = color[2];
	}
	row += bitmap->rowSize;
      }
    }
    break;
#if SPLASH_CMYK
  case splashModeCMYK8:
    if (color[0] == color[1] && color[1] == color[2] && color[2] == color[3]) {
      if (bitmap->rowSize < 0) {
	memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
	       color[0], -bitmap->rowSize * bitmap->height);
      } else {
	memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
      }
    } else {
      row = bitmap->data;
      for (y = 0; y < bitmap->height; ++y) {
	p = row;
	for (x = 0; x < bitmap->width; ++x) {
	  *p++ = color[0];
	  *p++ = color[1];
	  *p++ = color[2];
	  *p++ = color[3];
	}
	row += bitmap->rowSize;
      }
    }
    break;
#endif
  }

  if (bitmap->alpha) {
    memset(bitmap->alpha, alpha, bitmap->width * bitmap->height);
  }

  updateModX(0);
  updateModY(0);
  updateModX(bitmap->width - 1);
  updateModY(bitmap->height - 1);
}

SplashError Splash::stroke(SplashPath *path) {
  SplashPath *path2, *dPath;

  if (debugMode) {
    printf("stroke [dash:%d] [width:%.2f]:\n",
	   state->lineDashLength, (double)state->lineWidth);
    dumpPath(path);
  }
  opClipRes = splashClipAllOutside;
  if (path->length == 0) {
    return splashErrEmptyPath;
  }
  path2 = flattenPath(path, state->matrix, state->flatness);
  if (state->lineDashLength > 0) {
    dPath = makeDashedPath(path2);
    delete path2;
    path2 = dPath;
  }
  if (state->lineWidth == 0) {
    strokeNarrow(path2);
  } else {
    strokeWide(path2);
  }
  delete path2;
  return splashOk;
}

void Splash::strokeNarrow(SplashPath *path) {
  SplashPipe pipe;
  SplashXPath *xPath;
  SplashXPathSeg *seg;
  int x0, x1, x2, x3, y0, y1, x, y, t;
  SplashCoord dx, dy, dxdy;
  SplashClipResult clipRes;
  int nClipRes[3];
  int i;

  nClipRes[0] = nClipRes[1] = nClipRes[2] = 0;

  xPath = new SplashXPath(path, state->matrix, state->flatness, gFalse);

  pipeInit(&pipe, 0, 0, state->strokePattern, NULL, state->strokeAlpha,
	   gFalse, gFalse);

  for (i = 0, seg = xPath->segs; i < xPath->length; ++i, ++seg) {

    x0 = splashFloor(seg->x0);
    x1 = splashFloor(seg->x1);
    y0 = splashFloor(seg->y0);
    y1 = splashFloor(seg->y1);

    // horizontal segment
    if (y0 == y1) {
      if (x0 > x1) {
	t = x0; x0 = x1; x1 = t;
      }
      if ((clipRes = state->clip->testSpan(x0, x1, y0))
	  != splashClipAllOutside) {
	drawSpan(&pipe, x0, x1, y0, clipRes == splashClipAllInside);
      }

    // segment with |dx| > |dy|
    } else if (splashAbs(seg->dxdy) > 1) {
      dx = seg->x1 - seg->x0;
      dy = seg->y1 - seg->y0;
      dxdy = seg->dxdy;
      if (y0 > y1) {
	t = y0; y0 = y1; y1 = t;
	t = x0; x0 = x1; x1 = t;
	dx = -dx;
	dy = -dy;
      }
      if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0,
					   x0 <= x1 ? x1 : x0, y1))
	  != splashClipAllOutside) {
	if (dx > 0) {
	  x2 = x0;
	  x3 = splashFloor(seg->x0 + ((SplashCoord)y0 + 1 - seg->y0) * dxdy);
	  drawSpan(&pipe, x2, (x2 <= x3 - 1) ? x3 - 1 : x2, y0,
		   clipRes == splashClipAllInside);
	  x2 = x3;
	  for (y = y0 + 1; y <= y1 - 1; ++y) {
	    x3 = splashFloor(seg->x0 + ((SplashCoord)y + 1 - seg->y0) * dxdy);
	    drawSpan(&pipe, x2, x3 - 1, y, clipRes == splashClipAllInside);
	    x2 = x3;
	  }
	  drawSpan(&pipe, x2, x2 <= x1 ? x1 : x2, y1,
		   clipRes == splashClipAllInside);
	} else {
	  x2 = x0;
	  x3 = splashFloor(seg->x0 + ((SplashCoord)y0 + 1 - seg->y0) * dxdy);
	  drawSpan(&pipe, (x3 + 1 <= x2) ? x3 + 1 : x2, x2, y0,
		   clipRes == splashClipAllInside);
	  x2 = x3;
	  for (y = y0 + 1; y <= y1 - 1; ++y) {
	    x3 = splashFloor(seg->x0 + ((SplashCoord)y + 1 - seg->y0) * dxdy);
	    drawSpan(&pipe, x3 + 1, x2, y, clipRes == splashClipAllInside);
	    x2 = x3;
	  }
	  drawSpan(&pipe, x1, (x1 <= x2) ? x2 : x1, y1,
		   clipRes == splashClipAllInside);
	}
      }

    // segment with |dy| > |dx|
    } else {
      dxdy = seg->dxdy;
      if (y0 > y1) {
	t = x0; x0 = x1; x1 = t;
	t = y0; y0 = y1; y1 = t;
      }
      if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0,
					   x0 <= x1 ? x1 : x0, y1))
	  != splashClipAllOutside) {
	drawPixel(&pipe, x0, y0, clipRes == splashClipAllInside);
	for (y = y0 + 1; y <= y1 - 1; ++y) {
	  x = splashFloor(seg->x0 + ((SplashCoord)y - seg->y0) * dxdy);
	  drawPixel(&pipe, x, y, clipRes == splashClipAllInside);
	}
	drawPixel(&pipe, x1, y1, clipRes == splashClipAllInside);
      }
    }
    ++nClipRes[clipRes];
  }
  if (nClipRes[splashClipPartial] ||
      (nClipRes[splashClipAllInside] && nClipRes[splashClipAllOutside])) {
    opClipRes = splashClipPartial;
  } else if (nClipRes[splashClipAllInside]) {
    opClipRes = splashClipAllInside;
  } else {
    opClipRes = splashClipAllOutside;
  }

  delete xPath;
}

void Splash::strokeWide(SplashPath *path) {
  SplashPath *path2;

  path2 = makeStrokePath(path, gFalse);
  fillWithPattern(path2, gFalse, state->strokePattern, state->strokeAlpha);
  delete path2;
}

SplashPath *Splash::flattenPath(SplashPath *path, SplashCoord *matrix,
				SplashCoord flatness) {
  SplashPath *fPath;
  SplashCoord flatness2;
  Guchar flag;
  int i;

  fPath = new SplashPath();
  flatness2 = flatness * flatness;
  i = 0;
  while (i < path->length) {
    flag = path->flags[i];
    if (flag & splashPathFirst) {
      fPath->moveTo(path->pts[i].x, path->pts[i].y);
      ++i;
    } else {
      if (flag & splashPathCurve) {
	flattenCurve(path->pts[i-1].x, path->pts[i-1].y,
		     path->pts[i  ].x, path->pts[i  ].y,
		     path->pts[i+1].x, path->pts[i+1].y,
		     path->pts[i+2].x, path->pts[i+2].y,
		     matrix, flatness2, fPath);
	i += 3;
      } else {
	fPath->lineTo(path->pts[i].x, path->pts[i].y);
	++i;
      }
      if (path->flags[i-1] & splashPathClosed) {
	fPath->close();
      }
    }
  }
  return fPath;
}

void Splash::flattenCurve(SplashCoord x0, SplashCoord y0,
			  SplashCoord x1, SplashCoord y1,
			  SplashCoord x2, SplashCoord y2,
			  SplashCoord x3, SplashCoord y3,
			  SplashCoord *matrix, SplashCoord flatness2,
			  SplashPath *fPath) {
  SplashCoord cx[splashMaxCurveSplits + 1][3];
  SplashCoord cy[splashMaxCurveSplits + 1][3];
  int cNext[splashMaxCurveSplits + 1];
  SplashCoord xl0, xl1, xl2, xr0, xr1, xr2, xr3, xx1, xx2, xh;
  SplashCoord yl0, yl1, yl2, yr0, yr1, yr2, yr3, yy1, yy2, yh;
  SplashCoord dx, dy, mx, my, tx, ty, d1, d2;
  int p1, p2, p3;

  // initial segment
  p1 = 0;
  p2 = splashMaxCurveSplits;
  cx[p1][0] = x0;  cy[p1][0] = y0;
  cx[p1][1] = x1;  cy[p1][1] = y1;
  cx[p1][2] = x2;  cy[p1][2] = y2;
  cx[p2][0] = x3;  cy[p2][0] = y3;
  cNext[p1] = p2;

  while (p1 < splashMaxCurveSplits) {

    // get the next segment
    xl0 = cx[p1][0];  yl0 = cy[p1][0];
    xx1 = cx[p1][1];  yy1 = cy[p1][1];
    xx2 = cx[p1][2];  yy2 = cy[p1][2];
    p2 = cNext[p1];
    xr3 = cx[p2][0];  yr3 = cy[p2][0];

    // compute the distances (in device space) from the control points
    // to the midpoint of the straight line (this is a bit of a hack,
    // but it's much faster than computing the actual distances to the
    // line)
    transform(matrix, (xl0 + xr3) * 0.5, (yl0 + yr3) * 0.5, &mx, &my);
    transform(matrix, xx1, yy1, &tx, &ty);
    dx = tx - mx;
    dy = ty - my;
    d1 = dx*dx + dy*dy;
    transform(matrix, xx2, yy2, &tx, &ty);
    dx = tx - mx;
    dy = ty - my;
    d2 = dx*dx + dy*dy;

    // if the curve is flat enough, or no more subdivisions are
    // allowed, add the straight line segment
    if (p2 - p1 == 1 || (d1 <= flatness2 && d2 <= flatness2)) {
      fPath->lineTo(xr3, yr3);
      p1 = p2;

    // otherwise, subdivide the curve
    } else {
      xl1 = (xl0 + xx1) * 0.5;
      yl1 = (yl0 + yy1) * 0.5;
      xh = (xx1 + xx2) * 0.5;
      yh = (yy1 + yy2) * 0.5;
      xl2 = (xl1 + xh) * 0.5;
      yl2 = (yl1 + yh) * 0.5;
      xr2 = (xx2 + xr3) * 0.5;
      yr2 = (yy2 + yr3) * 0.5;
      xr1 = (xh + xr2) * 0.5;
      yr1 = (yh + yr2) * 0.5;
      xr0 = (xl2 + xr1) * 0.5;
      yr0 = (yl2 + yr1) * 0.5;
      // add the new subdivision points
      p3 = (p1 + p2) / 2;
      cx[p1][1] = xl1;  cy[p1][1] = yl1;
      cx[p1][2] = xl2;  cy[p1][2] = yl2;
      cNext[p1] = p3;
      cx[p3][0] = xr0;  cy[p3][0] = yr0;
      cx[p3][1] = xr1;  cy[p3][1] = yr1;
      cx[p3][2] = xr2;  cy[p3][2] = yr2;
      cNext[p3] = p2;
    }
  }
}

SplashPath *Splash::makeDashedPath(SplashPath *path) {
  SplashPath *dPath;
  SplashCoord lineDashTotal;
  SplashCoord lineDashStartPhase, lineDashDist, segLen;
  SplashCoord x0, y0, x1, y1, xa, ya;
  GBool lineDashStartOn, lineDashOn, newPath;
  int lineDashStartIdx, lineDashIdx;
  int i, j, k;

  lineDashTotal = 0;
  for (i = 0; i < state->lineDashLength; ++i) {
    lineDashTotal += state->lineDash[i];
  }
  lineDashStartPhase = state->lineDashPhase;
  i = splashFloor(lineDashStartPhase / lineDashTotal);
  lineDashStartPhase -= (SplashCoord)i * lineDashTotal;
  lineDashStartOn = gTrue;
  lineDashStartIdx = 0;
  while (lineDashStartPhase >= state->lineDash[lineDashStartIdx]) {
    lineDashStartOn = !lineDashStartOn;
    lineDashStartPhase -= state->lineDash[lineDashStartIdx];
    ++lineDashStartIdx;
  }

  dPath = new SplashPath();

  // process each subpath
  i = 0;
  while (i < path->length) {

    // find the end of the subpath
    for (j = i;
	 j < path->length - 1 && !(path->flags[j] & splashPathLast);
	 ++j) ;

    // initialize the dash parameters
    lineDashOn = lineDashStartOn;
    lineDashIdx = lineDashStartIdx;
    lineDashDist = state->lineDash[lineDashIdx] - lineDashStartPhase;

    // process each segment of the subpath
    newPath = gTrue;
    for (k = i; k < j; ++k) {

      // grab the segment
      x0 = path->pts[k].x;
      y0 = path->pts[k].y;
      x1 = path->pts[k+1].x;
      y1 = path->pts[k+1].y;
      segLen = splashDist(x0, y0, x1, y1);

      // process the segment
      while (segLen > 0) {

	if (lineDashDist >= segLen) {
	  if (lineDashOn) {
	    if (newPath) {
	      dPath->moveTo(x0, y0);
	      newPath = gFalse;
	    }
	    dPath->lineTo(x1, y1);
	  }
	  lineDashDist -= segLen;
	  segLen = 0;

	} else {
	  xa = x0 + (lineDashDist / segLen) * (x1 - x0);
	  ya = y0 + (lineDashDist / segLen) * (y1 - y0);
	  if (lineDashOn) {
	    if (newPath) {
	      dPath->moveTo(x0, y0);
	      newPath = gFalse;
	    }
	    dPath->lineTo(xa, ya);
	  }
	  x0 = xa;
	  y0 = ya;
	  segLen -= lineDashDist;
	  lineDashDist = 0;
	}

	// get the next entry in the dash array
	if (lineDashDist <= 0) {
	  lineDashOn = !lineDashOn;
	  if (++lineDashIdx == state->lineDashLength) {
	    lineDashIdx = 0;
	  }
	  lineDashDist = state->lineDash[lineDashIdx];
	  newPath = gTrue;
	}
      }
    }
    i = j + 1;
  }

  return dPath;
}

SplashError Splash::fill(SplashPath *path, GBool eo) {
  if (debugMode) {
    printf("fill [eo:%d]:\n", eo);
    dumpPath(path);
  }
  return fillWithPattern(path, eo, state->fillPattern, state->fillAlpha);
}

SplashError Splash::fillWithPattern(SplashPath *path, GBool eo,
				    SplashPattern *pattern,
				    SplashCoord alpha) {
  SplashPipe pipe;
  SplashXPath *xPath;
  SplashXPathScanner *scanner;
  int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y;
  SplashClipResult clipRes, clipRes2;

  if (path->length == 0) {
    return splashErrEmptyPath;
  }
  xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue);
  if (vectorAntialias) {
    xPath->aaScale();
  }
  xPath->sort();
  scanner = new SplashXPathScanner(xPath, eo);

  // get the min and max x and y values
  if (vectorAntialias) {
    scanner->getBBoxAA(&xMinI, &yMinI, &xMaxI, &yMaxI);
  } else {
    scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI);
  }

  // check clipping
  if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI))
      != splashClipAllOutside) {

    // limit the y range
    if (yMinI < state->clip->getYMinI()) {
      yMinI = state->clip->getYMinI();
    }
    if (yMaxI > state->clip->getYMaxI()) {
      yMaxI = state->clip->getYMaxI();
    }

    pipeInit(&pipe, 0, yMinI, pattern, NULL, alpha, vectorAntialias, gFalse);

    // draw the spans
    if (vectorAntialias) {
      for (y = yMinI; y <= yMaxI; ++y) {
	scanner->renderAALine(aaBuf, &x0, &x1, y);
	if (clipRes != splashClipAllInside) {
	  state->clip->clipAALine(aaBuf, &x0, &x1, y);
	}
	drawAALine(&pipe, x0, x1, y);
      }
    } else {
      for (y = yMinI; y <= yMaxI; ++y) {
	while (scanner->getNextSpan(y, &x0, &x1)) {
	  if (clipRes == splashClipAllInside) {
	    drawSpan(&pipe, x0, x1, y, gTrue);
	  } else {
	    // limit the x range
	    if (x0 < state->clip->getXMinI()) {
	      x0 = state->clip->getXMinI();
	    }
	    if (x1 > state->clip->getXMaxI()) {
	      x1 = state->clip->getXMaxI();
	    }
	    clipRes2 = state->clip->testSpan(x0, x1, y);
	    drawSpan(&pipe, x0, x1, y, clipRes2 == splashClipAllInside);
	  }
	}
      }
    }
  }
  opClipRes = clipRes;

  delete scanner;
  delete xPath;
  return splashOk;
}

SplashError Splash::xorFill(SplashPath *path, GBool eo) {
  SplashPipe pipe;
  SplashXPath *xPath;
  SplashXPathScanner *scanner;
  int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y;
  SplashClipResult clipRes, clipRes2;
  SplashBlendFunc origBlendFunc;

  if (path->length == 0) {
    return splashErrEmptyPath;
  }
  xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue);
  xPath->sort();
  scanner = new SplashXPathScanner(xPath, eo);

  // get the min and max x and y values
  scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI);

  // check clipping
  if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI))
      != splashClipAllOutside) {

    // limit the y range
    if (yMinI < state->clip->getYMinI()) {
      yMinI = state->clip->getYMinI();
    }
    if (yMaxI > state->clip->getYMaxI()) {
      yMaxI = state->clip->getYMaxI();
    }

    origBlendFunc = state->blendFunc;
    state->blendFunc = &blendXor;
    pipeInit(&pipe, 0, yMinI, state->fillPattern, NULL, 1, gFalse, gFalse);

    // draw the spans
    for (y = yMinI; y <= yMaxI; ++y) {
      while (scanner->getNextSpan(y, &x0, &x1)) {
	if (clipRes == splashClipAllInside) {
	  drawSpan(&pipe, x0, x1, y, gTrue);
	} else {
	  // limit the x range
	  if (x0 < state->clip->getXMinI()) {
	    x0 = state->clip->getXMinI();
	  }
	  if (x1 > state->clip->getXMaxI()) {
	    x1 = state->clip->getXMaxI();
	  }
	  clipRes2 = state->clip->testSpan(x0, x1, y);
	  drawSpan(&pipe, x0, x1, y, clipRes2 == splashClipAllInside);
	}
      }
    }
    state->blendFunc = origBlendFunc;
  }
  opClipRes = clipRes;

  delete scanner;
  delete xPath;
  return splashOk;
}

SplashError Splash::fillChar(SplashCoord x, SplashCoord y,
			     int c, SplashFont *font) {
  SplashGlyphBitmap glyph;
  SplashCoord xt, yt;
  int x0, y0, xFrac, yFrac;
  SplashError err;

  if (debugMode) {
    printf("fillChar: x=%.2f y=%.2f c=%3d=0x%02x='%c'\n",
	   (double)x, (double)y, c, c, c);
  }
  transform(state->matrix, x, y, &xt, &yt);
  x0 = splashFloor(xt);
  xFrac = splashFloor((xt - x0) * splashFontFraction);
  y0 = splashFloor(yt);
  yFrac = splashFloor((yt - y0) * splashFontFraction);
  if (!font->getGlyph(c, xFrac, yFrac, &glyph)) {
    return splashErrNoGlyph;
  }
  err = fillGlyph2(x0, y0, &glyph);
  if (glyph.freeData) {
    gfree(glyph.data);
  }
  return err;
}

SplashError Splash::fillGlyph(SplashCoord x, SplashCoord y,
			      SplashGlyphBitmap *glyph) {
  SplashCoord xt, yt;
  int x0, y0;

  transform(state->matrix, x, y, &xt, &yt);
  x0 = splashFloor(xt);
  y0 = splashFloor(yt);
  return fillGlyph2(x0, y0, glyph);
}

SplashError Splash::fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph) {
  SplashPipe pipe;
  SplashClipResult clipRes;
  GBool noClip;
  int alpha0, alpha;
  Guchar *p;
  int x1, y1, xx, xx1, yy;

  if ((clipRes = state->clip->testRect(x0 - glyph->x,
				       y0 - glyph->y,
				       x0 - glyph->x + glyph->w - 1,
				       y0 - glyph->y + glyph->h - 1))
      != splashClipAllOutside) {
    noClip = clipRes == splashClipAllInside;

    if (noClip) {
      if (glyph->aa) {
	pipeInit(&pipe, x0 - glyph->x, y0 - glyph->y,
		 state->fillPattern, NULL, state->fillAlpha, gTrue, gFalse);
	p = glyph->data;
	for (yy = 0, y1 = y0 - glyph->y; yy < glyph->h; ++yy, ++y1) {
	  pipeSetXY(&pipe, x0 - glyph->x, y1);
	  for (xx = 0, x1 = x0 - glyph->x; xx < glyph->w; ++xx, ++x1) {
	    alpha = *p++;
	    if (alpha != 0) {
	      pipe.shape = (SplashCoord)(alpha / 255.0);
	      pipeRun(&pipe);
	      updateModX(x1);
	      updateModY(y1);
	    } else {
	      pipeIncX(&pipe);
	    }
	  }
	}
      } else {
	pipeInit(&pipe, x0 - glyph->x, y0 - glyph->y,
		 state->fillPattern, NULL, state->fillAlpha, gFalse, gFalse);
	p = glyph->data;
	for (yy = 0, y1 = y0 - glyph->y; yy < glyph->h; ++yy, ++y1) {
	  pipeSetXY(&pipe, x0 - glyph->x, y1);
	  for (xx = 0, x1 = x0 - glyph->x; xx < glyph->w; xx += 8) {
	    alpha0 = *p++;
	    for (xx1 = 0; xx1 < 8 && xx + xx1 < glyph->w; ++xx1, ++x1) {
	      if (alpha0 & 0x80) {
		pipeRun(&pipe);
		updateModX(x1);
		updateModY(y1);
	      } else {
		pipeIncX(&pipe);
	      }
	      alpha0 <<= 1;
	    }
	  }
	}
      }
    } else {
      if (glyph->aa) {
	pipeInit(&pipe, x0 - glyph->x, y0 - glyph->y,
		 state->fillPattern, NULL, state->fillAlpha, gTrue, gFalse);
	p = glyph->data;
	for (yy = 0, y1 = y0 - glyph->y; yy < glyph->h; ++yy, ++y1) {
	  pipeSetXY(&pipe, x0 - glyph->x, y1);
	  for (xx = 0, x1 = x0 - glyph->x; xx < glyph->w; ++xx, ++x1) {
	    if (state->clip->test(x1, y1)) {
	      alpha = *p++;
	      if (alpha != 0) {
		pipe.shape = (SplashCoord)(alpha / 255.0);
		pipeRun(&pipe);
		updateModX(x1);
		updateModY(y1);
	      } else {
		pipeIncX(&pipe);
	      }
	    } else {
	      pipeIncX(&pipe);
	      ++p;
	    }
	  }
	}
      } else {
	pipeInit(&pipe, x0 - glyph->x, y0 - glyph->y,
		 state->fillPattern, NULL, state->fillAlpha, gFalse, gFalse);
	p = glyph->data;
	for (yy = 0, y1 = y0 - glyph->y; yy < glyph->h; ++yy, ++y1) {
	  pipeSetXY(&pipe, x0 - glyph->x, y1);
	  for (xx = 0, x1 = x0 - glyph->x; xx < glyph->w; xx += 8) {
	    alpha0 = *p++;
	    for (xx1 = 0; xx1 < 8 && xx + xx1 < glyph->w; ++xx1, ++x1) {
	      if (state->clip->test(x1, y1)) {
		if (alpha0 & 0x80) {
		  pipeRun(&pipe);
		  updateModX(x1);
		  updateModY(y1);
		} else {
		  pipeIncX(&pipe);
		}
	      } else {
		pipeIncX(&pipe);
	      }
	      alpha0 <<= 1;
	    }
	  }
	}
      }
    }
  }
  opClipRes = clipRes;

  return splashOk;
}

SplashError Splash::fillImageMask(SplashImageMaskSource src, void *srcData,
				  int w, int h, SplashCoord *mat,
				  GBool glyphMode) {
  SplashPipe pipe;
  GBool rot;
  SplashCoord xScale, yScale, xShear, yShear, yShear1;
  int tx, tx2, ty, ty2, scaledWidth, scaledHeight, xSign, ySign;
  int ulx, uly, llx, lly, urx, ury, lrx, lry;
  int ulx1, uly1, llx1, lly1, urx1, ury1, lrx1, lry1;
  int xMin, xMax, yMin, yMax;
  SplashClipResult clipRes, clipRes2;
  int yp, yq, yt, yStep, lastYStep;
  int xp, xq, xt, xStep, xSrc;
  int k1, spanXMin, spanXMax, spanY;
  SplashColorPtr pixBuf, p;
  int pixAcc;
  int x, y, x1, x2, y2;
  SplashCoord y1;
  int n, m, i, j;

  if (debugMode) {
    printf("fillImageMask: w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n",
	   w, h, (double)mat[0], (double)mat[1], (double)mat[2],
	   (double)mat[3], (double)mat[4], (double)mat[5]);
  }

  // check for singular matrix
  if (splashAbs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.000001) {
    return splashErrSingularMatrix;
  }

  // compute scale, shear, rotation, translation parameters
  rot = splashAbs(mat[1]) > splashAbs(mat[0]);
  if (rot) {
    xScale = -mat[1];
    yScale = mat[2] - (mat[0] * mat[3]) / mat[1];
    xShear = -mat[3] / yScale;
    yShear = -mat[0] / mat[1];
  } else {
    xScale = mat[0];
    yScale = mat[3] - (mat[1] * mat[2]) / mat[0];
    xShear = mat[2] / yScale;
    yShear = mat[1] / mat[0];
  }
  // Note 1: The PDF spec says that all pixels whose *centers* lie
  // within the region get painted -- but that doesn't seem to match
  // up with what Acrobat actually does: it ends up leaving gaps
  // between image stripes.  So we use the same rule here as for
  // fills: any pixel that overlaps the region gets painted.
  // Note 2: The "glyphMode" flag is a kludge: it switches back to
  // "correct" behavior (matching the spec), for use in rendering Type
  // 3 fonts.
  // Note 3: The +/-0.01 in these computations is to avoid floating
  // point precision problems which can lead to gaps between image
  // stripes (it can cause image stripes to overlap, but that's a much
  // less visible problem).
  if (glyphMode) {
    if (xScale >= 0) {
      tx = splashRound(mat[4]);
      tx2 = splashRound(mat[4] + xScale) - 1;
    } else {
      tx = splashRound(mat[4]) - 1;
      tx2 = splashRound(mat[4] + xScale);
    }
  } else {
    if (xScale >= 0) {
      tx = splashFloor(mat[4] - 0.01);
      tx2 = splashFloor(mat[4] + xScale + 0.01);
    } else {
      tx = splashFloor(mat[4] + 0.01);
      tx2 = splashFloor(mat[4] + xScale - 0.01);
    }
  }
  scaledWidth = abs(tx2 - tx) + 1;
  if (glyphMode) {
    if (yScale >= 0) {
      ty = splashRound(mat[5]);
      ty2 = splashRound(mat[5] + yScale) - 1;
    } else {
      ty = splashRound(mat[5]) - 1;
      ty2 = splashRound(mat[5] + yScale);
    }
  } else {
    if (yScale >= 0) {
      ty = splashFloor(mat[5] - 0.01);
      ty2 = splashFloor(mat[5] + yScale + 0.01);
    } else {
      ty = splashFloor(mat[5] + 0.01);
      ty2 = splashFloor(mat[5] + yScale - 0.01);
    }
  }
  scaledHeight = abs(ty2 - ty) + 1;
  xSign = (xScale < 0) ? -1 : 1;
  ySign = (yScale < 0) ? -1 : 1;
  yShear1 = (SplashCoord)xSign * yShear;

  // clipping
  ulx1 = 0;
  uly1 = 0;
  urx1 = xSign * (scaledWidth - 1);
  ury1 = (int)(yShear * urx1);
  llx1 = splashRound(xShear * ySign * (scaledHeight - 1));
  lly1 = ySign * (scaledHeight - 1) + (int)(yShear * llx1);
  lrx1 = xSign * (scaledWidth - 1) +
           splashRound(xShear * ySign * (scaledHeight - 1));
  lry1 = ySign * (scaledHeight - 1) + (int)(yShear * lrx1);
  if (rot) {
    ulx = tx + uly1;    uly = ty - ulx1;
    urx = tx + ury1;    ury = ty - urx1;
    llx = tx + lly1;    lly = ty - llx1;
    lrx = tx + lry1;    lry = ty - lrx1;
  } else {
    ulx = tx + ulx1;    uly = ty + uly1;
    urx = tx + urx1;    ury = ty + ury1;
    llx = tx + llx1;    lly = ty + lly1;
    lrx = tx + lrx1;    lry = ty + lry1;
  }
  xMin = (ulx < urx) ? (ulx < llx) ? (ulx < lrx) ? ulx : lrx
                                   : (llx < lrx) ? llx : lrx
		     : (urx < llx) ? (urx < lrx) ? urx : lrx
                                   : (llx < lrx) ? llx : lrx;
  xMax = (ulx > urx) ? (ulx > llx) ? (ulx > lrx) ? ulx : lrx
                                   : (llx > lrx) ? llx : lrx
		     : (urx > llx) ? (urx > lrx) ? urx : lrx
                                   : (llx > lrx) ? llx : lrx;
  yMin = (uly < ury) ? (uly < lly) ? (uly < lry) ? uly : lry
                                   : (lly < lry) ? lly : lry
		     : (ury < lly) ? (ury < lry) ? ury : lry
                                   : (lly < lry) ? lly : lry;
  yMax = (uly > ury) ? (uly > lly) ? (uly > lry) ? uly : lry
                                   : (lly > lry) ? lly : lry
		     : (ury > lly) ? (ury > lry) ? ury : lry
                                   : (lly > lry) ? lly : lry;
  clipRes = state->clip->testRect(xMin, yMin, xMax, yMax);
  opClipRes = clipRes;

  // compute Bresenham parameters for x and y scaling
  yp = h / scaledHeight;
  yq = h % scaledHeight;
  xp = w / scaledWidth;
  xq = w % scaledWidth;

  // allocate pixel buffer
  pixBuf = (SplashColorPtr)gmalloc((yp + 1) * w);

  // initialize the pixel pipe
  pipeInit(&pipe, 0, 0, state->fillPattern, NULL, state->fillAlpha,
	   gTrue, gFalse);
  if (vectorAntialias) {
    drawAAPixelInit();
  }

  // init y scale Bresenham
  yt = 0;
  lastYStep = 1;

  for (y = 0; y < scaledHeight; ++y) {

    // y scale Bresenham
    yStep = yp;
    yt += yq;
    if (yt >= scaledHeight) {
      yt -= scaledHeight;
      ++yStep;
    }

    // read row(s) from image
    n = (yp > 0) ? yStep : lastYStep;
    if (n > 0) {
      p = pixBuf;
      for (i = 0; i < n; ++i) {
	(*src)(srcData, p);
	p += w;
      }
    }
    lastYStep = yStep;

    // loop-invariant constants
    k1 = splashRound(xShear * ySign * y);

    // clipping test
    if (clipRes != splashClipAllInside &&
	!rot &&
	(int)(yShear * k1) ==
	  (int)(yShear * (xSign * (scaledWidth - 1) + k1))) {
      if (xSign > 0) {
	spanXMin = tx + k1;
	spanXMax = spanXMin + (scaledWidth - 1);
      } else {
	spanXMax = tx + k1;
	spanXMin = spanXMax - (scaledWidth - 1);
      }
      spanY = ty + ySign * y + (int)(yShear * k1);
      clipRes2 = state->clip->testSpan(spanXMin, spanXMax, spanY);
      if (clipRes2 == splashClipAllOutside) {
	continue;
      }
    } else {
      clipRes2 = clipRes;
    }

    // init x scale Bresenham
    xt = 0;
    xSrc = 0;

    // x shear
    x1 = k1;

    // y shear
    y1 = (SplashCoord)ySign * y + yShear * x1;
    // this is a kludge: if yShear1 is negative, then (int)y1 would
    // change immediately after the first pixel, which is not what we
    // want
    if (yShear1 < 0) {
      y1 += 0.999;
    }

    // loop-invariant constants
    n = yStep > 0 ? yStep : 1;

    for (x = 0; x < scaledWidth; ++x) {

      // x scale Bresenham
      xStep = xp;
      xt += xq;
      if (xt >= scaledWidth) {
	xt -= scaledWidth;
	++xStep;
      }

      // rotation
      if (rot) {
	x2 = (int)y1;
	y2 = -x1;
      } else {
	x2 = x1;
	y2 = (int)y1;
      }

      // compute the alpha value for (x,y) after the x and y scaling
      // operations
      m = xStep > 0 ? xStep : 1;
      p = pixBuf + xSrc;
      pixAcc = 0;
      for (i = 0; i < n; ++i) {
	for (j = 0; j < m; ++j) {
	  pixAcc += *p++;
	}
	p += w - m;
      }

      // blend fill color with background
      if (pixAcc != 0) {
	pipe.shape = (pixAcc == n * m)
	                 ? (SplashCoord)1
	                 : (SplashCoord)pixAcc / (SplashCoord)(n * m);
	if (vectorAntialias && clipRes2 != splashClipAllInside) {
	  drawAAPixel(&pipe, tx + x2, ty + y2);
	} else {
	  drawPixel(&pipe, tx + x2, ty + y2, clipRes2 == splashClipAllInside);
	}
      }

      // x scale Bresenham
      xSrc += xStep;

      // x shear
      x1 += xSign;

      // y shear
      y1 += yShear1;
    }
  }

  // free memory
  gfree(pixBuf);

  return splashOk;
}

SplashError Splash::drawImage(SplashImageSource src, void *srcData,
			      SplashColorMode srcMode, GBool srcAlpha,
			      int w, int h, SplashCoord *mat) {
  SplashPipe pipe;
  GBool ok, rot;
  SplashCoord xScale, yScale, xShear, yShear, yShear1;
  int tx, tx2, ty, ty2, scaledWidth, scaledHeight, xSign, ySign;
  int ulx, uly, llx, lly, urx, ury, lrx, lry;
  int ulx1, uly1, llx1, lly1, urx1, ury1, lrx1, lry1;
  int xMin, xMax, yMin, yMax;
  SplashClipResult clipRes, clipRes2;
  int yp, yq, yt, yStep, lastYStep;
  int xp, xq, xt, xStep, xSrc;
  int k1, spanXMin, spanXMax, spanY;
  SplashColorPtr colorBuf, p;
  SplashColor pix;
  Guchar *alphaBuf, *q;
#if SPLASH_CMYK
  int pixAcc0, pixAcc1, pixAcc2, pixAcc3;
#else
  int pixAcc0, pixAcc1, pixAcc2;
#endif
  int alphaAcc;
  SplashCoord pixMul, alphaMul, alpha;
  int x, y, x1, x2, y2;
  SplashCoord y1;
  int nComps, n, m, i, j;

  if (debugMode) {
    printf("drawImage: srcMode=%d srcAlpha=%d w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n",
	   srcMode, srcAlpha, w, h, (double)mat[0], (double)mat[1], (double)mat[2],
	   (double)mat[3], (double)mat[4], (double)mat[5]);
  }

  // check color modes
  ok = gFalse; // make gcc happy
  nComps = 0; // make gcc happy
  switch (bitmap->mode) {
  case splashModeMono1:
  case splashModeMono8:
    ok = srcMode == splashModeMono8;
    nComps = 1;
    break;
  case splashModeRGB8:
    ok = srcMode == splashModeRGB8;
    nComps = 3;
    break;
  case splashModeBGR8:
    ok = srcMode == splashModeBGR8;
    nComps = 3;
    break;
#if SPLASH_CMYK
  case splashModeCMYK8:
    ok = srcMode == splashModeCMYK8;
    nComps = 4;
    break;
#endif
  }
  if (!ok) {
    return splashErrModeMismatch;
  }

  // check for singular matrix
  if (splashAbs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.000001) {
    return splashErrSingularMatrix;
  }

  // compute scale, shear, rotation, translation parameters
  rot = splashAbs(mat[1]) > splashAbs(mat[0]);
  if (rot) {
    xScale = -mat[1];
    yScale = mat[2] - (mat[0] * mat[3]) / mat[1];
    xShear = -mat[3] / yScale;
    yShear = -mat[0] / mat[1];
  } else {
    xScale = mat[0];
    yScale = mat[3] - (mat[1] * mat[2]) / mat[0];
    xShear = mat[2] / yScale;
    yShear = mat[1] / mat[0];
  }
  // Note 1: The PDF spec says that all pixels whose *centers* lie
  // within the region get painted -- but that doesn't seem to match
  // up with what Acrobat actually does: it ends up leaving gaps
  // between image stripes.  So we use the same rule here as for
  // fills: any pixel that overlaps the region gets painted.
  // Note 2: The +/-0.01 in these computations is to avoid floating
  // point precision problems which can lead to gaps between image
  // stripes (it can cause image stripes to overlap, but that's a much
  // less visible problem).
  if (xScale >= 0) {
    tx = splashFloor(mat[4] - 0.01);
    tx2 = splashFloor(mat[4] + xScale + 0.01);
  } else {
    tx = splashFloor(mat[4] + 0.01);
    tx2 = splashFloor(mat[4] + xScale - 0.01);
  }
  scaledWidth = abs(tx2 - tx) + 1;
  if (yScale >= 0) {
    ty = splashFloor(mat[5] - 0.01);
    ty2 = splashFloor(mat[5] + yScale + 0.01);
  } else {
    ty = splashFloor(mat[5] + 0.01);
    ty2 = splashFloor(mat[5] + yScale - 0.01);
  }
  scaledHeight = abs(ty2 - ty) + 1;
  xSign = (xScale < 0) ? -1 : 1;
  ySign = (yScale < 0) ? -1 : 1;
  yShear1 = (SplashCoord)xSign * yShear;

  // clipping
  ulx1 = 0;
  uly1 = 0;
  urx1 = xSign * (scaledWidth - 1);
  ury1 = (int)(yShear * urx1);
  llx1 = splashRound(xShear * ySign * (scaledHeight - 1));
  lly1 = ySign * (scaledHeight - 1) + (int)(yShear * llx1);
  lrx1 = xSign * (scaledWidth - 1) +
           splashRound(xShear * ySign * (scaledHeight - 1));
  lry1 = ySign * (scaledHeight - 1) + (int)(yShear * lrx1);
  if (rot) {
    ulx = tx + uly1;    uly = ty - ulx1;
    urx = tx + ury1;    ury = ty - urx1;
    llx = tx + lly1;    lly = ty - llx1;
    lrx = tx + lry1;    lry = ty - lrx1;
  } else {
    ulx = tx + ulx1;    uly = ty + uly1;
    urx = tx + urx1;    ury = ty + ury1;
    llx = tx + llx1;    lly = ty + lly1;
    lrx = tx + lrx1;    lry = ty + lry1;
  }
  xMin = (ulx < urx) ? (ulx < llx) ? (ulx < lrx) ? ulx : lrx
                                   : (llx < lrx) ? llx : lrx
		     : (urx < llx) ? (urx < lrx) ? urx : lrx
                                   : (llx < lrx) ? llx : lrx;
  xMax = (ulx > urx) ? (ulx > llx) ? (ulx > lrx) ? ulx : lrx
                                   : (llx > lrx) ? llx : lrx
		     : (urx > llx) ? (urx > lrx) ? urx : lrx
                                   : (llx > lrx) ? llx : lrx;
  yMin = (uly < ury) ? (uly < lly) ? (uly < lry) ? uly : lry
                                   : (lly < lry) ? lly : lry
		     : (ury < lly) ? (ury < lry) ? ury : lry
                                   : (lly < lry) ? lly : lry;
  yMax = (uly > ury) ? (uly > lly) ? (uly > lry) ? uly : lry
                                   : (lly > lry) ? lly : lry
		     : (ury > lly) ? (ury > lry) ? ury : lry
                                   : (lly > lry) ? lly : lry;
  clipRes = state->clip->testRect(xMin, yMin, xMax, yMax);
  opClipRes = clipRes;
  if (clipRes == splashClipAllOutside) {
    return splashOk;
  }

  // compute Bresenham parameters for x and y scaling
  yp = h / scaledHeight;
  yq = h % scaledHeight;
  xp = w / scaledWidth;
  xq = w % scaledWidth;

  // allocate pixel buffers
  colorBuf = (SplashColorPtr)gmalloc((yp + 1) * w * nComps);
  if (srcAlpha) {
    alphaBuf = (Guchar *)gmalloc((yp + 1) * w);
  } else {
    alphaBuf = NULL;
  }

  pixAcc0 = pixAcc1 = pixAcc2 = 0; // make gcc happy
#if SPLASH_CMYK
  pixAcc3 = 0; // make gcc happy
#endif

  // initialize the pixel pipe
  pipeInit(&pipe, 0, 0, NULL, pix, state->fillAlpha,
	   srcAlpha || (vectorAntialias && clipRes != splashClipAllInside),
	   gFalse);
  if (vectorAntialias) {
    drawAAPixelInit();
  }

  if (srcAlpha) {

    // init y scale Bresenham
    yt = 0;
    lastYStep = 1;

    for (y = 0; y < scaledHeight; ++y) {

      // y scale Bresenham
      yStep = yp;
      yt += yq;
      if (yt >= scaledHeight) {
	yt -= scaledHeight;
	++yStep;
      }

      // read row(s) from image
      n = (yp > 0) ? yStep : lastYStep;
      if (n > 0) {
	p = colorBuf;
	q = alphaBuf;
	for (i = 0; i < n; ++i) {
	  (*src)(srcData, p, q);
	  p += w * nComps;
	  q += w;
	}
      }
      lastYStep = yStep;

      // loop-invariant constants
      k1 = splashRound(xShear * ySign * y);

      // clipping test
      if (clipRes != splashClipAllInside &&
	  !rot &&
	  (int)(yShear * k1) ==
	    (int)(yShear * (xSign * (scaledWidth - 1) + k1))) {
	if (xSign > 0) {
	  spanXMin = tx + k1;
	  spanXMax = spanXMin + (scaledWidth - 1);
	} else {
	  spanXMax = tx + k1;
	  spanXMin = spanXMax - (scaledWidth - 1);
	}
	spanY = ty + ySign * y + (int)(yShear * k1);
	clipRes2 = state->clip->testSpan(spanXMin, spanXMax, spanY);
	if (clipRes2 == splashClipAllOutside) {
	  continue;
	}
      } else {
	clipRes2 = clipRes;
      }

      // init x scale Bresenham
      xt = 0;
      xSrc = 0;

      // x shear
      x1 = k1;

      // y shear
      y1 = (SplashCoord)ySign * y + yShear * x1;
      // this is a kludge: if yShear1 is negative, then (int)y1 would
      // change immediately after the first pixel, which is not what
      // we want
      if (yShear1 < 0) {
	y1 += 0.999;
      }

      // loop-invariant constants
      n = yStep > 0 ? yStep : 1;

      switch (srcMode) {

      case splashModeMono1:
      case splashModeMono8:
	for (x = 0; x < scaledWidth; ++x) {

	  // x scale Bresenham
	  xStep = xp;
	  xt += xq;
	  if (xt >= scaledWidth) {
	    xt -= scaledWidth;
	    ++xStep;
	  }

	  // rotation
	  if (rot) {
	    x2 = (int)y1;
	    y2 = -x1;
	  } else {
	    x2 = x1;
	    y2 = (int)y1;
	  }

	  // compute the filtered pixel at (x,y) after the x and y scaling
	  // operations
	  m = xStep > 0 ? xStep : 1;
	  alphaAcc = 0;
	  p = colorBuf + xSrc;
	  q = alphaBuf + xSrc;
	  pixAcc0 = 0;
	  for (i = 0; i < n; ++i) {
	    for (j = 0; j < m; ++j) {
	      pixAcc0 += *p++;
	      alphaAcc += *q++;
	    }
	    p += w - m;
	    q += w - m;
	  }
	  pixMul = (SplashCoord)1 / (SplashCoord)(n * m);
	  alphaMul = pixMul * (1.0 / 255.0);
	  alpha = (SplashCoord)alphaAcc * alphaMul;

	  if (alpha > 0) {
	    pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);

	    // set pixel
	    pipe.shape = alpha;
	    if (vectorAntialias && clipRes != splashClipAllInside) {
	      drawAAPixel(&pipe, tx + x2, ty + y2);
	    } else {
	      drawPixel(&pipe, tx + x2, ty + y2,
			clipRes2 == splashClipAllInside);
	    }
	  }

	  // x scale Bresenham
	  xSrc += xStep;

	  // x shear
	  x1 += xSign;

	  // y shear
	  y1 += yShear1;
	}
	break;

      case splashModeRGB8:
      case splashModeBGR8:
	for (x = 0; x < scaledWidth; ++x) {

	  // x scale Bresenham
	  xStep = xp;
	  xt += xq;
	  if (xt >= scaledWidth) {
	    xt -= scaledWidth;
	    ++xStep;
	  }

	  // rotation
	  if (rot) {
	    x2 = (int)y1;
	    y2 = -x1;
	  } else {
	    x2 = x1;
	    y2 = (int)y1;
	  }

	  // compute the filtered pixel at (x,y) after the x and y scaling
	  // operations
	  m = xStep > 0 ? xStep : 1;
	  alphaAcc = 0;
	  p = colorBuf + xSrc * 3;
	  q = alphaBuf + xSrc;
	  pixAcc0 = pixAcc1 = pixAcc2 = 0;
	  for (i = 0; i < n; ++i) {
	    for (j = 0; j < m; ++j) {
	      pixAcc0 += *p++;
	      pixAcc1 += *p++;
	      pixAcc2 += *p++;
	      alphaAcc += *q++;
	    }
	    p += 3 * (w - m);
	    q += w - m;
	  }
	  pixMul = (SplashCoord)1 / (SplashCoord)(n * m);
	  alphaMul = pixMul * (1.0 / 255.0);
	  alpha = (SplashCoord)alphaAcc * alphaMul;

	  if (alpha > 0) {
	    pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);
	    pix[1] = (int)((SplashCoord)pixAcc1 * pixMul);
	    pix[2] = (int)((SplashCoord)pixAcc2 * pixMul);

	    // set pixel
	    pipe.shape = alpha;
	    if (vectorAntialias && clipRes != splashClipAllInside) {
	      drawAAPixel(&pipe, tx + x2, ty + y2);
	    } else {
	      drawPixel(&pipe, tx + x2, ty + y2,
			clipRes2 == splashClipAllInside);
	    }
	  }

	  // x scale Bresenham
	  xSrc += xStep;

	  // x shear
	  x1 += xSign;

	  // y shear
	  y1 += yShear1;
	}
	break;

#if SPLASH_CMYK
      case splashModeCMYK8:
	for (x = 0; x < scaledWidth; ++x) {

	  // x scale Bresenham
	  xStep = xp;
	  xt += xq;
	  if (xt >= scaledWidth) {
	    xt -= scaledWidth;
	    ++xStep;
	  }

	  // rotation
	  if (rot) {
	    x2 = (int)y1;
	    y2 = -x1;
	  } else {
	    x2 = x1;
	    y2 = (int)y1;
	  }

	  // compute the filtered pixel at (x,y) after the x and y scaling
	  // operations
	  m = xStep > 0 ? xStep : 1;
	  alphaAcc = 0;
	  p = colorBuf + xSrc * 4;
	  q = alphaBuf + xSrc;
	  pixAcc0 = pixAcc1 = pixAcc2 = pixAcc3 = 0;
	  for (i = 0; i < n; ++i) {
	    for (j = 0; j < m; ++j) {
	      pixAcc0 += *p++;
	      pixAcc1 += *p++;
	      pixAcc2 += *p++;
	      pixAcc3 += *p++;
	      alphaAcc += *q++;
	    }
	    p += 4 * (w - m);
	    q += w - m;
	  }
	  pixMul = (SplashCoord)1 / (SplashCoord)(n * m);
	  alphaMul = pixMul * (1.0 / 255.0);
	  alpha = (SplashCoord)alphaAcc * alphaMul;

	  if (alpha > 0) {
	    pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);
	    pix[1] = (int)((SplashCoord)pixAcc1 * pixMul);
	    pix[2] = (int)((SplashCoord)pixAcc2 * pixMul);
	    pix[3] = (int)((SplashCoord)pixAcc3 * pixMul);

	    // set pixel
	    pipe.shape = alpha;
	    if (vectorAntialias && clipRes != splashClipAllInside) {
	      drawAAPixel(&pipe, tx + x2, ty + y2);
	    } else {
	      drawPixel(&pipe, tx + x2, ty + y2,
			clipRes2 == splashClipAllInside);
	    }
	  }

	  // x scale Bresenham
	  xSrc += xStep;

	  // x shear
	  x1 += xSign;

	  // y shear
	  y1 += yShear1;
	}
	break;
#endif // SPLASH_CMYK
      }
    }

  } else {

    // init y scale Bresenham
    yt = 0;
    lastYStep = 1;

    for (y = 0; y < scaledHeight; ++y) {

      // y scale Bresenham
      yStep = yp;
      yt += yq;
      if (yt >= scaledHeight) {
	yt -= scaledHeight;
	++yStep;
      }

      // read row(s) from image
      n = (yp > 0) ? yStep : lastYStep;
      if (n > 0) {
	p = colorBuf;
	for (i = 0; i < n; ++i) {
	  (*src)(srcData, p, NULL);
	  p += w * nComps;
	}
      }
      lastYStep = yStep;

      // loop-invariant constants
      k1 = splashRound(xShear * ySign * y);

      // clipping test
      if (clipRes != splashClipAllInside &&
	  !rot &&
	  (int)(yShear * k1) ==
	    (int)(yShear * (xSign * (scaledWidth - 1) + k1))) {
	if (xSign > 0) {
	  spanXMin = tx + k1;
	  spanXMax = spanXMin + (scaledWidth - 1);
	} else {
	  spanXMax = tx + k1;
	  spanXMin = spanXMax - (scaledWidth - 1);
	}
	spanY = ty + ySign * y + (int)(yShear * k1);
	clipRes2 = state->clip->testSpan(spanXMin, spanXMax, spanY);
	if (clipRes2 == splashClipAllOutside) {
	  continue;
	}
      } else {
	clipRes2 = clipRes;
      }

      // init x scale Bresenham
      xt = 0;
      xSrc = 0;

      // x shear
      x1 = k1;

      // y shear
      y1 = (SplashCoord)ySign * y + yShear * x1;
      // this is a kludge: if yShear1 is negative, then (int)y1 would
      // change immediately after the first pixel, which is not what
      // we want
      if (yShear1 < 0) {
	y1 += 0.999;
      }

      // loop-invariant constants
      n = yStep > 0 ? yStep : 1;

      switch (srcMode) {

      case splashModeMono1:
      case splashModeMono8:
	for (x = 0; x < scaledWidth; ++x) {

	  // x scale Bresenham
	  xStep = xp;
	  xt += xq;
	  if (xt >= scaledWidth) {
	    xt -= scaledWidth;
	    ++xStep;
	  }

	  // rotation
	  if (rot) {
	    x2 = (int)y1;
	    y2 = -x1;
	  } else {
	    x2 = x1;
	    y2 = (int)y1;
	  }

	  // compute the filtered pixel at (x,y) after the x and y scaling
	  // operations
	  m = xStep > 0 ? xStep : 1;
	  p = colorBuf + xSrc;
	  pixAcc0 = 0;
	  for (i = 0; i < n; ++i) {
	    for (j = 0; j < m; ++j) {
	      pixAcc0 += *p++;
	    }
	    p += w - m;
	  }
	  pixMul = (SplashCoord)1 / (SplashCoord)(n * m);

	  pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);

	  // set pixel
	  if (vectorAntialias && clipRes != splashClipAllInside) {
	    pipe.shape = (SplashCoord)1;
	    drawAAPixel(&pipe, tx + x2, ty + y2);
	  } else {
	    drawPixel(&pipe, tx + x2, ty + y2,
		      clipRes2 == splashClipAllInside);
	  }

	  // x scale Bresenham
	  xSrc += xStep;

	  // x shear
	  x1 += xSign;

	  // y shear
	  y1 += yShear1;
	}
	break;

      case splashModeRGB8:
      case splashModeBGR8:
	for (x = 0; x < scaledWidth; ++x) {

	  // x scale Bresenham
	  xStep = xp;
	  xt += xq;
	  if (xt >= scaledWidth) {
	    xt -= scaledWidth;
	    ++xStep;
	  }

	  // rotation
	  if (rot) {
	    x2 = (int)y1;
	    y2 = -x1;
	  } else {
	    x2 = x1;
	    y2 = (int)y1;
	  }

	  // compute the filtered pixel at (x,y) after the x and y scaling
	  // operations
	  m = xStep > 0 ? xStep : 1;
	  p = colorBuf + xSrc * 3;
	  pixAcc0 = pixAcc1 = pixAcc2 = 0;
	  for (i = 0; i < n; ++i) {
	    for (j = 0; j < m; ++j) {
	      pixAcc0 += *p++;
	      pixAcc1 += *p++;
	      pixAcc2 += *p++;
	    }
	    p += 3 * (w - m);
	  }
	  pixMul = (SplashCoord)1 / (SplashCoord)(n * m);

	  pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);
	  pix[1] = (int)((SplashCoord)pixAcc1 * pixMul);
	  pix[2] = (int)((SplashCoord)pixAcc2 * pixMul);

	  // set pixel
	  if (vectorAntialias && clipRes != splashClipAllInside) {
	    pipe.shape = (SplashCoord)1;
	    drawAAPixel(&pipe, tx + x2, ty + y2);
	  } else {
	    drawPixel(&pipe, tx + x2, ty + y2,
		      clipRes2 == splashClipAllInside);
	  }

	  // x scale Bresenham
	  xSrc += xStep;

	  // x shear
	  x1 += xSign;

	  // y shear
	  y1 += yShear1;
	}
	break;

#if SPLASH_CMYK
      case splashModeCMYK8:
	for (x = 0; x < scaledWidth; ++x) {

	  // x scale Bresenham
	  xStep = xp;
	  xt += xq;
	  if (xt >= scaledWidth) {
	    xt -= scaledWidth;
	    ++xStep;
	  }

	  // rotation
	  if (rot) {
	    x2 = (int)y1;
	    y2 = -x1;
	  } else {
	    x2 = x1;
	    y2 = (int)y1;
	  }

	  // compute the filtered pixel at (x,y) after the x and y scaling
	  // operations
	  m = xStep > 0 ? xStep : 1;
	  p = colorBuf + xSrc * 4;
	  pixAcc0 = pixAcc1 = pixAcc2 = pixAcc3 = 0;
	  for (i = 0; i < n; ++i) {
	    for (j = 0; j < m; ++j) {
	      pixAcc0 += *p++;
	      pixAcc1 += *p++;
	      pixAcc2 += *p++;
	      pixAcc3 += *p++;
	    }
	    p += 4 * (w - m);
	  }
	  pixMul = (SplashCoord)1 / (SplashCoord)(n * m);

	  pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);
	  pix[1] = (int)((SplashCoord)pixAcc1 * pixMul);
	  pix[2] = (int)((SplashCoord)pixAcc2 * pixMul);
	  pix[3] = (int)((SplashCoord)pixAcc3 * pixMul);

	  // set pixel
	  if (vectorAntialias && clipRes != splashClipAllInside) {
	    pipe.shape = (SplashCoord)1;
	    drawAAPixel(&pipe, tx + x2, ty + y2);
	  } else {
	    drawPixel(&pipe, tx + x2, ty + y2,
		      clipRes2 == splashClipAllInside);
	  }

	  // x scale Bresenham
	  xSrc += xStep;

	  // x shear
	  x1 += xSign;

	  // y shear
	  y1 += yShear1;
	}
	break;
#endif // SPLASH_CMYK
      }
    }

  }

  gfree(colorBuf);
  gfree(alphaBuf);

  return splashOk;
}

SplashError Splash::composite(SplashBitmap *src, int xSrc, int ySrc,
			      int xDest, int yDest, int w, int h,
			      GBool noClip, GBool nonIsolated) {
  SplashPipe pipe;
  SplashColor pixel;
  Guchar alpha;
  Guchar *ap;
  int x, y;

  if (src->mode != bitmap->mode) {
    return splashErrModeMismatch;
  }

  if (src->alpha) {
    pipeInit(&pipe, xDest, yDest, NULL, pixel, state->fillAlpha,
	     gTrue, nonIsolated);
    for (y = 0; y < h; ++y) {
      pipeSetXY(&pipe, xDest, yDest + y);
      ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc;
      for (x = 0; x < w; ++x) {
	src->getPixel(xSrc + x, ySrc + y, pixel);
	alpha = *ap++;
	if (noClip || state->clip->test(xDest + x, yDest + y)) {
	  // this uses shape instead of alpha, which isn't technically
	  // correct, but works out the same
	  pipe.shape = (SplashCoord)(alpha / 255.0);
	  pipeRun(&pipe);
	  updateModX(xDest + x);
	  updateModY(yDest + y);
	} else {
	  pipeIncX(&pipe);
	}
      }
    }
  } else {
    pipeInit(&pipe, xDest, yDest, NULL, pixel, state->fillAlpha,
	     gFalse, nonIsolated);
    for (y = 0; y < h; ++y) {
      pipeSetXY(&pipe, xDest, yDest + y);
      for (x = 0; x < w; ++x) {
	src->getPixel(xSrc + x, ySrc + y, pixel);
	if (noClip || state->clip->test(xDest + x, yDest + y)) {
	  pipeRun(&pipe);
	  updateModX(xDest + x);
	  updateModY(yDest + y);
	} else {
	  pipeIncX(&pipe);
	}
      }
    }
  }

  return splashOk;
}

void Splash::compositeBackground(SplashColorPtr color) {
  SplashColorPtr p;
  Guchar *q;
  Guchar alpha, alpha1, c, color0, color1, color2, color3;
  int x, y, mask;

  switch (bitmap->mode) {
  case splashModeMono1:
    color0 = color[0];
    for (y = 0; y < bitmap->height; ++y) {
      p = &bitmap->data[y * bitmap->rowSize];
      q = &bitmap->alpha[y * bitmap->width];
      mask = 0x80;
      for (x = 0; x < bitmap->width; ++x) {
	alpha = *q++;
	alpha1 = 255 - alpha;
	c = (*p & mask) ? 0xff : 0x00;
	c = div255(alpha1 * color0 + alpha * c);
	if (c & 0x80) {
	  *p |= mask;
	} else {
	  *p &= ~mask;
	}
	if (!(mask >>= 1)) {
	  mask = 0x80;
	  ++p;
	}
      }
    }
    break;
  case splashModeMono8:
    color0 = color[0];
    for (y = 0; y < bitmap->height; ++y) {
      p = &bitmap->data[y * bitmap->rowSize];
      q = &bitmap->alpha[y * bitmap->width];
      for (x = 0; x < bitmap->width; ++x) {
	alpha = *q++;
	alpha1 = 255 - alpha;
	p[0] = div255(alpha1 * color0 + alpha * p[0]);
	++p;
      }
    }
    break;
  case splashModeRGB8:
  case splashModeBGR8:
    color0 = color[0];
    color1 = color[1];
    color2 = color[2];
    for (y = 0; y < bitmap->height; ++y) {
      p = &bitmap->data[y * bitmap->rowSize];
      q = &bitmap->alpha[y * bitmap->width];
      for (x = 0; x < bitmap->width; ++x) {
	alpha = *q++;
	alpha1 = 255 - alpha;
	p[0] = div255(alpha1 * color0 + alpha * p[0]);
	p[1] = div255(alpha1 * color1 + alpha * p[1]);
	p[2] = div255(alpha1 * color2 + alpha * p[2]);
	p += 3;
      }
    }
    break;
#if SPLASH_CMYK
  case splashModeCMYK8:
    color0 = color[0];
    color1 = color[1];
    color2 = color[2];
    color3 = color[3];
    for (y = 0; y < bitmap->height; ++y) {
      p = &bitmap->data[y * bitmap->rowSize];
      q = &bitmap->alpha[y * bitmap->width];
      for (x = 0; x < bitmap->width; ++x) {
	alpha = *q++;
	alpha1 = 255 - alpha;
	p[0] = div255(alpha1 * color0 + alpha * p[0]);
	p[1] = div255(alpha1 * color1 + alpha * p[1]);
	p[2] = div255(alpha1 * color2 + alpha * p[2]);
	p[3] = div255(alpha1 * color3 + alpha * p[3]);
	p += 4;
      }
    }
    break;
#endif
  }
  memset(bitmap->alpha, 255, bitmap->width * bitmap->height);
}

SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc,
				    int xDest, int yDest, int w, int h) {
  SplashColor pixel;
  SplashColorPtr p;
  Guchar *q;
  int x, y, mask;

  if (src->mode != bitmap->mode) {
    return splashErrModeMismatch;
  }

  switch (bitmap->mode) {
  case splashModeMono1:
    for (y = 0; y < h; ++y) {
      p = &bitmap->data[(yDest + y) * bitmap->rowSize + (xDest >> 3)];
      mask = 0x80 >> (xDest & 7);
      for (x = 0; x < w; ++x) {
	src->getPixel(xSrc + x, ySrc + y, pixel);
	if (pixel[0]) {
	  *p |= mask;
	} else {
	  *p &= ~mask;
	}
	if (!(mask >>= 1)) {
	  mask = 0x80;
	  ++p;
	}
      }
    }
    break;
  case splashModeMono8:
    for (y = 0; y < h; ++y) {
      p = &bitmap->data[(yDest + y) * bitmap->rowSize + xDest];
      for (x = 0; x < w; ++x) {
	src->getPixel(xSrc + x, ySrc + y, pixel);
	*p++ = pixel[0];
      }
    }
    break;
  case splashModeRGB8:
  case splashModeBGR8:
    for (y = 0; y < h; ++y) {
      p = &bitmap->data[(yDest + y) * bitmap->rowSize + 3 * xDest];
      for (x = 0; x < w; ++x) {
	src->getPixel(xSrc + x, ySrc + y, pixel);
	*p++ = pixel[0];
	*p++ = pixel[1];
	*p++ = pixel[2];
      }
    }
    break;
#if SPLASH_CMYK
  case splashModeCMYK8:
    for (y = 0; y < h; ++y) {
      p = &bitmap->data[(yDest + y) * bitmap->rowSize + 4 * xDest];
      for (x = 0; x < w; ++x) {
	src->getPixel(xSrc + x, ySrc + y, pixel);
	*p++ = pixel[0];
	*p++ = pixel[1];
	*p++ = pixel[2];
	*p++ = pixel[3];
      }
    }
    break;
#endif
  }

  if (bitmap->alpha) {
    for (y = 0; y < h; ++y) {
      q = &bitmap->alpha[(yDest + y) * bitmap->width + xDest];
      for (x = 0; x < w; ++x) {
	*q++ = 0x00;
      }
    }
  }

  return splashOk;
}

SplashPath *Splash::makeStrokePath(SplashPath *path, GBool flatten) {
  SplashPath *pathIn, *pathOut;
  SplashCoord w, d, dx, dy, wdx, wdy, dxNext, dyNext, wdxNext, wdyNext;
  SplashCoord crossprod, dotprod, miter, m;
  GBool first, last, closed;
  int subpathStart, next, i;
  int left0, left1, left2, right0, right1, right2, join0, join1, join2;
  int leftFirst, rightFirst, firstPt;

  if (flatten) {
    pathIn = flattenPath(path, state->matrix, state->flatness);
    if (state->lineDashLength > 0) {
      pathOut = makeDashedPath(pathIn);
      delete pathIn;
      pathIn = pathOut;
    }
  } else {
    pathIn = path;
  }

  subpathStart = 0; // make gcc happy
  closed = gFalse; // make gcc happy
  left0 = left1 = right0 = right1 = join0 = join1 = 0; // make gcc happy
  leftFirst = rightFirst = firstPt = 0; // make gcc happy

  pathOut = new SplashPath();
  w = state->lineWidth;

  for (i = 0; i < pathIn->length - 1; ++i) {
    if (pathIn->flags[i] & splashPathLast) {
      continue;
    }
    if ((first = pathIn->flags[i] & splashPathFirst)) {
      subpathStart = i;
      closed = pathIn->flags[i] & splashPathClosed;
    }
    last = pathIn->flags[i+1] & splashPathLast;

    // compute the deltas for segment (i, i+1)
    d = splashDist(pathIn->pts[i].x, pathIn->pts[i].y,
		   pathIn->pts[i+1].x, pathIn->pts[i+1].y);
    if (d == 0) {
      // we need to draw end caps on zero-length lines
      //~ not clear what the behavior should be for splashLineCapButt
      //~   with d==0
      dx = 0;
      dy = 1;
    } else {
      d = (SplashCoord)1 / d;
      dx = d * (pathIn->pts[i+1].x - pathIn->pts[i].x);
      dy = d * (pathIn->pts[i+1].y - pathIn->pts[i].y);
    }
    wdx = (SplashCoord)0.5 * w * dx;
    wdy = (SplashCoord)0.5 * w * dy;

    // compute the deltas for segment (i+1, next)
    next = last ? subpathStart + 1 : i + 2;
    d = splashDist(pathIn->pts[i+1].x, pathIn->pts[i+1].y,
		   pathIn->pts[next].x, pathIn->pts[next].y);
    if (d == 0) {
      // we need to draw end caps on zero-length lines
      //~ not clear what the behavior should be for splashLineCapButt
      //~   with d==0
      dxNext = 0;
      dyNext = 1;
    } else {
      d = (SplashCoord)1 / d;
      dxNext = d * (pathIn->pts[next].x - pathIn->pts[i+1].x);
      dyNext = d * (pathIn->pts[next].y - pathIn->pts[i+1].y);
    }
    wdxNext = (SplashCoord)0.5 * w * dxNext;
    wdyNext = (SplashCoord)0.5 * w * dyNext;

    // draw the start cap
    pathOut->moveTo(pathIn->pts[i].x - wdy, pathIn->pts[i].y + wdx);
    if (i == subpathStart) {
      firstPt = pathOut->length - 1;
    }
    if (first && !closed) {
      switch (state->lineCap) {
      case splashLineCapButt:
	pathOut->lineTo(pathIn->pts[i].x + wdy, pathIn->pts[i].y - wdx);
	break;
      case splashLineCapRound:
	pathOut->curveTo(pathIn->pts[i].x - wdy - bezierCircle * wdx,
			 pathIn->pts[i].y + wdx - bezierCircle * wdy,
			 pathIn->pts[i].x - wdx - bezierCircle * wdy,
			 pathIn->pts[i].y - wdy + bezierCircle * wdx,
			 pathIn->pts[i].x - wdx,
			 pathIn->pts[i].y - wdy);
	pathOut->curveTo(pathIn->pts[i].x - wdx + bezierCircle * wdy,
			 pathIn->pts[i].y - wdy - bezierCircle * wdx,
			 pathIn->pts[i].x + wdy - bezierCircle * wdx,
			 pathIn->pts[i].y - wdx - bezierCircle * wdy,
			 pathIn->pts[i].x + wdy,
			 pathIn->pts[i].y - wdx);
	break;
      case splashLineCapProjecting:
	pathOut->lineTo(pathIn->pts[i].x - wdx - wdy,
			pathIn->pts[i].y + wdx - wdy);
	pathOut->lineTo(pathIn->pts[i].x - wdx + wdy,
			pathIn->pts[i].y - wdx - wdy);
	pathOut->lineTo(pathIn->pts[i].x + wdy,
			pathIn->pts[i].y - wdx);
	break;
      }
    } else {
      pathOut->lineTo(pathIn->pts[i].x + wdy, pathIn->pts[i].y - wdx);
    }

    // draw the left side of the segment rectangle
    left2 = pathOut->length - 1;
    pathOut->lineTo(pathIn->pts[i+1].x + wdy, pathIn->pts[i+1].y - wdx);

    // draw the end cap
    if (last && !closed) {
      switch (state->lineCap) {
      case splashLineCapButt:
	pathOut->lineTo(pathIn->pts[i+1].x - wdy, pathIn->pts[i+1].y + wdx);
	break;
      case splashLineCapRound:
	pathOut->curveTo(pathIn->pts[i+1].x + wdy + bezierCircle * wdx,
			 pathIn->pts[i+1].y - wdx + bezierCircle * wdy,
			 pathIn->pts[i+1].x + wdx + bezierCircle * wdy,
			 pathIn->pts[i+1].y + wdy - bezierCircle * wdx,
			 pathIn->pts[i+1].x + wdx,
			 pathIn->pts[i+1].y + wdy);
	pathOut->curveTo(pathIn->pts[i+1].x + wdx - bezierCircle * wdy,
			 pathIn->pts[i+1].y + wdy + bezierCircle * wdx,
			 pathIn->pts[i+1].x - wdy + bezierCircle * wdx,
			 pathIn->pts[i+1].y + wdx + bezierCircle * wdy,
			 pathIn->pts[i+1].x - wdy,
			 pathIn->pts[i+1].y + wdx);
	break;
      case splashLineCapProjecting:
	pathOut->lineTo(pathIn->pts[i+1].x + wdy + wdx,
			pathIn->pts[i+1].y - wdx + wdy);
	pathOut->lineTo(pathIn->pts[i+1].x - wdy + wdx,
			pathIn->pts[i+1].y + wdx + wdy);
	pathOut->lineTo(pathIn->pts[i+1].x - wdy,
			pathIn->pts[i+1].y + wdx);
	break;
      }
    } else {
      pathOut->lineTo(pathIn->pts[i+1].x - wdy, pathIn->pts[i+1].y + wdx);
    }

    // draw the right side of the segment rectangle
    right2 = pathOut->length - 1;
    pathOut->close();

    // draw the join
    join2 = pathOut->length;
    if (!last || closed) {
      crossprod = dx * dyNext - dy * dxNext;
      dotprod = -(dx * dxNext + dy * dyNext);
      if (dotprod > 0.99999) {
	// avoid a divide-by-zero -- set miter to something arbitrary
	// such that sqrt(miter) will exceed miterLimit (and m is never
	// used in that situation)
	miter = (state->miterLimit + 1) * (state->miterLimit + 1);
	m = 0;
      } else {
	miter = (SplashCoord)2 / ((SplashCoord)1 - dotprod);
	if (miter < 1) {
	  // this can happen because of floating point inaccuracies
	  miter = 1;
	}
	m = splashSqrt(miter - 1);
      }

      // round join
      if (state->lineJoin == splashLineJoinRound) {
	pathOut->moveTo(pathIn->pts[i+1].x + (SplashCoord)0.5 * w,
			pathIn->pts[i+1].y);
	pathOut->curveTo(pathIn->pts[i+1].x + (SplashCoord)0.5 * w,
			 pathIn->pts[i+1].y + bezierCircle2 * w,
			 pathIn->pts[i+1].x + bezierCircle2 * w,
			 pathIn->pts[i+1].y + (SplashCoord)0.5 * w,
			 pathIn->pts[i+1].x,
			 pathIn->pts[i+1].y + (SplashCoord)0.5 * w);
	pathOut->curveTo(pathIn->pts[i+1].x - bezierCircle2 * w,
			 pathIn->pts[i+1].y + (SplashCoord)0.5 * w,
			 pathIn->pts[i+1].x - (SplashCoord)0.5 * w,
			 pathIn->pts[i+1].y + bezierCircle2 * w,
			 pathIn->pts[i+1].x - (SplashCoord)0.5 * w,
			 pathIn->pts[i+1].y);
	pathOut->curveTo(pathIn->pts[i+1].x - (SplashCoord)0.5 * w,
			 pathIn->pts[i+1].y - bezierCircle2 * w,
			 pathIn->pts[i+1].x - bezierCircle2 * w,
			 pathIn->pts[i+1].y - (SplashCoord)0.5 * w,
			 pathIn->pts[i+1].x,
			 pathIn->pts[i+1].y - (SplashCoord)0.5 * w);
	pathOut->curveTo(pathIn->pts[i+1].x + bezierCircle2 * w,
			 pathIn->pts[i+1].y - (SplashCoord)0.5 * w,
			 pathIn->pts[i+1].x + (SplashCoord)0.5 * w,
			 pathIn->pts[i+1].y - bezierCircle2 * w,
			 pathIn->pts[i+1].x + (SplashCoord)0.5 * w,
			 pathIn->pts[i+1].y);

      } else {
	pathOut->moveTo(pathIn->pts[i+1].x, pathIn->pts[i+1].y);

	// angle < 180
	if (crossprod < 0) {
	  pathOut->lineTo(pathIn->pts[i+1].x - wdyNext,
			  pathIn->pts[i+1].y + wdxNext);
	  // miter join inside limit
	  if (state->lineJoin == splashLineJoinMiter &&
	      splashSqrt(miter) <= state->miterLimit) {
	    pathOut->lineTo(pathIn->pts[i+1].x - wdy + wdx * m,
			    pathIn->pts[i+1].y + wdx + wdy * m);
	    pathOut->lineTo(pathIn->pts[i+1].x - wdy,
			    pathIn->pts[i+1].y + wdx);
	  // bevel join or miter join outside limit
	  } else {
	    pathOut->lineTo(pathIn->pts[i+1].x - wdy, pathIn->pts[i+1].y + wdx);
	  }

	// angle >= 180
	} else {
	  pathOut->lineTo(pathIn->pts[i+1].x + wdy,
			  pathIn->pts[i+1].y - wdx);
	  // miter join inside limit
	  if (state->lineJoin == splashLineJoinMiter &&
	      splashSqrt(miter) <= state->miterLimit) {
	    pathOut->lineTo(pathIn->pts[i+1].x + wdy + wdx * m,
			    pathIn->pts[i+1].y - wdx + wdy * m);
	    pathOut->lineTo(pathIn->pts[i+1].x + wdyNext,
			    pathIn->pts[i+1].y - wdxNext);
	  // bevel join or miter join outside limit
	  } else {
	    pathOut->lineTo(pathIn->pts[i+1].x + wdyNext,
			    pathIn->pts[i+1].y - wdxNext);
	  }
	}
      }

      pathOut->close();
    }

    // add stroke adjustment hints
    if (state->strokeAdjust) {
      if (i >= subpathStart + 1) {
	if (i >= subpathStart + 2) {
	  pathOut->addStrokeAdjustHint(left1, right1, left0 + 1, right0);
	  pathOut->addStrokeAdjustHint(left1, right1, join0, left2);
	} else {
	  pathOut->addStrokeAdjustHint(left1, right1, firstPt, left2);
	}
	pathOut->addStrokeAdjustHint(left1, right1, right2 + 1, right2 + 1);
      }
      left0 = left1;
      left1 = left2;
      right0 = right1;
      right1 = right2;
      join0 = join1;
      join1 = join2;
      if (i == subpathStart) {
	leftFirst = left2;
	rightFirst = right2;
      }
      if (last) {
	if (i >= subpathStart + 2) {
	  pathOut->addStrokeAdjustHint(left1, right1, left0 + 1, right0);
	  pathOut->addStrokeAdjustHint(left1, right1,
				       join0, pathOut->length - 1);
	} else {
	  pathOut->addStrokeAdjustHint(left1, right1,
				       firstPt, pathOut->length - 1);
	}
	if (closed) {
	  pathOut->addStrokeAdjustHint(left1, right1, firstPt, leftFirst);
	  pathOut->addStrokeAdjustHint(left1, right1,
				       rightFirst + 1, rightFirst + 1);
	  pathOut->addStrokeAdjustHint(leftFirst, rightFirst,
				       left1 + 1, right1);
	  pathOut->addStrokeAdjustHint(leftFirst, rightFirst,
				       join1, pathOut->length - 1);
	}
      }
    }
  }

  if (pathIn != path) {
    delete pathIn;
  }

  return pathOut;
}

void Splash::dumpPath(SplashPath *path) {
  int i;

  for (i = 0; i < path->length; ++i) {
    printf("  %3d: x=%8.2f y=%8.2f%s%s%s%s\n",
	   i, (double)path->pts[i].x, (double)path->pts[i].y,
	   (path->flags[i] & splashPathFirst) ? " first" : "",
	   (path->flags[i] & splashPathLast) ? " last" : "",
	   (path->flags[i] & splashPathClosed) ? " closed" : "",
	   (path->flags[i] & splashPathCurve) ? " curve" : "");
  }
}

void Splash::dumpXPath(SplashXPath *path) {
  int i;

  for (i = 0; i < path->length; ++i) {
    printf("  %4d: x0=%8.2f y0=%8.2f x1=%8.2f y1=%8.2f %s%s%s%s%s%s%s\n",
	   i, (double)path->segs[i].x0, (double)path->segs[i].y0,
	   (double)path->segs[i].x1, (double)path->segs[i].y1,
	   (path->segs[i].flags	& splashXPathFirst) ? "F" : " ",
	   (path->segs[i].flags	& splashXPathLast) ? "L" : " ",
	   (path->segs[i].flags	& splashXPathEnd0) ? "0" : " ",
	   (path->segs[i].flags	& splashXPathEnd1) ? "1" : " ",
	   (path->segs[i].flags	& splashXPathHoriz) ? "H" : " ",
	   (path->segs[i].flags	& splashXPathVert) ? "V" : " ",
	   (path->segs[i].flags	& splashXPathFlip) ? "P" : " ");
  }
}
//========================================================================
//
// Splash.h
//
//========================================================================

#ifndef SPLASH_H
#define SPLASH_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "SplashTypes.h"
#include "SplashClip.h"

class Splash;
class SplashBitmap;
struct SplashGlyphBitmap;
class SplashState;
class SplashPattern;
class SplashScreen;
class SplashPath;
class SplashXPath;
class SplashFont;
struct SplashPipe;

//------------------------------------------------------------------------

// Retrieves the next line of pixels in an image mask.  Normally,
// fills in *<line> and returns true.  If the image stream is
// exhausted, returns false.
typedef GBool (*SplashImageMaskSource)(void *data, SplashColorPtr pixel);

// Retrieves the next line of pixels in an image.  Normally, fills in
// *<line> and returns true.  If the image stream is exhausted,
// returns false.
typedef GBool (*SplashImageSource)(void *data, SplashColorPtr colorLine,
				   Guchar *alphaLine);

//------------------------------------------------------------------------

enum SplashPipeResultColorCtrl {
#if SPLASH_CMYK
  splashPipeResultColorNoAlphaBlendCMYK,
#endif
  splashPipeResultColorNoAlphaBlendRGB,
  splashPipeResultColorNoAlphaBlendMono,
  splashPipeResultColorAlphaNoBlendMono,
  splashPipeResultColorAlphaNoBlendRGB,
#if SPLASH_CMYK
  splashPipeResultColorAlphaNoBlendCMYK,
#endif
  splashPipeResultColorAlphaBlendMono,
  splashPipeResultColorAlphaBlendRGB
#if SPLASH_CMYK
  ,
  splashPipeResultColorAlphaBlendCMYK
#endif
};

//------------------------------------------------------------------------
// Splash
//------------------------------------------------------------------------

class Splash {
public:

  // Create a new rasterizer object.
  Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA,
	 SplashScreenParams *screenParams = NULL);
  Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA,
	 SplashScreen *screenA);

  ~Splash();

  //----- state read

  SplashCoord *getMatrix();
  SplashPattern *getStrokePattern();
  SplashPattern *getFillPattern();
  SplashScreen *getScreen();
  SplashBlendFunc getBlendFunc();
  SplashCoord getStrokeAlpha();
  SplashCoord getFillAlpha();
  SplashCoord getLineWidth();
  int getLineCap();
  int getLineJoin();
  SplashCoord getMiterLimit();
  SplashCoord getFlatness();
  SplashCoord *getLineDash();
  int getLineDashLength();
  SplashCoord getLineDashPhase();
  SplashClip *getClip();
  SplashBitmap *getSoftMask();
  GBool getInNonIsolatedGroup();

  //----- state write

  void setMatrix(SplashCoord *matrix);
  void setStrokePattern(SplashPattern *strokeColor);
  void setFillPattern(SplashPattern *fillColor);
  void setScreen(SplashScreen *screen);
  void setBlendFunc(SplashBlendFunc func);
  void setStrokeAlpha(SplashCoord alpha);
  void setFillAlpha(SplashCoord alpha);
  void setLineWidth(SplashCoord lineWidth);
  void setLineCap(int lineCap);
  void setLineJoin(int lineJoin);
  void setMiterLimit(SplashCoord miterLimit);
  void setFlatness(SplashCoord flatness);
  // the <lineDash> array will be copied
  void setLineDash(SplashCoord *lineDash, int lineDashLength,
		   SplashCoord lineDashPhase);
  void setStrokeAdjust(GBool strokeAdjust);
  // NB: uses transformed coordinates.
  void clipResetToRect(SplashCoord x0, SplashCoord y0,
		       SplashCoord x1, SplashCoord y1);
  // NB: uses transformed coordinates.
  SplashError clipToRect(SplashCoord x0, SplashCoord y0,
			 SplashCoord x1, SplashCoord y1);
  // NB: uses untransformed coordinates.
  SplashError clipToPath(SplashPath *path, GBool eo);
  void setSoftMask(SplashBitmap *softMask);
  void setInNonIsolatedGroup(SplashBitmap *alpha0BitmapA,
			     int alpha0XA, int alpha0YA);

  //----- state save/restore

  void saveState();
  SplashError restoreState();

  //----- drawing operations

  // Fill the bitmap with <color>.  This is not subject to clipping.
  void clear(SplashColorPtr color, Guchar alpha = 0x00);

  // Stroke a path using the current stroke pattern.
  SplashError stroke(SplashPath *path);

  // Fill a path using the current fill pattern.
  SplashError fill(SplashPath *path, GBool eo);

  // Fill a path, XORing with the current fill pattern.
  SplashError xorFill(SplashPath *path, GBool eo);

  // Draw a character, using the current fill pattern.
  SplashError fillChar(SplashCoord x, SplashCoord y, int c, SplashFont *font);

  // Draw a glyph, using the current fill pattern.  This function does
  // not free any data, i.e., it ignores glyph->freeData.
  SplashError fillGlyph(SplashCoord x, SplashCoord y,
			SplashGlyphBitmap *glyph);

  // Draws an image mask using the fill color.  This will read <h>
  // lines of <w> pixels from <src>, starting with the top line.  "1"
  // pixels will be drawn with the current fill color; "0" pixels are
  // transparent.  The matrix:
  //    [ mat[0] mat[1] 0 ]
  //    [ mat[2] mat[3] 0 ]
  //    [ mat[4] mat[5] 1 ]
  // maps a unit square to the desired destination for the image, in
  // PostScript style:
  //    [x' y' 1] = [x y 1] * mat
  // Note that the Splash y axis points downward, and the image source
  // is assumed to produce pixels in raster order, starting from the
  // top line.
  SplashError fillImageMask(SplashImageMaskSource src, void *srcData,
			    int w, int h, SplashCoord *mat,
			    GBool glyphMode);

  // Draw an image.  This will read <h> lines of <w> pixels from
  // <src>, starting with the top line.  These pixels are assumed to
  // be in the source mode, <srcMode>.  If <srcAlpha> is true, the
  // alpha values returned by <src> are used; otherwise they are
  // ignored.  The following combinations of source and target modes
  // are supported:
  //    source       target
  //    ------       ------
  //    Mono1        Mono1
  //    Mono8        Mono1   -- with dithering
  //    Mono8        Mono8
  //    RGB8         RGB8
  //    BGR8         BGR8
  //    CMYK8        CMYK8
  // The matrix behaves as for fillImageMask.
  SplashError drawImage(SplashImageSource src, void *srcData,
			SplashColorMode srcMode, GBool srcAlpha,
			int w, int h, SplashCoord *mat);

  // Composite a rectangular region from <src> onto this Splash
  // object.
  SplashError composite(SplashBitmap *src, int xSrc, int ySrc,
			int xDest, int yDest, int w, int h,
			GBool noClip, GBool nonIsolated);

  // Composite this Splash object onto a background color.  The
  // background alpha is assumed to be 1.
  void compositeBackground(SplashColorPtr color);

  // Copy a rectangular region from <src> onto the bitmap belonging to
  // this Splash object.  The destination alpha values are all set to
  // zero.
  SplashError blitTransparent(SplashBitmap *src, int xSrc, int ySrc,
			      int xDest, int yDest, int w, int h);

  //----- misc

  // Construct a path for a stroke, given the path to be stroked, and
  // using the current line parameters.  If <flatten> is true, this
  // function will first flatten the path and handle the linedash.
  SplashPath *makeStrokePath(SplashPath *path, GBool flatten = gTrue);

  // Return the associated bitmap.
  SplashBitmap *getBitmap() { return bitmap; }

  // Get a bounding box which includes all modifications since the
  // last call to clearModRegion.
  void getModRegion(int *xMin, int *yMin, int *xMax, int *yMax)
    { *xMin = modXMin; *yMin = modYMin; *xMax = modXMax; *yMax = modYMax; }

  // Clear the modified region bounding box.
  void clearModRegion();

  // Get clipping status for the last drawing operation subject to
  // clipping.
  SplashClipResult getClipRes() { return opClipRes; }

  // Toggle debug mode on or off.
  void setDebugMode(GBool debugModeA) { debugMode = debugModeA; }

#if 1 //~tmp: turn off anti-aliasing temporarily
  GBool getVectorAntialias() { return vectorAntialias; }
  void setVectorAntialias(GBool vaa) { vectorAntialias = vaa; }
#endif

private:

  void pipeInit(SplashPipe *pipe, int x, int y,
		SplashPattern *pattern, SplashColorPtr cSrc,
		SplashCoord aInput, GBool usesShape,
		GBool nonIsolatedGroup);
  void pipeRun(SplashPipe *pipe);
  void pipeSetXY(SplashPipe *pipe, int x, int y);
  void pipeIncX(SplashPipe *pipe);
  void drawPixel(SplashPipe *pipe, int x, int y, GBool noClip);
  void drawAAPixelInit();
  void drawAAPixel(SplashPipe *pipe, int x, int y);
  void drawSpan(SplashPipe *pipe, int x0, int x1, int y, GBool noClip);
  void drawAALine(SplashPipe *pipe, int x0, int x1, int y);
  void transform(SplashCoord *matrix, SplashCoord xi, SplashCoord yi,
		 SplashCoord *xo, SplashCoord *yo);
  void updateModX(int x);
  void updateModY(int y);
  void strokeNarrow(SplashPath *path);
  void strokeWide(SplashPath *path);
  SplashPath *flattenPath(SplashPath *path, SplashCoord *matrix,
			  SplashCoord flatness);
  void flattenCurve(SplashCoord x0, SplashCoord y0,
		    SplashCoord x1, SplashCoord y1,
		    SplashCoord x2, SplashCoord y2,
		    SplashCoord x3, SplashCoord y3,
		    SplashCoord *matrix, SplashCoord flatness2,
		    SplashPath *fPath);
  SplashPath *makeDashedPath(SplashPath *xPath);
  SplashError fillWithPattern(SplashPath *path, GBool eo,
			      SplashPattern *pattern, SplashCoord alpha);
  SplashError fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph);
  void dumpPath(SplashPath *path);
  void dumpXPath(SplashXPath *path);

  static SplashPipeResultColorCtrl pipeResultColorNoAlphaBlend[];
  static SplashPipeResultColorCtrl pipeResultColorAlphaNoBlend[];
  static SplashPipeResultColorCtrl pipeResultColorAlphaBlend[];
  static int pipeNonIsoGroupCorrection[];

  SplashBitmap *bitmap;
  SplashState *state;
  SplashBitmap *aaBuf;
  int aaBufY;
  SplashBitmap *alpha0Bitmap;	// for non-isolated groups, this is the
				//   bitmap containing the alpha0 values
  int alpha0X, alpha0Y;		// offset within alpha0Bitmap
  SplashCoord aaGamma[splashAASize * splashAASize + 1];
  int modXMin, modYMin, modXMax, modYMax;
  SplashClipResult opClipRes;
  GBool vectorAntialias;
  GBool debugMode;
};

#endif
//========================================================================
//
// SplashBitmap.cc
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stdio.h>
#include "gmem.h"
#include "SplashErrorCodes.h"
#include "SplashBitmap.h"

//------------------------------------------------------------------------
// SplashBitmap
//------------------------------------------------------------------------

SplashBitmap::SplashBitmap(int widthA, int heightA, int rowPad,
			   SplashColorMode modeA, GBool alphaA,
			   GBool topDown) {
  width = widthA;
  height = heightA;
  mode = modeA;
  switch (mode) {
  case splashModeMono1:
    rowSize = (width + 7) >> 3;
    break;
  case splashModeMono8:
    rowSize = width;
    break;
  case splashModeRGB8:
  case splashModeBGR8:
    rowSize = width * 3;
    break;
#if SPLASH_CMYK
  case splashModeCMYK8:
    rowSize = width * 4;
    break;
#endif
  }
  rowSize += rowPad - 1;
  rowSize -= rowSize % rowPad;
  data = (SplashColorPtr)gmalloc(rowSize * height);
  if (!topDown) {
    data += (height - 1) * rowSize;
    rowSize = -rowSize;
  }
  if (alphaA) {
    alpha = (Guchar *)gmalloc(width * height);
  } else {
    alpha = NULL;
  }
}


SplashBitmap::~SplashBitmap() {
  if (rowSize < 0) {
    gfree(data + (height - 1) * rowSize);
  } else {
    gfree(data);
  }
  gfree(alpha);
}

SplashError SplashBitmap::writePNMFile(char *fileName) {
  FILE *f;
  SplashColorPtr row, p;
  int x, y;

  if (!(f = fopen(fileName, "wb"))) {
    return splashErrOpenFile;
  }

  switch (mode) {

  case splashModeMono1:
    fprintf(f, "P4\n%d %d\n", width, height);
    row = data;
    for (y = 0; y < height; ++y) {
      p = row;
      for (x = 0; x < width; x += 8) {
	fputc(*p ^ 0xff, f);
	++p;
      }
      row += rowSize;
    }
    break;

  case splashModeMono8:
    fprintf(f, "P5\n%d %d\n255\n", width, height);
    row = data;
    for (y = 0; y < height; ++y) {
      p = row;
      for (x = 0; x < width; ++x) {
	fputc(*p, f);
	++p;
      }
      row += rowSize;
    }
    break;

  case splashModeRGB8:
    fprintf(f, "P6\n%d %d\n255\n", width, height);
    row = data;
    for (y = 0; y < height; ++y) {
      p = row;
      for (x = 0; x < width; ++x) {
	fputc(splashRGB8R(p), f);
	fputc(splashRGB8G(p), f);
	fputc(splashRGB8B(p), f);
	p += 3;
      }
      row += rowSize;
    }
    break;

  case splashModeBGR8:
    fprintf(f, "P6\n%d %d\n255\n", width, height);
    row = data;
    for (y = 0; y < height; ++y) {
      p = row;
      for (x = 0; x < width; ++x) {
	fputc(splashBGR8R(p), f);
	fputc(splashBGR8G(p), f);
	fputc(splashBGR8B(p), f);
	p += 3;
      }
      row += rowSize;
    }
    break;

#if SPLASH_CMYK
  case splashModeCMYK8:
    // PNM doesn't support CMYK
    break;
#endif
  }

  fclose(f);
  return splashOk;
}

void SplashBitmap::getPixel(int x, int y, SplashColorPtr pixel) {
  SplashColorPtr p;

  if (y < 0 || y >= height || x < 0 || x >= width) {
    return;
  }
  switch (mode) {
  case splashModeMono1:
    p = &data[y * rowSize + (x >> 3)];
    pixel[0] = (p[0] & (0x80 >> (x & 7))) ? 0xff : 0x00;
    break;
  case splashModeMono8:
    p = &data[y * rowSize + x];
    pixel[0] = p[0];
    break;
  case splashModeRGB8:
    p = &data[y * rowSize + 3 * x];
    pixel[0] = p[0];
    pixel[1] = p[1];
    pixel[2] = p[2];
    break;
  case splashModeBGR8:
    p = &data[y * rowSize + 3 * x];
    pixel[0] = p[2];
    pixel[1] = p[1];
    pixel[2] = p[0];
    break;
#if SPLASH_CMYK
  case splashModeCMYK8:
    p = &data[y * rowSize + 4 * x];
    pixel[0] = p[0];
    pixel[1] = p[1];
    pixel[2] = p[2];
    pixel[3] = p[3];
    break;
#endif
  }
}

Guchar SplashBitmap::getAlpha(int x, int y) {
  return alpha[y * width + x];
}
//========================================================================
//
// SplashBitmap.h
//
//========================================================================

#ifndef SPLASHBITMAP_H
#define SPLASHBITMAP_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "SplashTypes.h"
#include <stdio.h>

class ImgWriter;

//------------------------------------------------------------------------
// SplashBitmap
//------------------------------------------------------------------------

class SplashBitmap {
public:

  // Create a new bitmap.  It will have <widthA> x <heightA> pixels in
  // color mode <modeA>.  Rows will be padded out to a multiple of
  // <rowPad> bytes.  If <topDown> is false, the bitmap will be stored
  // upside-down, i.e., with the last row first in memory.
  SplashBitmap(int widthA, int heightA, int rowPad,
	       SplashColorMode modeA, GBool alphaA,
	       GBool topDown = gTrue);

  ~SplashBitmap();

  int getWidth() { return width; }
  int getHeight() { return height; }
  int getRowSize() { return rowSize; }
  int getAlphaRowSize() { return width; }
  SplashColorMode getMode() { return mode; }
  SplashColorPtr getDataPtr() { return data; }
  Guchar *getAlphaPtr() { return alpha; }

  SplashError writePNMFile(char *fileName);

  void getPixel(int x, int y, SplashColorPtr pixel);
  Guchar getAlpha(int x, int y);

private:

  int width, height;		// size of bitmap
  int rowSize;			// size of one row of data, in bytes
				//   - negative for bottom-up bitmaps
  SplashColorMode mode;		// color mode
  SplashColorPtr data;		// pointer to row zero of the color data
  Guchar *alpha;		// pointer to row zero of the alpha data
				//   (always top-down)

  friend class Splash;
};

#endif
//========================================================================
//
// SplashClip.cc
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stdlib.h>
#include <string.h>
#include "gmem.h"
#include "SplashErrorCodes.h"
#include "SplashPath.h"
#include "SplashXPath.h"
#include "SplashXPathScanner.h"
#include "SplashBitmap.h"
#include "SplashClip.h"

//------------------------------------------------------------------------
// SplashClip.flags
//------------------------------------------------------------------------

#define splashClipEO       0x01	// use even-odd rule

//------------------------------------------------------------------------
// SplashClip
//------------------------------------------------------------------------

SplashClip::SplashClip(SplashCoord x0, SplashCoord y0,
		       SplashCoord x1, SplashCoord y1,
		       GBool antialiasA) {
  antialias = antialiasA;
  if (x0 < x1) {
    xMin = x0;
    xMax = x1;
  } else {
    xMin = x1;
    xMax = x0;
  }
  if (y0 < y1) {
    yMin = y0;
    yMax = y1;
  } else {
    yMin = y1;
    yMax = y0;
  }
  xMinI = splashFloor(xMin);
  yMinI = splashFloor(yMin);
  xMaxI = splashFloor(xMax);
  yMaxI = splashFloor(yMax);
  paths = NULL;
  flags = NULL;
  scanners = NULL;
  length = size = 0;
}

SplashClip::SplashClip(SplashClip *clip) {
  int i;

  antialias = clip->antialias;
  xMin = clip->xMin;
  yMin = clip->yMin;
  xMax = clip->xMax;
  yMax = clip->yMax;
  xMinI = clip->xMinI;
  yMinI = clip->yMinI;
  xMaxI = clip->xMaxI;
  yMaxI = clip->yMaxI;
  length = clip->length;
  size = clip->size;
  paths = (SplashXPath **)gmallocn(size, sizeof(SplashXPath *));
  flags = (Guchar *)gmallocn(size, sizeof(Guchar));
  scanners = (SplashXPathScanner **)
                 gmallocn(size, sizeof(SplashXPathScanner *));
  for (i = 0; i < length; ++i) {
    paths[i] = clip->paths[i]->copy();
    flags[i] = clip->flags[i];
    scanners[i] = new SplashXPathScanner(paths[i], flags[i] & splashClipEO);
  }
}

SplashClip::~SplashClip() {
  int i;

  for (i = 0; i < length; ++i) {
    delete paths[i];
    delete scanners[i];
  }
  gfree(paths);
  gfree(flags);
  gfree(scanners);
}

void SplashClip::grow(int nPaths) {
  if (length + nPaths > size) {
    if (size == 0) {
      size = 32;
    }
    while (size < length + nPaths) {
      size *= 2;
    }
    paths = (SplashXPath **)greallocn(paths, size, sizeof(SplashXPath *));
    flags = (Guchar *)greallocn(flags, size, sizeof(Guchar));
    scanners = (SplashXPathScanner **)
                   greallocn(scanners, size, sizeof(SplashXPathScanner *));
  }
}

void SplashClip::resetToRect(SplashCoord x0, SplashCoord y0,
			     SplashCoord x1, SplashCoord y1) {
  int i;

  for (i = 0; i < length; ++i) {
    delete paths[i];
    delete scanners[i];
  }
  gfree(paths);
  gfree(flags);
  gfree(scanners);
  paths = NULL;
  flags = NULL;
  scanners = NULL;
  length = size = 0;

  if (x0 < x1) {
    xMin = x0;
    xMax = x1;
  } else {
    xMin = x1;
    xMax = x0;
  }
  if (y0 < y1) {
    yMin = y0;
    yMax = y1;
  } else {
    yMin = y1;
    yMax = y0;
  }
  xMinI = splashFloor(xMin);
  yMinI = splashFloor(yMin);
  xMaxI = splashFloor(xMax);
  yMaxI = splashFloor(yMax);
}

SplashError SplashClip::clipToRect(SplashCoord x0, SplashCoord y0,
				   SplashCoord x1, SplashCoord y1) {
  if (x0 < x1) {
    if (x0 > xMin) {
      xMin = x0;
      xMinI = splashFloor(xMin);
    }
    if (x1 < xMax) {
      xMax = x1;
      xMaxI = splashFloor(xMax);
    }
  } else {
    if (x1 > xMin) {
      xMin = x1;
      xMinI = splashFloor(xMin);
    }
    if (x0 < xMax) {
      xMax = x0;
      xMaxI = splashFloor(xMax);
    }
  }
  if (y0 < y1) {
    if (y0 > yMin) {
      yMin = y0;
      yMinI = splashFloor(yMin);
    }
    if (y1 < yMax) {
      yMax = y1;
      yMaxI = splashFloor(yMax);
    }
  } else {
    if (y1 > yMin) {
      yMin = y1;
      yMinI = splashFloor(yMin);
    }
    if (y0 < yMax) {
      yMax = y0;
      yMaxI = splashFloor(yMax);
    }
  }
  return splashOk;
}

SplashError SplashClip::clipToPath(SplashPath *path, SplashCoord *matrix,
				   SplashCoord flatness, GBool eo) {
  SplashXPath *xPath;

  xPath = new SplashXPath(path, matrix, flatness, gTrue);

  // check for an empty path
  if (xPath->length == 0) {
    xMax = xMin - 1;
    yMax = yMin - 1;
    xMaxI = splashFloor(xMax);
    yMaxI = splashFloor(yMax);
    delete xPath;

  // check for a rectangle
  } else if (xPath->length == 4 &&
	     ((xPath->segs[0].x0 == xPath->segs[0].x1 &&
	       xPath->segs[0].x0 == xPath->segs[1].x0 &&
	       xPath->segs[0].x0 == xPath->segs[3].x1 &&
	       xPath->segs[2].x0 == xPath->segs[2].x1 &&
	       xPath->segs[2].x0 == xPath->segs[1].x1 &&
	       xPath->segs[2].x0 == xPath->segs[3].x0 &&
	       xPath->segs[1].y0 == xPath->segs[1].y1 &&
	       xPath->segs[1].y0 == xPath->segs[0].y1 &&
	       xPath->segs[1].y0 == xPath->segs[2].y0 &&
	       xPath->segs[3].y0 == xPath->segs[3].y1 &&
	       xPath->segs[3].y0 == xPath->segs[0].y0 &&
	       xPath->segs[3].y0 == xPath->segs[2].y1) ||
	      (xPath->segs[0].y0 == xPath->segs[0].y1 &&
	       xPath->segs[0].y0 == xPath->segs[1].y0 &&
	       xPath->segs[0].y0 == xPath->segs[3].y1 &&
	       xPath->segs[2].y0 == xPath->segs[2].y1 &&
	       xPath->segs[2].y0 == xPath->segs[1].y1 &&
	       xPath->segs[2].y0 == xPath->segs[3].y0 &&
	       xPath->segs[1].x0 == xPath->segs[1].x1 &&
	       xPath->segs[1].x0 == xPath->segs[0].x1 &&
	       xPath->segs[1].x0 == xPath->segs[2].x0 &&
	       xPath->segs[3].x0 == xPath->segs[3].x1 &&
	       xPath->segs[3].x0 == xPath->segs[0].x0 &&
	       xPath->segs[3].x0 == xPath->segs[2].x1))) {
    clipToRect(xPath->segs[0].x0, xPath->segs[0].y0,
	       xPath->segs[2].x0, xPath->segs[2].y0);
    delete xPath;

  } else {
    grow(1);
    if (antialias) {
      xPath->aaScale();
    }
    xPath->sort();
    paths[length] = xPath;
    flags[length] = eo ? splashClipEO : 0;
    scanners[length] = new SplashXPathScanner(xPath, eo);
    ++length;
  }

  return splashOk;
}

GBool SplashClip::test(int x, int y) {
  int i;

  // check the rectangle
  if (x < xMinI || x > xMaxI || y < yMinI || y > yMaxI) {
    return gFalse;
  }

  // check the paths
  if (antialias) {
    for (i = 0; i < length; ++i) {
      if (!scanners[i]->test(x * splashAASize, y * splashAASize)) {
	return gFalse;
      }
    }
  } else {
    for (i = 0; i < length; ++i) {
      if (!scanners[i]->test(x, y)) {
	return gFalse;
      }
    }
  }

  return gTrue;
}

SplashClipResult SplashClip::testRect(int rectXMin, int rectYMin,
				      int rectXMax, int rectYMax) {
  // This tests the rectangle:
  //     x = [rectXMin, rectXMax + 1)    (note: rect coords are ints)
  //     y = [rectYMin, rectYMax + 1)
  // against the clipping region:
  //     x = [xMin, xMax]                (note: clipping coords are fp)
  //     y = [yMin, yMax]
  if ((SplashCoord)(rectXMax + 1) <= xMin || (SplashCoord)rectXMin > xMax ||
      (SplashCoord)(rectYMax + 1) <= yMin || (SplashCoord)rectYMin > yMax) {
    return splashClipAllOutside;
  }
  if ((SplashCoord)rectXMin >= xMin && (SplashCoord)(rectXMax + 1) <= xMax &&
      (SplashCoord)rectYMin >= yMin && (SplashCoord)(rectYMax + 1) <= yMax &&
      length == 0) {
    return splashClipAllInside;
  }
  return splashClipPartial;
}

SplashClipResult SplashClip::testSpan(int spanXMin, int spanXMax, int spanY) {
  int i;

  // This tests the rectangle:
  //     x = [spanXMin, spanXMax + 1)    (note: span coords are ints)
  //     y = [spanY, spanY + 1)
  // against the clipping region:
  //     x = [xMin, xMax]                (note: clipping coords are fp)
  //     y = [yMin, yMax]
  if ((SplashCoord)(spanXMax + 1) <= xMin || (SplashCoord)spanXMin > xMax ||
      (SplashCoord)(spanY + 1) <= yMin || (SplashCoord)spanY > yMax) {
    return splashClipAllOutside;
  }
  if (!((SplashCoord)spanXMin >= xMin && (SplashCoord)(spanXMax + 1) <= xMax &&
	(SplashCoord)spanY >= yMin && (SplashCoord)(spanY + 1) <= yMax)) {
    return splashClipPartial;
  }
  if (antialias) {
    for (i = 0; i < length; ++i) {
      if (!scanners[i]->testSpan(spanXMin * splashAASize,
				 spanXMax * splashAASize + (splashAASize - 1),
				 spanY * splashAASize)) {
	return splashClipPartial;
      }
    }
  } else {
    for (i = 0; i < length; ++i) {
      if (!scanners[i]->testSpan(spanXMin, spanXMax, spanY)) {
	return splashClipPartial;
      }
    }
  }
  return splashClipAllInside;
}

void SplashClip::clipAALine(SplashBitmap *aaBuf, int *x0, int *x1, int y) {
  int xx0, xx1, xx, yy, i;
  SplashColorPtr p;

  // zero out pixels with x < xMin
  xx0 = *x0 * splashAASize;
  xx1 = splashFloor(xMin * splashAASize);
  if (xx1 > aaBuf->getWidth()) {
    xx1 = aaBuf->getWidth();
  }
  if (xx0 < xx1) {
    xx0 &= ~7;
    for (yy = 0; yy < splashAASize; ++yy) {
      p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() + (xx0 >> 3);
      for (xx = xx0; xx + 7 < xx1; xx += 8) {
	*p++ = 0;
      }
      if (xx < xx1) {
	*p &= 0xff >> (xx1 & 7);
      }
    }
    *x0 = splashFloor(xMin);
  }

  // zero out pixels with x > xMax
  xx0 = splashFloor(xMax * splashAASize) + 1;
  if (xx0 < 0) {
    xx0 = 0;
  }
  xx1 = (*x1 + 1) * splashAASize;
  if (xx0 < xx1) {
    for (yy = 0; yy < splashAASize; ++yy) {
      p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() + (xx0 >> 3);
      xx = xx0;
      if (xx & 7) {
	*p &= 0xff00 >> (xx & 7);
	xx = (xx & ~7) + 8;
	++p;
      }
      for (; xx < xx1; xx += 8) {
	*p++ = 0;
      }
    }
    *x1 = splashFloor(xMax);
  }

  // check the paths
  for (i = 0; i < length; ++i) {
    scanners[i]->clipAALine(aaBuf, x0, x1, y);
  }
}
//========================================================================
//
// SplashClip.h
//
//========================================================================

#ifndef SPLASHCLIP_H
#define SPLASHCLIP_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "SplashTypes.h"
#include "SplashMath.h"

class SplashPath;
class SplashXPath;
class SplashXPathScanner;
class SplashBitmap;

//------------------------------------------------------------------------

enum SplashClipResult {
  splashClipAllInside,
  splashClipAllOutside,
  splashClipPartial
};

//------------------------------------------------------------------------
// SplashClip
//------------------------------------------------------------------------

class SplashClip {
public:

  // Create a clip, for the given rectangle.
  SplashClip(SplashCoord x0, SplashCoord y0,
	     SplashCoord x1, SplashCoord y1,
	     GBool antialiasA);

  // Copy a clip.
  SplashClip *copy() { return new SplashClip(this); }

  ~SplashClip();

  // Reset the clip to a rectangle.
  void resetToRect(SplashCoord x0, SplashCoord y0,
		   SplashCoord x1, SplashCoord y1);

  // Intersect the clip with a rectangle.
  SplashError clipToRect(SplashCoord x0, SplashCoord y0,
			 SplashCoord x1, SplashCoord y1);

  // Interesect the clip with <path>.
  SplashError clipToPath(SplashPath *path, SplashCoord *matrix,
			 SplashCoord flatness, GBool eo);

  // Returns true if (<x>,<y>) is inside the clip.
  GBool test(int x, int y);

  // Tests a rectangle against the clipping region.  Returns one of:
  //   - splashClipAllInside if the entire rectangle is inside the
  //     clipping region, i.e., all pixels in the rectangle are
  //     visible
  //   - splashClipAllOutside if the entire rectangle is outside the
  //     clipping region, i.e., all the pixels in the rectangle are
  //     clipped
  //   - splashClipPartial if the rectangle is part inside and part
  //     outside the clipping region
  SplashClipResult testRect(int rectXMin, int rectYMin,
			    int rectXMax, int rectYMax);

  // Similar to testRect, but tests a horizontal span.
  SplashClipResult testSpan(int spanXMin, int spanXMax, int spanY);

  // Clips an anti-aliased line by setting pixels to zero.  On entry,
  // all non-zero pixels are between <x0> and <x1>.  This function
  // will update <x0> and <x1>.
  void clipAALine(SplashBitmap *aaBuf, int *x0, int *x1, int y);

  // Get the rectangle part of the clip region, in integer coordinates.
  int getXMinI() { return xMinI; }
  int getXMaxI() { return xMaxI; }
  int getYMinI() { return yMinI; }
  int getYMaxI() { return yMaxI; }

  // Get the number of arbitrary paths used by the clip region.
  int getNumPaths() { return length; }

private:

  SplashClip(SplashClip *clip);
  void grow(int nPaths);

  GBool antialias;
  SplashCoord xMin, yMin, xMax, yMax;
  int xMinI, yMinI, xMaxI, yMaxI;
  SplashXPath **paths;
  Guchar *flags;
  SplashXPathScanner **scanners;
  int length, size;
};

#endif
//========================================================================
//
// SplashErrorCodes.h
//
//========================================================================

#ifndef SPLASHERRORCODES_H
#define SPLASHERRORCODES_H

#include <aconf.h>

//------------------------------------------------------------------------

#define splashOk                 0	// no error

#define splashErrNoCurPt         1	// no current point

#define splashErrEmptyPath       2	// zero points in path

#define splashErrBogusPath       3	// only one point in subpath

#define splashErrNoSave	         4	// state stack is empty

#define splashErrOpenFile        5	// couldn't open file

#define splashErrNoGlyph         6	// couldn't get the requested glyph

#define splashErrModeMismatch    7	// invalid combination of color modes

#define splashErrSingularMatrix  8	// matrix is singular

#endif
//========================================================================
//
// SplashFTFont.cc
//
//========================================================================

#include <aconf.h>

#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <ft2build.h>
#include FT_OUTLINE_H
#include FT_SIZES_H
#include FT_GLYPH_H
#include "gmem.h"
#include "SplashMath.h"
#include "SplashGlyphBitmap.h"
#include "SplashPath.h"
#include "SplashFTFontEngine.h"
#include "SplashFTFontFile.h"
#include "SplashFTFont.h"

//------------------------------------------------------------------------

static int glyphPathMoveTo(const FT_Vector *pt, void *path);
static int glyphPathLineTo(const FT_Vector *pt, void *path);
static int glyphPathConicTo(const FT_Vector *ctrl, const FT_Vector *pt,
			    void *path);
static int glyphPathCubicTo(const FT_Vector *ctrl1, const FT_Vector *ctrl2,
			    const FT_Vector *pt, void *path);

//------------------------------------------------------------------------
// SplashFTFont
//------------------------------------------------------------------------

SplashFTFont::SplashFTFont(SplashFTFontFile *fontFileA, SplashCoord *matA,
			   SplashCoord *textMatA):
  SplashFont(fontFileA, matA, textMatA, fontFileA->engine->aa)
{
  FT_Face face;
  SplashCoord size, div;
  int x, y;

  face = fontFileA->face;
  if (FT_New_Size(face, &sizeObj)) {
    return;
  }
  face->size = sizeObj;
  size = splashSqrt(mat[2]*mat[2] + mat[3]*mat[3]);
  if (FT_Set_Pixel_Sizes(face, 0, (int)size)) {
    return;
  }
  // if the textMat values are too small, FreeType's fixed point
  // arithmetic doesn't work so well
  textScale = splashSqrt(textMat[2]*textMat[2] + textMat[3]*textMat[3]) / size;

  div = face->bbox.xMax > 20000 ? 65536 : 1;

  // transform the four corners of the font bounding box -- the min
  // and max values form the bounding box of the transformed font
  x = (int)((mat[0] * face->bbox.xMin + mat[2] * face->bbox.yMin) /
	    (div * face->units_per_EM));
  xMin = xMax = x;
  y = (int)((mat[1] * face->bbox.xMin + mat[3] * face->bbox.yMin) /
	    (div * face->units_per_EM));
  yMin = yMax = y;
  x = (int)((mat[0] * face->bbox.xMin + mat[2] * face->bbox.yMax) /
	    (div * face->units_per_EM));
  if (x < xMin) {
    xMin = x;
  } else if (x > xMax) {
    xMax = x;
  }
  y = (int)((mat[1] * face->bbox.xMin + mat[3] * face->bbox.yMax) /
	    (div * face->units_per_EM));
  if (y < yMin) {
    yMin = y;
  } else if (y > yMax) {
    yMax = y;
  }
  x = (int)((mat[0] * face->bbox.xMax + mat[2] * face->bbox.yMin) /
	    (div * face->units_per_EM));
  if (x < xMin) {
    xMin = x;
  } else if (x > xMax) {
    xMax = x;
  }
  y = (int)((mat[1] * face->bbox.xMax + mat[3] * face->bbox.yMin) /
	    (div * face->units_per_EM));
  if (y < yMin) {
    yMin = y;
  } else if (y > yMax) {
    yMax = y;
  }
  x = (int)((mat[0] * face->bbox.xMax + mat[2] * face->bbox.yMax) /
	    (div * face->units_per_EM));
  if (x < xMin) {
    xMin = x;
  } else if (x > xMax) {
    xMax = x;
  }
  y = (int)((mat[1] * face->bbox.xMax + mat[3] * face->bbox.yMax) /
	    (div * face->units_per_EM));
  if (y < yMin) {
    yMin = y;
  } else if (y > yMax) {
    yMax = y;
  }
  // This is a kludge: some buggy PDF generators embed fonts with
  // zero bounding boxes.
  if (xMax == xMin) {
    xMin = 0;
    xMax = (int)size;
  }
  if (yMax == yMin) {
    yMin = 0;
    yMax = (int)((SplashCoord)1.2 * size);
  }

  // compute the transform matrix
#if USE_FIXEDPOINT
  matrix.xx = (FT_Fixed)((mat[0] / size).getRaw());
  matrix.yx = (FT_Fixed)((mat[1] / size).getRaw());
  matrix.xy = (FT_Fixed)((mat[2] / size).getRaw());
  matrix.yy = (FT_Fixed)((mat[3] / size).getRaw());
  textMatrix.xx = (FT_Fixed)((textMat[0] / (size * textScale)).getRaw());
  textMatrix.yx = (FT_Fixed)((textMat[1] / (size * textScale)).getRaw());
  textMatrix.xy = (FT_Fixed)((textMat[2] / (size * textScale)).getRaw());
  textMatrix.yy = (FT_Fixed)((textMat[3] / (size * textScale)).getRaw());
#else
  matrix.xx = (FT_Fixed)((mat[0] / size) * 65536);
  matrix.yx = (FT_Fixed)((mat[1] / size) * 65536);
  matrix.xy = (FT_Fixed)((mat[2] / size) * 65536);
  matrix.yy = (FT_Fixed)((mat[3] / size) * 65536);
  textMatrix.xx = (FT_Fixed)((textMat[0] / (size * textScale)) * 65536);
  textMatrix.yx = (FT_Fixed)((textMat[1] / (size * textScale)) * 65536);
  textMatrix.xy = (FT_Fixed)((textMat[2] / (size * textScale)) * 65536);
  textMatrix.yy = (FT_Fixed)((textMat[3] / (size * textScale)) * 65536);
#endif
}

SplashFTFont::~SplashFTFont() {
}

GBool SplashFTFont::getGlyph(int c, int xFrac, int yFrac,
			     SplashGlyphBitmap *bitmap) {
  return SplashFont::getGlyph(c, xFrac, 0, bitmap);
}

GBool SplashFTFont::makeGlyph(int c, int xFrac, int yFrac,
			      SplashGlyphBitmap *bitmap) {
  SplashFTFontFile *ff;
  FT_Vector offset;
  FT_GlyphSlot slot;
  FT_UInt gid;
  int rowSize;
  Guchar *p, *q;
  int i;

  ff = (SplashFTFontFile *)fontFile;

  ff->face->size = sizeObj;
  offset.x = (FT_Pos)(int)((SplashCoord)xFrac * splashFontFractionMul * 64);
  offset.y = 0;
  FT_Set_Transform(ff->face, &matrix, &offset);
  slot = ff->face->glyph;

  if (ff->codeToGID && c < ff->codeToGIDLen) {
    gid = (FT_UInt)ff->codeToGID[c];
  } else {
    gid = (FT_UInt)c;
  }
  if (ff->trueType && gid == 0) {
    // skip the TrueType notdef glyph
    return gFalse;
  }

  // if we have the FT2 bytecode interpreter, autohinting won't be used
#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER
  if (FT_Load_Glyph(ff->face, gid,
		    aa ? FT_LOAD_NO_BITMAP : FT_LOAD_DEFAULT)) {
    return gFalse;
  }
#else
  // FT2's autohinting doesn't always work very well (especially with
  // font subsets), so turn it off if anti-aliasing is enabled; if
  // anti-aliasing is disabled, this seems to be a tossup - some fonts
  // look better with hinting, some without, so leave hinting on
  if (FT_Load_Glyph(ff->face, gid,
		    aa ? FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP
                       : FT_LOAD_DEFAULT)) {
    return gFalse;
  }
#endif
  if (FT_Render_Glyph(slot, aa ? ft_render_mode_normal
		               : ft_render_mode_mono)) {
    return gFalse;
  }

  bitmap->x = -slot->bitmap_left;
  bitmap->y = slot->bitmap_top;
  bitmap->w = slot->bitmap.width;
  bitmap->h = slot->bitmap.rows;
  bitmap->aa = aa;
  if (aa) {
    rowSize = bitmap->w;
  } else {
    rowSize = (bitmap->w + 7) >> 3;
  }
  bitmap->data = (Guchar *)gmalloc(rowSize * bitmap->h);
  bitmap->freeData = gTrue;
  for (i = 0, p = bitmap->data, q = slot->bitmap.buffer;
       i < bitmap->h;
       ++i, p += rowSize, q += slot->bitmap.pitch) {
    memcpy(p, q, rowSize);
  }

  return gTrue;
}

struct SplashFTFontPath {
  SplashPath *path;
  SplashCoord textScale;
  GBool needClose;
};

SplashPath *SplashFTFont::getGlyphPath(int c) {
  static FT_Outline_Funcs outlineFuncs = {
#if FREETYPE_MINOR <= 1
    (int (*)(FT_Vector *, void *))&glyphPathMoveTo,
    (int (*)(FT_Vector *, void *))&glyphPathLineTo,
    (int (*)(FT_Vector *, FT_Vector *, void *))&glyphPathConicTo,
    (int (*)(FT_Vector *, FT_Vector *, FT_Vector *, void *))&glyphPathCubicTo,
#else
    &glyphPathMoveTo,
    &glyphPathLineTo,
    &glyphPathConicTo,
    &glyphPathCubicTo,
#endif
    0, 0
  };
  SplashFTFontFile *ff;
  SplashFTFontPath path;
  FT_GlyphSlot slot;
  FT_UInt gid;
  FT_Glyph glyph;

  ff = (SplashFTFontFile *)fontFile;
  ff->face->size = sizeObj;
  FT_Set_Transform(ff->face, &textMatrix, NULL);
  slot = ff->face->glyph;
  if (ff->codeToGID && c < ff->codeToGIDLen) {
    gid = ff->codeToGID[c];
  } else {
    gid = (FT_UInt)c;
  }
  if (ff->trueType && gid == 0) {
    // skip the TrueType notdef glyph
    return NULL;
  }
  if (FT_Load_Glyph(ff->face, gid, FT_LOAD_NO_BITMAP)) {
    return NULL;
  }
  if (FT_Get_Glyph(slot, &glyph)) {
    return NULL;
  }
  path.path = new SplashPath();
  path.textScale = textScale;
  path.needClose = gFalse;
  FT_Outline_Decompose(&((FT_OutlineGlyph)glyph)->outline,
		       &outlineFuncs, &path);
  if (path.needClose) {
    path.path->close();
  }
  FT_Done_Glyph(glyph);
  return path.path;
}

static int glyphPathMoveTo(const FT_Vector *pt, void *path) {
  SplashFTFontPath *p = (SplashFTFontPath *)path;

  if (p->needClose) {
    p->path->close();
    p->needClose = gFalse;
  }
  p->path->moveTo((SplashCoord)pt->x * p->textScale / 64.0,
		  (SplashCoord)pt->y * p->textScale / 64.0);
  return 0;
}

static int glyphPathLineTo(const FT_Vector *pt, void *path) {
  SplashFTFontPath *p = (SplashFTFontPath *)path;

  p->path->lineTo((SplashCoord)pt->x * p->textScale / 64.0,
		  (SplashCoord)pt->y * p->textScale / 64.0);
  p->needClose = gTrue;
  return 0;
}

static int glyphPathConicTo(const FT_Vector *ctrl, const FT_Vector *pt,
			    void *path) {
  SplashFTFontPath *p = (SplashFTFontPath *)path;
  SplashCoord x0, y0, x1, y1, x2, y2, x3, y3, xc, yc;

  if (!p->path->getCurPt(&x0, &y0)) {
    return 0;
  }
  xc = (SplashCoord)ctrl->x * p->textScale / 64.0;
  yc = (SplashCoord)ctrl->y * p->textScale / 64.0;
  x3 = (SplashCoord)pt->x * p->textScale / 64.0;
  y3 = (SplashCoord)pt->y * p->textScale / 64.0;

  // A second-order Bezier curve is defined by two endpoints, p0 and
  // p3, and one control point, pc:
  //
  //     p(t) = (1-t)^2*p0 + t*(1-t)*pc + t^2*p3
  //
  // A third-order Bezier curve is defined by the same two endpoints,
  // p0 and p3, and two control points, p1 and p2:
  //
  //     p(t) = (1-t)^3*p0 + 3t*(1-t)^2*p1 + 3t^2*(1-t)*p2 + t^3*p3
  //
  // Applying some algebra, we can convert a second-order curve to a
  // third-order curve:
  //
  //     p1 = (1/3) * (p0 + 2pc)
  //     p2 = (1/3) * (2pc + p3)

  x1 = (SplashCoord)(1.0 / 3.0) * (x0 + (SplashCoord)2 * xc);
  y1 = (SplashCoord)(1.0 / 3.0) * (y0 + (SplashCoord)2 * yc);
  x2 = (SplashCoord)(1.0 / 3.0) * ((SplashCoord)2 * xc + x3);
  y2 = (SplashCoord)(1.0 / 3.0) * ((SplashCoord)2 * yc + y3);

  p->path->curveTo(x1, y1, x2, y2, x3, y3);
  p->needClose = gTrue;
  return 0;
}

static int glyphPathCubicTo(const FT_Vector *ctrl1, const FT_Vector *ctrl2,
			    const FT_Vector *pt, void *path) {
  SplashFTFontPath *p = (SplashFTFontPath *)path;

  p->path->curveTo((SplashCoord)ctrl1->x * p->textScale / 64.0,
		   (SplashCoord)ctrl1->y * p->textScale / 64.0,
		   (SplashCoord)ctrl2->x * p->textScale / 64.0,
		   (SplashCoord)ctrl2->y * p->textScale / 64.0,
		   (SplashCoord)pt->x * p->textScale / 64.0,
		   (SplashCoord)pt->y * p->textScale / 64.0);
  p->needClose = gTrue;
  return 0;
}

#endif // HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
//========================================================================
//
// SplashFTFont.h
//
//========================================================================

#ifndef SPLASHFTFONT_H
#define SPLASHFTFONT_H

#include <aconf.h>

#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include <ft2build.h>
#include FT_FREETYPE_H
#include "SplashFont.h"

class SplashFTFontFile;

//------------------------------------------------------------------------
// SplashFTFont
//------------------------------------------------------------------------

class SplashFTFont: public SplashFont {
public:

  SplashFTFont(SplashFTFontFile *fontFileA, SplashCoord *matA,
	       SplashCoord *textMatA);

  virtual ~SplashFTFont();

  // Munge xFrac and yFrac before calling SplashFont::getGlyph.
  virtual GBool getGlyph(int c, int xFrac, int yFrac,
			 SplashGlyphBitmap *bitmap);

  // Rasterize a glyph.  The <xFrac> and <yFrac> values are the same
  // as described for getGlyph.
  virtual GBool makeGlyph(int c, int xFrac, int yFrac,
			  SplashGlyphBitmap *bitmap);

  // Return the path for a glyph.
  virtual SplashPath *getGlyphPath(int c);

private:

  FT_Size sizeObj;
  FT_Matrix matrix;
  FT_Matrix textMatrix;
  SplashCoord textScale;
};

#endif // HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H

#endif
//========================================================================
//
// SplashFTFontEngine.cc
//
//========================================================================

#include <aconf.h>

#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stdio.h>
#ifndef WIN32
#  include <unistd.h>
#endif
#include "gmem.h"
#include "GString.h"
#include "gfile.h"
#include "FoFiTrueType.h"
#include "FoFiType1C.h"
#include "SplashFTFontFile.h"
#include "SplashFTFontEngine.h"

#ifdef VMS
#if (__VMS_VER < 70000000)
extern "C" int unlink(char *filename);
#endif
#endif

//------------------------------------------------------------------------

static void fileWrite(void *stream, char *data, int len) {
  fwrite(data, 1, len, (FILE *)stream);
}

//------------------------------------------------------------------------
// SplashFTFontEngine
//------------------------------------------------------------------------

SplashFTFontEngine::SplashFTFontEngine(GBool aaA, FT_Library libA) {
  FT_Int major, minor, patch;

  aa = aaA;
  lib = libA;

  // as of FT 2.1.8, CID fonts are indexed by CID instead of GID
  FT_Library_Version(lib, &major, &minor, &patch);
  useCIDs = major > 2 ||
            (major == 2 && (minor > 1 || (minor == 1 && patch > 7)));
}

SplashFTFontEngine *SplashFTFontEngine::init(GBool aaA) {
  FT_Library libA;

  if (FT_Init_FreeType(&libA)) {
    return NULL;
  }
  return new SplashFTFontEngine(aaA, libA);
}

SplashFTFontEngine::~SplashFTFontEngine() {
  FT_Done_FreeType(lib);
}

SplashFontFile *SplashFTFontEngine::loadType1Font(SplashFontFileID *idA,
						  char *fileName,
						  GBool deleteFile,
						  char **enc) {
  return SplashFTFontFile::loadType1Font(this, idA, fileName, deleteFile, enc);
}

SplashFontFile *SplashFTFontEngine::loadType1CFont(SplashFontFileID *idA,
						   char *fileName,
						   GBool deleteFile,
						   char **enc) {
  return SplashFTFontFile::loadType1Font(this, idA, fileName, deleteFile, enc);
}

SplashFontFile *SplashFTFontEngine::loadOpenTypeT1CFont(SplashFontFileID *idA,
							char *fileName,
							GBool deleteFile,
							char **enc) {
  return SplashFTFontFile::loadType1Font(this, idA, fileName, deleteFile, enc);
}

SplashFontFile *SplashFTFontEngine::loadCIDFont(SplashFontFileID *idA,
						char *fileName,
						GBool deleteFile) {
  FoFiType1C *ff;
  Gushort *cidToGIDMap;
  int nCIDs;
  SplashFontFile *ret;

  // check for a CFF font
  if (useCIDs) {
    cidToGIDMap = NULL;
    nCIDs = 0;
  } else if ((ff = FoFiType1C::load(fileName))) {
    cidToGIDMap = ff->getCIDToGIDMap(&nCIDs);
    delete ff;
  } else {
    cidToGIDMap = NULL;
    nCIDs = 0;
  }
  ret = SplashFTFontFile::loadCIDFont(this, idA, fileName, deleteFile,
				      cidToGIDMap, nCIDs);
  if (!ret) {
    gfree(cidToGIDMap);
  }
  return ret;
}

SplashFontFile *SplashFTFontEngine::loadOpenTypeCFFFont(SplashFontFileID *idA,
							char *fileName,
							GBool deleteFile) {
  FoFiTrueType *ff;
  GBool isCID;
  Gushort *cidToGIDMap;
  int nCIDs;
  SplashFontFile *ret;

  cidToGIDMap = NULL;
  nCIDs = 0;
  isCID = gFalse;
  if (!useCIDs) {
    if ((ff = FoFiTrueType::load(fileName))) {
      if (ff->isOpenTypeCFF()) {
	cidToGIDMap = ff->getCIDToGIDMap(&nCIDs);
      }
      delete ff;
    }
  }
  ret = SplashFTFontFile::loadCIDFont(this, idA, fileName, deleteFile,
				      cidToGIDMap, nCIDs);
  if (!ret) {
    gfree(cidToGIDMap);
  }
  return ret;
}

SplashFontFile *SplashFTFontEngine::loadTrueTypeFont(SplashFontFileID *idA,
						     char *fileName,
						     GBool deleteFile,
						     Gushort *codeToGID,
						     int codeToGIDLen) {
  FoFiTrueType *ff;
  GString *tmpFileName;
  FILE *tmpFile;
  SplashFontFile *ret;

  if (!(ff = FoFiTrueType::load(fileName))) {
    return NULL;
  }
  tmpFileName = NULL;
  if (!openTempFile(&tmpFileName, &tmpFile, "wb", NULL)) {
    delete ff;
    return NULL;
  }
  ff->writeTTF(&fileWrite, tmpFile);
  delete ff;
  fclose(tmpFile);
  ret = SplashFTFontFile::loadTrueTypeFont(this, idA,
					   tmpFileName->getCString(),
					   gTrue, codeToGID, codeToGIDLen);
  if (ret) {
    if (deleteFile) {
      unlink(fileName);
    }
  } else {
    unlink(tmpFileName->getCString());
  }
  delete tmpFileName;
  return ret;
}

#endif // HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
//========================================================================
//
// SplashFTFontEngine.h
//
//========================================================================

#ifndef SPLASHFTFONTENGINE_H
#define SPLASHFTFONTENGINE_H

#include <aconf.h>

#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include <ft2build.h>
#include FT_FREETYPE_H
#include "gtypes.h"

class SplashFontFile;
class SplashFontFileID;

//------------------------------------------------------------------------
// SplashFTFontEngine
//------------------------------------------------------------------------

class SplashFTFontEngine {
public:

  static SplashFTFontEngine *init(GBool aaA);

  ~SplashFTFontEngine();

  // Load fonts.
  SplashFontFile *loadType1Font(SplashFontFileID *idA, char *fileName,
				GBool deleteFile, char **enc);
  SplashFontFile *loadType1CFont(SplashFontFileID *idA, char *fileName,
				 GBool deleteFile, char **enc);
  SplashFontFile *loadOpenTypeT1CFont(SplashFontFileID *idA, char *fileName,
				      GBool deleteFile, char **enc);
  SplashFontFile *loadCIDFont(SplashFontFileID *idA, char *fileName,
			      GBool deleteFile);
  SplashFontFile *loadOpenTypeCFFFont(SplashFontFileID *idA, char *fileName,
				      GBool deleteFile);
  SplashFontFile *loadTrueTypeFont(SplashFontFileID *idA, char *fileName,
				   GBool deleteFile,
				   Gushort *codeToGID, int codeToGIDLen);

private:

  SplashFTFontEngine(GBool aaA, FT_Library libA);

  GBool aa;
  FT_Library lib;
  GBool useCIDs;

  friend class SplashFTFontFile;
  friend class SplashFTFont;
};

#endif // HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H

#endif
//========================================================================
//
// SplashFTFontFile.cc
//
//========================================================================

#include <aconf.h>

#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include "gmem.h"
#include "SplashFTFontEngine.h"
#include "SplashFTFont.h"
#include "SplashFTFontFile.h"

//------------------------------------------------------------------------
// SplashFTFontFile
//------------------------------------------------------------------------

SplashFontFile *SplashFTFontFile::loadType1Font(SplashFTFontEngine *engineA,
						SplashFontFileID *idA,
						char *fileNameA,
						GBool deleteFileA,
						char **encA) {
  FT_Face faceA;
  Gushort *codeToGIDA;
  char *name;
  int i;

  if (FT_New_Face(engineA->lib, fileNameA, 0, &faceA)) {
    return NULL;
  }
  codeToGIDA = (Gushort *)gmallocn(256, sizeof(int));
  for (i = 0; i < 256; ++i) {
    codeToGIDA[i] = 0;
    if ((name = encA[i])) {
      codeToGIDA[i] = (Gushort)FT_Get_Name_Index(faceA, name);
    }
  }

  return new SplashFTFontFile(engineA, idA, fileNameA, deleteFileA,
			      faceA, codeToGIDA, 256, gFalse);
}

SplashFontFile *SplashFTFontFile::loadCIDFont(SplashFTFontEngine *engineA,
					      SplashFontFileID *idA,
					      char *fileNameA,
					      GBool deleteFileA,
					      Gushort *codeToGIDA,
					      int codeToGIDLenA) {
  FT_Face faceA;

  if (FT_New_Face(engineA->lib, fileNameA, 0, &faceA)) {
    return NULL;
  }

  return new SplashFTFontFile(engineA, idA, fileNameA, deleteFileA,
			      faceA, codeToGIDA, codeToGIDLenA, gFalse);
}

SplashFontFile *SplashFTFontFile::loadTrueTypeFont(SplashFTFontEngine *engineA,
						   SplashFontFileID *idA,
						   char *fileNameA,
						   GBool deleteFileA,
						   Gushort *codeToGIDA,
						   int codeToGIDLenA) {
  FT_Face faceA;

  if (FT_New_Face(engineA->lib, fileNameA, 0, &faceA)) {
    return NULL;
  }

  return new SplashFTFontFile(engineA, idA, fileNameA, deleteFileA,
			      faceA, codeToGIDA, codeToGIDLenA, gTrue);
}

SplashFTFontFile::SplashFTFontFile(SplashFTFontEngine *engineA,
				   SplashFontFileID *idA,
				   char *fileNameA, GBool deleteFileA,
				   FT_Face faceA,
				   Gushort *codeToGIDA, int codeToGIDLenA,
				   GBool trueTypeA):
  SplashFontFile(idA, fileNameA, deleteFileA)
{
  engine = engineA;
  face = faceA;
  codeToGID = codeToGIDA;
  codeToGIDLen = codeToGIDLenA;
  trueType = trueTypeA;
}

SplashFTFontFile::~SplashFTFontFile() {
  if (face) {
    FT_Done_Face(face);
  }
  if (codeToGID) {
    gfree(codeToGID);
  }
}

SplashFont *SplashFTFontFile::makeFont(SplashCoord *mat,
				       SplashCoord *textMat) {
  SplashFont *font;

  font = new SplashFTFont(this, mat, textMat);
  font->initCache();
  return font;
}

#endif // HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
//========================================================================
//
// SplashFTFontFile.h
//
//========================================================================

#ifndef SPLASHFTFONTFILE_H
#define SPLASHFTFONTFILE_H

#include <aconf.h>

#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include <ft2build.h>
#include FT_FREETYPE_H
#include "SplashFontFile.h"

class SplashFontFileID;
class SplashFTFontEngine;

//------------------------------------------------------------------------
// SplashFTFontFile
//------------------------------------------------------------------------

class SplashFTFontFile: public SplashFontFile {
public:

  static SplashFontFile *loadType1Font(SplashFTFontEngine *engineA,
				       SplashFontFileID *idA, char *fileNameA,
				       GBool deleteFileA, char **encA);
  static SplashFontFile *loadCIDFont(SplashFTFontEngine *engineA,
				     SplashFontFileID *idA, char *fileNameA,
				     GBool deleteFileA,
				     Gushort *codeToCIDA, int codeToGIDLenA);
  static SplashFontFile *loadTrueTypeFont(SplashFTFontEngine *engineA,
					  SplashFontFileID *idA,
					  char *fileNameA,
					  GBool deleteFileA,
					  Gushort *codeToGIDA,
					  int codeToGIDLenA);

  virtual ~SplashFTFontFile();

  // Create a new SplashFTFont, i.e., a scaled instance of this font
  // file.
  virtual SplashFont *makeFont(SplashCoord *mat,
			       SplashCoord *textMat);

private:

  SplashFTFontFile(SplashFTFontEngine *engineA,
		   SplashFontFileID *idA,
		   char *fileNameA, GBool deleteFileA,
		   FT_Face faceA,
		   Gushort *codeToGIDA, int codeToGIDLenA,
		   GBool trueTypeA);

  SplashFTFontEngine *engine;
  FT_Face face;
  Gushort *codeToGID;
  int codeToGIDLen;
  GBool trueType;

  friend class SplashFTFont;
};

#endif // HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H

#endif
//========================================================================
//
// SplashFont.cc
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <string.h>
#include "gmem.h"
#include "SplashMath.h"
#include "SplashGlyphBitmap.h"
#include "SplashFontFile.h"
#include "SplashFont.h"

//------------------------------------------------------------------------

struct SplashFontCacheTag {
  int c;
  short xFrac, yFrac;		// x and y fractions
  int mru;			// valid bit (0x80000000) and MRU index
  int x, y, w, h;		// offset and size of glyph
};

//------------------------------------------------------------------------
// SplashFont
//------------------------------------------------------------------------

SplashFont::SplashFont(SplashFontFile *fontFileA, SplashCoord *matA,
		       SplashCoord *textMatA, GBool aaA) {
  fontFile = fontFileA;
  fontFile->incRefCnt();
  mat[0] = matA[0];
  mat[1] = matA[1];
  mat[2] = matA[2];
  mat[3] = matA[3];
  textMat[0] = textMatA[0];
  textMat[1] = textMatA[1];
  textMat[2] = textMatA[2];
  textMat[3] = textMatA[3];
  aa = aaA;

  cache = NULL;
  cacheTags = NULL;

  xMin = yMin = xMax = yMax = 0;
}

void SplashFont::initCache() {
  int i;

  // this should be (max - min + 1), but we add some padding to
  // deal with rounding errors
  glyphW = xMax - xMin + 3;
  glyphH = yMax - yMin + 3;
  if (aa) {
    glyphSize = glyphW * glyphH;
  } else {
    glyphSize = ((glyphW + 7) >> 3) * glyphH;
  }

  // set up the glyph pixmap cache
  cacheAssoc = 8;
  if (glyphSize <= 256) {
    cacheSets = 8;
  } else if (glyphSize <= 512) {
    cacheSets = 4;
  } else if (glyphSize <= 1024) {
    cacheSets = 2;
  } else {
    cacheSets = 1;
  }
  cache = (Guchar *)gmallocn(cacheSets * cacheAssoc, glyphSize);
  cacheTags = (SplashFontCacheTag *)gmallocn(cacheSets * cacheAssoc,
					     sizeof(SplashFontCacheTag));
  for (i = 0; i < cacheSets * cacheAssoc; ++i) {
    cacheTags[i].mru = i & (cacheAssoc - 1);
  }
}

SplashFont::~SplashFont() {
  fontFile->decRefCnt();
  if (cache) {
    gfree(cache);
  }
  if (cacheTags) {
    gfree(cacheTags);
  }
}

GBool SplashFont::getGlyph(int c, int xFrac, int yFrac,
			   SplashGlyphBitmap *bitmap) {
  SplashGlyphBitmap bitmap2;
  int size;
  Guchar *p;
  int i, j, k;

  // no fractional coordinates for large glyphs or non-anti-aliased
  // glyphs
  if (!aa || glyphH > 50) {
    xFrac = yFrac = 0;
  }

  // check the cache
  i = (c & (cacheSets - 1)) * cacheAssoc;
  for (j = 0; j < cacheAssoc; ++j) {
    if ((cacheTags[i+j].mru & 0x80000000) &&
	cacheTags[i+j].c == c &&
	(int)cacheTags[i+j].xFrac == xFrac &&
	(int)cacheTags[i+j].yFrac == yFrac) {
      bitmap->x = cacheTags[i+j].x;
      bitmap->y = cacheTags[i+j].y;
      bitmap->w = cacheTags[i+j].w;
      bitmap->h = cacheTags[i+j].h;
      for (k = 0; k < cacheAssoc; ++k) {
	if (k != j &&
	    (cacheTags[i+k].mru & 0x7fffffff) <
	      (cacheTags[i+j].mru & 0x7fffffff)) {
	  ++cacheTags[i+k].mru;
	}
      }
      cacheTags[i+j].mru = 0x80000000;
      bitmap->aa = aa;
      bitmap->data = cache + (i+j) * glyphSize;
      bitmap->freeData = gFalse;
      return gTrue;
    }
  }

  // generate the glyph bitmap
  if (!makeGlyph(c, xFrac, yFrac, &bitmap2)) {
    return gFalse;
  }

  // if the glyph doesn't fit in the bounding box, return a temporary
  // uncached bitmap
  if (bitmap2.w > glyphW || bitmap2.h > glyphH) {
    *bitmap = bitmap2;
    return gTrue;
  }

  // insert glyph pixmap in cache
  if (aa) {
    size = bitmap2.w * bitmap2.h;
  } else {
    size = ((bitmap2.w + 7) >> 3) * bitmap2.h;
  }
  p = NULL; // make gcc happy
  for (j = 0; j < cacheAssoc; ++j) {
    if ((cacheTags[i+j].mru & 0x7fffffff) == cacheAssoc - 1) {
      cacheTags[i+j].mru = 0x80000000;
      cacheTags[i+j].c = c;
      cacheTags[i+j].xFrac = (short)xFrac;
      cacheTags[i+j].yFrac = (short)yFrac;
      cacheTags[i+j].x = bitmap2.x;
      cacheTags[i+j].y = bitmap2.y;
      cacheTags[i+j].w = bitmap2.w;
      cacheTags[i+j].h = bitmap2.h;
      p = cache + (i+j) * glyphSize;
      memcpy(p, bitmap2.data, size);
    } else {
      ++cacheTags[i+j].mru;
    }
  }
  *bitmap = bitmap2;
  bitmap->data = p;
  bitmap->freeData = gFalse;
  if (bitmap2.freeData) {
    gfree(bitmap2.data);
  }
  return gTrue;
}
//========================================================================
//
// SplashFont.h
//
//========================================================================

#ifndef SPLASHFONT_H
#define SPLASHFONT_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "gtypes.h"
#include "SplashTypes.h"

struct SplashGlyphBitmap;
struct SplashFontCacheTag;
class SplashFontFile;
class SplashPath;

//------------------------------------------------------------------------

// Fractional positioning uses this many bits to the right of the
// decimal points.
#define splashFontFractionBits 2
#define splashFontFraction     (1 << splashFontFractionBits)
#define splashFontFractionMul \
                       ((SplashCoord)1 / (SplashCoord)splashFontFraction)

//------------------------------------------------------------------------
// SplashFont
//------------------------------------------------------------------------

class SplashFont {
public:

  SplashFont(SplashFontFile *fontFileA, SplashCoord *matA,
	     SplashCoord *textMatA, GBool aaA);

  // This must be called after the constructor, so that the subclass
  // constructor has a chance to compute the bbox.
  void initCache();

  virtual ~SplashFont();

  SplashFontFile *getFontFile() { return fontFile; }

  // Return true if <this> matches the specified font file and matrix.
  GBool matches(SplashFontFile *fontFileA, SplashCoord *matA,
		SplashCoord *textMatA) {
    return fontFileA == fontFile &&
           matA[0] == mat[0] && matA[1] == mat[1] &&
           matA[2] == mat[2] && matA[3] == mat[3] &&
           textMatA[0] == textMat[0] && textMatA[1] == textMat[1] &&
           textMatA[2] == textMat[2] && textMatA[3] == textMat[3];
  }

  // Get a glyph - this does a cache lookup first, and if not found,
  // creates a new bitmap and adds it to the cache.  The <xFrac> and
  // <yFrac> values are splashFontFractionBits bits each, representing
  // the numerators of fractions in [0, 1), where the denominator is
  // splashFontFraction = 1 << splashFontFractionBits.  Subclasses
  // should override this to zero out xFrac and/or yFrac if they don't
  // support fractional coordinates.
  virtual GBool getGlyph(int c, int xFrac, int yFrac,
			 SplashGlyphBitmap *bitmap);

  // Rasterize a glyph.  The <xFrac> and <yFrac> values are the same
  // as described for getGlyph.
  virtual GBool makeGlyph(int c, int xFrac, int yFrac,
			  SplashGlyphBitmap *bitmap) = 0;

  // Return the path for a glyph.
  virtual SplashPath *getGlyphPath(int c) = 0;

  // Return the font transform matrix.
  SplashCoord *getMatrix() { return mat; }

  // Return the glyph bounding box.
  void getBBox(int *xMinA, int *yMinA, int *xMaxA, int *yMaxA)
    { *xMinA = xMin; *yMinA = yMin; *xMaxA = xMax; *yMaxA = yMax; }

protected:

  SplashFontFile *fontFile;
  SplashCoord mat[4];		// font transform matrix
				//   (text space -> device space)
  SplashCoord textMat[4];	// text transform matrix
				//   (text space -> user space)
  GBool aa;			// anti-aliasing
  int xMin, yMin, xMax, yMax;	// glyph bounding box
  Guchar *cache;		// glyph bitmap cache
  SplashFontCacheTag *		// cache tags
    cacheTags;
  int glyphW, glyphH;		// size of glyph bitmaps
  int glyphSize;		// size of glyph bitmaps, in bytes
  int cacheSets;		// number of sets in cache
  int cacheAssoc;		// cache associativity (glyphs per set)
};

#endif
//========================================================================
//
// SplashFontEngine.cc
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#if HAVE_T1LIB_H
#include <t1lib.h>
#endif

#include <stdlib.h>
#include <stdio.h>
#ifndef WIN32
#  include <unistd.h>
#endif
#include "gmem.h"
#include "GString.h"
#include "SplashMath.h"
#include "SplashT1FontEngine.h"
#include "SplashFTFontEngine.h"
#include "SplashFontFile.h"
#include "SplashFontFileID.h"
#include "SplashFont.h"
#include "SplashFontEngine.h"

#ifdef VMS
#if (__VMS_VER < 70000000)
extern "C" int unlink(char *filename);
#endif
#endif

//------------------------------------------------------------------------
// SplashFontEngine
//------------------------------------------------------------------------

SplashFontEngine::SplashFontEngine(
#if HAVE_T1LIB_H
				   GBool enableT1lib,
#endif
#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
				   GBool enableFreeType,
#endif
				   GBool aa) {
  int i;

  for (i = 0; i < splashFontCacheSize; ++i) {
    fontCache[i] = NULL;
  }

#if HAVE_T1LIB_H
  if (enableT1lib) {
    t1Engine = SplashT1FontEngine::init(aa);
  } else {
    t1Engine = NULL;
  }
#endif
#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
  if (enableFreeType) {
    ftEngine = SplashFTFontEngine::init(aa);
  } else {
    ftEngine = NULL;
  }
#endif
}

SplashFontEngine::~SplashFontEngine() {
  int i;

  for (i = 0; i < splashFontCacheSize; ++i) {
    if (fontCache[i]) {
      delete fontCache[i];
    }
  }

#if HAVE_T1LIB_H
  if (t1Engine) {
    delete t1Engine;
  }
#endif
#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
  if (ftEngine) {
    delete ftEngine;
  }
#endif
}

SplashFontFile *SplashFontEngine::getFontFile(SplashFontFileID *id) {
  SplashFontFile *fontFile;
  int i;

  for (i = 0; i < splashFontCacheSize; ++i) {
    if (fontCache[i]) {
      fontFile = fontCache[i]->getFontFile();
      if (fontFile && fontFile->getID()->matches(id)) {
	return fontFile;
      }
    }
  }
  return NULL;
}

SplashFontFile *SplashFontEngine::loadType1Font(SplashFontFileID *idA,
						char *fileName,
						GBool deleteFile, char **enc) {
  SplashFontFile *fontFile;

  fontFile = NULL;
#if HAVE_T1LIB_H
  if (!fontFile && t1Engine) {
    fontFile = t1Engine->loadType1Font(idA, fileName, deleteFile, enc);
  }
#endif
#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
  if (!fontFile && ftEngine) {
    fontFile = ftEngine->loadType1Font(idA, fileName, deleteFile, enc);
  }
#endif

#ifndef WIN32
  // delete the (temporary) font file -- with Unix hard link
  // semantics, this will remove the last link; otherwise it will
  // return an error, leaving the file to be deleted later (if
  // loadXYZFont failed, the file will always be deleted)
  if (deleteFile) {
    unlink(fontFile ? fontFile->fileName->getCString() : fileName);
  }
#endif

  return fontFile;
}

SplashFontFile *SplashFontEngine::loadType1CFont(SplashFontFileID *idA,
						 char *fileName,
						 GBool deleteFile,
						 char **enc) {
  SplashFontFile *fontFile;

  fontFile = NULL;
#if HAVE_T1LIB_H
  if (!fontFile && t1Engine) {
    fontFile = t1Engine->loadType1CFont(idA, fileName, deleteFile, enc);
  }
#endif
#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
  if (!fontFile && ftEngine) {
    fontFile = ftEngine->loadType1CFont(idA, fileName, deleteFile, enc);
  }
#endif

#ifndef WIN32
  // delete the (temporary) font file -- with Unix hard link
  // semantics, this will remove the last link; otherwise it will
  // return an error, leaving the file to be deleted later (if
  // loadXYZFont failed, the file will always be deleted)
  if (deleteFile) {
    unlink(fontFile ? fontFile->fileName->getCString() : fileName);
  }
#endif

  return fontFile;
}

SplashFontFile *SplashFontEngine::loadOpenTypeT1CFont(SplashFontFileID *idA,
						      char *fileName,
						      GBool deleteFile,
						      char **enc) {
  SplashFontFile *fontFile;

  fontFile = NULL;
#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
  if (!fontFile && ftEngine) {
    fontFile = ftEngine->loadOpenTypeT1CFont(idA, fileName, deleteFile, enc);
  }
#endif

#ifndef WIN32
  // delete the (temporary) font file -- with Unix hard link
  // semantics, this will remove the last link; otherwise it will
  // return an error, leaving the file to be deleted later (if
  // loadXYZFont failed, the file will always be deleted)
  if (deleteFile) {
    unlink(fontFile ? fontFile->fileName->getCString() : fileName);
  }
#endif

  return fontFile;
}

SplashFontFile *SplashFontEngine::loadCIDFont(SplashFontFileID *idA,
					      char *fileName,
					      GBool deleteFile) {
  SplashFontFile *fontFile;

  fontFile = NULL;
#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
  if (!fontFile && ftEngine) {
    fontFile = ftEngine->loadCIDFont(idA, fileName, deleteFile);
  }
#endif

#ifndef WIN32
  // delete the (temporary) font file -- with Unix hard link
  // semantics, this will remove the last link; otherwise it will
  // return an error, leaving the file to be deleted later (if
  // loadXYZFont failed, the file will always be deleted)
  if (deleteFile) {
    unlink(fontFile ? fontFile->fileName->getCString() : fileName);
  }
#endif

  return fontFile;
}

SplashFontFile *SplashFontEngine::loadOpenTypeCFFFont(SplashFontFileID *idA,
						      char *fileName,
						      GBool deleteFile) {
  SplashFontFile *fontFile;

  fontFile = NULL;
#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
  if (!fontFile && ftEngine) {
    fontFile = ftEngine->loadOpenTypeCFFFont(idA, fileName, deleteFile);
  }
#endif

#ifndef WIN32
  // delete the (temporary) font file -- with Unix hard link
  // semantics, this will remove the last link; otherwise it will
  // return an error, leaving the file to be deleted later (if
  // loadXYZFont failed, the file will always be deleted)
  if (deleteFile) {
    unlink(fontFile ? fontFile->fileName->getCString() : fileName);
  }
#endif

  return fontFile;
}

SplashFontFile *SplashFontEngine::loadTrueTypeFont(SplashFontFileID *idA,
						   char *fileName,
						   GBool deleteFile,
						   Gushort *codeToGID,
						   int codeToGIDLen) {
  SplashFontFile *fontFile;

  fontFile = NULL;
#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
  if (!fontFile && ftEngine) {
    fontFile = ftEngine->loadTrueTypeFont(idA, fileName, deleteFile,
					  codeToGID, codeToGIDLen);
  }
#endif

  if (!fontFile) {
    gfree(codeToGID);
  }

#ifndef WIN32
  // delete the (temporary) font file -- with Unix hard link
  // semantics, this will remove the last link; otherwise it will
  // return an error, leaving the file to be deleted later (if
  // loadXYZFont failed, the file will always be deleted)
  if (deleteFile) {
    unlink(fontFile ? fontFile->fileName->getCString() : fileName);
  }
#endif

  return fontFile;
}

SplashFont *SplashFontEngine::getFont(SplashFontFile *fontFile,
				      SplashCoord *textMat,
				      SplashCoord *ctm) {
  SplashCoord mat[4];
  SplashFont *font;
  int i, j;

  mat[0] = textMat[0] * ctm[0] + textMat[1] * ctm[2];
  mat[1] = -(textMat[0] * ctm[1] + textMat[1] * ctm[3]);
  mat[2] = textMat[2] * ctm[0] + textMat[3] * ctm[2];
  mat[3] = -(textMat[2] * ctm[1] + textMat[3] * ctm[3]);
  if (splashAbs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.01) {
    // avoid a singular (or close-to-singular) matrix
    mat[0] = 0.01;  mat[1] = 0;
    mat[2] = 0;     mat[3] = 0.01;
  }

  font = fontCache[0];
  if (font && font->matches(fontFile, mat, textMat)) {
    return font;
  }
  for (i = 1; i < splashFontCacheSize; ++i) {
    font = fontCache[i];
    if (font && font->matches(fontFile, mat, textMat)) {
      for (j = i; j > 0; --j) {
	fontCache[j] = fontCache[j-1];
      }
      fontCache[0] = font;
      return font;
    }
  }
  font = fontFile->makeFont(mat, textMat);
  if (fontCache[splashFontCacheSize - 1]) {
    delete fontCache[splashFontCacheSize - 1];
  }
  for (j = splashFontCacheSize - 1; j > 0; --j) {
    fontCache[j] = fontCache[j-1];
  }
  fontCache[0] = font;
  return font;
}
//========================================================================
//
// SplashFontEngine.h
//
//========================================================================

#ifndef SPLASHFONTENGINE_H
#define SPLASHFONTENGINE_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "gtypes.h"

class SplashT1FontEngine;
class SplashFTFontEngine;
class SplashDTFontEngine;
class SplashDT4FontEngine;
class SplashFontFile;
class SplashFontFileID;
class SplashFont;

//------------------------------------------------------------------------

#define splashFontCacheSize 16

//------------------------------------------------------------------------
// SplashFontEngine
//------------------------------------------------------------------------

class SplashFontEngine {
public:

  // Create a font engine.
  SplashFontEngine(
#if HAVE_T1LIB_H
		   GBool enableT1lib,
#endif
#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
		   GBool enableFreeType,
#endif
		   GBool aa);

  ~SplashFontEngine();

  // Get a font file from the cache.  Returns NULL if there is no
  // matching entry in the cache.
  SplashFontFile *getFontFile(SplashFontFileID *id);

  // Load fonts - these create new SplashFontFile objects.
  SplashFontFile *loadType1Font(SplashFontFileID *idA, char *fileName,
				GBool deleteFile, char **enc);
  SplashFontFile *loadType1CFont(SplashFontFileID *idA, char *fileName,
				 GBool deleteFile, char **enc);
  SplashFontFile *loadOpenTypeT1CFont(SplashFontFileID *idA, char *fileName,
				      GBool deleteFile, char **enc);
  SplashFontFile *loadCIDFont(SplashFontFileID *idA, char *fileName,
			      GBool deleteFile);
  SplashFontFile *loadOpenTypeCFFFont(SplashFontFileID *idA, char *fileName,
				      GBool deleteFile);
  SplashFontFile *loadTrueTypeFont(SplashFontFileID *idA, char *fileName,
				   GBool deleteFile,
				   Gushort *codeToGID, int codeToGIDLen);

  // Get a font - this does a cache lookup first, and if not found,
  // creates a new SplashFont object and adds it to the cache.  The
  // matrix, mat = textMat * ctm:
  //    [ mat[0] mat[1] ]
  //    [ mat[2] mat[3] ]
  // specifies the font transform in PostScript style:
  //    [x' y'] = [x y] * mat
  // Note that the Splash y axis points downward.
  SplashFont *getFont(SplashFontFile *fontFile,
		      SplashCoord *textMat, SplashCoord *ctm);

private:

  SplashFont *fontCache[splashFontCacheSize];

#if HAVE_T1LIB_H
  SplashT1FontEngine *t1Engine;
#endif
#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
  SplashFTFontEngine *ftEngine;
#endif
};

#endif
//========================================================================
//
// SplashFontFile.cc
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stdio.h>
#ifndef WIN32
#  include <unistd.h>
#endif
#include "GString.h"
#include "SplashFontFile.h"
#include "SplashFontFileID.h"

#ifdef VMS
#if (__VMS_VER < 70000000)
extern "C" int unlink(char *filename);
#endif
#endif

//------------------------------------------------------------------------
// SplashFontFile
//------------------------------------------------------------------------

SplashFontFile::SplashFontFile(SplashFontFileID *idA, char *fileNameA,
			       GBool deleteFileA) {
  id = idA;
  fileName = new GString(fileNameA);
  deleteFile = deleteFileA;
  refCnt = 0;
}

SplashFontFile::~SplashFontFile() {
  if (deleteFile) {
    unlink(fileName->getCString());
  }
  delete fileName;
  delete id;
}

void SplashFontFile::incRefCnt() {
  ++refCnt;
}

void SplashFontFile::decRefCnt() {
  if (!--refCnt) {
    delete this;
  }
}
//========================================================================
//
// SplashFontFile.h
//
//========================================================================

#ifndef SPLASHFONTFILE_H
#define SPLASHFONTFILE_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "gtypes.h"
#include "SplashTypes.h"

class GString;
class SplashFontEngine;
class SplashFont;
class SplashFontFileID;

//------------------------------------------------------------------------
// SplashFontFile
//------------------------------------------------------------------------

class SplashFontFile {
public:

  virtual ~SplashFontFile();

  // Create a new SplashFont, i.e., a scaled instance of this font
  // file.
  virtual SplashFont *makeFont(SplashCoord *mat, SplashCoord *textMat) = 0;

  // Get the font file ID.
  SplashFontFileID *getID() { return id; }

  // Increment the reference count.
  void incRefCnt();

  // Decrement the reference count.  If the new value is zero, delete
  // the SplashFontFile object.
  void decRefCnt();

protected:

  SplashFontFile(SplashFontFileID *idA, char *fileNameA,
		 GBool deleteFileA);

  SplashFontFileID *id;
  GString *fileName;
  GBool deleteFile;
  int refCnt;

  friend class SplashFontEngine;
};

#endif
//========================================================================
//
// SplashFontFileID.cc
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include "SplashFontFileID.h"

//------------------------------------------------------------------------
// SplashFontFileID
//------------------------------------------------------------------------

SplashFontFileID::SplashFontFileID() {
}

SplashFontFileID::~SplashFontFileID() {
}
//========================================================================
//
// SplashFontFileID.h
//
//========================================================================

#ifndef SPLASHFONTFILEID_H
#define SPLASHFONTFILEID_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "gtypes.h"

//------------------------------------------------------------------------
// SplashFontFileID
//------------------------------------------------------------------------

class SplashFontFileID {
public:

  SplashFontFileID();
  virtual ~SplashFontFileID();
  virtual GBool matches(SplashFontFileID *id) = 0;
};

#endif
//========================================================================
//
// SplashGlyphBitmap.h
//
//========================================================================

#ifndef SPLASHGLYPHBITMAP_H
#define SPLASHGLYPHBITMAP_H

#include <aconf.h>

#include "gtypes.h"

//------------------------------------------------------------------------
// SplashGlyphBitmap
//------------------------------------------------------------------------

struct SplashGlyphBitmap {
  int x, y, w, h;		// offset and size of glyph
  GBool aa;			// anti-aliased: true means 8-bit alpha
				//   bitmap; false means 1-bit
  Guchar *data;			// bitmap data
  GBool freeData;		// true if data memory should be freed
};

#endif
//========================================================================
//
// SplashMath.h
//
//========================================================================

#ifndef SPLASHMATH_H
#define SPLASHMATH_H

#include <aconf.h>
#if USE_FIXEDPONT
#include "FixedPoint.h"
#else
#include <math.h>
#endif
#include "SplashTypes.h"

static inline SplashCoord splashAbs(SplashCoord x) {
#if USE_FIXEDPOINT
  return FixedPoint::abs(x);
#else
  return fabs(x);
#endif
}

static inline int splashFloor(SplashCoord x) {
#if USE_FIXEDPOINT
  return FixedPoint::floor(x);
#else
  return (int)floor(x);
#endif
}

static inline int splashCeil(SplashCoord x) {
#if USE_FIXEDPOINT
  return FixedPoint::ceil(x);
#else
  return (int)ceil(x);
#endif
}

static inline int splashRound(SplashCoord x) {
#if USE_FIXEDPOINT
  return FixedPoint::round(x);
#else
  return (int)floor(x + 0.5);
#endif
}

static inline SplashCoord splashSqrt(SplashCoord x) {
#if USE_FIXEDPOINT
  return FixedPoint::sqrt(x);
#else
  return sqrt(x);
#endif
}

static inline SplashCoord splashPow(SplashCoord x, SplashCoord y) {
#if USE_FIXEDPOINT
  return FixedPoint::pow(x, y);
#else
  return pow(x, y);
#endif
}

static inline SplashCoord splashDist(SplashCoord x0, SplashCoord y0,
				     SplashCoord x1, SplashCoord y1) {
  SplashCoord dx, dy;
  dx = x1 - x0;
  dy = y1 - y0;
#if USE_FIXEDPOINT
  // this handles the situation where dx*dx or dy*dy is too large to
  // fit in the 16.16 fixed point format
  SplashCoord dxa, dya;
  dxa = splashAbs(dx);
  dya = splashAbs(dy);
  if (dxa == 0 && dya == 0) {
    return 0;
  } else if (dxa > dya) {
    return dxa * FixedPoint::sqrt(dya / dxa + 1);
  } else {
    return dya * FixedPoint::sqrt(dxa / dya + 1);
  }
#else
  return sqrt(dx * dx + dy * dy);
#endif
}

#endif
//========================================================================
//
// SplashPath.cc
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <string.h>
#include "gmem.h"
#include "SplashErrorCodes.h"
#include "SplashPath.h"

//------------------------------------------------------------------------
// SplashPath
//------------------------------------------------------------------------

// A path can be in three possible states:
//
// 1. no current point -- zero or more finished subpaths
//    [curSubpath == length]
//
// 2. one point in subpath
//    [curSubpath == length - 1]
//
// 3. open subpath with two or more points
//    [curSubpath < length - 1]

SplashPath::SplashPath() {
  pts = NULL;
  flags = NULL;
  length = size = 0;
  curSubpath = 0;
  hints = NULL;
  hintsLength = hintsSize = 0;
}

SplashPath::SplashPath(SplashPath *path) {
  length = path->length;
  size = path->size;
  pts = (SplashPathPoint *)gmallocn(size, sizeof(SplashPathPoint));
  flags = (Guchar *)gmallocn(size, sizeof(Guchar));
  memcpy(pts, path->pts, length * sizeof(SplashPathPoint));
  memcpy(flags, path->flags, length * sizeof(Guchar));
  curSubpath = path->curSubpath;
  if (path->hints) {
    hintsLength = hintsSize = path->hintsLength;
    hints = (SplashPathHint *)gmallocn(hintsSize, sizeof(SplashPathHint));
    memcpy(hints, path->hints, hintsLength * sizeof(SplashPathHint));
  } else {
    hints = NULL;
  }
}

SplashPath::~SplashPath() {
  gfree(pts);
  gfree(flags);
  gfree(hints);
}

// Add space for <nPts> more points.
void SplashPath::grow(int nPts) {
  if (length + nPts > size) {
    if (size == 0) {
      size = 32;
    }
    while (size < length + nPts) {
      size *= 2;
    }
    pts = (SplashPathPoint *)greallocn(pts, size, sizeof(SplashPathPoint));
    flags = (Guchar *)greallocn(flags, size, sizeof(Guchar));
  }
}

void SplashPath::append(SplashPath *path) {
  int i;

  curSubpath = length + path->curSubpath;
  grow(path->length);
  for (i = 0; i < path->length; ++i) {
    pts[length] = path->pts[i];
    flags[length] = path->flags[i];
    ++length;
  }
}

SplashError SplashPath::moveTo(SplashCoord x, SplashCoord y) {
  if (onePointSubpath()) {
    return splashErrBogusPath;
  }
  grow(1);
  pts[length].x = x;
  pts[length].y = y;
  flags[length] = splashPathFirst | splashPathLast;
  curSubpath = length++;
  return splashOk;
}

SplashError SplashPath::lineTo(SplashCoord x, SplashCoord y) {
  if (noCurrentPoint()) {
    return splashErrNoCurPt;
  }
  flags[length-1] &= ~splashPathLast;
  grow(1);
  pts[length].x = x;
  pts[length].y = y;
  flags[length] = splashPathLast;
  ++length;
  return splashOk;
}

SplashError SplashPath::curveTo(SplashCoord x1, SplashCoord y1,
				SplashCoord x2, SplashCoord y2,
				SplashCoord x3, SplashCoord y3) {
  if (noCurrentPoint()) {
    return splashErrNoCurPt;
  }
  flags[length-1] &= ~splashPathLast;
  grow(3);
  pts[length].x = x1;
  pts[length].y = y1;
  flags[length] = splashPathCurve;
  ++length;
  pts[length].x = x2;
  pts[length].y = y2;
  flags[length] = splashPathCurve;
  ++length;
  pts[length].x = x3;
  pts[length].y = y3;
  flags[length] = splashPathLast;
  ++length;
  return splashOk;
}

SplashError SplashPath::close() {
  if (noCurrentPoint()) {
    return splashErrNoCurPt;
  }
  if (curSubpath == length - 1 ||
      pts[length - 1].x != pts[curSubpath].x ||
      pts[length - 1].y != pts[curSubpath].y) {
    lineTo(pts[curSubpath].x, pts[curSubpath].y);
  }
  flags[curSubpath] |= splashPathClosed;
  flags[length - 1] |= splashPathClosed;
  curSubpath = length;
  return splashOk;
}

void SplashPath::addStrokeAdjustHint(int ctrl0, int ctrl1,
				     int firstPt, int lastPt) {
  if (hintsLength == hintsSize) {
    hintsSize = hintsLength ? 2 * hintsLength : 8;
    hints = (SplashPathHint *)greallocn(hints, hintsSize,
					sizeof(SplashPathHint));
  }
  hints[hintsLength].ctrl0 = ctrl0;
  hints[hintsLength].ctrl1 = ctrl1;
  hints[hintsLength].firstPt = firstPt;
  hints[hintsLength].lastPt = lastPt;
  ++hintsLength;
}

void SplashPath::offset(SplashCoord dx, SplashCoord dy) {
  int i;

  for (i = 0; i < length; ++i) {
    pts[i].x += dx;
    pts[i].y += dy;
  }
}

GBool SplashPath::getCurPt(SplashCoord *x, SplashCoord *y) {
  if (noCurrentPoint()) {
    return gFalse;
  }
  *x = pts[length - 1].x;
  *y = pts[length - 1].y;
  return gTrue;
}
//========================================================================
//
// SplashPath.h
//
//========================================================================

#ifndef SPLASHPATH_H
#define SPLASHPATH_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "SplashTypes.h"

//------------------------------------------------------------------------
// SplashPathPoint
//------------------------------------------------------------------------

struct SplashPathPoint {
  SplashCoord x, y;
};

//------------------------------------------------------------------------
// SplashPath.flags
//------------------------------------------------------------------------

// first point on each subpath sets this flag
#define splashPathFirst         0x01

// last point on each subpath sets this flag
#define splashPathLast          0x02

// if the subpath is closed, its first and last points must be
// identical, and must set this flag
#define splashPathClosed        0x04

// curve control points set this flag
#define splashPathCurve         0x08

//------------------------------------------------------------------------
// SplashPathHint
//------------------------------------------------------------------------

struct SplashPathHint {
  int ctrl0, ctrl1;
  int firstPt, lastPt;
};

//------------------------------------------------------------------------
// SplashPath
//------------------------------------------------------------------------

class SplashPath {
public:

  // Create an empty path.
  SplashPath();

  // Copy a path.
  SplashPath *copy() { return new SplashPath(this); }

  ~SplashPath();

  // Append <path> to <this>.
  void append(SplashPath *path);

  // Start a new subpath.
  SplashError moveTo(SplashCoord x, SplashCoord y);

  // Add a line segment to the last subpath.
  SplashError lineTo(SplashCoord x, SplashCoord y);

  // Add a third-order (cubic) Bezier curve segment to the last
  // subpath.
  SplashError curveTo(SplashCoord x1, SplashCoord y1,
		      SplashCoord x2, SplashCoord y2,
		      SplashCoord x3, SplashCoord y3);

  // Close the last subpath, adding a line segment if necessary.
  SplashError close();

  // Add a stroke adjustment hint.  The controlling segments are
  // <ctrl0> and <ctrl1> (where segments are identified by their first
  // point), and the points to be adjusted are <firstPt> .. <lastPt>.
  void addStrokeAdjustHint(int ctrl0, int ctrl1, int firstPt, int lastPt);

  // Add (<dx>, <dy>) to every point on this path.
  void offset(SplashCoord dx, SplashCoord dy);

  // Get the points on the path.
  int getLength() { return length; }
  void getPoint(int i, double *x, double *y, Guchar *f)
    { *x = pts[i].x; *y = pts[i].y; *f = flags[i]; }

  // Get the current point.
  GBool getCurPt(SplashCoord *x, SplashCoord *y);

private:

  SplashPath(SplashPath *path);
  void grow(int nPts);
  GBool noCurrentPoint() { return curSubpath == length; }
  GBool onePointSubpath() { return curSubpath == length - 1; }
  GBool openSubpath() { return curSubpath < length - 1; }

  SplashPathPoint *pts;		// array of points
  Guchar *flags;		// array of flags
  int length, size;		// length/size of the pts and flags arrays
  int curSubpath;		// index of first point in last subpath

  SplashPathHint *hints;	// list of hints
  int hintsLength, hintsSize;

  friend class SplashXPath;
  friend class Splash;
};

#endif
//========================================================================
//
// SplashPattern.cc
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include "SplashMath.h"
#include "SplashScreen.h"
#include "SplashPattern.h"

//------------------------------------------------------------------------
// SplashPattern
//------------------------------------------------------------------------

SplashPattern::SplashPattern() {
}

SplashPattern::~SplashPattern() {
}

//------------------------------------------------------------------------
// SplashSolidColor
//------------------------------------------------------------------------

SplashSolidColor::SplashSolidColor(SplashColorPtr colorA) {
  splashColorCopy(color, colorA);
}

SplashSolidColor::~SplashSolidColor() {
}

void SplashSolidColor::getColor(int x, int y, SplashColorPtr c) {
  splashColorCopy(c, color);
}
//========================================================================
//
// SplashPattern.h
//
//========================================================================

#ifndef SPLASHPATTERN_H
#define SPLASHPATTERN_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "SplashTypes.h"

class SplashScreen;

//------------------------------------------------------------------------
// SplashPattern
//------------------------------------------------------------------------

class SplashPattern {
public:

  SplashPattern();

  virtual SplashPattern *copy() = 0;

  virtual ~SplashPattern();

  // Return the color value for a specific pixel.
  virtual void getColor(int x, int y, SplashColorPtr c) = 0;

  // Returns true if this pattern object will return the same color
  // value for all pixels.
  virtual GBool isStatic() = 0;

private:
};

//------------------------------------------------------------------------
// SplashSolidColor
//------------------------------------------------------------------------

class SplashSolidColor: public SplashPattern {
public:

  SplashSolidColor(SplashColorPtr colorA);

  virtual SplashPattern *copy() { return new SplashSolidColor(color); }

  virtual ~SplashSolidColor();

  virtual void getColor(int x, int y, SplashColorPtr c);

  virtual GBool isStatic() { return gTrue; }

private:

  SplashColor color;
};

#endif
//========================================================================
//
// SplashScreen.cc
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stdlib.h>
#include <string.h>
#include "gmem.h"
#include "SplashMath.h"
#include "SplashScreen.h"

//------------------------------------------------------------------------

static SplashScreenParams defaultParams = {
  splashScreenDispersed,	// type
  2,				// size
  2,				// dotRadius
  1.0,				// gamma
  0.0,				// blackThreshold
  1.0				// whiteThreshold
};

//------------------------------------------------------------------------

struct SplashScreenPoint {
  int x, y;
  int dist;
};

static int cmpDistances(const void *p0, const void *p1) {
  return ((SplashScreenPoint *)p0)->dist - ((SplashScreenPoint *)p1)->dist;
}

//------------------------------------------------------------------------
// SplashScreen
//------------------------------------------------------------------------

// If <clustered> is true, this generates a 45 degree screen using a
// circular dot spot function.  DPI = resolution / ((size / 2) *
// sqrt(2)).  If <clustered> is false, this generates an optimal
// threshold matrix using recursive tesselation.  Gamma correction
// (gamma = 1 / 1.33) is also computed here.
SplashScreen::SplashScreen(SplashScreenParams *params) {
  Guchar u, black, white;
  int i;

  if (!params) {
    params = &defaultParams;
  }

  switch (params->type) {

  case splashScreenDispersed:
    // size must be a power of 2
    for (size = 1; size < params->size; size <<= 1) ;
    mat = (Guchar *)gmallocn(size * size, sizeof(Guchar));
    buildDispersedMatrix(size/2, size/2, 1, size/2, 1);
    break;

  case splashScreenClustered:
    // size must be even
    size = (params->size >> 1) << 1;
    if (size < 2) {
      size = 2;
    }
    mat = (Guchar *)gmallocn(size * size, sizeof(Guchar));
    buildClusteredMatrix();
    break;

  case splashScreenStochasticClustered:
    // size must be at least 2*r
    if (params->size < 2 * params->dotRadius) {
      size = 2 * params->dotRadius;
    } else {
      size = params->size;
    }
    mat = (Guchar *)gmallocn(size * size, sizeof(Guchar));
    buildSCDMatrix(params->dotRadius);
    break;
  }

  // do gamma correction and compute minVal/maxVal
  minVal = 255;
  maxVal = 0;
  black = splashRound((SplashCoord)255.0 * params->blackThreshold);
  if (black < 1) {
    black = 1;
  }
  white = splashRound((SplashCoord)255.0 * params->whiteThreshold);
  if (white > 255) {
    white = 255;
  }
  for (i = 0; i < size * size; ++i) {
    u = splashRound((SplashCoord)255.0 *
		    splashPow((SplashCoord)mat[i] / 255.0, params->gamma));
    if (u < black) {
      u = black;
    } else if (u >= white) {
      u = white;
    }
    mat[i] = u;
    if (u < minVal) {
      minVal = u;
    } else if (u > maxVal) {
      maxVal = u;
    }
  }
}

void SplashScreen::buildDispersedMatrix(int i, int j, int val,
					int delta, int offset) {
  if (delta == 0) {
    // map values in [1, size^2] --> [1, 255]
    mat[i * size + j] = 1 + (254 * (val - 1)) / (size * size - 1);
  } else {
    buildDispersedMatrix(i, j,
			 val, delta / 2, 4*offset);
    buildDispersedMatrix((i + delta) % size, (j + delta) % size,
			 val + offset, delta / 2, 4*offset);
    buildDispersedMatrix((i + delta) % size, j,
			 val + 2*offset, delta / 2, 4*offset);
    buildDispersedMatrix((i + 2*delta) % size, (j + delta) % size,
			 val + 3*offset, delta / 2, 4*offset);
  }
}

void SplashScreen::buildClusteredMatrix() {
  SplashCoord *dist;
  SplashCoord u, v, d;
  Guchar val;
  int size2, x, y, x1, y1, i;

  size2 = size >> 1;

  // initialize the threshold matrix
  for (y = 0; y < size; ++y) {
    for (x = 0; x < size; ++x) {
      mat[y * size + x] = 0;
    }
  }

  // build the distance matrix
  dist = (SplashCoord *)gmallocn(size * size2, sizeof(SplashCoord));
  for (y = 0; y < size2; ++y) {
    for (x = 0; x < size2; ++x) {
      if (x + y < size2 - 1) {
	u = (SplashCoord)x + 0.5 - 0;
	v = (SplashCoord)y + 0.5 - 0;
      } else {
	u = (SplashCoord)x + 0.5 - (SplashCoord)size2;
	v = (SplashCoord)y + 0.5 - (SplashCoord)size2;
      }
      dist[y * size2 + x] = u*u + v*v;
    }
  }
  for (y = 0; y < size2; ++y) {
    for (x = 0; x < size2; ++x) {
      if (x < y) {
	u = (SplashCoord)x + 0.5 - 0;
	v = (SplashCoord)y + 0.5 - (SplashCoord)size2;
      } else {
	u = (SplashCoord)x + 0.5 - (SplashCoord)size2;
	v = (SplashCoord)y + 0.5 - 0;
      }
      dist[(size2 + y) * size2 + x] = u*u + v*v;
    }
  }

  // build the threshold matrix
  minVal = 1;
  maxVal = 0;
  x1 = y1 = 0; // make gcc happy
  for (i = 0; i < size * size2; ++i) {
    d = -1;
    for (y = 0; y < size; ++y) {
      for (x = 0; x < size2; ++x) {
	if (mat[y * size + x] == 0 &&
	    dist[y * size2 + x] > d) {
	  x1 = x;
	  y1 = y;
	  d = dist[y1 * size2 + x1];
	}
      }
    }
    // map values in [0, 2*size*size2-1] --> [1, 255]
    val = 1 + (254 * (2*i)) / (2*size*size2 - 1);
    mat[y1 * size + x1] = val;
    val = 1 + (254 * (2*i+1)) / (2*size*size2 - 1);
    if (y1 < size2) {
      mat[(y1 + size2) * size + x1 + size2] = val;
    } else {
      mat[(y1 - size2) * size + x1 + size2] = val;
    }
  }

  gfree(dist);
}

// Compute the distance between two points on a toroid.
int SplashScreen::distance(int x0, int y0, int x1, int y1) {
  int dx0, dx1, dx, dy0, dy1, dy;

  dx0 = abs(x0 - x1);
  dx1 = size - dx0;
  dx = dx0 < dx1 ? dx0 : dx1;
  dy0 = abs(y0 - y1);
  dy1 = size - dy0;
  dy = dy0 < dy1 ? dy0 : dy1;
  return dx * dx + dy * dy;
}

// Algorithm taken from:
// Victor Ostromoukhov and Roger D. Hersch, "Stochastic Clustered-Dot
// Dithering" in Color Imaging: Device-Independent Color, Color
// Hardcopy, and Graphic Arts IV, SPIE Vol. 3648, pp. 496-505, 1999.
void SplashScreen::buildSCDMatrix(int r) {
  SplashScreenPoint *dots, *pts;
  int dotsLen, dotsSize;
  char *tmpl;
  char *grid;
  int *region, *dist;
  int x, y, xx, yy, x0, x1, y0, y1, i, j, d, iMin, dMin, n;

  //~ this should probably happen somewhere else
  srand(123);

  // generate the random space-filling curve
  pts = (SplashScreenPoint *)gmallocn(size * size, sizeof(SplashScreenPoint));
  i = 0;
  for (y = 0; y < size; ++y) {
    for (x = 0; x < size; ++x) {
      pts[i].x = x;
      pts[i].y = y;
      ++i;
    }
  }
  for (i = 0; i < size * size; ++i) {
    j = i + (int)((double)(size * size - i) *
		  (double)rand() / ((double)RAND_MAX + 1.0));
    x = pts[i].x;
    y = pts[i].y;
    pts[i].x = pts[j].x;
    pts[i].y = pts[j].y;
    pts[j].x = x;
    pts[j].y = y;
  }

  // construct the circle template
  tmpl = (char *)gmallocn((r+1)*(r+1), sizeof(char));
  for (y = 0; y <= r; ++y) {
    for (x = 0; x <= r; ++x) {
      tmpl[y*(r+1) + x] = (x * y <= r * r) ? 1 : 0;
    }
  }

  // mark all grid cells as free
  grid = (char *)gmallocn(size * size, sizeof(char));
  for (y = 0; y < size; ++y) {
    for (x = 0; x < size; ++x) {
      grid[y*size + x] = 0;
    }
  }

  // walk the space-filling curve, adding dots
  dotsLen = 0;
  dotsSize = 32;
  dots = (SplashScreenPoint *)gmallocn(dotsSize, sizeof(SplashScreenPoint));
  for (i = 0; i < size * size; ++i) {
    x = pts[i].x;
    y = pts[i].y;
    if (!grid[y*size + x]) {
      if (dotsLen == dotsSize) {
	dotsSize *= 2;
	dots = (SplashScreenPoint *)greallocn(dots, dotsSize,
					      sizeof(SplashScreenPoint));
      }
      dots[dotsLen++] = pts[i];
      for (yy = 0; yy <= r; ++yy) {
	y0 = (y + yy) % size;
	y1 = (y - yy + size) % size;
	for (xx = 0; xx <= r; ++xx) {
	  if (tmpl[yy*(r+1) + xx]) {
	    x0 = (x + xx) % size;
	    x1 = (x - xx + size) % size;
	    grid[y0*size + x0] = 1;
	    grid[y0*size + x1] = 1;
	    grid[y1*size + x0] = 1;
	    grid[y1*size + x1] = 1;
	  }
	}
      }
    }
  }

  gfree(tmpl);
  gfree(grid);

  // assign each cell to a dot, compute distance to center of dot
  region = (int *)gmallocn(size * size, sizeof(int));
  dist = (int *)gmallocn(size * size, sizeof(int));
  for (y = 0; y < size; ++y) {
    for (x = 0; x < size; ++x) {
      iMin = 0;
      dMin = distance(dots[0].x, dots[0].y, x, y);
      for (i = 1; i < dotsLen; ++i) {
	d = distance(dots[i].x, dots[i].y, x, y);
	if (d < dMin) {
	  iMin = i;
	  dMin = d;
	}
      }
      region[y*size + x] = iMin;
      dist[y*size + x] = dMin;
    }
  }

  // compute threshold values
  for (i = 0; i < dotsLen; ++i) {
    n = 0;
    for (y = 0; y < size; ++y) {
      for (x = 0; x < size; ++x) {
	if (region[y*size + x] == i) {
	  pts[n].x = x;
	  pts[n].y = y;
	  pts[n].dist = distance(dots[i].x, dots[i].y, x, y);
	  ++n;
	}
      }
    }
    qsort(pts, n, sizeof(SplashScreenPoint), &cmpDistances);
    for (j = 0; j < n; ++j) {
      // map values in [0 .. n-1] --> [255 .. 1]
      mat[pts[j].y * size + pts[j].x] = 255 - (254 * j) / (n - 1);
    }
  }

  gfree(pts);
  gfree(region);
  gfree(dist);

  gfree(dots);
}

SplashScreen::SplashScreen(SplashScreen *screen) {
  size = screen->size;
  mat = (Guchar *)gmallocn(size * size, sizeof(Guchar));
  memcpy(mat, screen->mat, size * size * sizeof(Guchar));
  minVal = screen->minVal;
  maxVal = screen->maxVal;
}

SplashScreen::~SplashScreen() {
  gfree(mat);
}

int SplashScreen::test(int x, int y, Guchar value) {
  int xx, yy;

  if (value < minVal) {
    return 0;
  }
  if (value >= maxVal) {
    return 1;
  }
  if ((xx = x % size) < 0) {
    xx = -xx;
  }
  if ((yy = y % size) < 0) {
    yy = -yy;
  }
  return value < mat[yy * size + xx] ? 0 : 1;
}

GBool SplashScreen::isStatic(Guchar value) {
  return value < minVal || value >= maxVal;
}
//========================================================================
//
// SplashScreen.h
//
//========================================================================

#ifndef SPLASHSCREEN_H
#define SPLASHSCREEN_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "SplashTypes.h"

//------------------------------------------------------------------------
// SplashScreen
//------------------------------------------------------------------------

class SplashScreen {
public:

  SplashScreen(SplashScreenParams *params);
  SplashScreen(SplashScreen *screen);
  ~SplashScreen();

  SplashScreen *copy() { return new SplashScreen(this); }

  // Return the computed pixel value (0=black, 1=white) for the gray
  // level <value> at (<x>, <y>).
  int test(int x, int y, Guchar value);

  // Returns true if value is above the white threshold or below the
  // black threshold, i.e., if the corresponding halftone will be
  // solid white or black.
  GBool isStatic(Guchar value);

private:

  void buildDispersedMatrix(int i, int j, int val,
			    int delta, int offset);
  void buildClusteredMatrix();
  int distance(int x0, int y0, int x1, int y1);
  void buildSCDMatrix(int r);

  Guchar *mat;			// threshold matrix
  int size;			// size of the threshold matrix
  Guchar minVal;		// any pixel value below minVal generates
				//   solid black
  Guchar maxVal;		// any pixel value above maxVal generates
				//   solid white
};

#endif
//========================================================================
//
// SplashState.cc
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <string.h>
#include "gmem.h"
#include "SplashPattern.h"
#include "SplashScreen.h"
#include "SplashClip.h"
#include "SplashBitmap.h"
#include "SplashState.h"

//------------------------------------------------------------------------
// SplashState
//------------------------------------------------------------------------

// number of components in each color mode
int splashColorModeNComps[] = {
  1, 1, 3, 3, 4
};

SplashState::SplashState(int width, int height, GBool vectorAntialias,
			 SplashScreenParams *screenParams) {
  SplashColor color;

  matrix[0] = 1;  matrix[1] = 0;
  matrix[2] = 0;  matrix[3] = 1;
  matrix[4] = 0;  matrix[5] = 0;
  memset(&color, 0, sizeof(SplashColor));
  strokePattern = new SplashSolidColor(color);
  fillPattern = new SplashSolidColor(color);
  screen = new SplashScreen(screenParams);
  blendFunc = NULL;
  strokeAlpha = 1;
  fillAlpha = 1;
  lineWidth = 0;
  lineCap = splashLineCapButt;
  lineJoin = splashLineJoinMiter;
  miterLimit = 10;
  flatness = 1;
  lineDash = NULL;
  lineDashLength = 0;
  lineDashPhase = 0;
  strokeAdjust = gFalse;
  clip = new SplashClip(0, 0, width - 0.001, height - 0.001, vectorAntialias);
  softMask = NULL;
  deleteSoftMask = gFalse;
  inNonIsolatedGroup = gFalse;
  next = NULL;
}

SplashState::SplashState(int width, int height, GBool vectorAntialias,
			 SplashScreen *screenA) {
  SplashColor color;

  matrix[0] = 1;  matrix[1] = 0;
  matrix[2] = 0;  matrix[3] = 1;
  matrix[4] = 0;  matrix[5] = 0;
  memset(&color, 0, sizeof(SplashColor));
  strokePattern = new SplashSolidColor(color);
  fillPattern = new SplashSolidColor(color);
  screen = screenA->copy();
  blendFunc = NULL;
  strokeAlpha = 1;
  fillAlpha = 1;
  lineWidth = 0;
  lineCap = splashLineCapButt;
  lineJoin = splashLineJoinMiter;
  miterLimit = 10;
  flatness = 1;
  lineDash = NULL;
  lineDashLength = 0;
  lineDashPhase = 0;
  strokeAdjust = gFalse;
  clip = new SplashClip(0, 0, width - 0.001, height - 0.001, vectorAntialias);
  softMask = NULL;
  deleteSoftMask = gFalse;
  inNonIsolatedGroup = gFalse;
  next = NULL;
}

SplashState::SplashState(SplashState *state) {
  memcpy(matrix, state->matrix, 6 * sizeof(SplashCoord));
  strokePattern = state->strokePattern->copy();
  fillPattern = state->fillPattern->copy();
  screen = state->screen->copy();
  blendFunc = state->blendFunc;
  strokeAlpha = state->strokeAlpha;
  fillAlpha = state->fillAlpha;
  lineWidth = state->lineWidth;
  lineCap = state->lineCap;
  lineJoin = state->lineJoin;
  miterLimit = state->miterLimit;
  flatness = state->flatness;
  if (state->lineDash) {
    lineDashLength = state->lineDashLength;
    lineDash = (SplashCoord *)gmallocn(lineDashLength, sizeof(SplashCoord));
    memcpy(lineDash, state->lineDash, lineDashLength * sizeof(SplashCoord));
  } else {
    lineDash = NULL;
    lineDashLength = 0;
  }
  lineDashPhase = state->lineDashPhase;
  strokeAdjust = state->strokeAdjust;
  clip = state->clip->copy();
  softMask = state->softMask;
  deleteSoftMask = gFalse;
  inNonIsolatedGroup = state->inNonIsolatedGroup;
  next = NULL;
}

SplashState::~SplashState() {
  delete strokePattern;
  delete fillPattern;
  delete screen;
  gfree(lineDash);
  delete clip;
  if (deleteSoftMask && softMask) {
    delete softMask;
  }
}

void SplashState::setStrokePattern(SplashPattern *strokePatternA) {
  delete strokePattern;
  strokePattern = strokePatternA;
}

void SplashState::setFillPattern(SplashPattern *fillPatternA) {
  delete fillPattern;
  fillPattern = fillPatternA;
}

void SplashState::setScreen(SplashScreen *screenA) {
  delete screen;
  screen = screenA;
}

void SplashState::setLineDash(SplashCoord *lineDashA, int lineDashLengthA,
			      SplashCoord lineDashPhaseA) {
  gfree(lineDash);
  lineDashLength = lineDashLengthA;
  if (lineDashLength > 0) {
    lineDash = (SplashCoord *)gmallocn(lineDashLength, sizeof(SplashCoord));
    memcpy(lineDash, lineDashA, lineDashLength * sizeof(SplashCoord));
  } else {
    lineDash = NULL;
  }
  lineDashPhase = lineDashPhaseA;
}

void SplashState::setSoftMask(SplashBitmap *softMaskA) {
  if (deleteSoftMask) {
    delete softMask;
  }
  softMask = softMaskA;
  deleteSoftMask = gTrue;
}
//========================================================================
//
// SplashState.h
//
//========================================================================

#ifndef SPLASHSTATE_H
#define SPLASHSTATE_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "SplashTypes.h"

class SplashPattern;
class SplashScreen;
class SplashClip;
class SplashBitmap;

//------------------------------------------------------------------------
// line cap values
//------------------------------------------------------------------------

#define splashLineCapButt       0
#define splashLineCapRound      1
#define splashLineCapProjecting 2

//------------------------------------------------------------------------
// line join values
//------------------------------------------------------------------------

#define splashLineJoinMiter     0
#define splashLineJoinRound     1
#define splashLineJoinBevel     2

//------------------------------------------------------------------------
// SplashState
//------------------------------------------------------------------------

class SplashState {
public:

  // Create a new state object, initialized with default settings.
  SplashState(int width, int height, GBool vectorAntialias,
	      SplashScreenParams *screenParams);
  SplashState(int width, int height, GBool vectorAntialias,
	      SplashScreen *screenA);

  // Copy a state object.
  SplashState *copy() { return new SplashState(this); }

  ~SplashState();

  // Set the stroke pattern.  This does not copy <strokePatternA>.
  void setStrokePattern(SplashPattern *strokePatternA);

  // Set the fill pattern.  This does not copy <fillPatternA>.
  void setFillPattern(SplashPattern *fillPatternA);

  // Set the screen.  This does not copy <screenA>.
  void setScreen(SplashScreen *screenA);

  // Set the line dash pattern.  This copies the <lineDashA> array.
  void setLineDash(SplashCoord *lineDashA, int lineDashLengthA,
		   SplashCoord lineDashPhaseA);

  // Set the soft mask bitmap.
  void setSoftMask(SplashBitmap *softMaskA);

private:

  SplashState(SplashState *state);

  SplashCoord matrix[6];
  SplashPattern *strokePattern;
  SplashPattern *fillPattern;
  SplashScreen *screen;
  SplashBlendFunc blendFunc;
  SplashCoord strokeAlpha;
  SplashCoord fillAlpha;
  SplashCoord lineWidth;
  int lineCap;
  int lineJoin;
  SplashCoord miterLimit;
  SplashCoord flatness;
  SplashCoord *lineDash;
  int lineDashLength;
  SplashCoord lineDashPhase;
  GBool strokeAdjust;
  SplashClip *clip;
  SplashBitmap *softMask;
  GBool deleteSoftMask;
  GBool inNonIsolatedGroup;

  SplashState *next;		// used by Splash class

  friend class Splash;
};

#endif
//========================================================================
//
// SplashT1Font.cc
//
//========================================================================

#include <aconf.h>

#if HAVE_T1LIB_H

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stdlib.h>
#include <t1lib.h>
#include "gmem.h"
#include "SplashMath.h"
#include "SplashGlyphBitmap.h"
#include "SplashPath.h"
#include "SplashT1FontEngine.h"
#include "SplashT1FontFile.h"
#include "SplashT1Font.h"

//------------------------------------------------------------------------

static Guchar bitReverse[256] = {
  0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
  0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
  0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
  0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
  0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
  0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
  0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
  0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
  0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
  0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
  0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
  0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
  0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
  0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
  0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
  0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
  0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
  0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
  0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
  0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
  0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
  0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
  0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
  0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
  0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
  0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
  0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
  0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
  0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
  0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
  0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
  0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
};

//------------------------------------------------------------------------
// SplashT1Font
//------------------------------------------------------------------------

SplashT1Font::SplashT1Font(SplashT1FontFile *fontFileA, SplashCoord *matA,
			   SplashCoord *textMatA):
  SplashFont(fontFileA, matA, textMatA, fontFileA->engine->aa)
{
  T1_TMATRIX matrix;
  BBox bbox;
  SplashCoord bbx0, bby0, bbx1, bby1;
  int x, y;

  t1libID = T1_CopyFont(fontFileA->t1libID);
  outlineID = -1;

  // compute font size
  size = (float)splashSqrt(mat[2]*mat[2] + mat[3]*mat[3]);

  // transform the four corners of the font bounding box -- the min
  // and max values form the bounding box of the transformed font
  bbox = T1_GetFontBBox(t1libID);
  bbx0 = 0.001 * bbox.llx;
  bby0 = 0.001 * bbox.lly;
  bbx1 = 0.001 * bbox.urx;
  bby1 = 0.001 * bbox.ury;
  // some fonts are completely broken, so we fake it (with values
  // large enough that most glyphs should fit)
  if (bbx0 == 0 && bby0 == 0 && bbx1 == 0 && bby1 == 0) {
    bbx0 = bby0 = -0.5;
    bbx1 = bby1 = 1.5;
  }
  x = (int)(mat[0] * bbx0 + mat[2] * bby0);
  xMin = xMax = x;
  y = (int)(mat[1] * bbx0 + mat[3] * bby0);
  yMin = yMax = y;
  x = (int)(mat[0] * bbx0 + mat[2] * bby1);
  if (x < xMin) {
    xMin = x;
  } else if (x > xMax) {
    xMax = x;
  }
  y = (int)(mat[1] * bbx0 + mat[3] * bby1);
  if (y < yMin) {
    yMin = y;
  } else if (y > yMax) {
    yMax = y;
  }
  x = (int)(mat[0] * bbx1 + mat[2] * bby0);
  if (x < xMin) {
    xMin = x;
  } else if (x > xMax) {
    xMax = x;
  }
  y = (int)(mat[1] * bbx1 + mat[3] * bby0);
  if (y < yMin) {
    yMin = y;
  } else if (y > yMax) {
    yMax = y;
  }
  x = (int)(mat[0] * bbx1 + mat[2] * bby1);
  if (x < xMin) {
    xMin = x;
  } else if (x > xMax) {
    xMax = x;
  }
  y = (int)(mat[1] * bbx1 + mat[3] * bby1);
  if (y < yMin) {
    yMin = y;
  } else if (y > yMax) {
    yMax = y;
  }
  // This is a kludge: some buggy PDF generators embed fonts with
  // zero bounding boxes.
  if (xMax == xMin) {
    xMin = 0;
    xMax = (int)size;
  }
  if (yMax == yMin) {
    yMin = 0;
    yMax = (int)(1.2 * size);
  }
  // Another kludge: an unusually large xMin or yMin coordinate is
  // probably wrong.
  if (xMin > 0) {
    xMin = 0;
  }
  if (yMin > 0) {
    yMin = 0;
  }
  // Another kludge: t1lib doesn't correctly handle fonts with
  // real (non-integer) bounding box coordinates.
  if (xMax - xMin > 5000) {
    xMin = 0;
    xMax = (int)size;
  }
  if (yMax - yMin > 5000) {
    yMin = 0;
    yMax = (int)(1.2 * size);
  }

  // transform the font
  matrix.cxx = (double)mat[0] / size;
  matrix.cxy = (double)mat[1] / size;
  matrix.cyx = (double)mat[2] / size;
  matrix.cyy = (double)mat[3] / size;
  T1_TransformFont(t1libID, &matrix);
}

SplashT1Font::~SplashT1Font() {
  T1_DeleteFont(t1libID);
  if (outlineID >= 0) {
    T1_DeleteFont(outlineID);
  }
}

GBool SplashT1Font::getGlyph(int c, int xFrac, int yFrac,
			     SplashGlyphBitmap *bitmap) {
  return SplashFont::getGlyph(c, 0, 0, bitmap);
}

GBool SplashT1Font::makeGlyph(int c, int xFrac, int yFrac,
			      SplashGlyphBitmap *bitmap) {
  GLYPH *glyph;
  int n, i;

  if (aa) {
    glyph = T1_AASetChar(t1libID, c, size, NULL);
  } else {
    glyph = T1_SetChar(t1libID, c, size, NULL);
  }
  if (!glyph) {
    return gFalse;
  }

  bitmap->x = -glyph->metrics.leftSideBearing;
  bitmap->y = glyph->metrics.ascent;
  bitmap->w = glyph->metrics.rightSideBearing - glyph->metrics.leftSideBearing;
  bitmap->h = glyph->metrics.ascent - glyph->metrics.descent;
  bitmap->aa = aa;
  if (aa) {
    bitmap->data = (Guchar *)glyph->bits;
    bitmap->freeData = gFalse;
  } else {
    n = bitmap->h * ((bitmap->w + 7) >> 3);
    bitmap->data = (Guchar *)gmalloc(n);
    for (i = 0; i < n; ++i) {
      bitmap->data[i] = bitReverse[glyph->bits[i] & 0xff];
    }
    bitmap->freeData = gTrue;
  }

  return gTrue;
}

SplashPath *SplashT1Font::getGlyphPath(int c) {
  T1_TMATRIX matrix;
  SplashPath *path;
  T1_OUTLINE *outline;
  T1_PATHSEGMENT *seg;
  T1_BEZIERSEGMENT *bez;
  SplashCoord x, y, x1, y1;
  GBool needClose;

  if (outlineID < 0) {
    outlineID = T1_CopyFont(((SplashT1FontFile *)fontFile)->t1libID);
    outlineSize = (float)splashSqrt(textMat[2]*textMat[2] +
				    textMat[3]*textMat[3]);
    matrix.cxx = (double)textMat[0] / outlineSize;
    matrix.cxy = (double)textMat[1] / outlineSize;
    matrix.cyx = (double)textMat[2] / outlineSize;
    matrix.cyy = (double)textMat[3] / outlineSize;
    // t1lib doesn't seem to handle small sizes correctly here, so set
    // the size to 1000, and scale the resulting coordinates later
    outlineMul = (float)(outlineSize / 65536000.0);
    outlineSize = 1000;
    T1_TransformFont(outlineID, &matrix);
  }

  path = new SplashPath();
  if ((outline = T1_GetCharOutline(outlineID, c, outlineSize, NULL))) {
    x = 0;
    y = 0;
    needClose = gFalse;
    for (seg = outline; seg; seg = seg->link) {
      switch (seg->type) {
      case T1_PATHTYPE_MOVE:
	if (needClose) {
	  path->close();
	  needClose = gFalse;
	}
	x += seg->dest.x * outlineMul;
	y += seg->dest.y * outlineMul;
	path->moveTo(x, -y);
	break;
      case T1_PATHTYPE_LINE:
	x += seg->dest.x * outlineMul;
	y += seg->dest.y * outlineMul;
	path->lineTo(x, -y);
	needClose = gTrue;
	break;
      case T1_PATHTYPE_BEZIER:
	bez = (T1_BEZIERSEGMENT *)seg;
	x1 = x + (SplashCoord)(bez->dest.x * outlineMul);
	y1 = y + (SplashCoord)(bez->dest.y * outlineMul);
	path->curveTo(x + (SplashCoord)(bez->B.x * outlineMul),
		      -(y + (SplashCoord)(bez->B.y * outlineMul)),
		      x + (SplashCoord)(bez->C.x * outlineMul),
		      -(y + (SplashCoord)(bez->C.y * outlineMul)),
		      x1, -y1);
	x = x1;
	y = y1;
	needClose = gTrue;
	break;
      }
    }
    if (needClose) {
      path->close();
    }
    T1_FreeOutline(outline);
  }

  return path;
}

#endif // HAVE_T1LIB_H
//========================================================================
//
// SplashT1Font.h
//
//========================================================================

#ifndef SPLASHT1FONT_H
#define SPLASHT1FONT_H

#include <aconf.h>

#if HAVE_T1LIB_H

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "SplashFont.h"

class SplashT1FontFile;

//------------------------------------------------------------------------
// SplashT1Font
//------------------------------------------------------------------------

class SplashT1Font: public SplashFont {
public:

  SplashT1Font(SplashT1FontFile *fontFileA, SplashCoord *matA,
	       SplashCoord *textMatA);

  virtual ~SplashT1Font();

  // Munge xFrac and yFrac before calling SplashFont::getGlyph.
  virtual GBool getGlyph(int c, int xFrac, int yFrac,
			 SplashGlyphBitmap *bitmap);

  // Rasterize a glyph.  The <xFrac> and <yFrac> values are the same
  // as described for getGlyph.
  virtual GBool makeGlyph(int c, int xFrac, int yFrac,
			  SplashGlyphBitmap *bitmap);

  // Return the path for a glyph.
  virtual SplashPath *getGlyphPath(int c);

private:

  int t1libID;			// t1lib font ID
  int outlineID;		// t1lib font ID for glyph outlines
  float size;
  float outlineSize;		// size for glyph outlines
  float outlineMul;
};

#endif // HAVE_T1LIB_H

#endif
//========================================================================
//
// SplashT1FontEngine.cc
//
//========================================================================

#include <aconf.h>

#if HAVE_T1LIB_H

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stdlib.h>
#include <stdio.h>
#ifndef WIN32
#  include <unistd.h>
#endif
#include <t1lib.h>
#include "GString.h"
#include "gfile.h"
#include "FoFiType1C.h"
#include "SplashT1FontFile.h"
#include "SplashT1FontEngine.h"

#ifdef VMS
#if (__VMS_VER < 70000000)
extern "C" int unlink(char *filename);
#endif
#endif

//------------------------------------------------------------------------

int SplashT1FontEngine::t1libInitCount = 0;

//------------------------------------------------------------------------

static void fileWrite(void *stream, char *data, int len) {
  fwrite(data, 1, len, (FILE *)stream);
}

//------------------------------------------------------------------------
// SplashT1FontEngine
//------------------------------------------------------------------------

SplashT1FontEngine::SplashT1FontEngine(GBool aaA) {
  aa = aaA;
}

SplashT1FontEngine *SplashT1FontEngine::init(GBool aaA) {
  // grayVals[i] = round(i * 255 / 16)
  static unsigned long grayVals[17] = {
    0, 16, 32, 48, 64, 80, 96, 112, 128, 143, 159, 175, 191, 207, 223, 239, 255
  };

  //~ for multithreading: need a mutex here
  if (t1libInitCount == 0) {
    T1_SetBitmapPad(8);
    if (!T1_InitLib(NO_LOGFILE | IGNORE_CONFIGFILE | IGNORE_FONTDATABASE |
		    T1_NO_AFM)) {
      return NULL;
    }
    if (aaA) {
      T1_AASetBitsPerPixel(8);
      T1_AASetLevel(T1_AA_HIGH);
      T1_AAHSetGrayValues(grayVals);
    } else {
      T1_AANSetGrayValues(0, 1);
    }
  }
  ++t1libInitCount;

  return new SplashT1FontEngine(aaA);
}

SplashT1FontEngine::~SplashT1FontEngine() {
  //~ for multithreading: need a mutex here
  if (--t1libInitCount == 0) {
    T1_CloseLib();
  }
}

SplashFontFile *SplashT1FontEngine::loadType1Font(SplashFontFileID *idA,
						  char *fileName,
						  GBool deleteFile,
						  char **enc) {
  return SplashT1FontFile::loadType1Font(this, idA, fileName, deleteFile, enc);
}

SplashFontFile *SplashT1FontEngine::loadType1CFont(SplashFontFileID *idA,
						   char *fileName,
						   GBool deleteFile,
						   char **enc) {
  FoFiType1C *ff;
  GString *tmpFileName;
  FILE *tmpFile;
  SplashFontFile *ret;

  if (!(ff = FoFiType1C::load(fileName))) {
    return NULL;
  }
  tmpFileName = NULL;
  if (!openTempFile(&tmpFileName, &tmpFile, "wb", NULL)) {
    delete ff;
    return NULL;
  }
  ff->convertToType1(NULL, NULL, gTrue, &fileWrite, tmpFile);
  delete ff;
  fclose(tmpFile);
  ret = SplashT1FontFile::loadType1Font(this, idA, tmpFileName->getCString(),
					gTrue, enc);
  if (ret) {
    if (deleteFile) {
      unlink(fileName);
    }
  } else {
    unlink(tmpFileName->getCString());
  }
  delete tmpFileName;
  return ret;
}

#endif // HAVE_T1LIB_H
//========================================================================
//
// SplashT1FontEngine.h
//
//========================================================================

#ifndef SPLASHT1FONTENGINE_H
#define SPLASHT1FONTENGINE_H

#include <aconf.h>

#if HAVE_T1LIB_H

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "gtypes.h"

class SplashFontFile;
class SplashFontFileID;

//------------------------------------------------------------------------
// SplashT1FontEngine
//------------------------------------------------------------------------

class SplashT1FontEngine {
public:

  static SplashT1FontEngine *init(GBool aaA);

  ~SplashT1FontEngine();

  // Load fonts.
  SplashFontFile *loadType1Font(SplashFontFileID *idA, char *fileName,
				GBool deleteFile, char **enc);
  SplashFontFile *loadType1CFont(SplashFontFileID *idA, char *fileName,
				 GBool deleteFile, char **enc);

private:

  SplashT1FontEngine(GBool aaA);

  static int t1libInitCount;
  GBool aa;

  friend class SplashT1FontFile;
  friend class SplashT1Font;
};

#endif // HAVE_T1LIB_H

#endif
//========================================================================
//
// SplashT1FontFile.cc
//
//========================================================================

#include <aconf.h>

#if HAVE_T1LIB_H

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <string.h>
#include <t1lib.h>
#include "gmem.h"
#include "SplashT1FontEngine.h"
#include "SplashT1Font.h"
#include "SplashT1FontFile.h"

//------------------------------------------------------------------------
// SplashT1FontFile
//------------------------------------------------------------------------

SplashFontFile *SplashT1FontFile::loadType1Font(SplashT1FontEngine *engineA,
						SplashFontFileID *idA,
						char *fileNameA,
						GBool deleteFileA,
						char **encA) {
  int t1libIDA;
  char **encTmp;
  char *encStrTmp;
  int encStrSize;
  char *encPtr;
  int i;

  // load the font file
  if ((t1libIDA = T1_AddFont(fileNameA)) < 0) {
    return NULL;
  }
  T1_LoadFont(t1libIDA);

  // reencode it
  encStrSize = 0;
  for (i = 0; i < 256; ++i) {
    if (encA[i]) {
      encStrSize += strlen(encA[i]) + 1;
    }
  }
  encTmp = (char **)gmallocn(257, sizeof(char *));
  encStrTmp = (char *)gmallocn(encStrSize, sizeof(char));
  encPtr = encStrTmp;
  for (i = 0; i < 256; ++i) {
    if (encA[i]) {
      strcpy(encPtr, encA[i]);
      encTmp[i] = encPtr;
      encPtr += strlen(encPtr) + 1;
    } else {
      encTmp[i] = ".notdef";
    }
  }
  encTmp[256] = "custom";
  T1_ReencodeFont(t1libIDA, encTmp);

  return new SplashT1FontFile(engineA, idA, fileNameA, deleteFileA,
			      t1libIDA, encTmp, encStrTmp);
}

SplashT1FontFile::SplashT1FontFile(SplashT1FontEngine *engineA,
				   SplashFontFileID *idA,
				   char *fileNameA, GBool deleteFileA,
				   int t1libIDA, char **encA, char *encStrA):
  SplashFontFile(idA, fileNameA, deleteFileA)
{
  engine = engineA;
  t1libID = t1libIDA;
  enc = encA;
  encStr = encStrA;
}

SplashT1FontFile::~SplashT1FontFile() {
  gfree(encStr);
  gfree(enc);
  T1_DeleteFont(t1libID);
}

SplashFont *SplashT1FontFile::makeFont(SplashCoord *mat,
				       SplashCoord *textMat) {
  SplashFont *font;

  font = new SplashT1Font(this, mat, textMat);
  font->initCache();
  return font;
}

#endif // HAVE_T1LIB_H
//========================================================================
//
// SplashT1FontFile.h
//
//========================================================================

#ifndef SPLASHT1FONTFILE_H
#define SPLASHT1FONTFILE_H

#include <aconf.h>

#if HAVE_T1LIB_H

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "SplashFontFile.h"

class SplashT1FontEngine;

//------------------------------------------------------------------------
// SplashT1FontFile
//------------------------------------------------------------------------

class SplashT1FontFile: public SplashFontFile {
public:

  static SplashFontFile *loadType1Font(SplashT1FontEngine *engineA,
				       SplashFontFileID *idA,
				       char *fileNameA, GBool deleteFileA,
				       char **encA);

  virtual ~SplashT1FontFile();

  // Create a new SplashT1Font, i.e., a scaled instance of this font
  // file.
  virtual SplashFont *makeFont(SplashCoord *mat,
			       SplashCoord *textMat);

private:

  SplashT1FontFile(SplashT1FontEngine *engineA,
		   SplashFontFileID *idA,
		   char *fileNameA, GBool deleteFileA,
		   int t1libIDA, char **encA, char *encStrA);

  SplashT1FontEngine *engine;
  int t1libID;			// t1lib font ID
  char **enc;
  char *encStr;

  friend class SplashT1Font;
};

#endif // HAVE_T1LIB_H

#endif
//========================================================================
//
// SplashTypes.h
//
//========================================================================

#ifndef SPLASHTYPES_H
#define SPLASHTYPES_H

#include <aconf.h>
#include "gtypes.h"

//------------------------------------------------------------------------
// coordinates
//------------------------------------------------------------------------

#if USE_FIXEDPOINT
#include "FixedPoint.h"
typedef FixedPoint SplashCoord;
#else
typedef double SplashCoord;
#endif

//------------------------------------------------------------------------
// antialiasing
//------------------------------------------------------------------------

#define splashAASize 4

//------------------------------------------------------------------------
// colors
//------------------------------------------------------------------------

enum SplashColorMode {
  splashModeMono1,		// 1 bit per component, 8 pixels per byte,
				//   MSbit is on the left
  splashModeMono8,		// 1 byte per component, 1 byte per pixel
  splashModeRGB8,		// 1 byte per component, 3 bytes per pixel:
				//   RGBRGB...
  splashModeBGR8		// 1 byte per component, 3 bytes per pixel:
				//   BGRBGR...

#if SPLASH_CMYK
  ,
  splashModeCMYK8		// 1 byte per component, 4 bytes per pixel:
				//   CMYKCMYK...
#endif
};

// number of components in each color mode
// (defined in SplashState.cc)
extern int splashColorModeNComps[];

// max number of components in any SplashColor
#if SPLASH_CMYK
#  define splashMaxColorComps 4
#else
#  define splashMaxColorComps 3
#endif

typedef Guchar SplashColor[splashMaxColorComps];
typedef Guchar *SplashColorPtr;

// RGB8
static inline Guchar splashRGB8R(SplashColorPtr rgb8) { return rgb8[0]; }
static inline Guchar splashRGB8G(SplashColorPtr rgb8) { return rgb8[1]; }
static inline Guchar splashRGB8B(SplashColorPtr rgb8) { return rgb8[2]; }

// BGR8
static inline Guchar splashBGR8R(SplashColorPtr bgr8) { return bgr8[2]; }
static inline Guchar splashBGR8G(SplashColorPtr bgr8) { return bgr8[1]; }
static inline Guchar splashBGR8B(SplashColorPtr bgr8) { return bgr8[0]; }

#if SPLASH_CMYK
// CMYK8
static inline Guchar splashCMYK8C(SplashColorPtr cmyk8) { return cmyk8[0]; }
static inline Guchar splashCMYK8M(SplashColorPtr cmyk8) { return cmyk8[1]; }
static inline Guchar splashCMYK8Y(SplashColorPtr cmyk8) { return cmyk8[2]; }
static inline Guchar splashCMYK8K(SplashColorPtr cmyk8) { return cmyk8[3]; }
#endif

static inline void splashColorCopy(SplashColorPtr dest, SplashColorPtr src) {
  dest[0] = src[0];
  dest[1] = src[1];
  dest[2] = src[2];
#if SPLASH_CMYK
  dest[3] = src[3];
#endif
}

static inline void splashColorXor(SplashColorPtr dest, SplashColorPtr src) {
  dest[0] ^= src[0];
  dest[1] ^= src[1];
  dest[2] ^= src[2];
#if SPLASH_CMYK
  dest[3] ^= src[3];
#endif
}

//------------------------------------------------------------------------
// blend functions
//------------------------------------------------------------------------

typedef void (*SplashBlendFunc)(SplashColorPtr src, SplashColorPtr dest,
				SplashColorPtr blend, SplashColorMode cm);

//------------------------------------------------------------------------
// screen parameters
//------------------------------------------------------------------------

enum SplashScreenType {
  splashScreenDispersed,
  splashScreenClustered,
  splashScreenStochasticClustered
};

struct SplashScreenParams {
  SplashScreenType type;
  int size;
  int dotRadius;
  SplashCoord gamma;
  SplashCoord blackThreshold;
  SplashCoord whiteThreshold;
};

//------------------------------------------------------------------------
// error results
//------------------------------------------------------------------------

typedef int SplashError;

#endif
//========================================================================
//
// SplashXPath.cc
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stdlib.h>
#include <string.h>
#include "gmem.h"
#include "SplashMath.h"
#include "SplashPath.h"
#include "SplashXPath.h"

//------------------------------------------------------------------------

struct SplashXPathPoint {
  SplashCoord x, y;
};

struct SplashXPathAdjust {
  int firstPt, lastPt;		// range of points
  GBool vert;			// vertical or horizontal hint
  SplashCoord x0a, x0b,		// hint boundaries
              xma, xmb,
              x1a, x1b;
  SplashCoord x0, x1, xm;	// adjusted coordinates
};

//------------------------------------------------------------------------

// Transform a point from user space to device space.
inline void SplashXPath::transform(SplashCoord *matrix,
				   SplashCoord xi, SplashCoord yi,
				   SplashCoord *xo, SplashCoord *yo) {
  //                          [ m[0] m[1] 0 ]
  // [xo yo 1] = [xi yi 1] *  [ m[2] m[3] 0 ]
  //                          [ m[4] m[5] 1 ]
  *xo = xi * matrix[0] + yi * matrix[2] + matrix[4];
  *yo = xi * matrix[1] + yi * matrix[3] + matrix[5];
}

//------------------------------------------------------------------------
// SplashXPath
//------------------------------------------------------------------------

SplashXPath::SplashXPath() {
  segs = NULL;
  length = size = 0;
}

SplashXPath::SplashXPath(SplashPath *path, SplashCoord *matrix,
			 SplashCoord flatness, GBool closeSubpaths) {
  SplashPathHint *hint;
  SplashXPathPoint *pts;
  SplashXPathAdjust *adjusts, *adjust;
  SplashCoord x0, y0, x1, y1, x2, y2, x3, y3, xsp, ysp;
  SplashCoord adj0, adj1, w;
  int ww;
  int curSubpath, curSubpathX, i, j;

  // transform the points
  pts = (SplashXPathPoint *)gmallocn(path->length, sizeof(SplashXPathPoint));
  for (i = 0; i < path->length; ++i) {
    transform(matrix, path->pts[i].x, path->pts[i].y, &pts[i].x, &pts[i].y);
  }

  // set up the stroke adjustment hints
  if (path->hints) {
    adjusts = (SplashXPathAdjust *)gmallocn(path->hintsLength,
					    sizeof(SplashXPathAdjust));
    for (i = 0; i < path->hintsLength; ++i) {
      hint = &path->hints[i];
      x0 = pts[hint->ctrl0    ].x;    y0 = pts[hint->ctrl0    ].y;
      x1 = pts[hint->ctrl0 + 1].x;    y1 = pts[hint->ctrl0 + 1].y;
      x2 = pts[hint->ctrl1    ].x;    y2 = pts[hint->ctrl1    ].y;
      x3 = pts[hint->ctrl1 + 1].x;    y3 = pts[hint->ctrl1 + 1].y;
      if (x0 == x1 && x2 == x3) {
	adjusts[i].vert = gTrue;
	adj0 = x0;
	adj1 = x2;
      } else if (y0 == y1 && y2 == y3) {
	adjusts[i].vert = gFalse;
	adj0 = y0;
	adj1 = y2;
      } else {
	gfree(adjusts);
	adjusts = NULL;
	break;
      }
      if (adj0 > adj1) {
	x0 = adj0;
	adj0 = adj1;
	adj1 = x0;
      }
      w = adj1 - adj0;
      ww = splashRound(w);
      if (ww == 0) {
	ww = 1;
      }
      adjusts[i].x0a = adj0 - 0.01;
      adjusts[i].x0b = adj0 + 0.01;
      adjusts[i].xma = (SplashCoord)0.5 * (adj0 + adj1) - 0.01;
      adjusts[i].xmb = (SplashCoord)0.5 * (adj0 + adj1) + 0.01;
      adjusts[i].x1a = adj1 - 0.01;
      adjusts[i].x1b = adj1 + 0.01;
      adjusts[i].x0 = (SplashCoord)splashRound(adj0);
      adjusts[i].x1 = adjusts[i].x0 + ww - 0.01;
      adjusts[i].xm = (SplashCoord)0.5 * (adjusts[i].x0 + adjusts[i].x1);
      adjusts[i].firstPt = hint->firstPt;
      adjusts[i].lastPt = hint->lastPt;
    }

  } else {
    adjusts = NULL;
  }

  // perform stroke adjustment
  if (adjusts) {
    for (i = 0, adjust = adjusts; i < path->hintsLength; ++i, ++adjust) {
      for (j = adjust->firstPt; j <= adjust->lastPt; ++j) {
	strokeAdjust(adjust, &pts[j].x, &pts[j].y);
      }
    }
    gfree(adjusts);
  }

  segs = NULL;
  length = size = 0;

  x0 = y0 = xsp = ysp = 0; // make gcc happy
  adj0 = adj1 = 0; // make gcc happy
  curSubpath = 0;
  curSubpathX = 0;
  i = 0;
  while (i < path->length) {

    // first point in subpath - skip it
    if (path->flags[i] & splashPathFirst) {
      x0 = pts[i].x;
      y0 = pts[i].y;
      xsp = x0;
      ysp = y0;
      curSubpath = i;
      curSubpathX = length;
      ++i;

    } else {

      // curve segment
      if (path->flags[i] & splashPathCurve) {
	x1 = pts[i].x;
	y1 = pts[i].y;
	x2 = pts[i+1].x;
	y2 = pts[i+1].y;
	x3 = pts[i+2].x;
	y3 = pts[i+2].y;
	addCurve(x0, y0, x1, y1, x2, y2, x3, y3,
		 flatness,
		 (path->flags[i-1] & splashPathFirst),
		 (path->flags[i+2] & splashPathLast),
		 !closeSubpaths &&
		   (path->flags[i-1] & splashPathFirst) &&
		   !(path->flags[i-1] & splashPathClosed),
		 !closeSubpaths &&
		   (path->flags[i+2] & splashPathLast) &&
		   !(path->flags[i+2] & splashPathClosed));
	x0 = x3;
	y0 = y3;
	i += 3;

      // line segment
      } else {
	x1 = pts[i].x;
	y1 = pts[i].y;
	addSegment(x0, y0, x1, y1,
		   path->flags[i-1] & splashPathFirst,
		   path->flags[i] & splashPathLast,
		   !closeSubpaths &&
		     (path->flags[i-1] & splashPathFirst) &&
		     !(path->flags[i-1] & splashPathClosed),
		   !closeSubpaths &&
		     (path->flags[i] & splashPathLast) &&
		     !(path->flags[i] & splashPathClosed));
	x0 = x1;
	y0 = y1;
	++i;
      }

      // close a subpath
      if (closeSubpaths &&
	  (path->flags[i-1] & splashPathLast) &&
	  (pts[i-1].x != pts[curSubpath].x ||
	   pts[i-1].y != pts[curSubpath].y)) {
	addSegment(x0, y0, xsp, ysp,
		   gFalse, gTrue, gFalse, gFalse);
      }
    }
  }

  gfree(pts);
}

// Apply the stroke adjust hints to point <pt>: (*<xp>, *<yp>).
void SplashXPath::strokeAdjust(SplashXPathAdjust *adjust,
			       SplashCoord *xp, SplashCoord *yp) {
  SplashCoord x, y;

  if (adjust->vert) {
    x = *xp;
    if (x > adjust->x0a && x < adjust->x0b) {
      *xp = adjust->x0;
    } else if (x > adjust->xma && x < adjust->xmb) {
      *xp = adjust->xm;
    } else if (x > adjust->x1a && x < adjust->x1b) {
      *xp = adjust->x1;
    }
  } else {
    y = *yp;
    if (y > adjust->x0a && y < adjust->x0b) {
      *yp = adjust->x0;
    } else if (y > adjust->xma && y < adjust->xmb) {
      *yp = adjust->xm;
    } else if (y > adjust->x1a && y < adjust->x1b) {
      *yp = adjust->x1;
    }
  }
}

SplashXPath::SplashXPath(SplashXPath *xPath) {
  length = xPath->length;
  size = xPath->size;
  segs = (SplashXPathSeg *)gmallocn(size, sizeof(SplashXPathSeg));
  memcpy(segs, xPath->segs, length * sizeof(SplashXPathSeg));
}

SplashXPath::~SplashXPath() {
  gfree(segs);
}

// Add space for <nSegs> more segments
void SplashXPath::grow(int nSegs) {
  if (length + nSegs > size) {
    if (size == 0) {
      size = 32;
    }
    while (size < length + nSegs) {
      size *= 2;
    }
    segs = (SplashXPathSeg *)greallocn(segs, size, sizeof(SplashXPathSeg));
  }
}

void SplashXPath::addCurve(SplashCoord x0, SplashCoord y0,
			   SplashCoord x1, SplashCoord y1,
			   SplashCoord x2, SplashCoord y2,
			   SplashCoord x3, SplashCoord y3,
			   SplashCoord flatness,
			   GBool first, GBool last, GBool end0, GBool end1) {
  SplashCoord cx[splashMaxCurveSplits + 1][3];
  SplashCoord cy[splashMaxCurveSplits + 1][3];
  int cNext[splashMaxCurveSplits + 1];
  SplashCoord xl0, xl1, xl2, xr0, xr1, xr2, xr3, xx1, xx2, xh;
  SplashCoord yl0, yl1, yl2, yr0, yr1, yr2, yr3, yy1, yy2, yh;
  SplashCoord dx, dy, mx, my, d1, d2, flatness2;
  int p1, p2, p3;

  flatness2 = flatness * flatness;

  // initial segment
  p1 = 0;
  p2 = splashMaxCurveSplits;
  cx[p1][0] = x0;  cy[p1][0] = y0;
  cx[p1][1] = x1;  cy[p1][1] = y1;
  cx[p1][2] = x2;  cy[p1][2] = y2;
  cx[p2][0] = x3;  cy[p2][0] = y3;
  cNext[p1] = p2;

  while (p1 < splashMaxCurveSplits) {

    // get the next segment
    xl0 = cx[p1][0];  yl0 = cy[p1][0];
    xx1 = cx[p1][1];  yy1 = cy[p1][1];
    xx2 = cx[p1][2];  yy2 = cy[p1][2];
    p2 = cNext[p1];
    xr3 = cx[p2][0];  yr3 = cy[p2][0];

    // compute the distances from the control points to the
    // midpoint of the straight line (this is a bit of a hack, but
    // it's much faster than computing the actual distances to the
    // line)
    mx = (xl0 + xr3) * 0.5;
    my = (yl0 + yr3) * 0.5;
    dx = xx1 - mx;
    dy = yy1 - my;
    d1 = dx*dx + dy*dy;
    dx = xx2 - mx;
    dy = yy2 - my;
    d2 = dx*dx + dy*dy;

    // if the curve is flat enough, or no more subdivisions are
    // allowed, add the straight line segment
    if (p2 - p1 == 1 || (d1 <= flatness2 && d2 <= flatness2)) {
      addSegment(xl0, yl0, xr3, yr3,
		 p1 == 0 && first,
		 p2 == splashMaxCurveSplits && last,
		 p1 == 0 && end0,
		 p2 == splashMaxCurveSplits && end1);
      p1 = p2;

    // otherwise, subdivide the curve
    } else {
      xl1 = (xl0 + xx1) * 0.5;
      yl1 = (yl0 + yy1) * 0.5;
      xh = (xx1 + xx2) * 0.5;
      yh = (yy1 + yy2) * 0.5;
      xl2 = (xl1 + xh) * 0.5;
      yl2 = (yl1 + yh) * 0.5;
      xr2 = (xx2 + xr3) * 0.5;
      yr2 = (yy2 + yr3) * 0.5;
      xr1 = (xh + xr2) * 0.5;
      yr1 = (yh + yr2) * 0.5;
      xr0 = (xl2 + xr1) * 0.5;
      yr0 = (yl2 + yr1) * 0.5;
      // add the new subdivision points
      p3 = (p1 + p2) / 2;
      cx[p1][1] = xl1;  cy[p1][1] = yl1;
      cx[p1][2] = xl2;  cy[p1][2] = yl2;
      cNext[p1] = p3;
      cx[p3][0] = xr0;  cy[p3][0] = yr0;
      cx[p3][1] = xr1;  cy[p3][1] = yr1;
      cx[p3][2] = xr2;  cy[p3][2] = yr2;
      cNext[p3] = p2;
    }
  }
}

void SplashXPath::addSegment(SplashCoord x0, SplashCoord y0,
			     SplashCoord x1, SplashCoord y1,
			     GBool first, GBool last, GBool end0, GBool end1) {
  grow(1);
  segs[length].x0 = x0;
  segs[length].y0 = y0;
  segs[length].x1 = x1;
  segs[length].y1 = y1;
  segs[length].flags = 0;
  if (first) {
    segs[length].flags |= splashXPathFirst;
  }
  if (last) {
    segs[length].flags |= splashXPathLast;
  }
  if (end0) {
    segs[length].flags |= splashXPathEnd0;
  }
  if (end1) {
    segs[length].flags |= splashXPathEnd1;
  }
  if (y1 == y0) {
    segs[length].dxdy = segs[length].dydx = 0;
    segs[length].flags |= splashXPathHoriz;
    if (x1 == x0) {
      segs[length].flags |= splashXPathVert;
    }
  } else if (x1 == x0) {
    segs[length].dxdy = segs[length].dydx = 0;
    segs[length].flags |= splashXPathVert;
  } else {
#if USE_FIXEDPOINT
    if (FixedPoint::divCheck(x1 - x0, y1 - y0, &segs[length].dxdy)) {
      segs[length].dydx = (SplashCoord)1 / segs[length].dxdy;
    } else {
      segs[length].dxdy = segs[length].dydx = 0;
      if (splashAbs(x1 - x0) > splashAbs(y1 - y0)) {
	segs[length].flags |= splashXPathHoriz;
      } else {
	segs[length].flags |= splashXPathVert;
      }
    }
#else
    segs[length].dxdy = (x1 - x0) / (y1 - y0);
    segs[length].dydx = (SplashCoord)1 / segs[length].dxdy;
#endif
  }
  if (y0 > y1) {
    segs[length].flags |= splashXPathFlip;
  }
  ++length;
}

static int cmpXPathSegs(const void *arg0, const void *arg1) {
  SplashXPathSeg *seg0 = (SplashXPathSeg *)arg0;
  SplashXPathSeg *seg1 = (SplashXPathSeg *)arg1;
  SplashCoord x0, y0, x1, y1;

  if (seg0->flags & splashXPathFlip) {
    x0 = seg0->x1;
    y0 = seg0->y1;
  } else {
    x0 = seg0->x0;
    y0 = seg0->y0;
  }
  if (seg1->flags & splashXPathFlip) {
    x1 = seg1->x1;
    y1 = seg1->y1;
  } else {
    x1 = seg1->x0;
    y1 = seg1->y0;
  }
  if (y0 != y1) {
    return (y0 > y1) ? 1 : -1;
  }
  if (x0 != x1) {
    return (x0 > x1) ? 1 : -1;
  }
  return 0;
}

void SplashXPath::aaScale() {
  SplashXPathSeg *seg;
  int i;

  for (i = 0, seg = segs; i < length; ++i, ++seg) {
    seg->x0 *= splashAASize;
    seg->y0 *= splashAASize;
    seg->x1 *= splashAASize;
    seg->y1 *= splashAASize;
  }
}

void SplashXPath::sort() {
  qsort(segs, length, sizeof(SplashXPathSeg), &cmpXPathSegs);
}
//========================================================================
//
// SplashXPath.h
//
//========================================================================

#ifndef SPLASHXPATH_H
#define SPLASHXPATH_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "SplashTypes.h"

class SplashPath;
struct SplashXPathAdjust;

//------------------------------------------------------------------------

#define splashMaxCurveSplits (1 << 10)

//------------------------------------------------------------------------
// SplashXPathSeg
//------------------------------------------------------------------------

struct SplashXPathSeg {
  SplashCoord x0, y0;		// first endpoint
  SplashCoord x1, y1;		// second endpoint
  SplashCoord dxdy;		// slope: delta-x / delta-y
  SplashCoord dydx;		// slope: delta-y / delta-x
  Guint flags;
};

#define splashXPathFirst   0x01	// first segment of a subpath
#define splashXPathLast    0x02	// last segment of a subpath
#define splashXPathEnd0    0x04	// first endpoint is end of an open subpath
#define splashXPathEnd1    0x08 // second endpoint is end of an open subpath
#define splashXPathHoriz   0x10 // segment is vertical (y0 == y1)
				//   (dxdy is undef)
#define splashXPathVert    0x20 // segment is horizontal (x0 == x1)
				//   (dydx is undef)
#define splashXPathFlip	   0x40	// y0 > y1

//------------------------------------------------------------------------
// SplashXPath
//------------------------------------------------------------------------

class SplashXPath {
public:

  // Expands (converts to segments) and flattens (converts curves to
  // lines) <path>.  Transforms all points from user space to device
  // space, via <matrix>.  If <closeSubpaths> is true, closes all open
  // subpaths.
  SplashXPath(SplashPath *path, SplashCoord *matrix,
	      SplashCoord flatness, GBool closeSubpaths);

  // Copy an expanded path.
  SplashXPath *copy() { return new SplashXPath(this); }

  ~SplashXPath();

  // Multiply all coordinates by splashAASize, in preparation for
  // anti-aliased rendering.
  void aaScale();

  // Sort by upper coordinate (lower y), in y-major order.
  void sort();

private:

  SplashXPath();
  SplashXPath(SplashXPath *xPath);
  void transform(SplashCoord *matrix, SplashCoord xi, SplashCoord yi,
		 SplashCoord *xo, SplashCoord *yo);
  void strokeAdjust(SplashXPathAdjust *adjust,
		    SplashCoord *xp, SplashCoord *yp);
  void grow(int nSegs);
  void addCurve(SplashCoord x0, SplashCoord y0,
		SplashCoord x1, SplashCoord y1,
		SplashCoord x2, SplashCoord y2,
		SplashCoord x3, SplashCoord y3,
		SplashCoord flatness,
		GBool first, GBool last, GBool end0, GBool end1);
  void addSegment(SplashCoord x0, SplashCoord y0,
		  SplashCoord x1, SplashCoord y1,
		  GBool first, GBool last, GBool end0, GBool end1);

  SplashXPathSeg *segs;
  int length, size;		// length and size of segs array

  friend class SplashXPathScanner;
  friend class SplashClip;
  friend class Splash;
};

#endif
//========================================================================
//
// SplashXPathScanner.cc
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stdlib.h>
#include <string.h>
#include "gmem.h"
#include "SplashMath.h"
#include "SplashXPath.h"
#include "SplashBitmap.h"
#include "SplashXPathScanner.h"

//------------------------------------------------------------------------

struct SplashIntersect {
  int x0, x1;			// intersection of segment with [y, y+1)
  int count;			// EO/NZWN counter increment
};

static int cmpIntersect(const void *p0, const void *p1) {
  return ((SplashIntersect *)p0)->x0 - ((SplashIntersect *)p1)->x0;
}

//------------------------------------------------------------------------
// SplashXPathScanner
//------------------------------------------------------------------------

SplashXPathScanner::SplashXPathScanner(SplashXPath *xPathA, GBool eoA) {
  SplashXPathSeg *seg;
  SplashCoord xMinFP, yMinFP, xMaxFP, yMaxFP;
  int i;

  xPath = xPathA;
  eo = eoA;

  // compute the bbox
  if (xPath->length == 0) {
    xMin = yMin = 1;
    xMax = yMax = 0;
  } else {
    seg = &xPath->segs[0];
    if (seg->x0 <= seg->x1) {
      xMinFP = seg->x0;
      xMaxFP = seg->x1;
    } else {
      xMinFP = seg->x1;
      xMaxFP = seg->x0;
    }
    if (seg->flags & splashXPathFlip) {
      yMinFP = seg->y1;
      yMaxFP = seg->y0;
    } else {
      yMinFP = seg->y0;
      yMaxFP = seg->y1;
    }
    for (i = 1; i < xPath->length; ++i) {
      seg = &xPath->segs[i];
      if (seg->x0 < xMinFP) {
	xMinFP = seg->x0;
      } else if (seg->x0 > xMaxFP) {
	xMaxFP = seg->x0;
      }
      if (seg->x1 < xMinFP) {
	xMinFP = seg->x1;
      } else if (seg->x1 > xMaxFP) {
	xMaxFP = seg->x1;
      }
      if (seg->flags & splashXPathFlip) {
	if (seg->y0 > yMaxFP) {
	  yMaxFP = seg->y0;
	}
      } else {
	if (seg->y1 > yMaxFP) {
	  yMaxFP = seg->y1;
	}
      }
    }
    xMin = splashFloor(xMinFP);
    xMax = splashFloor(xMaxFP);
    yMin = splashFloor(yMinFP);
    yMax = splashFloor(yMaxFP);
  }

  interY = yMin - 1;
  xPathIdx = 0;
  inter = NULL;
  interLen = interSize = 0;
}

SplashXPathScanner::~SplashXPathScanner() {
  gfree(inter);
}

void SplashXPathScanner::getBBoxAA(int *xMinA, int *yMinA,
				   int *xMaxA, int *yMaxA) {
  *xMinA = xMin / splashAASize;
  *yMinA = yMin / splashAASize;
  *xMaxA = xMax / splashAASize;
  *yMaxA = yMax / splashAASize;
}

void SplashXPathScanner::getSpanBounds(int y, int *spanXMin, int *spanXMax) {
  if (interY != y) {
    computeIntersections(y);
  }
  if (interLen > 0) {
    *spanXMin = inter[0].x0;
    *spanXMax = inter[interLen - 1].x1;
  } else {
    *spanXMin = xMax + 1;
    *spanXMax = xMax;
  }
}

GBool SplashXPathScanner::test(int x, int y) {
  int count, i;

  if (interY != y) {
    computeIntersections(y);
  }
  count = 0;
  for (i = 0; i < interLen && inter[i].x0 <= x; ++i) {
    if (x <= inter[i].x1) {
      return gTrue;
    }
    count += inter[i].count;
  }
  return eo ? (count & 1) : (count != 0);
}

GBool SplashXPathScanner::testSpan(int x0, int x1, int y) {
  int count, xx1, i;

  if (interY != y) {
    computeIntersections(y);
  }

  count = 0;
  for (i = 0; i < interLen && inter[i].x1 < x0; ++i) {
    count += inter[i].count;
  }

  // invariant: the subspan [x0,xx1] is inside the path
  xx1 = x0 - 1;
  while (xx1 < x1) {
    if (i >= interLen) {
      return gFalse;
    }
    if (inter[i].x0 > xx1 + 1 &&
	!(eo ? (count & 1) : (count != 0))) {
      return gFalse;
    }
    if (inter[i].x1 > xx1) {
      xx1 = inter[i].x1;
    }
    count += inter[i].count;
    ++i;
  }

  return gTrue;
}

GBool SplashXPathScanner::getNextSpan(int y, int *x0, int *x1) {
  int xx0, xx1;

  if (interY != y) {
    computeIntersections(y);
  }
  if (interIdx >= interLen) {
    return gFalse;
  }
  xx0 = inter[interIdx].x0;
  xx1 = inter[interIdx].x1;
  interCount += inter[interIdx].count;
  ++interIdx;
  while (interIdx < interLen &&
	 (inter[interIdx].x0 <= xx1 ||
	  (eo ? (interCount & 1) : (interCount != 0)))) {
    if (inter[interIdx].x1 > xx1) {
      xx1 = inter[interIdx].x1;
    }
    interCount += inter[interIdx].count;
    ++interIdx;
  }
  *x0 = xx0;
  *x1 = xx1;
  return gTrue;
}

void SplashXPathScanner::computeIntersections(int y) {
  SplashCoord xSegMin, xSegMax, ySegMin, ySegMax, xx0, xx1;
  SplashXPathSeg *seg;
  int i, j;

  // find the first segment that intersects [y, y+1)
  i = (y >= interY) ? xPathIdx : 0;
  while (i < xPath->length &&
	 xPath->segs[i].y0 < y && xPath->segs[i].y1 < y) {
    ++i;
  }
  xPathIdx = i;

  // find all of the segments that intersect [y, y+1) and create an
  // Intersect element for each one
  interLen = 0;
  for (j = i; j < xPath->length; ++j) {
    seg = &xPath->segs[j];
    if (seg->flags & splashXPathFlip) {
      ySegMin = seg->y1;
      ySegMax = seg->y0;
    } else {
      ySegMin = seg->y0;
      ySegMax = seg->y1;
    }

    // ensure that:      ySegMin < y+1
    //              y <= ySegMax
    if (ySegMin >= y + 1) {
      break;
    }
    if (ySegMax < y) {
      continue;
    }

    if (interLen == interSize) {
      if (interSize == 0) {
	interSize = 16;
      } else {
	interSize *= 2;
      }
      inter = (SplashIntersect *)greallocn(inter, interSize,
					   sizeof(SplashIntersect));
    }

    if (seg->flags & splashXPathHoriz) {
      xx0 = seg->x0;
      xx1 = seg->x1;
    } else if (seg->flags & splashXPathVert) {
      xx0 = xx1 = seg->x0;
    } else {
      if (seg->x0 < seg->x1) {
	xSegMin = seg->x0;
	xSegMax = seg->x1;
      } else {
	xSegMin = seg->x1;
	xSegMax = seg->x0;
      }
      // intersection with top edge
      xx0 = seg->x0 + ((SplashCoord)y - seg->y0) * seg->dxdy;
      // intersection with bottom edge
      xx1 = seg->x0 + ((SplashCoord)y + 1 - seg->y0) * seg->dxdy;
      // the segment may not actually extend to the top and/or bottom edges
      if (xx0 < xSegMin) {
	xx0 = xSegMin;
      } else if (xx0 > xSegMax) {
	xx0 = xSegMax;
      }
      if (xx1 < xSegMin) {
	xx1 = xSegMin;
      } else if (xx1 > xSegMax) {
	xx1 = xSegMax;
      }
    }
    if (xx0 < xx1) {
      inter[interLen].x0 = splashFloor(xx0);
      inter[interLen].x1 = splashFloor(xx1);
    } else {
      inter[interLen].x0 = splashFloor(xx1);
      inter[interLen].x1 = splashFloor(xx0);
    }
    if (ySegMin <= y &&
	(SplashCoord)y < ySegMax &&
	!(seg->flags & splashXPathHoriz)) {
      inter[interLen].count = eo ? 1
	                         : (seg->flags & splashXPathFlip) ? 1 : -1;
    } else {
      inter[interLen].count = 0;
    }
    ++interLen;
  }

  qsort(inter, interLen, sizeof(SplashIntersect), &cmpIntersect);

  interY = y;
  interIdx = 0;
  interCount = 0;
}

void SplashXPathScanner::renderAALine(SplashBitmap *aaBuf,
				      int *x0, int *x1, int y) {
  int xx0, xx1, xx, xxMin, xxMax, yy;
  Guchar mask;
  SplashColorPtr p;

  memset(aaBuf->getDataPtr(), 0, aaBuf->getRowSize() * aaBuf->getHeight());
  xxMin = aaBuf->getWidth();
  xxMax = -1;
  for (yy = 0; yy < splashAASize; ++yy) {
    computeIntersections(splashAASize * y + yy);
    while (interIdx < interLen) {
      xx0 = inter[interIdx].x0;
      xx1 = inter[interIdx].x1;
      interCount += inter[interIdx].count;
      ++interIdx;
      while (interIdx < interLen &&
	     (inter[interIdx].x0 <= xx1 ||
	      (eo ? (interCount & 1) : (interCount != 0)))) {
	if (inter[interIdx].x1 > xx1) {
	  xx1 = inter[interIdx].x1;
	}
	interCount += inter[interIdx].count;
	++interIdx;
      }
      if (xx0 < 0) {
	xx0 = 0;
      }
      ++xx1;
      if (xx1 > aaBuf->getWidth()) {
	xx1 = aaBuf->getWidth();
      }
      // set [xx0, xx1) to 1
      if (xx0 < xx1) {
	xx = xx0;
	p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() + (xx >> 3);
	if (xx & 7) {
	  mask = 0xff >> (xx & 7);
	  if ((xx & ~7) == (xx1 & ~7)) {
	    mask &= (Guchar)(0xff00 >> (xx1 & 7));
	  }
	  *p++ |= mask;
	  xx = (xx & ~7) + 8;
	}
	for (; xx + 7 < xx1; xx += 8) {
	  *p++ |= 0xff;
	}
	if (xx < xx1) {
	  *p |= (Guchar)(0xff00 >> (xx1 & 7));
	}
      }
      if (xx0 < xxMin) {
	xxMin = xx0;
      }
      if (xx1 > xxMax) {
	xxMax = xx1;
      }
    }
  }
  *x0 = xxMin / splashAASize;
  *x1 = (xxMax - 1) / splashAASize;
}

void SplashXPathScanner::clipAALine(SplashBitmap *aaBuf,
				    int *x0, int *x1, int y) {
  int xx0, xx1, xx, yy;
  Guchar mask;
  SplashColorPtr p;

  for (yy = 0; yy < splashAASize; ++yy) {
    xx = *x0 * splashAASize;
    computeIntersections(splashAASize * y + yy);
    while (interIdx < interLen && xx < (*x1 + 1) * splashAASize) {
      xx0 = inter[interIdx].x0;
      xx1 = inter[interIdx].x1;
      interCount += inter[interIdx].count;
      ++interIdx;
      while (interIdx < interLen &&
	     (inter[interIdx].x0 <= xx1 ||
	      (eo ? (interCount & 1) : (interCount != 0)))) {
	if (inter[interIdx].x1 > xx1) {
	  xx1 = inter[interIdx].x1;
	}
	interCount += inter[interIdx].count;
	++interIdx;
      }
      if (xx0 > aaBuf->getWidth()) {
	xx0 = aaBuf->getWidth();
      }
      // set [xx, xx0) to 0
      if (xx < xx0) {
	p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() + (xx >> 3);
	if (xx & 7) {
	  mask = (Guchar)(0xff00 >> (xx & 7));
	  if ((xx & ~7) == (xx0 & ~7)) {
	    mask |= 0xff >> (xx0 & 7);
	  }
	  *p++ &= mask;
	  xx = (xx & ~7) + 8;
	}
	for (; xx + 7 <= xx0; xx += 8) {
	  *p++ = 0x00;
	}
	if (xx <= xx0) {
	  *p &= 0xff >> (xx0 & 7);
	}
      }
      if (xx1 >= xx) {
	xx = xx1 + 1;
      }
    }
    xx0 = (*x1 + 1) * splashAASize;
    // set [xx, xx0) to 0
    if (xx < xx0) {
      p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() + (xx >> 3);
      if (xx & 7) {
	mask = (Guchar)(0xff00 >> (xx & 7));
	if ((xx & ~7) == (xx0 & ~7)) {
	  mask &= 0xff >> (xx0 & 7);
	}
	*p++ &= mask;
	xx = (xx & ~7) + 8;
      }
      for (; xx + 7 <= xx0; xx += 8) {
	*p++ = 0x00;
      }
      if (xx <= xx0) {
	*p &= 0xff >> (xx0 & 7);
      }
    }
  }
}
//========================================================================
//
// SplashXPathScanner.h
//
//========================================================================

#ifndef SPLASHXPATHSCANNER_H
#define SPLASHXPATHSCANNER_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "SplashTypes.h"

class SplashXPath;
class SplashBitmap;
struct SplashIntersect;

//------------------------------------------------------------------------
// SplashXPathScanner
//------------------------------------------------------------------------

class SplashXPathScanner {
public:

  // Create a new SplashXPathScanner object.  <xPathA> must be sorted.
  SplashXPathScanner(SplashXPath *xPathA, GBool eoA);

  ~SplashXPathScanner();

  // Return the path's bounding box.
  void getBBox(int *xMinA, int *yMinA, int *xMaxA, int *yMaxA)
    { *xMinA = xMin; *yMinA = yMin; *xMaxA = xMax; *yMaxA = yMax; }

  // Return the path's bounding box.
  void getBBoxAA(int *xMinA, int *yMinA, int *xMaxA, int *yMaxA);

  // Return the min/max x values for the span at <y>.
  void getSpanBounds(int y, int *spanXMin, int *spanXMax);

  // Returns true if (<x>,<y>) is inside the path.
  GBool test(int x, int y);

  // Returns true if the entire span ([<x0>,<x1>], <y>) is inside the
  // path.
  GBool testSpan(int x0, int x1, int y);

  // Returns the next span inside the path at <y>.  If <y> is
  // different than the previous call to getNextSpan, this returns the
  // first span at <y>; otherwise it returns the next span (relative
  // to the previous call to getNextSpan).  Returns false if there are
  // no more spans at <y>.
  GBool getNextSpan(int y, int *x0, int *x1);

  // Renders one anti-aliased line into <aaBuf>.  Returns the min and
  // max x coordinates with non-zero pixels in <x0> and <x1>.
  void renderAALine(SplashBitmap *aaBuf, int *x0, int *x1, int y);

  // Clips an anti-aliased line by setting pixels to zero.  On entry,
  // all non-zero pixels are between <x0> and <x1>.  This function
  // will update <x0> and <x1>.
  void clipAALine(SplashBitmap *aaBuf, int *x0, int *x1, int y);

private:

  void computeIntersections(int y);

  SplashXPath *xPath;
  GBool eo;
  int xMin, yMin, xMax, yMax;

  int interY;			// current y value
  int interIdx;			// current index into <inter> - used by
				//   getNextSpan 
  int interCount;		// current EO/NZWN counter - used by
				//   getNextSpan
  int xPathIdx;			// current index into <xPath> - used by
				//   computeIntersections
  SplashIntersect *inter;	// intersections array for <interY>
  int interLen;			// number of intersections in <inter>
  int interSize;		// size of the <inter> array
};

#endif
#ifndef _VECTOR_H
#define _VECTOR_H
#include "gtypes.h"


template<class T>
class GVector{
private:
   
   int _size;
   T*  last;
   T*  storage; 
 
   void resize(){
     if (_size==0) _size=2;else _size=2*_size;
      T *tmp=new T[_size];
     if (storage){
       last=copy(storage,last,tmp);
       delete [] storage;
      }
     else last=tmp; 
     storage=tmp;
    }

   T* copy(T* src1,T* scr2,T* dest){
     T* tmp=src1;
     T* d=dest;
      while(tmp!=scr2){
        *d=*tmp;
         d++;tmp++;
       }
      return d;
   }

public:
 typedef T* iterator;

 GVector(){
  _size=0;
  last=0;
  storage=0;
}



virtual ~GVector(){
  delete[] storage ;
}  

void reset(){
  last=storage;
}

int size(){
  return (last-storage);
}   
void push_back(const T& elem){
  if (!storage||(size() >=_size)) resize();
        *last=elem;
         last++;
  
     
} 


T pop_back() {
    if (last!=storage) last--;

    return *last;
} 


T operator[](unsigned int i){
 return *(storage+i);
}
  

GBool isEmpty() const{
 return !_size || (last==storage) ;
}



iterator begin() const{
 return storage;
}

iterator end() const {
  return last;
}
};
#endif



   
  
  



//========================================================================
//
// ImgOutputDev.cc
//
// Copyright 2011 Devaldi Ltd
//
// Copyright 1997-2002 Glyph & Cog, LLC
//
// Changed 1999-2000 by G.Ovtcharov
//
// Changed 2002 by Mikhail Kruk
//
//========================================================================

#ifdef __GNUC__
#pragma implementation
#endif

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stddef.h>
#include <ctype.h>
#include <math.h>
#include "GString.h"
#include "GList.h"
#include "UnicodeMap.h"
#include "gmem.h"
#include "config.h"
#include "Error.h"
#include "GfxState.h"
#include "GlobalParams.h"
#include "ImgOutputDev.h"
#include "XmlFonts.h"


int HtmlPage::pgNum=0;
int ImgOutputDev::imgNum=1;

extern double scale;
extern GBool complexMode;
extern GBool ignore;
extern GBool printCommands;
extern GBool printHtml;
extern GBool noframes;
extern GBool stout;
extern GBool xml;
extern GBool showHidden;
extern GBool noMerge;

static GString* basename(GString* str){
  
  char *p=str->getCString();
  int len=str->getLength();
  for (int i=len-1;i>=0;i--)
    if (*(p+i)==SLASH) 
      return new GString((p+i+1),len-i-1);
  return new GString(str);
}

static GString* Dirname(GString* str){
  
  char *p=str->getCString();
  int len=str->getLength();
  for (int i=len-1;i>=0;i--)
    if (*(p+i)==SLASH) 
      return new GString(p,i+1);
  return new GString();
} 

//------------------------------------------------------------------------
// HtmlString
//------------------------------------------------------------------------

HtmlString::HtmlString(GfxState *state, double fontSize, double _charspace, XmlFontAccu* fonts) {
  GfxFont *font;
  double x, y;

  state->transform(state->getCurX(), state->getCurY(), &x, &y);
  if ((font = state->getFont())) {
    yMin = y - font->getAscent() * fontSize;
    yMax = y - font->getDescent() * fontSize;
    GfxRGB rgb;
    state->getFillRGB(&rgb);
    GString *name = state->getFont()->getName();
    if (!name) name = XmlFont::getDefaultFont(); //new GString("default");
    //XmlFont hfont=XmlFont(name, static_cast<int>(fontSize-1),_charspace, rgb);
    XmlFont hfont=XmlFont(name, static_cast<int>(fontSize-1),0.0, rgb);
    fontpos = fonts->AddFont(hfont);
  } else {
    // this means that the PDF file draws text without a current font,
    // which should never happen
    yMin = y - 0.95 * fontSize;
    yMax = y + 0.35 * fontSize;
    fontpos=0;
  }
  if (yMin == yMax) {
    // this is a sanity check for a case that shouldn't happen -- but
    // if it does happen, we want to avoid dividing by zero later
    yMin = y;
    yMax = y + 1;
  }
  col = 0;
  text = NULL;
  xRight = NULL;
  link = NULL;
  len = size = 0;
  yxNext = NULL;
  xyNext = NULL;
  strSize = 0;
  htext=new GString();
  htext2=new GString();
  dir = textDirUnknown;
}


HtmlString::~HtmlString() {
  delete text;
  delete htext;
  delete htext2;
//  delete strSize;
  gfree(xRight);
}

void HtmlString::addChar(GfxState *state, double x, double y,
			 double dx, double dy, Unicode u) {
    
  if ( !showHidden && (state->getRender() & 3) == 3) {
    return;
  }
    
  if (dir == textDirUnknown) {
    dir = UnicodeMap::getDirection(u);
  } 

  if (len == size) {
    size += 16;
    text = (Unicode *)grealloc(text, size * sizeof(Unicode));
    xRight = (double *)grealloc(xRight, size * sizeof(double));
  }
  text[len] = u;
  if (len == 0) {
    xMin = x;
  }
  xMax = xRight[len] = x + dx;
  //xMax = xRight[len] = x;
  ++strSize;
//printf("added char: %f %f xright = %f\n", x, dx, x+dx);
  ++len;
}

void HtmlString::endString()
{
  if( dir == textDirRightLeft && len > 1 )
  {
    //printf("will reverse!\n");
    for (int i = 0; i < len / 2; i++)
    {
      Unicode ch = text[i];
      text[i] = text[len - i - 1];
      text[len - i - 1] = ch;
    }
  }
}

//------------------------------------------------------------------------
// HtmlPage
//------------------------------------------------------------------------

HtmlPage::HtmlPage(GBool rawOrder, GBool textAsJSON, GBool compressData, char *imgExtVal) {
  this->rawOrder = rawOrder;
  this->textAsJSON = textAsJSON;
  this->compressData = compressData;
  curStr = NULL;
  yxStrings = NULL;
  xyStrings = NULL;
  yxCur1 = yxCur2 = NULL;
  fonts=new XmlFontAccu();
  links=new XmlLinks();
  pageWidth=0;
  pageHeight=0;
  X1=0;
  X2=0;  
  Y1=0;
  Y2=0;  
  fontsPageMarker = 0;
  DocName=NULL;
  firstPage = -1;
  imgExt = new GString(imgExtVal);
}

HtmlPage::~HtmlPage() {
  clear();
  if (DocName) delete DocName;
  if (fonts) delete fonts;
  if (links) delete links;
  if (imgExt) delete imgExt;  
}

void HtmlPage::updateFont(GfxState *state) {
  GfxFont *font;
  double *fm;
  char *name;
  int code;
  double w;
  
  // adjust the font size
  fontSize = state->getTransformedFontSize();
  if ((font = state->getFont()) && font->getType() == fontType3) {
    // This is a hack which makes it possible to deal with some Type 3
    // fonts.  The problem is that it's impossible to know what the
    // base coordinate system used in the font is without actually
    // rendering the font.  This code tries to guess by looking at the
    // width of the character 'm' (which breaks if the font is a
    // subset that doesn't contain 'm').
    for (code = 0; code < 256; ++code) {
      if ((name = ((Gfx8BitFont *)font)->getCharName(code)) &&
	  name[0] == 'm' && name[1] == '\0') {
	break;
      }
    }
    if (code < 256) {
      w = ((Gfx8BitFont *)font)->getWidth(code);
      if (w != 0) {
	// 600 is a generic average 'm' width -- yes, this is a hack
	fontSize *= w / 0.6;
      }
    }
    fm = font->getFontMatrix();
    if (fm[0] != 0) {
      fontSize *= fabs(fm[3] / fm[0]);
    }
  }
}

void HtmlPage::beginString(GfxState *state, GString *s) {
  curStr = new HtmlString(state, fontSize,charspace, fonts);
}


void HtmlPage::conv(){
  HtmlString *tmp;

  int linkIndex = 0;
  XmlFont* h;

  for(tmp=yxStrings;tmp;tmp=tmp->yxNext){
     int pos=tmp->fontpos;
     //  printf("%d\n",pos);
     h=fonts->Get(pos);

     if (tmp->htext) delete tmp->htext; 
     tmp->htext=XmlFont::simple(h,tmp->text,tmp->len);
     tmp->htext2=XmlFont::simple(h,tmp->text,tmp->len);

     if (links->inLink(tmp->xMin,tmp->yMin,tmp->xMax,tmp->yMax, linkIndex)){
       tmp->link = links->getLink(linkIndex);
       /*GString *t=tmp->htext;
       tmp->htext=links->getLink(k)->Link(tmp->htext);
       delete t;*/
     }
  }

}


void HtmlPage::addChar(GfxState *state, double x, double y,
		       double dx, double dy, 
			double ox, double oy, Unicode *u, int uLen) {

  if ( !showHidden && (state->getRender() & 3) == 3) {
      return;
  }
    
  double x1, y1, w1, h1, dx2, dy2;
  int n, i, d;
  state->transform(x, y, &x1, &y1);
  n = curStr->len;
  d = 0;
 
  // check that new character is in the same direction as current string
  // and is not too far away from it before adding 
/*  if ((UnicodeMap::getDirection(u[0]) != curStr->dir) || 
     (n > 0 && 
      fabs(x1 - curStr->xRight[n-1]) > 0.1 * (curStr->yMax - curStr->yMin))) {
    endString();
    beginString(state, NULL);
  }*/
  state->textTransformDelta(state->getCharSpace() * state->getHorizScaling(),
			    0, &dx2, &dy2);
  dx -= dx2;
  dy -= dy2;
  state->transformDelta(dx, dy, &w1, &h1);
  if (uLen != 0) {
    w1 /= uLen;
    h1 /= uLen;
  }
/* if (d != 3)
 {
 endString();
 beginString(state, NULL);
 }
*/


  for (i = 0; i < uLen; ++i) 
  {
	if (u[i] == ' ')
        {
	    curStr->addChar(state, x1 + i*w1, y1 + i*h1, w1, h1, u[i]);
	    endString();
	    beginString(state, NULL);
	}
        else {
	    curStr->addChar(state, x1 + i*w1, y1 + i*h1, w1, h1, u[i]); /* xyString  */
        }
   }

/*
  for (i = 0; i < uLen; ++i) {
    curStr->addChar(state, x1 + i*w1, y1 + i*h1, w1, h1, u[i]);
  }
*/
}

void HtmlPage::endString() {
  HtmlString *p1, *p2;
  double h, y1, y2;

  // throw away zero-length strings -- they don't have valid xMin/xMax
  // values, and they're useless anyway
    
   if (curStr->len == 0) {
    delete curStr;
     curStr = NULL;
    return;
   }

  curStr->endString();

#if 0 //~tmp
  if (curStr->yMax - curStr->yMin > 20) {
    delete curStr;
    curStr = NULL;
    return;
  }
#endif

  // insert string in y-major list
  h = curStr->yMax - curStr->yMin;
  y1 = curStr->yMin + 0.5 * h;
  y2 = curStr->yMin + 0.8 * h;
  
  if (rawOrder) {
    p1 = yxCur1;
    p2 = NULL;
  } else if ((!yxCur1 ||
              (y1 >= yxCur1->yMin &&
               (y2 >= yxCur1->yMax || curStr->xMax >= yxCur1->xMin))) &&
             (!yxCur2 ||
              (y1 < yxCur2->yMin ||
               (y2 < yxCur2->yMax && curStr->xMax < yxCur2->xMin)))) { /* add space */
    p1 = yxCur1;
    p2 = yxCur2;
  } else {
    for (p1 = NULL, p2 = yxStrings; p2; p1 = p2, p2 = p2->yxNext) {
      if (y1 < p2->yMin || (y2 < p2->yMax && curStr->xMax < p2->xMin))
        break;
    }
    yxCur2 = p2;
  }

  yxCur1 = curStr;

  if (p1)
    p1->yxNext = curStr;
  else
    yxStrings = curStr;

  curStr->yxNext = p2;
  curStr = NULL;
}

void HtmlPage::coalesce() {
  HtmlString *str1, *str2;
  XmlFont *hfont1, *hfont2;
  double space, horSpace, vertSpace, vertOverlap;
  GBool addSpace, addLineBreak;
  int n, i;
  double curX, curY, lastX, lastY;
  int sSize = 0;      
  double diff = 0.0;
  double pxSize = 0.0;
  double strSize = 0.0;
  double cspace = 0.0;

#if 0 //~ for debugging
  for (str1 = yxStrings; str1; str1 = str1->yxNext) {
    printf("x=%f..%f  y=%f..%f  size=%2d '",
	   str1->xMin, str1->xMax, str1->yMin, str1->yMax,
	   (int)(str1->yMax - str1->yMin));
    for (i = 0; i < str1->len; ++i) {
      fputc(str1->text[i] & 0xff, stdout);
    }
    printf("'\n");
  }
  printf("\n------------------------------------------------------------\n\n");
#endif
    
    
  str1 = yxStrings;
  if( !str1 ) return;
  
  hfont1 = getFont(str1);

  str1->htext2->append(str1->htext);
  if( str1->getLink() != NULL ) {
    GString *ls = str1->getLink()->getLinkStart();
    str1->htext->insert(0, ls);
    delete ls;
  }

  curX = str1->xMin; curY = str1->yMin;
  lastX = str1->xMin; lastY = str1->yMin;

  while (str1 && (str2 = str1->yxNext)) {
    hfont2 = getFont(str2);
    space = str1->yMax - str1->yMin;
    horSpace = str2->xMin - str1->xMax;
    addLineBreak = !noMerge && (fabs(str1->xMin - str2->xMin) < 0.4);
    vertSpace = str2->yMin - str1->yMax;

    //printf("coalesce %d %d %f? ", str1->dir, str2->dir, d);

    if (str2->yMin >= str1->yMin && str2->yMin <= str1->yMax)
    {
        vertOverlap = str1->yMax - str2->yMin;
    } else
        if (str2->yMax >= str1->yMin && str2->yMax <= str1->yMax)
        {
            vertOverlap = str2->yMax - str1->yMin;
        } else
        {
            vertOverlap = 0;
        } 
    
    // str1->dir == str2->dir, in complex mode fonts must be the same, in other modes fonts do not metter
    if ((((
           (rawOrder && vertOverlap > 0.5 * space)
           ||
           (!rawOrder && str2->yMin < str1->yMax)
           ) &&
            (horSpace > -0.5 * space && horSpace < space)
           ) ||
            (vertSpace >= 0 && vertSpace < 0.5 * space && addLineBreak)
           ) 
        &&
        str1->dir == str2->dir // text direction the same
        && 
        !(str2->len == 1 && str2->htext->getCString()[0] == ' ')
        &&
        !(str1->htext->getCString()[str1->len-1] == ' ')
        &&
        !(str1->htext->getLength() >= str1->len+1 && str1->htext->getCString()[str1->len+1] == ' ')
       ) 
    {
        diff = str2->xMax - str1->xMin;

        n = str1->len + str2->len;

        
        if ((addSpace = horSpace > 0.1 * space)) {
            ++n;
        }
         
              
        str1->size = (n + 15) & ~15;
        str1->text = (Unicode *)grealloc(str1->text,
				       str1->size * sizeof(Unicode));   
        str1->xRight = (double *)grealloc(str1->xRight,
					str1->size * sizeof(double));
        
        if (addSpace) {
            str1->text[str1->len] = 0x20;
            str1->htext->append(" ");
            str1->htext2->append(" ");
            str1->xRight[str1->len] = str2->xMin;
            ++str1->len;
            ++str1->strSize;
            
            str1->xMin = curX; str1->yMin = curY; 
	        str1 = str2;
	        curX = str1->xMin; curY = str1->yMin;
	        hfont1 = hfont2;
	
	        if( str1->getLink() != NULL ) {
	            GString *ls = str1->getLink()->getLinkStart();
	            str1->htext->insert(0, ls);
	            delete ls;
	        }
        }else{
	        str1->htext2->append(str2->htext2);
	
	        XmlLink *hlink1 = str1->getLink();
	        XmlLink *hlink2 = str2->getLink();
	
	        for (i = 0; i < str2->len; ++i) {
	            str1->text[str1->len] = str2->text[i];
	            str1->xRight[str1->len] = str2->xRight[i];
	            ++str1->len;
	        }
	
	        if( !hlink1 || !hlink2 || !hlink1->isEqualDest(*hlink2) ) {
	            
	            if(hlink1 != NULL ){
	                //str1->htext->append("\"]");
	            }
	            if(hlink2 != NULL ) {
	                GString *ls = hlink2->getLinkStart();
	                str1->htext->append(ls);
	                delete ls;
	            }
	        }
	
	        str1->htext->append(str2->htext);
	        sSize = str1->htext2->getLength();      
	        pxSize = xoutRoundLower(hfont1->getSize()/scale);
	        strSize = (pxSize*(sSize-2));   
	        cspace = (diff / strSize);//(strSize-pxSize));
	        // we check if the fonts are the same and create a new font to ajust the text
	        //      double diff = str2->xMin - str1->xMin;
	        //      printf("%s\n",str1->htext2->getCString());
	        // str1 now contains href for link of str2 (if it is defined)
	        str1->link = str2->link; 
	
	        //XmlFont *newfnt = new XmlFont(*hfont1);
	        //newfnt->setCharSpace(cspace);
	        //newfnt->setLineSize(curLineSize);
	        //str1->fontpos = fonts->AddFont(*newfnt);
	        //delete newfnt;
	        hfont1 = getFont(str1);
	        // we have to reget hfont2 because it's location could have
	        // changed on resize  GStri;ng *iStr=GString::fromInt(i);
	        hfont2 = getFont(str2); 
	
	        hfont1 = hfont2;
	
	        if (str2->xMax > str1->xMax) {
	            str1->xMax = str2->xMax;
	        }
	        
	        if (str2->yMax > str1->yMax) {
	            str1->yMax = str2->yMax;
	        }
	
	        str1->yxNext = str2->yxNext;
	
	        delete str2;
        }
    } else { 

        //printf("startX = %f, endX = %f, diff = %f, fontsize = %d, pxSize = %f, stringSize = %d, cspace = %f, strSize = %f\n",str1->xMin,str1->xMax,diff,hfont1->getSize(),pxSize,sSize,cspace,strSize);

        // keep strings separate
        //      printf("no\n"); 
        //      if( hfont1->isBold() )
//        if(str1->getLink() != NULL )
  //          str1->htext->append("\"]");  
     
        str1->xMin = curX; str1->yMin = curY; 
        str1 = str2;
        curX = str1->xMin; curY = str1->yMin;
        hfont1 = hfont2;

        if( str1->getLink() != NULL ) {
            GString *ls = str1->getLink()->getLinkStart();
            str1->htext->insert(0, ls);
            delete ls;
        }
    }
  }
  str1->xMin = curX; str1->yMin = curY;

//  if(str1->getLink() != NULL )
  //  str1->htext->append("]");

#if 0 //~ for debugging
  for (str1 = yxStrings; str1; str1 = str1->yxNext) {
    printf("x=%3d..%3d  y=%3d..%3d  size=%2d ",
	   (int)str1->xMin, (int)str1->xMax, (int)str1->yMin, (int)str1->yMax,
	   (int)(str1->yMax - str1->yMin));
    printf("'%s'\n", str1->htext->getCString());  
  }
  printf("\n-end--------------------------------------------------------\n\n");
#endif

}


void HtmlPage::dumpAsXML(FILE* f,int page, GBool passedFirstPage, int totalPages){  
//    printf(this->numPages);
    printf("");
  if(textAsJSON){
    if(passedFirstPage){
	fprintf(f, ",");
    }
    fprintf(f, "{\"number\":%d,\"pages\":%d,\"height\":%d,\"width\":%d,", page,totalPages,pageHeight,pageWidth);
  }else{ 
    fprintf(f, "<page number=\"%d\" pages=\"%d\"", page,totalPages);
    fprintf(f," height=\"%d\" width=\"%d\">", pageHeight,pageWidth);
  }

  GBool passedFirst = false;

  // output fonts
  if(textAsJSON){fprintf(f,"\"fonts\":[");}
  for(int i=fontsPageMarker;i < fonts->size();i++) {
    GString *fontCSStyle = fonts->CSStyle(i,textAsJSON);
    if(textAsJSON && passedFirst) {fprintf(f,",");}
    fprintf(f,"%s",fontCSStyle->getCString());
    passedFirst = true;
    delete fontCSStyle;
  }
  if(textAsJSON){fprintf(f,"]");}
  
  GString *str, *str1;
  
  passedFirst = false;
  if(textAsJSON){
     fprintf(f,",\"text\":[");
      //fprintf(f,"\"text\":[");
  }

  for(HtmlString *tmp=yxStrings;tmp;tmp=tmp->yxNext){
    if (tmp->htext){
      str=new GString(tmp->htext);
      
      if(!compressData){
	      if(textAsJSON){      
              if(passedFirst){
                  fprintf(f,",");
              }
              fprintf(f,"{\"top\":%d,\"left\":%d,",xoutRound(tmp->yMin+this->movey),xoutRound(tmp->xMin+this->movex));	
              fprintf(f,"\"width\":%d,\"height\":%d,",xoutRound(tmp->xMax-tmp->xMin),xoutRound(tmp->yMax-tmp->yMin));
              fprintf(f,"\"font\":%d,\"data\":\"", tmp->fontpos);
              if (tmp->fontpos!=-1){
                  str1=fonts->getCSStyle(tmp->fontpos, str);
              }
              fputs(str1->getCString(),f);
              fprintf(f,"\"}");
              //fprintf(f,"\"}");
              passedFirst = true;
	      }else{
              fprintf(f,"<text top=\"%d\" left=\"%d\" ",xoutRound(tmp->yMin+this->movey),xoutRound(tmp->xMin+this->movey));
              fprintf(f,"width=\"%d\" height=\"%d\" ",xoutRound(tmp->xMax-tmp->xMin),xoutRound(tmp->yMax-tmp->yMin));
              fprintf(f,"font=\"%d\">", tmp->fontpos);
              if (tmp->fontpos!=-1){
                  str1=fonts->getCSStyle(tmp->fontpos, str);
              }
              fputs(str1->getCString(),f);
              delete str;
              delete str1;
              fputs("</text>\n",f);
	      }
      }else{
		if(textAsJSON){      
            if(passedFirst){
              fprintf(f,",");
            }
          
            //fprintf(f,"{\"t\":%d,\"l\":%d,",xoutRound(tmp->yMin),xoutRound(tmp->xMin));	
            //fprintf(f,"\"w\":%d,\"h\":%d,",xoutRound(tmp->xMax-tmp->xMin),xoutRound(tmp->yMax-tmp->yMin));
            //fprintf(f,"\"f\":%d,\"d\":\"", tmp->fontpos);
            fprintf(f,"[%d,%d,",xoutRound(tmp->yMin+this->movey),xoutRound(tmp->xMin+this->movex));	
            fprintf(f,"%d,%d,",xoutRound(tmp->xMax-tmp->xMin),xoutRound(tmp->yMax-tmp->yMin));
            fprintf(f,"%d,\"", tmp->fontpos);
		  
            if (tmp->fontpos!=-1){
                str1=fonts->getCSStyle(tmp->fontpos, str);
            }
            fputs(str1->getCString(),f);
            fprintf(f,"\"]");
            
            passedFirst = true;
        }else{
              fprintf(f,"<t t=\"%d\" l=\"%d\" ",xoutRound(tmp->yMin+this->movey),xoutRound(tmp->xMin+this->movex));
              fprintf(f,"w=\"%d\" h=\"%d\" ",xoutRound(tmp->xMax-tmp->xMin),xoutRound(tmp->yMax-tmp->yMin));
              fprintf(f,"f=\"%d\">", tmp->fontpos);
              if (tmp->fontpos!=-1){
                  str1=fonts->getCSStyle(tmp->fontpos, str);
              }
              fputs(str1->getCString(),f);
              delete str;
              delete str1;
              fputs("</t>\n",f);
	      }
      }
    }
  }

  if(textAsJSON){
  	fputs("]}",f);
  }else{
  	fputs("</page>",f);
  }
}

void HtmlPage::dump(FILE *f, int pageNum, GBool passedFirstPage, int totalPages) 
{
  if (complexMode)
  {
    if (xml) dumpAsXML(f, pageNum, passedFirstPage, totalPages);
  }
}

void HtmlPage::clear() {
  HtmlString *p1, *p2;

  if (curStr) {
    delete curStr;
    curStr = NULL;
  }
  for (p1 = yxStrings; p1; p1 = p2) {
    p2 = p1->yxNext;
    delete p1;
  }
  yxStrings = NULL;
  xyStrings = NULL;
  yxCur1 = yxCur2 = NULL;

  if( !noframes )
  {
      delete fonts;
      fonts=new XmlFontAccu();
      fontsPageMarker = 0;
  }
  else
  {
      fontsPageMarker = fonts->size();
  }

  delete links;
  links=new XmlLinks();
 

}

void HtmlPage::setDocName(char *fname){
  DocName=new GString(fname);
}

void HtmlPage::updateCharSpace(GfxState *state)
{
	charspace = state->getCharSpace();
}

//------------------------------------------------------------------------
// HtmlMetaVar
//------------------------------------------------------------------------

HtmlMetaVar::HtmlMetaVar(char *_name, char *_content)
{
    name = new GString(_name);
    content = new GString(_content);
}

HtmlMetaVar::~HtmlMetaVar()
{
   delete name;
   delete content;
} 
    
GString* HtmlMetaVar::toString()	
{
    GString *result = new GString("<META name=\"");
    result->append(name);
    result->append("\" content=\"");
    result->append(content);
    result->append("\">"); 
    return result;
}

//------------------------------------------------------------------------
// ImgOutputDev
//------------------------------------------------------------------------

static char* HtmlEncodings[][2] = {
    {"Latin1", "ISO-8859-1"},
    {NULL, NULL}
};


char* ImgOutputDev::mapEncodingToHtml(GString* encoding)
{
    char* enc = encoding->getCString();
    for(int i = 0; HtmlEncodings[i][0] != NULL; i++)
    {
	if( strcmp(enc, HtmlEncodings[i][0]) == 0 )
	{
	    return HtmlEncodings[i][1];
	}
    }
    return enc; 
}

ImgOutputDev::ImgOutputDev(char *fileName, char *title, 
	char *author, char *keywords, char *subject, char *date,
	char *extension,
	GBool rawOrder, GBool textAsJSON, GBool compressData, int split, int firstPage, GBool outline, int numPages) 
{
  char *htmlEncoding;
  this->numPages = numPages;
  fContentsFrame = NULL;
  docTitle = new GString(title);
  pages = NULL;
  dumpJPEG=gTrue;
  //write = gTrue;
  this->rawOrder = rawOrder;
  this->textAsJSON = textAsJSON;
  this->compressData = compressData;
  this->split = split;  
  this->doOutline = outline;
  ok = gFalse;
  passedFirstPage = gFalse;
  imgNum=1;
  //this->firstPage = firstPage;
  //pageNum=firstPage;
  // open file
  needClose = gFalse;
  pages = new HtmlPage(rawOrder, textAsJSON, compressData, extension);
  
  glMetaVars = new GList();
  glMetaVars->append(new HtmlMetaVar("generator", "pdf2json 0.69"));  
  if( author ) glMetaVars->append(new HtmlMetaVar("author", author));  
  if( keywords ) glMetaVars->append(new HtmlMetaVar("keywords", keywords));  
  if( date ) glMetaVars->append(new HtmlMetaVar("date", date));  
  if( subject ) glMetaVars->append(new HtmlMetaVar("subject", subject));
 
  maxPageWidth = 0;
  maxPageHeight = 0;

  pages->setDocName(fileName);
  Docname=new GString (fileName);

    
  if (noframes) {
    if (stout) page=stdout;
    else {
      GString* right=new GString(fileName);
      //if (xml && !textAsJSON) right->append(".xml");
      //else if (textAsJSON) right->append(".js");
      
      if(this->split>0 && this->hasValidSplitFileName()){
        this->setSplitFileName(this->split,false);
      }else if (!(page=fopen(right->getCString(),"w"))){
          delete right;
          error(-1, "Couldn't open html file '%s'", right->getCString());
          return;
      } 
        
      delete right;
    }

    htmlEncoding = mapEncodingToHtml(globalParams->getTextEncodingName()); 
    if (xml && !textAsJSON) 
    {
      fprintf(page, "<?xml version=\"1.0\" encoding=\"%s\"?>\n", htmlEncoding);
      fputs("<!DOCTYPE pdf2xml SYSTEM \"pdf2xml.dtd\">\n\n", page);
      fputs("<pdf2xml>\n",page);
    }else if(textAsJSON){
      fputs("[",page);
    } 
  }
  ok = gTrue; 
}

ImgOutputDev::~ImgOutputDev() {
    XmlFont::clear(); 
    
    delete Docname;
    delete docTitle;

    deleteGList(glMetaVars, HtmlMetaVar);

    if (xml && !textAsJSON) {
      fputs("</pdf2xml>\n",page);  
      fclose(page);
    } else if(textAsJSON){
      fputs("]",page);
      fclose(page);
    } 
    if (pages)
      delete pages;
}



void ImgOutputDev::startPage(int pageNum, GfxState *state,double crop_x1, double crop_y1, double crop_x2, double crop_y2) {
    double x1,y1,x2,y2;
    state->transform(crop_x1,crop_y1,&x1,&y1);
    state->transform(crop_x2,crop_y2,&x2,&y2);
    if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
    if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
    
    pages->movex = -(int)x1;
    pages->movey = -(int)y1; 
    
  this->pageNum = pageNum;
  GString *str=basename(Docname);
  pages->clear(); 
  if(!noframes)
  {
    if (fContentsFrame)
	{
      if (complexMode)
		fprintf(fContentsFrame,"<A href=\"%s-%d.html\"",str->getCString(),pageNum);
      else 
		fprintf(fContentsFrame,"<A href=\"%ss.html#%d\"",str->getCString(),pageNum);
      fprintf(fContentsFrame," target=\"contents\" >Page %d</a><br>\n",pageNum);
    }
  }

//  pages->pageWidth=static_cast<int>(state->getPageWidth());
  //pages->pageHeight=static_cast<int>(state->getPageHeight());
    pages->pageWidth = (int)(x2-x1);
    pages->pageHeight = (int)(y2-y1);
    
    
  delete str;
} 

GBool ImgOutputDev::hasValidSplitFileName() {
    for( int i = 0, j = 0; i < Docname->getLength(); i++, j++ ){
        if(Docname->getChar(i) == '\%'){
            return gTrue;
        }
    }
    
    return gFalse;
}

void ImgOutputDev::setSplitFileName(int pageNum, GBool closeprev) {
    GString* tmp = NULL;
    
    char *pn, ptext[32];
    
    for( int i = 0, j = 0; i < Docname->getLength(); i++, j++ ){
        const char *replace = NULL;
        switch ( Docname->getChar(i) ){
            case '\%': 
                sprintf(ptext,"%d",pageNum);
                pn = ptext;
                
                replace = pn;  
                break;
        }
        
        if( replace ){
            if( !tmp ) tmp = new GString( Docname );
            if( tmp ){
                tmp->del( j, 1 );
                int l = strlen( replace );
                tmp->insert( j, replace, l );
                j += l - 1;
            }
        }
        
    }
    
    if(closeprev)
        fclose(page);
    
    page=fopen(tmp->getCString(),"w");
}

void ImgOutputDev::endPage() {
  pages->conv();
  pages->coalesce();

  // reassign page if running split mode ever nth page  
  /*
   if(split>0){
   int p;
   for(p = 0; p < split; p++)
   {
   GString* tmp = NULL;
   
   char *pn, ptext[32];
   
   for( int i = 0, j = 0; i < Docname->getLength(); i++, j++ ){
   const char *replace = NULL;
   switch ( Docname->getChar(i) ){
   case '\%': 
   sprintf(ptext,"%d",p);
   pn = ptext;
   
   replace = pn;  
   break;
   }
   
   if( replace ){
   if( !tmp ) tmp = new GString( Docname );
   if( tmp ){
   tmp->del( j, 1 );
   int l = strlen( replace );
   tmp->insert( j, replace, l );
   j += l - 1;
   }
   }
   
   }
   
   printf("PROCESS: %s",tmp->getCString());
   }        
   }
   */
    
  //printf("doc: %s",Docname->getCString());
  //printf("split: %d", this->split);  
  
  // reassign page based on split if split is higher than 1
  if(this->split>0 && (pageNum % this->split) == 0 && this->hasValidSplitFileName()){
      if(textAsJSON){fputs("]",page);}else{fputs("</pdf2xml>\n",page);}
      this->passedFirstPage = gFalse;
      //this->setSplitFileName(pageNum + this->split,true);
      if(this->split>1) {
         this->setSplitFileName(pageNum + this->split,true);
      } else {
         this->setSplitFileName(pageNum,true);
      }
      if(textAsJSON){fputs("[",page);}else{fputs("<pdf2xml>\n",page);  }
      //printf("PROCESS: %s",tmp->getCString());
      //printf("mod %d",(this->split-(this->split/pageNum)*pageNum)); 
  }
    
  pages->dump(page, pageNum, passedFirstPage,(this->numPages));
  passedFirstPage = gTrue;
  // I don't yet know what to do in the case when there are pages of different
  // sizes and we want complex output: running ghostscript many times 
  // seems very inefficient. So for now I'll just use last page's size
  maxPageWidth = pages->pageWidth;
  maxPageHeight = pages->pageHeight;
  
  if(!stout && !globalParams->getErrQuiet()) printf("Page-%d\n",(pageNum));
}

void ImgOutputDev::updateFont(GfxState *state) {
  pages->updateFont(state);
}

void ImgOutputDev::beginString(GfxState *state, GString *s) {
  pages->beginString(state, s);
}

void ImgOutputDev::endString(GfxState *state) {
  pages->endString();
}

void ImgOutputDev::drawChar(GfxState *state, double x, double y,
	      double dx, double dy,
	      double originX, double originY,
	      CharCode code, int nBytes, Unicode *u, int uLen) 
{
  if ( !showHidden && (state->getRender() & 3) == 3) {
    return;
  }
//    printf("movex: %f",dx);
  pages->addChar(state, x, y, dx, dy, originX, originY, u, uLen);
}

void ImgOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
			      int width, int height, GBool invert,
			      GBool inlineImg) {

  int i, j;

  if (ignore||complexMode) {
    OutputDev::drawImageMask(state, ref, str, width, height, invert, inlineImg);
    return;
  }
  
  FILE *f1;
  int c;
  
  int x0, y0;			// top left corner of image
  int w0, h0, w1, h1;		// size of image
  double xt, yt, wt, ht;
  GBool rotate, xFlip, yFlip;
  GBool dither;
  int x, y;
  int ix, iy;
  int px1, px2, qx, dx;
  int py1, py2, qy, dy;
  Gulong pixel;
  int nComps, nVals, nBits;
  double r1, g1, b1;
 
  // get image position and size
  state->transform(0, 0, &xt, &yt);
  state->transformDelta(1, 1, &wt, &ht);
  if (wt > 0) {
    x0 = xoutRound(xt);
    w0 = xoutRound(wt);
  } else {
    x0 = xoutRound(xt + wt);
    w0 = xoutRound(-wt);
  }
  if (ht > 0) {
    y0 = xoutRound(yt);
    h0 = xoutRound(ht);
  } else {
    y0 = xoutRound(yt + ht);
    h0 = xoutRound(-ht);
  }
  state->transformDelta(1, 0, &xt, &yt);
  rotate = fabs(xt) < fabs(yt);
  if (rotate) {
    w1 = h0;
    h1 = w0;
    xFlip = ht < 0;
    yFlip = wt > 0;
  } else {
    w1 = w0;
    h1 = h0;
    xFlip = wt < 0;
    yFlip = ht > 0;
  }

  // dump JPEG file
  if (dumpJPEG  && str->getKind() == strDCT) {
    GString *fName=new GString(Docname);
    fName->append("-");
    GString *pgNum=GString::fromInt(pageNum);
    GString *imgnum=GString::fromInt(imgNum);
    // open the image file
    fName->append(pgNum)->append("_")->append(imgnum)->append(".jpg");
    ++imgNum;
    if (!(f1 = fopen(fName->getCString(), "wb"))) {
      error(-1, "Couldn't open image file '%s'", fName->getCString());
      return;
    }

    // initialize stream
    str = ((DCTStream *)str)->getRawStream();
    str->reset();

    // copy the stream
    while ((c = str->getChar()) != EOF)
      fputc(c, f1);

    fclose(f1);
   
  if (pgNum) delete pgNum;
  if (imgnum) delete imgnum;
  if (fName) delete fName;
  }
  else {
    OutputDev::drawImageMask(state, ref, str, width, height, invert, inlineImg);
  }
}

void ImgOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
			  int width, int height, GfxImageColorMap *colorMap,
			  int *maskColors, GBool inlineImg) {

  int i, j;

  if (ignore||complexMode) {
    OutputDev::drawImage(state, ref, str, width, height, colorMap, 
			 maskColors, inlineImg);
    return;
  }

  FILE *f1;
  ImageStream *imgStr;
  Guchar pixBuf[4];
  GfxColor color;
  int c;
  
  int x0, y0;			// top left corner of image
  int w0, h0, w1, h1;		// size of image
  double xt, yt, wt, ht;
  GBool rotate, xFlip, yFlip;
  GBool dither;
  int x, y;
  int ix, iy;
  int px1, px2, qx, dx;
  int py1, py2, qy, dy;
  Gulong pixel;
  int nComps, nVals, nBits;
  double r1, g1, b1;
 
  // get image position and size
  state->transform(0, 0, &xt, &yt);
  state->transformDelta(1, 1, &wt, &ht);
  if (wt > 0) {
    x0 = xoutRound(xt);
    w0 = xoutRound(wt);
  } else {
    x0 = xoutRound(xt + wt);
    w0 = xoutRound(-wt);
  }
  if (ht > 0) {
    y0 = xoutRound(yt);
    h0 = xoutRound(ht);
  } else {
    y0 = xoutRound(yt + ht);
    h0 = xoutRound(-ht);
  }
  state->transformDelta(1, 0, &xt, &yt);
  rotate = fabs(xt) < fabs(yt);
  if (rotate) {
    w1 = h0;
    h1 = w0;
    xFlip = ht < 0;
    yFlip = wt > 0;
  } else {
    w1 = w0;
    h1 = h0;
    xFlip = wt < 0;
    yFlip = ht > 0;
  }

  /*if( !globalParams->getErrQuiet() )
    printf("image stream of kind %d\n", str->getKind());*/
  // dump JPEG file
  if (dumpJPEG && str->getKind() == strDCT) {
    GString *fName=new GString(Docname);
    fName->append("-");
    GString *pgNum= GString::fromInt(pageNum);
    GString *imgnum= GString::fromInt(imgNum);  
    
    // open the image file
    fName->append(pgNum)->append("_")->append(imgnum)->append(".jpg");
    ++imgNum;
    
    if (!(f1 = fopen(fName->getCString(), "wb"))) {
      error(-1, "Couldn't open image file '%s'", fName->getCString());
      return;
    }

    // initialize stream
    str = ((DCTStream *)str)->getRawStream();
    str->reset();

    // copy the stream
    while ((c = str->getChar()) != EOF)
      fputc(c, f1);
    
    fclose(f1);
  
    delete fName;
    delete pgNum;
    delete imgnum;
  }
  else {
    OutputDev::drawImage(state, ref, str, width, height, colorMap,
			 maskColors, inlineImg);
  }
}

void ImgOutputDev::drawLink(Link* link,Catalog *cat){
  double _x1,_y1,_x2,_y2,w;
  int x1,y1,x2,y2;
  
  link->getBorder(&_x1,&_y1,&_x2,&_y2,&w);
  cvtUserToDev(_x1,_y1,&x1,&y1);
  
  cvtUserToDev(_x2,_y2,&x2,&y2); 


  GString* _dest=getLinkDest(link,cat);
  XmlLink t((double) x1,(double) y2,(double) x2,(double) y1,_dest);
  pages->AddLink(t);
  delete _dest;
}

GString* ImgOutputDev::getLinkDest(Link *link,Catalog* catalog){
  char *p;
  switch(link->getAction()->getKind()) 
  {
      case actionGoTo:
	  { 
          GString* file=new GString("actionGoTo:");
          int page=1;
          LinkGoTo *ha=(LinkGoTo *)link->getAction();
          LinkDest *dest=NULL;
          if (ha->getDest()==NULL) 
              dest=catalog->findDest(ha->getNamedDest());
          else if (ha->getNamedDest()!=NULL)
          	  dest=catalog->findDest(ha->getNamedDest());
          else
	      	  dest=ha->getDest()->copy();
	      
          if (dest){ 
              if (dest->isPageRef()){
                  Ref pageref=dest->getPageRef();
                  page=catalog->findPage(pageref.num,pageref.gen);
              }
	      else {
              page=dest->getPageNum();
	      }

	      delete dest;

	      GString *str=GString::fromInt(page);
          file->append(str);
          file->append(",");
              
          if (printCommands) printf(" link to page %d ",page);
              delete str;
              return file;
          }
          else 
          {
              return new GString();
          }
	  }
      case actionGoToR:
	  {
          LinkGoToR *ha=(LinkGoToR *) link->getAction();
          LinkDest *dest=NULL;
          int page=1;
          GString *file=new GString("actionGoToR:");
          
          if (ha->getDest()!=NULL)  dest=ha->getDest()->copy();
          
          if (dest&&file){
              if (!(dest->isPageRef()))  page=dest->getPageNum();
              delete dest;

              if (printCommands) printf(" link to page %d ",page);
              if (printHtml){
                  p=file->getCString()+file->getLength()-4;
                  file->append(GString::fromInt(page));
                  file->append(",");
              }
          }
          if (printCommands) printf("filename %s\n",file->getCString());
            return file;
      }
      case actionURI:
	  { 
          LinkURI *ha=(LinkURI *) link->getAction();
          //GString* file=new GString(ha->getURI()->getCString());
          GString *file=new GString("actionURI");
          file->append("(");file->append(ha->getURI()->getCString());file->append("):");
          //file->append(ha->getURI()->getCString());
          // printf("uri : %s\n",file->getCString());
          return file;
	  }
      case actionLaunch:
	  {
          LinkLaunch *ha=(LinkLaunch *) link->getAction();
          GString* file=new GString(ha->getFileName()->getCString());
          if (printHtml) { 
              p=file->getCString()+file->getLength()-4;
              if (!strcmp(p, ".pdf") || !strcmp(p, ".PDF")){
                  file->del(file->getLength()-4,4);
                  file->append(".html");
              }
              if (printCommands) printf("filename %s",file->getCString());
    
              return file;     
          }
	  }
      default:
	  return new GString();
  }
}

void ImgOutputDev::dumpMetaVars(FILE *file)
{
  GString *var;

  for(int i = 0; i < glMetaVars->getLength(); i++)
  {
     HtmlMetaVar *t = (HtmlMetaVar*)glMetaVars->get(i); 
     var = t->toString(); 
     fprintf(file, "%s\n", var->getCString());
     delete var;
  }
}

GBool ImgOutputDev::dumpDocOutline(Catalog* catalog)
{ 
	FILE * output;
	GBool bClose = gFalse;

	if (!ok || xml)
    	return gFalse;
}

void ImgOutputDev::updateCharSpace(GfxState *state)
{
	pages->updateCharSpace(state);
}
//========================================================================
//
// ImgOutputDev.h
//
// Copyright 1997 Derek B. Noonburg
//
// Changed 1999 by G.Ovtcharov
//========================================================================

#ifndef HTMLOUTPUTDEV_H
#define HTMLOUTPUTDEV_H

#ifdef __GNUC__
#pragma interface
#endif

#include <stdio.h>
#include "gtypes.h"
#include "GList.h"
#include "GfxFont.h"
#include "OutputDev.h"
#include "XmlLinks.h"
#include "XmlFonts.h"
#include "Link.h"
#include "Catalog.h"
#include "UnicodeMap.h"


#ifdef WIN32
#  define SLASH '\\'
#else
#  define SLASH '/'
#endif

#define xoutRound(x) ((int)(x + 0.5))
#define xoutRoundLower(x) ((int)(x - 0.5))

#define DOCTYPE "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">"
#define DOCTYPE_FRAMES "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Frameset//EN\"\n\"http://www.w3.org/TR/html4/frameset.dtd\">"

class GfxState;
class GString;
//------------------------------------------------------------------------
// HtmlString
//------------------------------------------------------------------------



class HtmlString {
public:

  // Constructor.
  HtmlString(GfxState *state, double fontSize, double charspace, XmlFontAccu* fonts);

  // Destructor.
  ~HtmlString();

  // Add a character to the string.
  void addChar(GfxState *state, double x, double y,
	       double dx, double dy,
	       Unicode u); 
  XmlLink* getLink() { return link; }
  void endString(); // postprocessing

private:
// aender die text variable
  XmlLink *link;
  double xMin, xMax;		// bounding box x coordinates
  double yMin, yMax;		// bounding box y coordinates
  int col;			// starting column
  Unicode *text;		// the text
  double *xRight;		// right-hand x coord of each char
  HtmlString *yxNext;		// next string in y-major order
  HtmlString *xyNext;		// next string in x-major order
  int fontpos;
  GString* htext;
  GString* htext2;
  int strSize;
  int len;			// length of text and xRight
  int size;			// size of text and xRight arrays
  UnicodeTextDirection dir;	// direction (left to right/right to left)
  
  friend class HtmlPage;

};


//------------------------------------------------------------------------
// HtmlPage
//------------------------------------------------------------------------
class HtmlPage {
public:

  // Constructor.
  HtmlPage(GBool rawOrder, GBool textAsJSON, GBool compressData, char *imgExtVal);

  // Destructor.
  ~HtmlPage();

  // Begin a new string.
  void beginString(GfxState *state, GString *s);

  // Add a character to the current string.
  void addChar(GfxState *state, double x, double y,
	       double dx, double dy, 
		double ox, double oy, 
		Unicode *u, int uLen); //Guchar c);

  void updateFont(GfxState *state);
  void updateCharSpace(GfxState *state);

  // End the current string, sorting it into the list of strings.
  void endString();

  // Coalesce strings that look like parts of the same line.
  void coalesce();

  // Find a string.  If <top> is true, starts looking at top of page;
  // otherwise starts looking at <xMin>,<yMin>.  If <bottom> is true,
  // stops looking at bottom of page; otherwise stops looking at
  // <xMax>,<yMax>.  If found, sets the text bounding rectange and
  // returns true; otherwise returns false.
  

  // new functions
  void AddLink(const XmlLink& x){
    links->AddLink(x);
  }

 void dump(FILE *f, int pageNum, GBool passedFirstPage, int totalPages);

  // Clear the page.
  void clear();
  
  void conv();
private:
  XmlFont* getFont(HtmlString *hStr) { return fonts->Get(hStr->fontpos); }

  double fontSize;		// current font size
  GBool rawOrder;		// keep strings in content stream order
  GBool textAsJSON;		// print text data in JSON format
  GBool compressData;		// compress data to a minimum format
  double charspace;
  HtmlString *curStr;		// currently active string

  HtmlString *yxStrings;	// strings in y-major order
  HtmlString *xyStrings;	// strings in x-major order
  HtmlString *yxCur1, *yxCur2;	// cursors for yxStrings list
  
  void setDocName(char* fname);
  void dumpAsXML(FILE* f,int page, GBool passedFirstPage, int totalPages);
  void dumpComplex(FILE* f, int page);

  // marks the position of the fonts that belong to current page (for noframes)
  int fontsPageMarker; 
  XmlFontAccu *fonts;
  XmlLinks *links; 
  
  GString *DocName;
  GString *imgExt;
  int pageWidth;
  int pageHeight;
  int X1;
  int X2;    
  int Y1;
  int Y2;
  int movex;
  int movey;  
  static int pgNum;
  int firstPage;                // used to begin the numeration of pages

  friend class ImgOutputDev;
};

//------------------------------------------------------------------------
// HtmlMetaVar
//------------------------------------------------------------------------
class HtmlMetaVar {
public:
    HtmlMetaVar(char *_name, char *_content);
    ~HtmlMetaVar();    
    
    GString* toString();	

private:

    GString *name;
    GString *content;
};

//------------------------------------------------------------------------
// ImgOutputDev
//------------------------------------------------------------------------

class ImgOutputDev: public OutputDev {
public:

  // Open a text output file.  If <fileName> is NULL, no file is written
  // (this is useful, e.g., for searching text).  If <useASCII7> is true,
  // text is converted to 7-bit ASCII; otherwise, text is converted to
  // 8-bit ISO Latin-1.  <useASCII7> should also be set for Japanese
  // (EUC-JP) text.  If <rawOrder> is true, the text is kept in content
  // stream order.
  ImgOutputDev(char *fileName, char *title, 
	  char *author,
	  char *keywords,
	  char *subject,
	  char *date,
	  char *extension,
	  GBool rawOrder,
	  GBool textAsJSON,
	  GBool compressData,
      int split = -1,
	  int firstPage = 1,
	  GBool outline = 0,
	  int numPages = 0);

  // Destructor.
  virtual ~ImgOutputDev();

  // Check if file was successfully created.
  virtual GBool isOk() { return ok; }

  //---- get info about output device

  // Does this device use upside-down coordinates?
  // (Upside-down means (0,0) is the top left corner of the page.)
  virtual GBool upsideDown() { return gTrue; }

  // Does this device use drawChar() or drawString()?
  virtual GBool useDrawChar() { return gTrue; }

  // Does this device use beginType3Char/endType3Char?  Otherwise,
  // text in Type 3 fonts will be drawn with drawChar/drawString.
  virtual GBool interpretType3Chars() { return gFalse; }

  // Does this device need non-text content?
  virtual GBool needNonText() { return gFalse; }

  //----- initialization and control

  // Start a page.
  virtual void startPage(int pageNum, GfxState *state, double x1,double y1,double x2,double y2);

  // End a page.
  virtual void endPage();

  virtual GBool hasValidSplitFileName();      
    
  virtual void setSplitFileName(int pageNum, GBool closeprev);  

  //----- update text state
  virtual void updateFont(GfxState *state);
  virtual void updateCharSpace(GfxState *state);

  //----- text drawing
  virtual void beginString(GfxState *state, GString *s);
  virtual void endString(GfxState *state);
  virtual void drawChar(GfxState *state, double x, double y,
			double dx, double dy,
			double originX, double originY,
			CharCode code,int nBytes, Unicode *u, int uLen);
  
  virtual void drawImageMask(GfxState *state, Object *ref, 
			     Stream *str,
			     int width, int height, GBool invert,
			     GBool inlineImg);
  virtual void drawImage(GfxState *state, Object *ref, Stream *str,
			  int width, int height, GfxImageColorMap *colorMap,
			 int *maskColors, GBool inlineImg);

  //new feature    
  virtual int DevType() {return 1234;}
  virtual void drawLink(Link *link,Catalog *cat); 

  int getPageWidth() { return maxPageWidth; }
  int getPageHeight() { return maxPageHeight; }

  GBool dumpDocOutline(Catalog* catalog);

private:
  // convert encoding into a HTML standard, or encoding->getCString if not
  // recognized
  static char* mapEncodingToHtml(GString* encoding);
  GString* getLinkDest(Link *link,Catalog *catalog);
  void dumpMetaVars(FILE *);
  void doFrame(int firstPage);
  GBool newOutlineLevel(FILE *output, Object *node, Catalog* catalog, int level = 1);

  FILE *fContentsFrame;
  FILE *page;                   // html file
  //FILE *tin;                    // image log file
  //GBool write;
  GBool needClose;		// need to close the file?
  HtmlPage *pages;		// text for the current page
  GBool rawOrder;		// keep text in content stream order
  GBool textAsJSON;		// print text data in JSON format
  GBool compressData;		// use compressed data mode
  GBool doOutline;		// output document outline
  GBool ok;			// set up ok?
  GBool dumpJPEG;
  GBool passedFirstPage;
  int pageNum;
  int maxPageWidth;
  int maxPageHeight;
  static int imgNum;
  GString *Docname;
  GString *docTitle;
  GList *glMetaVars;
  int split;                // split output every n    
  int numPages;
  friend class HtmlPage;
};

#endif

SHELL = /bin/sh

SRCDIR = .
XPDFSRCDIR = ../xpdf
XPDFLIBDIR = ../xpdf
GOOSRCDIR = ../goo
GOOLIBDIR = ../goo
FOFISRCDIR = ../fofi
FOFILIBDIR = ../fofi
SPLASHSRCDIR = ../splash
SPLASHLIBDIR = ../splash

CXXFLAGS = -I/usr/local/include -g -O2 -DHAVE_CONFIG_H -DHAVE_DIRENT_H=1  -I.. -DHAVE_REWINDDIR=1 -DHAVE_POPEN=1 -I.. -I$(GOOSRCDIR) -I$(XPDFSRCDIR) -I$(FOFISRCDIR) -I$(SPLASHSRCDIR) -I$(srcdir)           -I/usr/X11R6/include

LDFLAGS = 
FTLIBS = 

OTHERLIBS = 

CXX ?= c++

LIBPREFIX = lib
EXE = 

#------------------------------------------------------------------------

.SUFFIXES: .cc

.cc.o:
	$(CXX) $(CXXFLAGS) -c $<

#------------------------------------------------------------------------

CXX_SRC = \
	$(SRCDIR)/pdf2json.cc \
	$(SRCDIR)/ImgOutputDev.cc \
	$(SRCDIR)/XmlFonts.cc \
	$(SRCDIR)/XmlLinks.cc 

#------------------------------------------------------------------------

all: pdf2json$(EXE)

#-------------------------------------------------------------------------

PDF2JSON_OBJS = ImgOutputDev.o XmlFonts.o XmlLinks.o \
    pdf2json.o
PDF2JSON_LIBS = -L$(GOOLIBDIR) -L$(FOFILIBDIR) -L$(SPLASHLIBDIR) $(FTLIBS) -L$(XPDFLIBDIR) $(OTHERLIBS) -lXpdf -lGoo -lfofi -lsplash -lm

pdf2json$(EXE): $(PDF2JSON_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
	$(CXX) $(CXXFLAGS) $(LDFLAGS) -o pdf2json$(EXE) $(PDF2JSON_OBJS) \
		$(PDF2JSON_LIBS)

#-------------------------------------------------------------------------
PDF2JSON_WINOBJS = pdf2json.exe ImgOutPutDev.obj  pdf2json.obj  XmlFonts.obj  XmlLinks.obj

clean:
	rm -f $(PDF2JSON_OBJS) pdf2json$(EXE)
	rm -f $(PDF2JSON_WINOBJS)

#------------------------------------------------------------------------

distdepend:
	cp Makefile.in Makefile.in.bak
	sed '/^#----- dependences -----/q' Makefile.in.bak >Makefile.in
	$(CXX) $(CXXFLAGS) -MM $(CXX_SRC) >>Makefile.in

#include "XmlFonts.h"
#include "GlobalParams.h"
#include "UnicodeMap.h"
#include <stdio.h>

#ifndef _WIN32
#include <unistd.h>
#endif

 struct Fonts{
    char *Fontname;
    char *name;
  };

const int font_num=13;

static Fonts fonts[font_num+1]={  
     {"Courier",               "Courier" },
     {"Courier-Bold",           "Courier"},
     {"Courier-BoldOblique",    "Courier"},
     {"Courier-Oblique",        "Courier"},
     {"Helvetica",              "Helvetica"},
     {"Helvetica-Bold",         "Helvetica"},
     {"Helvetica-BoldOblique",  "Helvetica"},
     {"Helvetica-Oblique",      "Helvetica"},
     {"Symbol",                 "Symbol"   },
     {"Times-Bold",             "Times"    },
     {"Times-BoldItalic",       "Times"    },
     {"Times-Italic",           "Times"    },
     {"Times-Roman",            "Times"    },
     {" "          ,            "Times"    },
};

#define xoutRound(x) ((int)(x + 0.5))
extern GBool xml;

GString* XmlFont::DefaultFont=new GString("Times"); // Arial,Helvetica,sans-serif

XmlFontColor::XmlFontColor(GfxRGB rgb){
 /*
  r=static_cast<int>(255*rgb.r);
  g=static_cast<int>(255*rgb.g);
  b=static_cast<int>(255*rgb.b);
*/
  r=colToByte(rgb.r);
  g=colToByte(rgb.g);
  b=colToByte(rgb.b);
  if (!(Ok(r)&&Ok(b)&&Ok(g))) {printf("Error : Bad color \n");r=0;g=0;b=0;}

}

GString *XmlFontColor::convtoX(unsigned int xcol) const{
  GString *xret=new GString();
  char tmp;
  unsigned  int k;
  k = (xcol/16);
  if ((k>=0)&&(k<10)) tmp=(char) ('0'+k); else tmp=(char)('a'+k-10);
  xret->append(tmp);
  k = (xcol%16);
  if ((k>=0)&&(k<10)) tmp=(char) ('0'+k); else tmp=(char)('a'+k-10);
  xret->append(tmp);
 return xret;
}

GString *XmlFontColor::toString() const{
  GString *tmp=new GString("#");
  GString *tmpr=convtoX(r); 
  GString *tmpg=convtoX(g);
  GString *tmpb=convtoX(b);
  tmp->append(tmpr);
  tmp->append(tmpg);
  tmp->append(tmpb);
  delete tmpr;
  delete tmpg;
  delete tmpb;
  return tmp;
} 

XmlFont::XmlFont(GString* ftname,int _size, double _charspace, GfxRGB rgb){
  //if (col) color=XmlFontColor(col); 
  //else color=XmlFontColor();
  color=XmlFontColor(rgb);

  GString *fontname = NULL;
  charspace = _charspace;

  if( ftname ){
    fontname = new GString(ftname);
    FontName=new GString(ftname);
  }
  else {
    fontname = NULL;
    FontName = NULL;
  }
  
  lineSize = -1;

  size=(_size-1);
  italic = gFalse;
  bold = gFalse;
  oblique = gFalse;

  if (fontname){
    if (strstr(fontname->lowerCase()->getCString(),"bold"))  bold=gTrue;    
    if (strstr(fontname->lowerCase()->getCString(),"italic")) italic=gTrue;
    if (strstr(fontname->lowerCase()->getCString(),"oblique")) oblique=gTrue;
    /*||strstr(fontname->lowerCase()->getCString(),"oblique"))  italic=gTrue;*/ 
    
    int i=0;
    while (strcmp(ftname->getCString(),fonts[i].Fontname)&&(i<font_num)) 
	{
		i++;
	}
    pos=i;
    delete fontname;
  }  
  if (!DefaultFont) DefaultFont=new GString(fonts[font_num].name);

}
 
XmlFont::XmlFont(const XmlFont& x){
   size=x.size;
   lineSize=x.lineSize;
   italic=x.italic;
   oblique=x.oblique;
   bold=x.bold;
   pos=x.pos;
   charspace=x.charspace;
   color=x.color;
   if (x.FontName) FontName=new GString(x.FontName);
 }


XmlFont::~XmlFont(){
  if (FontName) delete FontName;
}

XmlFont& XmlFont::operator=(const XmlFont& x){
   if (this==&x) return *this; 
   size=x.size;
   lineSize=x.lineSize;
   italic=x.italic;
   oblique=x.oblique;
   bold=x.bold;
   pos=x.pos;
   color=x.color;
   charspace=x.charspace;
   if (FontName) delete FontName;
   if (x.FontName) FontName=new GString(x.FontName);
   return *this;
}

void XmlFont::clear(){
  if(DefaultFont) delete DefaultFont;
  DefaultFont = NULL;
}



/*
  This function is used to compare font uniquily for insertion into
  the list of all encountered fonts
*/
GBool XmlFont::isEqual(const XmlFont& x) const{
  return ((size==x.size) && (lineSize==x.lineSize) && (charspace==x.charspace) &&
	  (pos==x.pos) && (bold==x.bold) && (oblique==x.oblique) && (italic==x.italic) &&
	  (color.isEqual(x.getColor())));
}

/*
  This one is used to decide whether two pieces of text can be joined together
  and therefore we don't care about bold/italics properties
*/
GBool XmlFont::isEqualIgnoreBold(const XmlFont& x) const{
  return ((size==x.size) &&
	  (color.isEqual(x.getColor())));
}

GString* XmlFont::getFontName(){
   if (pos!=font_num) return new GString(fonts[pos].name);
    else return new GString(DefaultFont);
//    return new GString(FontName);
}

GString* XmlFont::getFullName(){
  if (FontName)
    return new GString(FontName);
  else return new GString(DefaultFont);
} 

void XmlFont::setDefaultFont(GString* defaultFont){
  if (DefaultFont) delete DefaultFont;
  DefaultFont=new GString(defaultFont);
}


GString* XmlFont::getDefaultFont(){
  return DefaultFont;
}

// this method if plain wrong todo
GString* XmlFont::HtmlFilter(Unicode* u, int uLen) {
  GString *tmp = new GString();
  UnicodeMap *uMap;
  char buf[8];
  int n;

  // get the output encoding
  if (!(uMap = globalParams->getTextEncoding())) {
    return tmp;
  }

  for (int i = 0; i < uLen; ++i) {
    switch (u[i])
      { 
	case '"': tmp->append("&quot;");  break;
	case '&': tmp->append("&amp;");  break;
	case '<': tmp->append("&lt;");  break;
	case '>': tmp->append("&gt;");  break;
	default:  
	  {
	    // convert unicode to string
	    if ((n = uMap->mapUnicode(u[i], buf, sizeof(buf))) > 0) {
	      tmp->append(buf, n); 
	  }
      }
    }
  }

  uMap->decRefCnt();
  return tmp;
}

GString* XmlFont::simple(XmlFont* font, Unicode* content, int uLen){
  GString *cont=HtmlFilter (content, uLen); 

  /*if (font.isBold()) {
    cont->insert(0,"<b>",3);
    cont->append("</b>",4);
  }
  if (font.isItalic()) {
    cont->insert(0,"<i>",3);
    cont->append("</i>",4);x.oblique
    } */

  return cont;
}

XmlFontAccu::XmlFontAccu(){
  accu=new GVector<XmlFont>();
}

XmlFontAccu::~XmlFontAccu(){
  if (accu) delete accu;
}

int XmlFontAccu::AddFont(const XmlFont& font){
 GVector<XmlFont>::iterator i; 
 for (i=accu->begin();i!=accu->end();i++)
 {
	if (font.isEqual(*i)) 
	{
		return (int)(i-(accu->begin()));
	}
 }

 accu->push_back(font);
 return (accu->size()-1);
}

static GString* EscapeSpecialChars( GString* s)
{
    GString* tmp = NULL;
    for( int i = 0, j = 0; i < s->getLength(); i++, j++ ){
        const char *replace = NULL;
            switch ( s->getChar(i) ){
                case '"': replace = "\\u0022";  break;
                case '\\': replace = "\\u005C";  break;
                case '<': replace = "\\u003C";  break;
                case '>': replace = "\\u003E";  break;
                case '\'': replace = "\\u0027";  break;
                case '&': replace = "\\u0026";  break;
                case 0x01: replace = "";  break;
                case 0x02: replace = "";  break;                
                case 0x03: replace = "";  break;                
                case 0x04: replace = "";  break;                
                case 0x05: replace = "";  break;                
                case 0x06: replace = "";  break;                                
                case 0x07: replace = "";  break;
                case 0x08: replace = "";  break;                
                case 0x09: replace = "";  break;                
                case 0x0a: replace = "";  break;                
                case 0x0b: replace = "";  break;                
                case 0x0c: replace = "";  break;                
                case 0x0d: replace = "";  break;                
                case 0x0e: replace = "";  break;                
                case 0x0f: replace = "";  break;                
                case 0x10: replace = "";  break;                                
                case 0x11: replace = "";  break;                                                
                case 0x12: replace = "";  break;                                                
                case 0x13: replace = "";  break;                                                
                case 0x14: replace = "";  break;                                                
                case 0x15: replace = "";  break;                                                
                case 0x16: replace = "";  break;                                                
                case 0x17: replace = "";  break;                                                
                case 0x18: replace = "";  break;                                                
                case 0x19: replace = "";  break;  
                case 0x1a: replace = "";  break;                                                                
                case 0x1b: replace = "";  break;                  
                case 0x1c: replace = "";  break;                  
                case 0x1d: replace = "";  break;                  
                case 0x1e: replace = "";  break;                  
                case 0x1f: replace = "";  break;                  
                default: continue;
            }    
	    if( replace ){
	        if( !tmp ) tmp = new GString( s );
	        if( tmp ){
	            tmp->del( j, 1 );
	            int l = strlen( replace );
	            tmp->insert( j, replace, l );
	            j += l - 1;
	        }
	    }
	}
	return tmp ? tmp : s;
}

// get CSS font name for font #i 
GString* XmlFontAccu::getCSStyle(int i, GString* content){
  GString *tmp;
  GString *iStr=GString::fromInt(i);
  
  if (!xml) {
    tmp = new GString("<span class=\"ft");
    tmp->append(iStr);
    tmp->append("\">");
    tmp->append(content);
    tmp->append("</span>");
  } else {
    tmp = EscapeSpecialChars(content);
    //tmp->append(content);
  } 

  delete iStr;
  return tmp;
}


// get CSS font definition for font #i 
GString* XmlFontAccu::CSStyle(int i,GBool textAsJSON){
   GString *tmp=new GString();
   GString *iStr=GString::fromInt(i);

   GVector<XmlFont>::iterator g=accu->begin();
   g+=i;
   XmlFont font=*g;
   GString *Size=GString::fromInt(font.getSize());
   GString *colorStr=font.getColor().toString();
   GString *fontName=font.getFontName();
   GString *lSize;
   double _charspace = font.getCharSpace();
   char *cspace = new char [20];
   sprintf(cspace,"%0.05f",_charspace);
   
   if (xml && !textAsJSON) {
     tmp->append("<fontspec id=\"");
     tmp->append(iStr);
     tmp->append("\" size=\"");
     tmp->append(Size);
     tmp->append("\" family=\"");
     tmp->append(fontName); //font.getFontName());
     tmp->append("\" color=\"");
     tmp->append(colorStr);
     tmp->append("\"/>");
   }else if(textAsJSON){
     tmp->append("{\"fontspec\":\"");
     tmp->append(iStr);
     tmp->append("\",\"size\":\"");
     tmp->append(Size);
     tmp->append("\",\"family\":\"");
     tmp->append(fontName); //font.getFontName());
     tmp->append("\",\"color\":\"");
     tmp->append(colorStr);
     tmp->append("\"}");
   }

   delete fontName;
   delete colorStr;
   delete iStr;
   delete Size;
   delete cspace;
   return tmp;
}
 

#ifndef _XML_FONTS_H
#define _XML_FONTS_H
#include "GVector.h"
#include "GString.h"
#include "GfxState.h"
#include "CharTypes.h"


class XmlFontColor{
 private:
   unsigned int r;
   unsigned int g;
   unsigned int b;
   GBool Ok(unsigned int xcol){ return ((xcol<=255)&&(xcol>=0));}
   GString *convtoX(unsigned  int xcol) const;
 public:
   XmlFontColor():r(0),g(0),b(0){}
   XmlFontColor(GfxRGB rgb);
   XmlFontColor(const XmlFontColor& x){r=x.r;g=x.g;b=x.b;}
   XmlFontColor& operator=(const XmlFontColor &x){
     r=x.r;g=x.g;b=x.b;
     return *this;
   }
   ~XmlFontColor(){};
   GString* toString() const;
   GBool isEqual(const XmlFontColor& col) const{
     return ((r==col.r)&&(g==col.g)&&(b==col.b));
   }
} ;  


class XmlFont{
 private:
   unsigned int size;
   double charspace;
   int lineSize;
   GBool italic;
   GBool bold;
   GBool oblique;
   int pos; // position of the font name in the fonts array
   static GString *DefaultFont;
   GString *FontName;
   XmlFontColor color;
   static GString* HtmlFilter(Unicode* u, int uLen); //char* s);
public:  

   XmlFont(){FontName=NULL;};
   XmlFont(GString* fontname,int _size,double _charspace, GfxRGB rgb);
   XmlFont(const XmlFont& x);
   XmlFont& operator=(const XmlFont& x);
   XmlFontColor getColor() const {return color;}
   ~XmlFont();
   static void clear();
   GString* getFullName();
   GBool isItalic() const {return italic;}
   GBool isBold() const {return bold;}
   GBool isOblique() const {return oblique;}
   unsigned int getSize() const {return size;}
   int getLineSize() const {return lineSize;}
   void setLineSize(int _lineSize) { lineSize = _lineSize; }
   GString* getFontName();
   double getCharSpace() const {return charspace;}
   void setCharSpace(double _charspace){charspace = _charspace;}
   static GString* getDefaultFont();
   static void setDefaultFont(GString* defaultFont);
   GBool isEqual(const XmlFont& x) const;
   GBool isEqualIgnoreBold(const XmlFont& x) const;
   static GString* simple(XmlFont *font, Unicode *content, int uLen);
   void print() const {printf("font: %s %d %s%spos: %d\n", FontName->getCString(), size, bold ? "bold " : "", italic ? "italic " : "", pos);};
};

class XmlFontAccu{
private:
  GVector<XmlFont> *accu;
  
public:
  XmlFontAccu();
  ~XmlFontAccu();
  int AddFont(const XmlFont& font);
  XmlFont* Get(int i){
    GVector<XmlFont>::iterator g=accu->begin();
    g+=i;  
    return g;
  } 
  GString* getCSStyle (int i, GString* content);
 /* GString* EscapeSpecialChars(GString* content); */
  GString* CSStyle(int i,GBool textAsJSON);
  int size() const {return accu->size();}
  
};  
#endif
#include "XmlLinks.h"

XmlLink::XmlLink(const XmlLink& x){
  Xmin=x.Xmin;
  Ymin=x.Ymin;
  Xmax=x.Xmax;
  Ymax=x.Ymax;
  dest=new GString(x.dest);
}

XmlLink::XmlLink(double xmin,double ymin,double xmax,double ymax,GString * _dest)
{
   if (xmin < xmax) {
    Xmin=xmin;
    Xmax=xmax;
  } else {
    Xmin=xmax;
    Xmax=xmin;
  }
  if (ymin < ymax) {
    Ymin=ymin;
    Ymax=ymax;
  } else {
    Ymin=ymax;
    Ymax=ymin;
  }                    
  dest=new GString(_dest);
}

XmlLink::~XmlLink(){
 if (dest) delete dest;
}

GBool XmlLink::isEqualDest(const XmlLink& x) const{
  return (!strcmp(dest->getCString(), x.dest->getCString()));
}

GBool XmlLink::inLink(double xmin,double ymin,double xmax,double ymax) const {
  double y=(ymin+ymax)/2;
  if (y>Ymax) return gFalse;
  return (y>Ymin)&&(xmin<Xmax)&&(xmax>Xmin);
 }
  

XmlLink& XmlLink::operator=(const XmlLink& x){
  if (this==&x) return *this;
  if (dest) {delete dest;dest=NULL;} 
  Xmin=x.Xmin;
  Ymin=x.Ymin;
  Xmax=x.Xmax;
  Ymax=x.Ymax;
  dest=new GString(x.dest);
  return *this;
} 

GString* XmlLink::getLinkStart() {
  GString *res = new GString("");
  res->append(dest);
//  res->append("\">");
  return res;
}

/*GString* XmlLink::Link(GString* content){
  //GString* _dest=new GString(dest);
  GString *tmp=new GString("<a href=\"");
  tmp->append(dest);
  tmp->append("\">");
  tmp->append(content);
  tmp->append("</a>");
  //delete _dest;
  return tmp;
  }*/

   

XmlLinks::XmlLinks(){
  accu=new GVector<XmlLink>();
}

XmlLinks::~XmlLinks(){
  delete accu;
  accu=NULL; 
}

GBool XmlLinks::inLink(double xmin,double ymin,double xmax,double ymax,int& p)const {
  
  for(GVector<XmlLink>::iterator i=accu->begin();i!=accu->end();i++){
    if (i->inLink(xmin,ymin,xmax,ymax)) {
        p=(i - accu->begin());
        return 1;
    }
   }
  return 0;
}

XmlLink* XmlLinks::getLink(int i) const{
  GVector<XmlLink>::iterator g=accu->begin();
  g+=i; 
  return g;
}

#ifndef _HTML_LINKS
#define _HTML_LINKS

#include "GVector.h"
#include "GString.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef _WIN32
#include <sys/unistd.h>
#endif

class XmlLink{

private:  
  double Xmin;
  double Ymin;
  double Xmax;
  double Ymax;
  GString* dest;

public:
  XmlLink(){dest=NULL;}
  XmlLink(const XmlLink& x);
  XmlLink& operator=(const XmlLink& x);
  XmlLink(double xmin,double ymin,double xmax,double ymax,GString *_dest);
  ~XmlLink();
  //GBool XmlLink::isEqualDest(const XmlLink& x) const; // this kills gcc 4.*
  GBool isEqualDest(const XmlLink& x) const;
  GString *getDest(){return new GString(dest);}
  double getX1() const {return Xmin;}
  double getX2() const {return Xmax;}
  double getY1() const {return Ymin;}
  double getY2() const {return Ymax;}
  GBool inLink(double xmin,double ymin,double xmax,double ymax) const ;
  //GString *Link(GString *content);
  GString* getLinkStart();
  
};

class XmlLinks{
private:
 GVector<XmlLink> *accu;
public:
 XmlLinks();
 ~XmlLinks();
 void AddLink(const XmlLink& x) {accu->push_back(x);}
 GBool inLink(double xmin,double ymin,double xmax,double ymax,int& p) const;
 XmlLink* getLink(int i) const;

};

#endif
   
//========================================================================
//
// pdf2json.cc
//
//
// Copyright 2011 Devaldi Ltd
//
// Copyright 1997-2002 Glyph & Cog, LLC
//
// Changed 1999-2000 by G.Ovtcharov
//
// Changed 2002 by Mikhail Kruk
//========================================================================

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <aconf.h>
#include <time.h>
#include "parseargs.h"
#include "GString.h"
#include "gmem.h"
#include "Object.h"
#include "Stream.h"
#include "Array.h"
#include "Dict.h"
#include "XRef.h"
#include "Catalog.h"
#include "Page.h"
#include "PDFDoc.h"
#include "ImgOutputDev.h"
#include "GlobalParams.h"
#include "Error.h"
#include "config.h"
#include "gfile.h"

static int firstPage = 1;
static int lastPage = 0;
static GBool rawOrder = gTrue;
static GBool textAsJSON = gTrue;
static GBool compressData = gFalse;
GBool printCommands = gTrue;
static GBool printHelp = gFalse;
GBool printHtml = gFalse;
GBool complexMode=gTrue;
GBool ignore=gFalse;
double scale=1.5;
GBool noframes=gFalse;
GBool stout=gFalse;
GBool xml=gFalse;
GBool errQuiet=gFalse;
static int split = -1;

GBool showHidden = gFalse;
GBool noMerge = gTrue;
static char ownerPassword[33] = "";
static char userPassword[33] = "";
static char gsDevice[33] = "png16m";
static GBool printVersion = gFalse;

static GString* getInfoString(Dict *infoDict, char *key);
static GString* getInfoDate(Dict *infoDict, char *key);

static char textEncName[128] = "";

static ArgDesc argDesc[] = {
  {"-f",      argInt,      &firstPage,     0,
   "first page to convert"},
  {"-l",      argInt,      &lastPage,      0,
   "last page to convert"},
  {"-compress",   argFlag, &compressData,  0,
    "Use compressed mode"},
  {"-q",      argFlag,     &errQuiet,      0,
   "don't print any messages or errors"},
  {"-h",      argFlag,     &printHelp,     0,
   "print usage information"},
  {"-help",   argFlag,     &printHelp,     0,
   "print usage information"},
  {"-i",      argFlag,     &ignore,        0,
   "ignore images"},
  {"-noframes", argFlag,   &noframes,      0,
   "use standard output"},
/*  {"-zoom",   argFP,    &scale,         0,
   "zoom the pdf document (default 1.5)"}, */
  {"-xml",    argFlag,    &xml,         0,
   "output for XML post-processing"},
  {"-split",   argInt,    &split,         0,
     "split the output every nth page"},     
  {"-hidden", argFlag,   &showHidden,   0,
   "output hidden text"},
  {"-enc",    argString,   textEncName,    sizeof(textEncName),
   "output text encoding name"},
  {"-v",      argFlag,     &printVersion,  0,
   "print copyright and version info"},
  {"-opw",    argString,   ownerPassword,  sizeof(ownerPassword),
   "owner password (for encrypted files)"},
  {"-upw",    argString,   userPassword,   sizeof(userPassword),
   "user password (for encrypted files)"},
  {NULL}
};

int main(int argc, char *argv[]) {
  PDFDoc *doc = NULL;
  GString *fileName = NULL;
  GString *docTitle = NULL;
  GString *author = NULL, *keywords = NULL, *subject = NULL, *date = NULL;
  GString *htmlFileName = NULL;
  ImgOutputDev *htmlOut = NULL;
  //PSOutputDev *psOut = NULL;
  GBool ok;
  char *p;
  char extension[16] = "png";
  GString *ownerPW, *userPW;
  Object info;
  char * extsList[] = {"png", "jpeg", "bmp", "pcx", "tiff", "pbm", NULL};

  // parse args
  ok = parseArgs(argDesc, &argc, argv);
  if (!ok || argc < 2 || argc > 3 || printHelp || printVersion) {
    fprintf(stderr, "pdf2json version %s http://flexpaper.devaldi.com/pdf2json/, based on Xpdf version %s\n", "0.69", xpdfVersion);
    fprintf(stderr, "%s\n", "Copyright 1999-2011 Devaldi Ltd, Gueorgui Ovtcharov and Rainer Dorsch");
    fprintf(stderr, "%s\n\n", xpdfCopyright);
    if (!printVersion) {
      printUsage("pdf2json", "<PDF-file> [<xml-file>]", argDesc);
    }
    exit(1);
  }
 
  // read config file
  globalParams = new GlobalParams("");

  if (errQuiet) {
    globalParams->setErrQuiet(errQuiet);
    printCommands = gFalse; // I'm not 100% what is the differecne between them
  }

  if (textEncName[0]) {
    globalParams->setTextEncoding(textEncName);
    if( !globalParams->getTextEncoding() )  {
	goto error;    
    }
  }

  // open PDF file
  if (ownerPassword[0]) {
    ownerPW = new GString(ownerPassword);
  } else {
    ownerPW = NULL;
  }
  if (userPassword[0]) {
    userPW = new GString(userPassword);
  } else {
    userPW = NULL;
  }

  fileName = new GString(argv[1]);

  doc = new PDFDoc(fileName, ownerPW, userPW);
  if (userPW) {
    delete userPW;
  }
  if (ownerPW) {
    delete ownerPW;
  }
  if (!doc->isOk()) {
    goto error;
  }

  // check for copy permission
  if (!doc->okToCopy()) {
    error(-1, "Copying of text from this document is not allowed.");
    goto error;
  }

  // construct text file name
  if (argc == 3) {
    GString* tmp = new GString(argv[2]);
    p=tmp->getCString()+tmp->getLength()-5;
   
    if (!strcmp(p, ".xml") || !strcmp(p, ".XML"))
	htmlFileName = new GString(tmp->getCString(),
				   tmp->getLength() - 5);
    else htmlFileName =new GString(tmp);
    
    delete tmp;
  } else {
    p = fileName->getCString() + fileName->getLength() - 4;
    if (!strcmp(p, ".pdf") || !strcmp(p, ".PDF"))
      htmlFileName = new GString(fileName->getCString(),
				 fileName->getLength() - 4);
    else
      htmlFileName = fileName->copy();
  }
  
   if (scale>3.0) scale=3.0;
   if (scale<0.5) scale=0.5;
   
   stout=gFalse;
   complexMode = gTrue;
   noframes = gTrue;
   noMerge = gTrue;

  // get page range
  if (firstPage < 1)
    firstPage = 1;
  if (lastPage < 1 || lastPage > doc->getNumPages())
    lastPage = doc->getNumPages();

  doc->getDocInfo(&info);
  if (info.isDict()) {
    docTitle = getInfoString(info.getDict(), "Title");
    author = getInfoString(info.getDict(), "Author");
    keywords = getInfoString(info.getDict(), "Keywords");
    subject = getInfoString(info.getDict(), "Subject");
    date = getInfoDate(info.getDict(), "ModDate");
    if( !date )
	date = getInfoDate(info.getDict(), "CreationDate");
  }
  info.free();
  if( !docTitle ) docTitle = new GString(htmlFileName);

  /* determine extensions of output backgroun images */
  {int i;
  for(i = 0; extsList[i]; i++)
  {
	  if( strstr(gsDevice, extsList[i]) != (char *) NULL )
	  {
		  strncpy(extension, extsList[i], sizeof(extension));
		  break;
	  }
  }}

  rawOrder = complexMode; // todo: figure out what exactly rawOrder do :)
  
  if(textAsJSON)
	xml = gTrue;

  
  // write text file
  htmlOut = new ImgOutputDev(htmlFileName->getCString(), 
      docTitle->getCString(), 
      author ? author->getCString() : NULL,
      keywords ? keywords->getCString() : NULL, 
          subject ? subject->getCString() : NULL, 
      date ? date->getCString() : NULL,
      extension,
      rawOrder,
      textAsJSON,
      compressData, 
      split,
      firstPage,                  
      doc->getCatalog()->getOutline()->isDict(),
      doc->getNumPages());
  delete docTitle;
  if( author )
  {   
      delete author;
  }
  if( keywords )
  {
      delete keywords;
  }
  if( subject )
  {
      delete subject;
  }
  if( date )
  {
      delete date;
  }

  if (htmlOut->isOk())
  {
    doc->displayPages(htmlOut, firstPage, lastPage, static_cast<int>(72*scale), static_cast<int>(72*scale), 0, gTrue, gTrue,gTrue,NULL);
    
    if (!xml)
    {
        htmlOut->dumpDocOutline(doc->getCatalog());
    }
  }
    
  delete htmlOut;


  // clean up
 error:
  if(doc) delete doc;
  if(globalParams) delete globalParams;

  if(htmlFileName) delete htmlFileName;
  XmlFont::clear();
  
  // check for memory leaks
  Object::memCheck(stderr);
  gMemReport(stderr);

  return 0;
}

static GString* getInfoString(Dict *infoDict, char *key) {
  Object obj;
  GString *s1 = NULL;

  if (infoDict->lookup(key, &obj)->isString()) {
    s1 = new GString(obj.getString());
  }
  obj.free();
  return s1;
}

static GString* getInfoDate(Dict *infoDict, char *key) {
  Object obj;
  char *s;
  int year, mon, day, hour, min, sec;
  struct tm tmStruct;
  GString *result = NULL;
  char buf[256];

  if (infoDict->lookup(key, &obj)->isString()) {
    s = obj.getString()->getCString();
    if (s[0] == 'D' && s[1] == ':') {
      s += 2;
    }
    if (sscanf(s, "%4d%2d%2d%2d%2d%2d",
               &year, &mon, &day, &hour, &min, &sec) == 6) {
      tmStruct.tm_year = year - 1900;
      tmStruct.tm_mon = mon - 1;
      tmStruct.tm_mday = day;
      tmStruct.tm_hour = hour;
      tmStruct.tm_min = min;
      tmStruct.tm_sec = sec;
      tmStruct.tm_wday = -1;
      tmStruct.tm_yday = -1;
      tmStruct.tm_isdst = -1;
      mktime(&tmStruct); // compute the tm_wday and tm_yday fields
      if (strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S+00:00", &tmStruct)) {
	result = new GString(buf);
      } else {
        result = new GString(s);
      }
    } else {
      result = new GString(s);
    }
  }
  obj.free();
  return result;
}

<?xml version="1.0" encoding="UTF-8"?>
<!ELEMENT pdf2xml (page*)>
<!ELEMENT page (text*)>
<!ELEMENT text (#PCDATA)>

<!ATTLIST page
    width CDATA #REQUIRED
    height CDATA #REQUIRED
    number CDATA #REQUIRED
>

<!ATTLIST text
    top CDATA #REQUIRED
    left CDATA #REQUIRED
    width CDATA #REQUIRED
    height CDATA #REQUIRED
    color CDATA #REQUIRED
    bgcolor CDATA #IMPLIED
    family CDATA #REQUIRED
    face CDATA #REQUIRED
    size CDATA #REQUIRED
>
<?xml version="1.0" encoding="UTF-8"?>
<!ELEMENT pdf2xmljson (page*)>
<!ELEMENT page (text*)>

<!ATTLIST page
    width CDATA #REQUIRED
    height CDATA #REQUIRED
    number CDATA #REQUIRED
>
//========================================================================
//
// Annot.cc
//
// Copyright 2000-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stdlib.h>
#include <math.h>
#include "gmem.h"
#include "GList.h"
#include "Error.h"
#include "Object.h"
#include "Catalog.h"
#include "Gfx.h"
#include "GfxFont.h"
#include "Lexer.h"
#include "Annot.h"

//------------------------------------------------------------------------

#define annotFlagHidden    0x0002
#define annotFlagPrint     0x0004
#define annotFlagNoView    0x0020

#define fieldFlagReadOnly           0x00000001
#define fieldFlagRequired           0x00000002
#define fieldFlagNoExport           0x00000004
#define fieldFlagMultiline          0x00001000
#define fieldFlagPassword           0x00002000
#define fieldFlagNoToggleToOff      0x00004000
#define fieldFlagRadio              0x00008000
#define fieldFlagPushbutton         0x00010000
#define fieldFlagCombo              0x00020000
#define fieldFlagEdit               0x00040000
#define fieldFlagSort               0x00080000
#define fieldFlagFileSelect         0x00100000
#define fieldFlagMultiSelect        0x00200000
#define fieldFlagDoNotSpellCheck    0x00400000
#define fieldFlagDoNotScroll        0x00800000
#define fieldFlagComb               0x01000000
#define fieldFlagRichText           0x02000000
#define fieldFlagRadiosInUnison     0x02000000
#define fieldFlagCommitOnSelChange  0x04000000

#define fieldQuadLeft   0
#define fieldQuadCenter 1
#define fieldQuadRight  2

// distance of Bezier control point from center for circle approximation
// = (4 * (sqrt(2) - 1) / 3) * r
#define bezierCircle 0.55228475

//------------------------------------------------------------------------
// AnnotBorderStyle
//------------------------------------------------------------------------

AnnotBorderStyle::AnnotBorderStyle(AnnotBorderType typeA, double widthA,
				   double *dashA, int dashLengthA,
				   double rA, double gA, double bA) {
  type = typeA;
  width = widthA;
  dash = dashA;
  dashLength = dashLengthA;
  r = rA;
  g = gA;
  b = bA;
}

AnnotBorderStyle::~AnnotBorderStyle() {
  if (dash) {
    gfree(dash);
  }
}

//------------------------------------------------------------------------
// Annot
//------------------------------------------------------------------------

Annot::Annot(XRef *xrefA, Dict *acroForm, Dict *dict, Ref *refA) {
  Object apObj, asObj, obj1, obj2, obj3;
  AnnotBorderType borderType;
  double borderWidth;
  double *borderDash;
  int borderDashLength;
  double borderR, borderG, borderB;
  double t;
  int i;

  ok = gTrue;
  xref = xrefA;
  ref = *refA;
  type = NULL;
  appearBuf = NULL;
  borderStyle = NULL;

  //----- parse the type

  if (dict->lookup("Subtype", &obj1)->isName()) {
    type = new GString(obj1.getName());
  }
  obj1.free();

  //----- parse the rectangle

  if (dict->lookup("Rect", &obj1)->isArray() &&
      obj1.arrayGetLength() == 4) {
    xMin = yMin = xMax = yMax = 0;
    if (obj1.arrayGet(0, &obj2)->isNum()) {
      xMin = obj2.getNum();
    }
    obj2.free();
    if (obj1.arrayGet(1, &obj2)->isNum()) {
      yMin = obj2.getNum();
    }
    obj2.free();
    if (obj1.arrayGet(2, &obj2)->isNum()) {
      xMax = obj2.getNum();
    }
    obj2.free();
    if (obj1.arrayGet(3, &obj2)->isNum()) {
      yMax = obj2.getNum();
    }
    obj2.free();
    if (xMin > xMax) {
      t = xMin; xMin = xMax; xMax = t;
    }
    if (yMin > yMax) {
      t = yMin; yMin = yMax; yMax = t;
    }
  } else {
    error(-1, "Bad bounding box for annotation");
    ok = gFalse;
  }
  obj1.free();

  //----- parse the flags

  if (dict->lookup("F", &obj1)->isInt()) {
    flags = obj1.getInt();
  } else {
    flags = 0;
  }
  obj1.free();

  //----- parse the border style

  borderType = annotBorderSolid;
  borderWidth = 1;
  borderDash = NULL;
  borderDashLength = 0;
  borderR = 0;
  borderG = 0;
  borderB = 1;
  if (dict->lookup("BS", &obj1)->isDict()) {
    if (obj1.dictLookup("S", &obj2)->isName()) {
      if (obj2.isName("S")) {
	borderType = annotBorderSolid;
      } else if (obj2.isName("D")) {
	borderType = annotBorderDashed;
      } else if (obj2.isName("B")) {
	borderType = annotBorderBeveled;
      } else if (obj2.isName("I")) {
	borderType = annotBorderInset;
      } else if (obj2.isName("U")) {
	borderType = annotBorderUnderlined;
      }
    }
    obj2.free();
    if (obj1.dictLookup("W", &obj2)->isNum()) {
      borderWidth = obj2.getNum();
    }
    obj2.free();
    if (obj1.dictLookup("D", &obj2)->isArray()) {
      borderDashLength = obj2.arrayGetLength();
      borderDash = (double *)gmallocn(borderDashLength, sizeof(double));
      for (i = 0; i < borderDashLength; ++i) {
	if (obj2.arrayGet(i, &obj3)->isNum()) {
	  borderDash[i] = obj3.getNum();
	} else {
	  borderDash[i] = 1;
	}
	obj3.free();
      }
    }
    obj2.free();
  } else {
    obj1.free();
    if (dict->lookup("Border", &obj1)->isArray()) {
      if (obj1.arrayGetLength() >= 3) {
	if (obj1.arrayGet(2, &obj2)->isNum()) {
	  borderWidth = obj2.getNum();
	}
	obj2.free();
	if (obj1.arrayGetLength() >= 4) {
	  if (obj1.arrayGet(3, &obj2)->isArray()) {
	    borderType = annotBorderDashed;
	    borderDashLength = obj2.arrayGetLength();
	    borderDash = (double *)gmallocn(borderDashLength, sizeof(double));
	    for (i = 0; i < borderDashLength; ++i) {
	      if (obj2.arrayGet(i, &obj3)->isNum()) {
		borderDash[i] = obj3.getNum();
	      } else {
		borderDash[i] = 1;
	      }
	      obj3.free();
	    }
	  } else {
	    // Adobe draws no border at all if the last element is of
	    // the wrong type.
	    borderWidth = 0;
	  }
	  obj2.free();
	}
      }
    }
  }
  obj1.free();
  if (dict->lookup("C", &obj1)->isArray() && obj1.arrayGetLength() == 3) {
    if (obj1.arrayGet(0, &obj2)->isNum()) {
      borderR = obj2.getNum();
    }
    obj1.free();
    if (obj1.arrayGet(1, &obj2)->isNum()) {
      borderG = obj2.getNum();
    }
    obj1.free();
    if (obj1.arrayGet(2, &obj2)->isNum()) {
      borderB = obj2.getNum();
    }
    obj1.free();
  }
  obj1.free();
  borderStyle = new AnnotBorderStyle(borderType, borderWidth,
				     borderDash, borderDashLength,
				     borderR, borderG, borderB);

  //----- get the annotation appearance

  if (dict->lookup("AP", &apObj)->isDict()) {
    if (dict->lookup("AS", &asObj)->isName()) {
      if (apObj.dictLookup("N", &obj1)->isDict()) {
	if (obj1.dictLookupNF(asObj.getName(), &obj2)->isRef()) {
	  obj2.copy(&appearance);
	  ok = gTrue;
	} else {
	  obj2.free();
	  if (obj1.dictLookupNF("Off", &obj2)->isRef()) {
	    obj2.copy(&appearance);
	  }
	}
	obj2.free();
      }
      obj1.free();
    } else {
      if (apObj.dictLookupNF("N", &obj1)->isRef()) {
	obj1.copy(&appearance);
      }
      obj1.free();
    }
    asObj.free();
  }
  apObj.free();
}

Annot::~Annot() {
  if (type) {
    delete type;
  }
  appearance.free();
  if (appearBuf) {
    delete appearBuf;
  }
  if (borderStyle) {
    delete borderStyle;
  }
}

void Annot::generateFieldAppearance(Dict *field, Dict *annot, Dict *acroForm) {
  Object mkObj, ftObj, appearDict, drObj, obj1, obj2, obj3;
  Dict *mkDict;
  MemStream *appearStream;
  GfxFontDict *fontDict;
  GBool hasCaption;
  double w, dx, dy, r;
  double *dash;
  GString *caption, *da;
  GString **text;
  GBool *selection;
  int dashLength, ff, quadding, comb, nOptions, topIdx, i, j;

  // must be a Widget annotation
  if (type->cmp("Widget")) {
    return;
  }

  appearBuf = new GString();

  // get the appearance characteristics (MK) dictionary
  if (annot->lookup("MK", &mkObj)->isDict()) {
    mkDict = mkObj.getDict();
  } else {
    mkDict = NULL;
  }

  // draw the background
  if (mkDict) {
    if (mkDict->lookup("BG", &obj1)->isArray() &&
	obj1.arrayGetLength() > 0) {
      setColor(obj1.getArray(), gTrue, 0);
      appearBuf->appendf("0 0 {0:.2f} {1:.2f} re f\n",
			 xMax - xMin, yMax - yMin);
    }
    obj1.free();
  }

  // get the field type
  fieldLookup(field, "FT", &ftObj);

  // get the field flags (Ff) value
  if (fieldLookup(field, "Ff", &obj1)->isInt()) {
    ff = obj1.getInt();
  } else {
    ff = 0;
  }
  obj1.free();

  // draw the border
  if (mkDict) {
    w = borderStyle->getWidth();
    if (w > 0) {
      mkDict->lookup("BC", &obj1);
      if (!(obj1.isArray() && obj1.arrayGetLength() > 0)) {
	mkDict->lookup("BG", &obj1);
      }
      if (obj1.isArray() && obj1.arrayGetLength() > 0) {
	dx = xMax - xMin;
	dy = yMax - yMin;

	// radio buttons with no caption have a round border
	hasCaption = mkDict->lookup("CA", &obj2)->isString();
	obj2.free();
	if (ftObj.isName("Btn") && (ff & fieldFlagRadio) && !hasCaption) {
	  r = 0.5 * (dx < dy ? dx : dy);
	  switch (borderStyle->getType()) {
	  case annotBorderDashed:
	    appearBuf->append("[");
	    borderStyle->getDash(&dash, &dashLength);
	    for (i = 0; i < dashLength; ++i) {
	      appearBuf->appendf(" {0:.2f}", dash[i]);
	    }
	    appearBuf->append("] 0 d\n");
	    // fall through to the solid case
	  case annotBorderSolid:
	  case annotBorderUnderlined:
	    appearBuf->appendf("{0:.2f} w\n", w);
	    setColor(obj1.getArray(), gFalse, 0);
	    drawCircle(0.5 * dx, 0.5 * dy, r - 0.5 * w, gFalse);
	    break;
	  case annotBorderBeveled:
	  case annotBorderInset:
	    appearBuf->appendf("{0:.2f} w\n", 0.5 * w);
	    setColor(obj1.getArray(), gFalse, 0);
	    drawCircle(0.5 * dx, 0.5 * dy, r - 0.25 * w, gFalse);
	    setColor(obj1.getArray(), gFalse,
		     borderStyle->getType() == annotBorderBeveled ? 1 : -1);
	    drawCircleTopLeft(0.5 * dx, 0.5 * dy, r - 0.75 * w);
	    setColor(obj1.getArray(), gFalse,
		     borderStyle->getType() == annotBorderBeveled ? -1 : 1);
	    drawCircleBottomRight(0.5 * dx, 0.5 * dy, r - 0.75 * w);
	    break;
	  }

	} else {
	  switch (borderStyle->getType()) {
	  case annotBorderDashed:
	    appearBuf->append("[");
	    borderStyle->getDash(&dash, &dashLength);
	    for (i = 0; i < dashLength; ++i) {
	      appearBuf->appendf(" {0:.2f}", dash[i]);
	    }
	    appearBuf->append("] 0 d\n");
	    // fall through to the solid case
	  case annotBorderSolid:
	    appearBuf->appendf("{0:.2f} w\n", w);
	    setColor(obj1.getArray(), gFalse, 0);
	    appearBuf->appendf("{0:.2f} {0:.2f} {1:.2f} {2:.2f} re s\n",
			       0.5 * w, dx - w, dy - w);
	    break;
	  case annotBorderBeveled:
	  case annotBorderInset:
	    setColor(obj1.getArray(), gTrue,
		     borderStyle->getType() == annotBorderBeveled ? 1 : -1);
	    appearBuf->append("0 0 m\n");
	    appearBuf->appendf("0 {0:.2f} l\n", dy);
	    appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx, dy);
	    appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx - w, dy - w);
	    appearBuf->appendf("{0:.2f} {1:.2f} l\n", w, dy - w);
	    appearBuf->appendf("{0:.2f} {0:.2f} l\n", w);
	    appearBuf->append("f\n");
	    setColor(obj1.getArray(), gTrue,
		     borderStyle->getType() == annotBorderBeveled ? -1 : 1);
	    appearBuf->append("0 0 m\n");
	    appearBuf->appendf("{0:.2f} 0 l\n", dx);
	    appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx, dy);
	    appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx - w, dy - w);
	    appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx - w, w);
	    appearBuf->appendf("{0:.2f} {0:.2f} l\n", w);
	    appearBuf->append("f\n");
	    break;
	  case annotBorderUnderlined:
	    appearBuf->appendf("{0:.2f} w\n", w);
	    setColor(obj1.getArray(), gFalse, 0);
	    appearBuf->appendf("0 0 m {0:.2f} 0 l s\n", dx);
	    break;
	  }

	  // clip to the inside of the border
	  appearBuf->appendf("{0:.2f} {0:.2f} {1:.2f} {2:.2f} re W n\n",
			     w, dx - 2 * w, dy - 2 * w);
	}
      }
      obj1.free();
    }
  }

  // get the resource dictionary
  acroForm->lookup("DR", &drObj);

  // build the font dictionary
  if (drObj.isDict() && drObj.dictLookup("Font", &obj1)->isDict()) {
    fontDict = new GfxFontDict(xref, NULL, obj1.getDict());
  } else {
    fontDict = NULL;
  }
  obj1.free();

  // get the default appearance string
  if (fieldLookup(field, "DA", &obj1)->isNull()) {
    obj1.free();
    acroForm->lookup("DA", &obj1);
  }
  if (obj1.isString()) {
    da = obj1.getString()->copy();
  } else {
    da = NULL;
  }
  obj1.free();

  // draw the field contents
  if (ftObj.isName("Btn")) {
    caption = NULL;
    if (mkDict) {
      if (mkDict->lookup("CA", &obj1)->isString()) {
	caption = obj1.getString()->copy();
      }
      obj1.free();
    }
    // radio button
    if (ff & fieldFlagRadio) {
      //~ Acrobat doesn't draw a caption if there is no AP dict (?)
      if (fieldLookup(field, "V", &obj1)->isName()) {
	if (annot->lookup("AS", &obj2)->isName(obj1.getName())) {
	  if (caption) {
	    drawText(caption, da, fontDict, gFalse, 0, fieldQuadCenter,
		     gFalse, gTrue);
	  } else {
	    if (mkDict) {
	      if (mkDict->lookup("BC", &obj3)->isArray() &&
		  obj3.arrayGetLength() > 0) {
		dx = xMax - xMin;
		dy = yMax - yMin;
		setColor(obj3.getArray(), gTrue, 0);
		drawCircle(0.5 * dx, 0.5 * dy, 0.2 * (dx < dy ? dx : dy),
			   gTrue);
	      }
	      obj3.free();
	    }
	  }
	}
	obj2.free();
      }
      obj1.free();
    // pushbutton
    } else if (ff & fieldFlagPushbutton) {
      if (caption) {
	drawText(caption, da, fontDict, gFalse, 0, fieldQuadCenter,
		 gFalse, gFalse);
      }
    // checkbox
    } else {
      // According to the PDF spec the off state must be named "Off",
      // and the on state can be named anything, but Acrobat apparently
      // looks for "Yes" and treats anything else as off.
      if (fieldLookup(field, "V", &obj1)->isName("Yes")) {
	if (!caption) {
	  caption = new GString("3"); // ZapfDingbats checkmark
	}
	drawText(caption, da, fontDict, gFalse, 0, fieldQuadCenter,
		 gFalse, gTrue);
      }
      obj1.free();
    }
    if (caption) {
      delete caption;
    }
  } else if (ftObj.isName("Tx")) {
    //~ value strings can be Unicode
    if (fieldLookup(field, "V", &obj1)->isString()) {
      if (fieldLookup(field, "Q", &obj2)->isInt()) {
	quadding = obj2.getInt();
      } else {
	quadding = fieldQuadLeft;
      }
      obj2.free();
      comb = 0;
      if (ff & fieldFlagComb) {
	if (fieldLookup(field, "MaxLen", &obj2)->isInt()) {
	  comb = obj2.getInt();
	}
	obj2.free();
      }
      drawText(obj1.getString(), da, fontDict,
	       ff & fieldFlagMultiline, comb, quadding, gTrue, gFalse);
    }
    obj1.free();
  } else if (ftObj.isName("Ch")) {
    //~ value/option strings can be Unicode
    if (fieldLookup(field, "Q", &obj1)->isInt()) {
      quadding = obj1.getInt();
    } else {
      quadding = fieldQuadLeft;
    }
    obj1.free();
    // combo box
    if (ff & fieldFlagCombo) {
      if (fieldLookup(field, "V", &obj1)->isString()) {
	drawText(obj1.getString(), da, fontDict,
		 gFalse, 0, quadding, gTrue, gFalse);
	//~ Acrobat draws a popup icon on the right side
      }
      obj1.free();
    // list box
    } else {
      if (field->lookup("Opt", &obj1)->isArray()) {
	nOptions = obj1.arrayGetLength();
	// get the option text
	text = (GString **)gmallocn(nOptions, sizeof(GString *));
	for (i = 0; i < nOptions; ++i) {
	  text[i] = NULL;
	  obj1.arrayGet(i, &obj2);
	  if (obj2.isString()) {
	    text[i] = obj2.getString()->copy();
	  } else if (obj2.isArray() && obj2.arrayGetLength() == 2) {
	    if (obj2.arrayGet(1, &obj3)->isString()) {
	      text[i] = obj3.getString()->copy();
	    }
	    obj3.free();
	  }
	  obj2.free();
	  if (!text[i]) {
	    text[i] = new GString();
	  }
	}
	// get the selected option(s)
	selection = (GBool *)gmallocn(nOptions, sizeof(GBool));
	//~ need to use the I field in addition to the V field
	fieldLookup(field, "V", &obj2);
	for (i = 0; i < nOptions; ++i) {
	  selection[i] = gFalse;
	  if (obj2.isString()) {
	    if (!obj2.getString()->cmp(text[i])) {
	      selection[i] = gTrue;
	    }
	  } else if (obj2.isArray()) {
	    for (j = 0; j < obj2.arrayGetLength(); ++j) {
	      if (obj2.arrayGet(j, &obj3)->isString() &&
		  !obj3.getString()->cmp(text[i])) {
		selection[i] = gTrue;
	      }
	      obj3.free();
	    }
	  }
	}
	obj2.free();
	// get the top index
	if (field->lookup("TI", &obj2)->isInt()) {
	  topIdx = obj2.getInt();
	} else {
	  topIdx = 0;
	}
	obj2.free();
	// draw the text
	drawListBox(text, selection, nOptions, topIdx, da, fontDict, quadding);
	for (i = 0; i < nOptions; ++i) {
	  delete text[i];
	}
	gfree(text);
	gfree(selection);
      }
      obj1.free();
    }
  } else if (ftObj.isName("Sig")) {
    //~unimp
  } else {
    error(-1, "Unknown field type");
  }

  if (da) {
    delete da;
  }

  // build the appearance stream dictionary
  appearDict.initDict(xref);
  appearDict.dictAdd(copyString("Length"),
		     obj1.initInt(appearBuf->getLength()));
  appearDict.dictAdd(copyString("Subtype"), obj1.initName("Form"));
  obj1.initArray(xref);
  obj1.arrayAdd(obj2.initReal(0));
  obj1.arrayAdd(obj2.initReal(0));
  obj1.arrayAdd(obj2.initReal(xMax - xMin));
  obj1.arrayAdd(obj2.initReal(yMax - yMin));
  appearDict.dictAdd(copyString("BBox"), &obj1);

  // set the resource dictionary
  if (drObj.isDict()) {
    appearDict.dictAdd(copyString("Resources"), drObj.copy(&obj1));
  }
  drObj.free();

  // build the appearance stream
  appearStream = new MemStream(appearBuf->getCString(), 0,
			       appearBuf->getLength(), &appearDict);
  appearance.free();
  appearance.initStream(appearStream);

  if (fontDict) {
    delete fontDict;
  }
  ftObj.free();
  mkObj.free();
}

// Set the current fill or stroke color, based on <a> (which should
// have 1, 3, or 4 elements).  If <adjust> is +1, color is brightened;
// if <adjust> is -1, color is darkened; otherwise color is not
// modified.
void Annot::setColor(Array *a, GBool fill, int adjust) {
  Object obj1;
  double color[4];
  int nComps, i;

  nComps = a->getLength();
  if (nComps > 4) {
    nComps = 4;
  }
  for (i = 0; i < nComps && i < 4; ++i) {
    if (a->get(i, &obj1)->isNum()) {
      color[i] = obj1.getNum();
    } else {
      color[i] = 0;
    }
    obj1.free();
  }
  if (nComps == 4) {
    adjust = -adjust;
  }
  if (adjust > 0) {
    for (i = 0; i < nComps; ++i) {
      color[i] = 0.5 * color[i] + 0.5;
    }
  } else if (adjust < 0) {
    for (i = 0; i < nComps; ++i) {
      color[i] = 0.5 * color[i];
    }
  }
  if (nComps == 4) {
    appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:c}\n",
		       color[0], color[1], color[2], color[3],
		       fill ? 'k' : 'K');
  } else if (nComps == 3) {
    appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:s}\n",
		       color[0], color[1], color[2],
		       fill ? "rg" : "RG");
  } else {
    appearBuf->appendf("{0:.2f} {1:c}\n",
		       color[0],
		       fill ? 'g' : 'G');
  }
}

// Draw the variable text or caption for a field.
void Annot::drawText(GString *text, GString *da, GfxFontDict *fontDict,
		     GBool multiline, int comb, int quadding,
		     GBool txField, GBool forceZapfDingbats) {
  GList *daToks;
  GString *tok;
  GfxFont *font;
  double fontSize, fontSize2, border, x, xPrev, y, w, w2, wMax;
  int tfPos, tmPos, i, j, k, c;

  //~ if there is no MK entry, this should use the existing content stream,
  //~ and only replace the marked content portion of it
  //~ (this is only relevant for Tx fields)

  // parse the default appearance string
  tfPos = tmPos = -1;
  if (da) {
    daToks = new GList();
    i = 0;
    while (i < da->getLength()) {
      while (i < da->getLength() && Lexer::isSpace(da->getChar(i))) {
	++i;
      }
      if (i < da->getLength()) {
	for (j = i + 1;
	     j < da->getLength() && !Lexer::isSpace(da->getChar(j));
	     ++j) ;
	daToks->append(new GString(da, i, j - i));
	i = j;
      }
    }
    for (i = 2; i < daToks->getLength(); ++i) {
      if (i >= 2 && !((GString *)daToks->get(i))->cmp("Tf")) {
	tfPos = i - 2;
      } else if (i >= 6 && !((GString *)daToks->get(i))->cmp("Tm")) {
	tmPos = i - 6;
      }
    }
  } else {
    daToks = NULL;
  }

  // force ZapfDingbats
  //~ this should create the font if needed (?)
  if (forceZapfDingbats) {
    if (tfPos >= 0) {
      tok = (GString *)daToks->get(tfPos);
      if (tok->cmp("/ZaDb")) {
	tok->clear();
	tok->append("/ZaDb");
      }
    }
  }

  // get the font and font size
  font = NULL;
  fontSize = 0;
  if (tfPos >= 0) {
    tok = (GString *)daToks->get(tfPos);
    if (tok->getLength() >= 1 && tok->getChar(0) == '/') {
      if (!fontDict || !(font = fontDict->lookup(tok->getCString() + 1))) {
	error(-1, "Unknown font in field's DA string");
      }
    } else {
      error(-1, "Invalid font name in 'Tf' operator in field's DA string");
    }
    tok = (GString *)daToks->get(tfPos + 1);
    fontSize = atof(tok->getCString());
  } else {
    error(-1, "Missing 'Tf' operator in field's DA string");
  }

  // get the border width
  border = borderStyle->getWidth();

  // setup
  if (txField) {
    appearBuf->append("/Tx BMC\n");
  }
  appearBuf->append("q\n");
  appearBuf->append("BT\n");

  // multi-line text
  if (multiline) {
    // note: the comb flag is ignored in multiline mode

    wMax = xMax - xMin - 2 * border - 4;

    // compute font autosize
    if (fontSize == 0) {
      for (fontSize = 20; fontSize > 1; --fontSize) {
	y = yMax - yMin;
	w2 = 0;
	i = 0;
	while (i < text->getLength()) {
	  getNextLine(text, i, font, fontSize, wMax, &j, &w, &k);
	  if (w > w2) {
	    w2 = w;
	  }
	  i = k;
	  y -= fontSize;
	}
	// approximate the descender for the last line
	if (y >= 0.33 * fontSize) {
	  break;
	}
      }
      if (tfPos >= 0) {
	tok = (GString *)daToks->get(tfPos + 1);
	tok->clear();
	tok->appendf("{0:.2f}", fontSize);
      }
    }

    // starting y coordinate
    // (note: each line of text starts with a Td operator that moves
    // down a line)
    y = yMax - yMin;

    // set the font matrix
    if (tmPos >= 0) {
      tok = (GString *)daToks->get(tmPos + 4);
      tok->clear();
      tok->append('0');
      tok = (GString *)daToks->get(tmPos + 5);
      tok->clear();
      tok->appendf("{0:.2f}", y);
    }

    // write the DA string
    if (daToks) {
      for (i = 0; i < daToks->getLength(); ++i) {
	appearBuf->append((GString *)daToks->get(i))->append(' ');
      }
    }

    // write the font matrix (if not part of the DA string)
    if (tmPos < 0) {
      appearBuf->appendf("1 0 0 1 0 {0:.2f} Tm\n", y);
    }

    // write a series of lines of text
    i = 0;
    xPrev = 0;
    while (i < text->getLength()) {

      getNextLine(text, i, font, fontSize, wMax, &j, &w, &k);

      // compute text start position
      switch (quadding) {
      case fieldQuadLeft:
      default:
	x = border + 2;
	break;
      case fieldQuadCenter:
	x = (xMax - xMin - w) / 2;
	break;
      case fieldQuadRight:
	x = xMax - xMin - border - 2 - w;
	break;
      }

      // draw the line
      appearBuf->appendf("{0:.2f} {1:.2f} Td\n", x - xPrev, -fontSize);
      appearBuf->append('(');
      for (; i < j; ++i) {
	c = text->getChar(i) & 0xff;
	if (c == '(' || c == ')' || c == '\\') {
	  appearBuf->append('\\');
	  appearBuf->append(c);
	} else if (c < 0x20 || c >= 0x80) {
	  appearBuf->appendf("\\{0:03o}", c);
	} else {
	  appearBuf->append(c);
	}
      }
      appearBuf->append(") Tj\n");

      // next line
      i = k;
      xPrev = x;
    }

  // single-line text
  } else {
    //~ replace newlines with spaces? - what does Acrobat do?

    // comb formatting
    if (comb > 0) {

      // compute comb spacing
      w = (xMax - xMin - 2 * border) / comb;

      // compute font autosize
      if (fontSize == 0) {
	fontSize = yMax - yMin - 2 * border;
	if (w < fontSize) {
	  fontSize = w;
	}
	fontSize = floor(fontSize);
	if (tfPos >= 0) {
	  tok = (GString *)daToks->get(tfPos + 1);
	  tok->clear();
	  tok->appendf("{0:.2f}", fontSize);
	}
      }

      // compute text start position
      switch (quadding) {
      case fieldQuadLeft:
      default:
	x = border + 2;
	break;
      case fieldQuadCenter:
	x = border + 2 + 0.5 * (comb - text->getLength()) * w;
	break;
      case fieldQuadRight:
	x = border + 2 + (comb - text->getLength()) * w;
	break;
      }
      y = 0.5 * (yMax - yMin) - 0.4 * fontSize;

      // set the font matrix
      if (tmPos >= 0) {
	tok = (GString *)daToks->get(tmPos + 4);
	tok->clear();
	tok->appendf("{0:.2f}", x);
	tok = (GString *)daToks->get(tmPos + 5);
	tok->clear();
	tok->appendf("{0:.2f}", y);
      }

      // write the DA string
      if (daToks) {
	for (i = 0; i < daToks->getLength(); ++i) {
	  appearBuf->append((GString *)daToks->get(i))->append(' ');
	}
      }

      // write the font matrix (if not part of the DA string)
      if (tmPos < 0) {
	appearBuf->appendf("1 0 0 1 {0:.2f} {1:.2f} Tm\n", x, y);
      }

      // write the text string
      //~ this should center (instead of left-justify) each character within
      //~     its comb cell
      for (i = 0; i < text->getLength(); ++i) {
	if (i > 0) {
	  appearBuf->appendf("{0:.2f} 0 Td\n", w);
	}
	appearBuf->append('(');
	c = text->getChar(i) & 0xff;
	if (c == '(' || c == ')' || c == '\\') {
	  appearBuf->append('\\');
	  appearBuf->append(c);
	} else if (c < 0x20 || c >= 0x80) {
	  appearBuf->appendf("{0:.2f} 0 Td\n", w);
	} else {
	  appearBuf->append(c);
	}
	appearBuf->append(") Tj\n");
      }

    // regular (non-comb) formatting
    } else {

      // compute string width
      if (font && !font->isCIDFont()) {
	w = 0;
	for (i = 0; i < text->getLength(); ++i) {
	  w += ((Gfx8BitFont *)font)->getWidth(text->getChar(i));
	}
      } else {
	// otherwise, make a crude estimate
	w = text->getLength() * 0.5;
      }

      // compute font autosize
      if (fontSize == 0) {
	fontSize = yMax - yMin - 2 * border;
	fontSize2 = (xMax - xMin - 4 - 2 * border) / w;
	if (fontSize2 < fontSize) {
	  fontSize = fontSize2;
	}
	fontSize = floor(fontSize);
	if (tfPos >= 0) {
	  tok = (GString *)daToks->get(tfPos + 1);
	  tok->clear();
	  tok->appendf("{0:.2f}", fontSize);
	}
      }

      // compute text start position
      w *= fontSize;
      switch (quadding) {
      case fieldQuadLeft:
      default:
	x = border + 2;
	break;
      case fieldQuadCenter:
	x = (xMax - xMin - w) / 2;
	break;
      case fieldQuadRight:
	x = xMax - xMin - border - 2 - w;
	break;
      }
      y = 0.5 * (yMax - yMin) - 0.4 * fontSize;

      // set the font matrix
      if (tmPos >= 0) {
	tok = (GString *)daToks->get(tmPos + 4);
	tok->clear();
	tok->appendf("{0:.2f}", x);
	tok = (GString *)daToks->get(tmPos + 5);
	tok->clear();
	tok->appendf("{0:.2f}", y);
      }

      // write the DA string
      if (daToks) {
	for (i = 0; i < daToks->getLength(); ++i) {
	  appearBuf->append((GString *)daToks->get(i))->append(' ');
	}
      }

      // write the font matrix (if not part of the DA string)
      if (tmPos < 0) {
	appearBuf->appendf("1 0 0 1 {0:.2f} {1:.2f} Tm\n", x, y);
      }

      // write the text string
      appearBuf->append('(');
      for (i = 0; i < text->getLength(); ++i) {
	c = text->getChar(i) & 0xff;
	if (c == '(' || c == ')' || c == '\\') {
	  appearBuf->append('\\');
	  appearBuf->append(c);
	} else if (c < 0x20 || c >= 0x80) {
	  appearBuf->appendf("\\{0:03o}", c);
	} else {
	  appearBuf->append(c);
	}
      }
      appearBuf->append(") Tj\n");
    }
  }

  // cleanup
  appearBuf->append("ET\n");
  appearBuf->append("Q\n");
  if (txField) {
    appearBuf->append("EMC\n");
  }

  if (daToks) {
    deleteGList(daToks, GString);
  }
}

// Draw the variable text or caption for a field.
void Annot::drawListBox(GString **text, GBool *selection,
			int nOptions, int topIdx,
			GString *da, GfxFontDict *fontDict, GBool quadding) {
  GList *daToks;
  GString *tok;
  GfxFont *font;
  double fontSize, fontSize2, border, x, y, w, wMax;
  int tfPos, tmPos, i, j, c;

  //~ if there is no MK entry, this should use the existing content stream,
  //~ and only replace the marked content portion of it
  //~ (this is only relevant for Tx fields)

  // parse the default appearance string
  tfPos = tmPos = -1;
  if (da) {
    daToks = new GList();
    i = 0;
    while (i < da->getLength()) {
      while (i < da->getLength() && Lexer::isSpace(da->getChar(i))) {
	++i;
      }
      if (i < da->getLength()) {
	for (j = i + 1;
	     j < da->getLength() && !Lexer::isSpace(da->getChar(j));
	     ++j) ;
	daToks->append(new GString(da, i, j - i));
	i = j;
      }
    }
    for (i = 2; i < daToks->getLength(); ++i) {
      if (i >= 2 && !((GString *)daToks->get(i))->cmp("Tf")) {
	tfPos = i - 2;
      } else if (i >= 6 && !((GString *)daToks->get(i))->cmp("Tm")) {
	tmPos = i - 6;
      }
    }
  } else {
    daToks = NULL;
  }

  // get the font and font size
  font = NULL;
  fontSize = 0;
  if (tfPos >= 0) {
    tok = (GString *)daToks->get(tfPos);
    if (tok->getLength() >= 1 && tok->getChar(0) == '/') {
      if (!fontDict || !(font = fontDict->lookup(tok->getCString() + 1))) {
	error(-1, "Unknown font in field's DA string");
      }
    } else {
      error(-1, "Invalid font name in 'Tf' operator in field's DA string");
    }
    tok = (GString *)daToks->get(tfPos + 1);
    fontSize = atof(tok->getCString());
  } else {
    error(-1, "Missing 'Tf' operator in field's DA string");
  }

  // get the border width
  border = borderStyle->getWidth();

  // compute font autosize
  if (fontSize == 0) {
    wMax = 0;
    for (i = 0; i < nOptions; ++i) {
      if (font && !font->isCIDFont()) {
	w = 0;
	for (j = 0; j < text[i]->getLength(); ++j) {
	  w += ((Gfx8BitFont *)font)->getWidth(text[i]->getChar(j));
	}
      } else {
	// otherwise, make a crude estimate
	w = text[i]->getLength() * 0.5;
      }
      if (w > wMax) {
	wMax = w;
      }
    }
    fontSize = yMax - yMin - 2 * border;
    fontSize2 = (xMax - xMin - 4 - 2 * border) / wMax;
    if (fontSize2 < fontSize) {
      fontSize = fontSize2;
    }
    fontSize = floor(fontSize);
    if (tfPos >= 0) {
      tok = (GString *)daToks->get(tfPos + 1);
      tok->clear();
      tok->appendf("{0:.2f}", fontSize);
    }
  }

  // draw the text
  y = yMax - yMin - 1.1 * fontSize;
  for (i = topIdx; i < nOptions; ++i) {

    // setup
    appearBuf->append("q\n");

    // draw the background if selected
    if (selection[i]) {
      appearBuf->append("0 g f\n");
      appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} re f\n",
	      border,
	      y - 0.2 * fontSize,
	      xMax - xMin - 2 * border,
	      1.1 * fontSize);
    }

    // setup
    appearBuf->append("BT\n");

    // compute string width
    if (font && !font->isCIDFont()) {
      w = 0;
      for (j = 0; j < text[i]->getLength(); ++j) {
	w += ((Gfx8BitFont *)font)->getWidth(text[i]->getChar(j));
      }
    } else {
      // otherwise, make a crude estimate
      w = text[i]->getLength() * 0.5;
    }

    // compute text start position
    w *= fontSize;
    switch (quadding) {
    case fieldQuadLeft:
    default:
      x = border + 2;
      break;
    case fieldQuadCenter:
      x = (xMax - xMin - w) / 2;
      break;
    case fieldQuadRight:
      x = xMax - xMin - border - 2 - w;
      break;
    }

    // set the font matrix
    if (tmPos >= 0) {
      tok = (GString *)daToks->get(tmPos + 4);
      tok->clear();
      tok->appendf("{0:.2f}", x);
      tok = (GString *)daToks->get(tmPos + 5);
      tok->clear();
      tok->appendf("{0:.2f}", y);
    }

    // write the DA string
    if (daToks) {
      for (j = 0; j < daToks->getLength(); ++j) {
	appearBuf->append((GString *)daToks->get(j))->append(' ');
      }
    }

    // write the font matrix (if not part of the DA string)
    if (tmPos < 0) {
      appearBuf->appendf("1 0 0 1 {0:.2f} {1:.2f} Tm\n", x, y);
    }

    // change the text color if selected
    if (selection[i]) {
      appearBuf->append("1 g\n");
    }

    // write the text string
    appearBuf->append('(');
    for (j = 0; j < text[i]->getLength(); ++j) {
      c = text[i]->getChar(j) & 0xff;
      if (c == '(' || c == ')' || c == '\\') {
	appearBuf->append('\\');
	appearBuf->append(c);
      } else if (c < 0x20 || c >= 0x80) {
	appearBuf->appendf("\\{0:03o}", c);
      } else {
	appearBuf->append(c);
      }
    }
    appearBuf->append(") Tj\n");

    // cleanup
    appearBuf->append("ET\n");
    appearBuf->append("Q\n");

    // next line
    y -= 1.1 * fontSize;
  }

  if (daToks) {
    deleteGList(daToks, GString);
  }
}

// Figure out how much text will fit on the next line.  Returns:
// *end = one past the last character to be included
// *width = width of the characters start .. end-1
// *next = index of first character on the following line
void Annot::getNextLine(GString *text, int start,
			GfxFont *font, double fontSize, double wMax,
			int *end, double *width, int *next) {
  double w, dw;
  int j, k, c;

  // figure out how much text will fit on the line
  //~ what does Adobe do with tabs?
  w = 0;
  for (j = start; j < text->getLength() && w <= wMax; ++j) {
    c = text->getChar(j) & 0xff;
    if (c == 0x0a || c == 0x0d) {
      break;
    }
    if (font && !font->isCIDFont()) {
      dw = ((Gfx8BitFont *)font)->getWidth(c) * fontSize;
    } else {
      // otherwise, make a crude estimate
      dw = 0.5 * fontSize;
    }
    w += dw;
  }
  if (w > wMax) {
    for (k = j; k > start && text->getChar(k-1) != ' '; --k) ;
    for (; k > start && text->getChar(k-1) == ' '; --k) ;
    if (k > start) {
      j = k;
    }
    if (j == start) {
      // handle the pathological case where the first character is
      // too wide to fit on the line all by itself
      j = start + 1;
    }
  }
  *end = j;

  // compute the width
  w = 0;
  for (k = start; k < j; ++k) {
    if (font && !font->isCIDFont()) {
      dw = ((Gfx8BitFont *)font)->getWidth(text->getChar(k)) * fontSize;
    } else {
      // otherwise, make a crude estimate
      dw = 0.5 * fontSize;
    }
    w += dw;
  }
  *width = w;

  // next line
  while (j < text->getLength() && text->getChar(j) == ' ') {
    ++j;
  }
  if (j < text->getLength() && text->getChar(j) == 0x0d) {
    ++j;
  }
  if (j < text->getLength() && text->getChar(j) == 0x0a) {
    ++j;
  }
  *next = j;
}

// Draw an (approximate) circle of radius <r> centered at (<cx>, <cy>).
// If <fill> is true, the circle is filled; otherwise it is stroked.
void Annot::drawCircle(double cx, double cy, double r, GBool fill) {
  appearBuf->appendf("{0:.2f} {1:.2f} m\n",
		     cx + r, cy);
  appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
		     cx + r, cy + bezierCircle * r,
		     cx + bezierCircle * r, cy + r,
		     cx, cy + r);
  appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
		     cx - bezierCircle * r, cy + r,
		     cx - r, cy + bezierCircle * r,
		     cx - r, cy);
  appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
		     cx - r, cy - bezierCircle * r,
		     cx - bezierCircle * r, cy - r,
		     cx, cy - r);
  appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
		     cx + bezierCircle * r, cy - r,
		     cx + r, cy - bezierCircle * r,
		     cx + r, cy);
  appearBuf->append(fill ? "f\n" : "s\n");
}

// Draw the top-left half of an (approximate) circle of radius <r>
// centered at (<cx>, <cy>).
void Annot::drawCircleTopLeft(double cx, double cy, double r) {
  double r2;

  r2 = r / sqrt(2.0);
  appearBuf->appendf("{0:.2f} {1:.2f} m\n",
		     cx + r2, cy + r2);
  appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
		     cx + (1 - bezierCircle) * r2,
		     cy + (1 + bezierCircle) * r2,
		     cx - (1 - bezierCircle) * r2,
		     cy + (1 + bezierCircle) * r2,
		     cx - r2,
		     cy + r2);
  appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
		     cx - (1 + bezierCircle) * r2,
		     cy + (1 - bezierCircle) * r2,
		     cx - (1 + bezierCircle) * r2,
		     cy - (1 - bezierCircle) * r2,
		     cx - r2,
		     cy - r2);
  appearBuf->append("S\n");
}

// Draw the bottom-right half of an (approximate) circle of radius <r>
// centered at (<cx>, <cy>).
void Annot::drawCircleBottomRight(double cx, double cy, double r) {
  double r2;

  r2 = r / sqrt(2.0);
  appearBuf->appendf("{0:.2f} {1:.2f} m\n",
		     cx - r2, cy - r2);
  appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
		     cx - (1 - bezierCircle) * r2,
		     cy - (1 + bezierCircle) * r2,
		     cx + (1 - bezierCircle) * r2,
		     cy - (1 + bezierCircle) * r2,
		     cx + r2,
		     cy - r2);
  appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
		     cx + (1 + bezierCircle) * r2,
		     cy - (1 - bezierCircle) * r2,
		     cx + (1 + bezierCircle) * r2,
		     cy + (1 - bezierCircle) * r2,
		     cx + r2,
		     cy + r2);
  appearBuf->append("S\n");
}

// Look up an inheritable field dictionary entry.
Object *Annot::fieldLookup(Dict *field, char *key, Object *obj) {
  Dict *dict;
  Object parent;

  dict = field;
  if (!dict->lookup(key, obj)->isNull()) {
    return obj;
  }
  obj->free();
  if (dict->lookup("Parent", &parent)->isDict()) {
    fieldLookup(parent.getDict(), key, obj);
  } else {
    obj->initNull();
  }
  parent.free();
  return obj;
}

void Annot::draw(Gfx *gfx, GBool printing) {
  Object obj;
  GBool isLink;

  // check the flags
  if ((flags & annotFlagHidden) ||
      (printing && !(flags & annotFlagPrint)) ||
      (!printing && (flags & annotFlagNoView))) {
    return;
  }

  // draw the appearance stream
  isLink = type && !type->cmp("Link");
  appearance.fetch(xref, &obj);
  gfx->drawAnnot(&obj, isLink ? borderStyle : (AnnotBorderStyle *)NULL,
		 xMin, yMin, xMax, yMax);
  obj.free();
}

//------------------------------------------------------------------------
// Annots
//------------------------------------------------------------------------

Annots::Annots(XRef *xref, Catalog *catalog, Object *annotsObj) {
  Dict *acroForm;
  Annot *annot;
  Object obj1;
  Ref ref;
  int size;
  int i;

  annots = NULL;
  size = 0;
  nAnnots = 0;

  acroForm = catalog->getAcroForm()->isDict() ?
               catalog->getAcroForm()->getDict() : NULL;
  if (annotsObj->isArray()) {
    for (i = 0; i < annotsObj->arrayGetLength(); ++i) {
      if (annotsObj->arrayGetNF(i, &obj1)->isRef()) {
	ref = obj1.getRef();
	obj1.free();
	annotsObj->arrayGet(i, &obj1);
      } else {
	ref.num = ref.gen = -1;
      }
      if (obj1.isDict()) {
	annot = new Annot(xref, acroForm, obj1.getDict(), &ref);
	if (annot->isOk()) {
	  if (nAnnots >= size) {
	    size += 16;
	    annots = (Annot **)greallocn(annots, size, sizeof(Annot *));
	  }
	  annots[nAnnots++] = annot;
	} else {
	  delete annot;
	}
      }
      obj1.free();
    }
  }
}

Annots::~Annots() {
  int i;

  for (i = 0; i < nAnnots; ++i) {
    delete annots[i];
  }
  gfree(annots);
}

void Annots::generateAppearances(Dict *acroForm) {
  Object obj1, obj2;
  Ref ref;
  int i;

  if (acroForm->lookup("Fields", &obj1)->isArray()) {
    for (i = 0; i < obj1.arrayGetLength(); ++i) {
      if (obj1.arrayGetNF(i, &obj2)->isRef()) {
	ref = obj2.getRef();
	obj2.free();
	obj1.arrayGet(i, &obj2);
      } else {
	ref.num = ref.gen = -1;
      }
      if (obj2.isDict()) {
	scanFieldAppearances(obj2.getDict(), &ref, NULL, acroForm);
      }
      obj2.free();
    }
  }
  obj1.free();
}

void Annots::scanFieldAppearances(Dict *node, Ref *ref, Dict *parent,
				  Dict *acroForm) {
  Annot *annot;
  Object obj1, obj2;
  Ref ref2;
  int i;

  // non-terminal node: scan the children
  if (node->lookup("Kids", &obj1)->isArray()) {
    for (i = 0; i < obj1.arrayGetLength(); ++i) {
      if (obj1.arrayGetNF(i, &obj2)->isRef()) {
	ref2 = obj2.getRef();
	obj2.free();
	obj1.arrayGet(i, &obj2);
      } else {
	ref2.num = ref2.gen = -1;
      }
      if (obj2.isDict()) {
	scanFieldAppearances(obj2.getDict(), &ref2, node, acroForm);
      }
      obj2.free();
    }
    obj1.free();
    return;
  }
  obj1.free();

  // terminal node: this is either a combined annot/field dict, or an
  // annot dict whose parent is a field
  if ((annot = findAnnot(ref))) {
    node->lookupNF("Parent", &obj1);
    if (!parent || !obj1.isNull()) {
      annot->generateFieldAppearance(node, node, acroForm);
    } else {
      annot->generateFieldAppearance(parent, node, acroForm);
    }
    obj1.free();
  }
}

Annot *Annots::findAnnot(Ref *ref) {
  int i;

  for (i = 0; i < nAnnots; ++i) {
    if (annots[i]->match(ref)) {
      return annots[i];
    }
  }
  return NULL;
}
//========================================================================
//
// Annot.h
//
// Copyright 2000-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef ANNOT_H
#define ANNOT_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

class XRef;
class Catalog;
class Gfx;
class GfxFontDict;

//------------------------------------------------------------------------
// AnnotBorderStyle
//------------------------------------------------------------------------

enum AnnotBorderType {
  annotBorderSolid,
  annotBorderDashed,
  annotBorderBeveled,
  annotBorderInset,
  annotBorderUnderlined
};

class AnnotBorderStyle {
public:

  AnnotBorderStyle(AnnotBorderType typeA, double widthA,
		   double *dashA, int dashLengthA,
		   double rA, double gA, double bA);
  ~AnnotBorderStyle();

  AnnotBorderType getType() { return type; }
  double getWidth() { return width; }
  void getDash(double **dashA, int *dashLengthA)
    { *dashA = dash; *dashLengthA = dashLength; }
  void getColor(double *rA, double *gA, double *bA)
    { *rA = r; *gA = g; *bA = b; }

private:

  AnnotBorderType type;
  double width;
  double *dash;
  int dashLength;
  double r, g, b;
};

//------------------------------------------------------------------------
// Annot
//------------------------------------------------------------------------

class Annot {
public:

  Annot(XRef *xrefA, Dict *acroForm, Dict *dict, Ref *refA);
  ~Annot();
  GBool isOk() { return ok; }

  void draw(Gfx *gfx, GBool printing);

  // Get appearance object.
  Object *getAppearance(Object *obj) { return appearance.fetch(xref, obj); }

  AnnotBorderStyle *getBorderStyle() { return borderStyle; }

  GBool match(Ref *refA)
    { return ref.num == refA->num && ref.gen == refA->gen; }

  void generateFieldAppearance(Dict *field, Dict *annot, Dict *acroForm);

private:
 
  void setColor(Array *a, GBool fill, int adjust);
  void drawText(GString *text, GString *da, GfxFontDict *fontDict,
		GBool multiline, int comb, int quadding,
		GBool txField, GBool forceZapfDingbats);
  void drawListBox(GString **text, GBool *selection,
		   int nOptions, int topIdx,
		   GString *da, GfxFontDict *fontDict, GBool quadding);
  void getNextLine(GString *text, int start,
		   GfxFont *font, double fontSize, double wMax,
		   int *end, double *width, int *next);
  void drawCircle(double cx, double cy, double r, GBool fill);
  void drawCircleTopLeft(double cx, double cy, double r);
  void drawCircleBottomRight(double cx, double cy, double r);
  Object *fieldLookup(Dict *field, char *key, Object *obj);

  XRef *xref;			// the xref table for this PDF file
  Ref ref;			// object ref identifying this annotation
  GString *type;		// annotation type
  Object appearance;		// a reference to the Form XObject stream
				//   for the normal appearance
  GString *appearBuf;
  double xMin, yMin,		// annotation rectangle
         xMax, yMax;
  Guint flags;
  AnnotBorderStyle *borderStyle;
  GBool ok;
};

//------------------------------------------------------------------------
// Annots
//------------------------------------------------------------------------

class Annots {
public:

  // Build a list of Annot objects.
  Annots(XRef *xref, Catalog *catalog, Object *annotsObj);

  ~Annots();

  // Iterate through list of annotations.
  int getNumAnnots() { return nAnnots; }
  Annot *getAnnot(int i) { return annots[i]; }

  // (Re)generate the appearance streams for all annotations belonging
  // to a form field.
  void generateAppearances(Dict *acroForm);

private:

  void scanFieldAppearances(Dict *node, Ref *ref, Dict *parent,
			    Dict *acroForm);
  Annot *findAnnot(Ref *ref);

  Annot **annots;
  int nAnnots;
};

#endif
//========================================================================
//
// Array.cc
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stdlib.h>
#include <stddef.h>
#include "gmem.h"
#include "Object.h"
#include "Array.h"

//------------------------------------------------------------------------
// Array
//------------------------------------------------------------------------

Array::Array(XRef *xrefA) {
  xref = xrefA;
  elems = NULL;
  size = length = 0;
  ref = 1;
}

Array::~Array() {
  int i;

  for (i = 0; i < length; ++i)
    elems[i].free();
  gfree(elems);
}

void Array::add(Object *elem) {
  if (length == size) {
    if (length == 0) {
      size = 8;
    } else {
      size *= 2;
    }
    elems = (Object *)greallocn(elems, size, sizeof(Object));
  }
  elems[length] = *elem;
  ++length;
}

Object *Array::get(int i, Object *obj) {
  if (i < 0 || i >= length) {
#ifdef DEBUG_MEM
    abort();
#else
    return obj->initNull();
#endif
  }
  return elems[i].fetch(xref, obj);
}

Object *Array::getNF(int i, Object *obj) {
  if (i < 0 || i >= length) {
#ifdef DEBUG_MEM
    abort();
#else
    return obj->initNull();
#endif
  }
  return elems[i].copy(obj);
}
//========================================================================
//
// Array.h
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef ARRAY_H
#define ARRAY_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "Object.h"

class XRef;

//------------------------------------------------------------------------
// Array
//------------------------------------------------------------------------

class Array {
public:

  // Constructor.
  Array(XRef *xrefA);

  // Destructor.
  ~Array();

  // Reference counting.
  int incRef() { return ++ref; }
  int decRef() { return --ref; }

  // Get number of elements.
  int getLength() { return length; }

  // Add an element.
  void add(Object *elem);

  // Accessors.
  Object *get(int i, Object *obj);
  Object *getNF(int i, Object *obj);

private:

  XRef *xref;			// the xref table for this PDF file
  Object *elems;		// array of elements
  int size;			// size of <elems> array
  int length;			// number of elements in array
  int ref;			// reference count
};

#endif
//========================================================================
//
// BuiltinFont.cc
//
// Copyright 2001-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stdlib.h>
#include <string.h>
#include "gmem.h"
#include "FontEncodingTables.h"
#include "BuiltinFont.h"

//------------------------------------------------------------------------

BuiltinFontWidths::BuiltinFontWidths(BuiltinFontWidth *widths, int sizeA) {
  int i, h;

  size = sizeA;
  tab = (BuiltinFontWidth **)gmallocn(size, sizeof(BuiltinFontWidth *));
  for (i = 0; i < size; ++i) {
    tab[i] = NULL;
  }
  for (i = 0; i < sizeA; ++i) {
    h = hash(widths[i].name);
    widths[i].next = tab[h];
    tab[h] = &widths[i];
  }
}

BuiltinFontWidths::~BuiltinFontWidths() {
  gfree(tab);
}

GBool BuiltinFontWidths::getWidth(char *name, Gushort *width) {
  int h;
  BuiltinFontWidth *p;

  h = hash(name);
  for (p = tab[h]; p; p = p->next) {
    if (!strcmp(p->name, name)) {
      *width = p->width;
      return gTrue;
    }
  }
  return gFalse;
}

int BuiltinFontWidths::hash(char *name) {
  char *p;
  unsigned int h;

  h = 0;
  for (p = name; *p; ++p) {
    h = 17 * h + (int)(*p & 0xff);
  }
  return (int)(h % size);
}
//========================================================================
//
// BuiltinFont.h
//
// Copyright 2001-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef BUILTINFONT_H
#define BUILTINFONT_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "gtypes.h"

struct BuiltinFont;
class BuiltinFontWidths;

//------------------------------------------------------------------------

struct BuiltinFont {
  char *name;
  char **defaultBaseEnc;
  short ascent;
  short descent;
  short bbox[4];
  BuiltinFontWidths *widths;
};

//------------------------------------------------------------------------

struct BuiltinFontWidth {
  char *name;
  Gushort width;
  BuiltinFontWidth *next;
};

class BuiltinFontWidths {
public:

  BuiltinFontWidths(BuiltinFontWidth *widths, int sizeA);
  ~BuiltinFontWidths();
  GBool getWidth(char *name, Gushort *width);

private:

  int hash(char *name);

  BuiltinFontWidth **tab;
  int size;
};

#endif
//========================================================================
//
// BuiltinFontTables.cc
//
// Copyright 2001-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>
#include <stdlib.h>
#include "FontEncodingTables.h"
#include "BuiltinFontTables.h"

static BuiltinFontWidth courierWidthsTab[] = {
  { "Ntilde",                            600, NULL },
  { "rcaron",                            600, NULL },
  { "kcommaaccent",                      600, NULL },
  { "Ncommaaccent",                      600, NULL },
  { "Zacute",                            600, NULL },
  { "comma",                             600, NULL },
  { "cedilla",                           600, NULL },
  { "plusminus",                         600, NULL },
  { "circumflex",                        600, NULL },
  { "dotaccent",                         600, NULL },
  { "edotaccent",                        600, NULL },
  { "asciitilde",                        600, NULL },
  { "colon",                             600, NULL },
  { "onehalf",                           600, NULL },
  { "dollar",                            600, NULL },
  { "Lcaron",                            600, NULL },
  { "ntilde",                            600, NULL },
  { "Aogonek",                           600, NULL },
  { "ncommaaccent",                      600, NULL },
  { "minus",                             600, NULL },
  { "Iogonek",                           600, NULL },
  { "zacute",                            600, NULL },
  { "yen",                               600, NULL },
  { "space",                             600, NULL },
  { "Omacron",                           600, NULL },
  { "questiondown",                      600, NULL },
  { "emdash",                            600, NULL },
  { "Agrave",                            600, NULL },
  { "three",                             600, NULL },
  { "numbersign",                        600, NULL },
  { "lcaron",                            600, NULL },
  { "A",                                 600, NULL },
  { "B",                                 600, NULL },
  { "C",                                 600, NULL },
  { "aogonek",                           600, NULL },
  { "D",                                 600, NULL },
  { "E",                                 600, NULL },
  { "onequarter",                        600, NULL },
  { "F",                                 600, NULL },
  { "G",                                 600, NULL },
  { "H",                                 600, NULL },
  { "I",                                 600, NULL },
  { "J",                                 600, NULL },
  { "K",                                 600, NULL },
  { "iogonek",                           600, NULL },
  { "L",                                 600, NULL },
  { "backslash",                         600, NULL },
  { "periodcentered",                    600, NULL },
  { "M",                                 600, NULL },
  { "N",                                 600, NULL },
  { "omacron",                           600, NULL },
  { "Tcommaaccent",                      600, NULL },
  { "O",                                 600, NULL },
  { "P",                                 600, NULL },
  { "Q",                                 600, NULL },
  { "Uhungarumlaut",                     600, NULL },
  { "R",                                 600, NULL },
  { "Aacute",                            600, NULL },
  { "caron",                             600, NULL },
  { "S",                                 600, NULL },
  { "T",                                 600, NULL },
  { "U",                                 600, NULL },
  { "agrave",                            600, NULL },
  { "V",                                 600, NULL },
  { "W",                                 600, NULL },
  { "equal",                             600, NULL },
  { "question",                          600, NULL },
  { "X",                                 600, NULL },
  { "Y",                                 600, NULL },
  { "Z",                                 600, NULL },
  { "four",                              600, NULL },
  { "a",                                 600, NULL },
  { "Gcommaaccent",                      600, NULL },
  { "b",                                 600, NULL },
  { "c",                                 600, NULL },
  { "d",                                 600, NULL },
  { "e",                                 600, NULL },
  { "f",                                 600, NULL },
  { "g",                                 600, NULL },
  { "bullet",                            600, NULL },
  { "h",                                 600, NULL },
  { "i",                                 600, NULL },
  { "Oslash",                            600, NULL },
  { "dagger",                            600, NULL },
  { "j",                                 600, NULL },
  { "k",                                 600, NULL },
  { "l",                                 600, NULL },
  { "m",                                 600, NULL },
  { "n",                                 600, NULL },
  { "tcommaaccent",                      600, NULL },
  { "o",                                 600, NULL },
  { "ordfeminine",                       600, NULL },
  { "ring",                              600, NULL },
  { "p",                                 600, NULL },
  { "q",                                 600, NULL },
  { "uhungarumlaut",                     600, NULL },
  { "r",                                 600, NULL },
  { "twosuperior",                       600, NULL },
  { "aacute",                            600, NULL },
  { "s",                                 600, NULL },
  { "OE",                                600, NULL },
  { "t",                                 600, NULL },
  { "divide",                            600, NULL },
  { "u",                                 600, NULL },
  { "Ccaron",                            600, NULL },
  { "v",                                 600, NULL },
  { "w",                                 600, NULL },
  { "x",                                 600, NULL },
  { "y",                                 600, NULL },
  { "z",                                 600, NULL },
  { "Gbreve",                            600, NULL },
  { "commaaccent",                       600, NULL },
  { "hungarumlaut",                      600, NULL },
  { "Idotaccent",                        600, NULL },
  { "Nacute",                            600, NULL },
  { "quotedbl",                          600, NULL },
  { "gcommaaccent",                      600, NULL },
  { "mu",                                600, NULL },
  { "greaterequal",                      600, NULL },
  { "Scaron",                            600, NULL },
  { "Lslash",                            600, NULL },
  { "semicolon",                         600, NULL },
  { "oslash",                            600, NULL },
  { "lessequal",                         600, NULL },
  { "lozenge",                           600, NULL },
  { "parenright",                        600, NULL },
  { "ccaron",                            600, NULL },
  { "Ecircumflex",                       600, NULL },
  { "gbreve",                            600, NULL },
  { "trademark",                         600, NULL },
  { "daggerdbl",                         600, NULL },
  { "nacute",                            600, NULL },
  { "macron",                            600, NULL },
  { "Otilde",                            600, NULL },
  { "Emacron",                           600, NULL },
  { "ellipsis",                          600, NULL },
  { "scaron",                            600, NULL },
  { "AE",                                600, NULL },
  { "Ucircumflex",                       600, NULL },
  { "lslash",                            600, NULL },
  { "quotedblleft",                      600, NULL },
  { "hyphen",                            600, NULL },
  { "guilsinglright",                    600, NULL },
  { "quotesingle",                       600, NULL },
  { "eight",                             600, NULL },
  { "exclamdown",                        600, NULL },
  { "endash",                            600, NULL },
  { "oe",                                600, NULL },
  { "Abreve",                            600, NULL },
  { "Umacron",                           600, NULL },
  { "ecircumflex",                       600, NULL },
  { "Adieresis",                         600, NULL },
  { "copyright",                         600, NULL },
  { "Egrave",                            600, NULL },
  { "slash",                             600, NULL },
  { "Edieresis",                         600, NULL },
  { "otilde",                            600, NULL },
  { "Idieresis",                         600, NULL },
  { "parenleft",                         600, NULL },
  { "one",                               600, NULL },
  { "emacron",                           600, NULL },
  { "Odieresis",                         600, NULL },
  { "ucircumflex",                       600, NULL },
  { "bracketleft",                       600, NULL },
  { "Ugrave",                            600, NULL },
  { "quoteright",                        600, NULL },
  { "Udieresis",                         600, NULL },
  { "perthousand",                       600, NULL },
  { "Ydieresis",                         600, NULL },
  { "umacron",                           600, NULL },
  { "abreve",                            600, NULL },
  { "Eacute",                            600, NULL },
  { "adieresis",                         600, NULL },
  { "egrave",                            600, NULL },
  { "edieresis",                         600, NULL },
  { "idieresis",                         600, NULL },
  { "Eth",                               600, NULL },
  { "ae",                                600, NULL },
  { "asterisk",                          600, NULL },
  { "odieresis",                         600, NULL },
  { "Uacute",                            600, NULL },
  { "ugrave",                            600, NULL },
  { "five",                              600, NULL },
  { "nine",                              600, NULL },
  { "udieresis",                         600, NULL },
  { "Zcaron",                            600, NULL },
  { "Scommaaccent",                      600, NULL },
  { "threequarters",                     600, NULL },
  { "guillemotright",                    600, NULL },
  { "Ccedilla",                          600, NULL },
  { "ydieresis",                         600, NULL },
  { "tilde",                             600, NULL },
  { "at",                                600, NULL },
  { "eacute",                            600, NULL },
  { "underscore",                        600, NULL },
  { "Euro",                              600, NULL },
  { "Dcroat",                            600, NULL },
  { "zero",                              600, NULL },
  { "multiply",                          600, NULL },
  { "eth",                               600, NULL },
  { "Scedilla",                          600, NULL },
  { "Racute",                            600, NULL },
  { "Ograve",                            600, NULL },
  { "partialdiff",                       600, NULL },
  { "uacute",                            600, NULL },
  { "braceleft",                         600, NULL },
  { "Thorn",                             600, NULL },
  { "zcaron",                            600, NULL },
  { "scommaaccent",                      600, NULL },
  { "ccedilla",                          600, NULL },
  { "Dcaron",                            600, NULL },
  { "dcroat",                            600, NULL },
  { "scedilla",                          600, NULL },
  { "Oacute",                            600, NULL },
  { "Ocircumflex",                       600, NULL },
  { "ogonek",                            600, NULL },
  { "ograve",                            600, NULL },
  { "racute",                            600, NULL },
  { "Tcaron",                            600, NULL },
  { "Eogonek",                           600, NULL },
  { "thorn",                             600, NULL },
  { "degree",                            600, NULL },
  { "registered",                        600, NULL },
  { "radical",                           600, NULL },
  { "Aring",                             600, NULL },
  { "percent",                           600, NULL },
  { "six",                               600, NULL },
  { "paragraph",                         600, NULL },
  { "dcaron",                            600, NULL },
  { "Uogonek",                           600, NULL },
  { "two",                               600, NULL },
  { "summation",                         600, NULL },
  { "Igrave",                            600, NULL },
  { "Lacute",                            600, NULL },
  { "ocircumflex",                       600, NULL },
  { "oacute",                            600, NULL },
  { "Uring",                             600, NULL },
  { "Lcommaaccent",                      600, NULL },
  { "tcaron",                            600, NULL },
  { "eogonek",                           600, NULL },
  { "Delta",                             600, NULL },
  { "Ohungarumlaut",                     600, NULL },
  { "asciicircum",                       600, NULL },
  { "aring",                             600, NULL },
  { "grave",                             600, NULL },
  { "uogonek",                           600, NULL },
  { "bracketright",                      600, NULL },
  { "ampersand",                         600, NULL },
  { "Iacute",                            600, NULL },
  { "lacute",                            600, NULL },
  { "igrave",                            600, NULL },
  { "Ncaron",                            600, NULL },
  { "plus",                              600, NULL },
  { "uring",                             600, NULL },
  { "quotesinglbase",                    600, NULL },
  { "lcommaaccent",                      600, NULL },
  { "Yacute",                            600, NULL },
  { "ohungarumlaut",                     600, NULL },
  { "threesuperior",                     600, NULL },
  { "acute",                             600, NULL },
  { "section",                           600, NULL },
  { "dieresis",                          600, NULL },
  { "quotedblbase",                      600, NULL },
  { "iacute",                            600, NULL },
  { "ncaron",                            600, NULL },
  { "florin",                            600, NULL },
  { "yacute",                            600, NULL },
  { "Rcommaaccent",                      600, NULL },
  { "fi",                                600, NULL },
  { "fl",                                600, NULL },
  { "Acircumflex",                       600, NULL },
  { "Cacute",                            600, NULL },
  { "Icircumflex",                       600, NULL },
  { "guillemotleft",                     600, NULL },
  { "germandbls",                        600, NULL },
  { "seven",                             600, NULL },
  { "Amacron",                           600, NULL },
  { "Sacute",                            600, NULL },
  { "ordmasculine",                      600, NULL },
  { "dotlessi",                          600, NULL },
  { "sterling",                          600, NULL },
  { "notequal",                          600, NULL },
  { "Imacron",                           600, NULL },
  { "rcommaaccent",                      600, NULL },
  { "Zdotaccent",                        600, NULL },
  { "acircumflex",                       600, NULL },
  { "cacute",                            600, NULL },
  { "Ecaron",                            600, NULL },
  { "braceright",                        600, NULL },
  { "icircumflex",                       600, NULL },
  { "quotedblright",                     600, NULL },
  { "amacron",                           600, NULL },
  { "sacute",                            600, NULL },
  { "imacron",                           600, NULL },
  { "cent",                              600, NULL },
  { "currency",                          600, NULL },
  { "logicalnot",                        600, NULL },
  { "zdotaccent",                        600, NULL },
  { "Atilde",                            600, NULL },
  { "breve",                             600, NULL },
  { "bar",                               600, NULL },
  { "fraction",                          600, NULL },
  { "less",                              600, NULL },
  { "ecaron",                            600, NULL },
  { "guilsinglleft",                     600, NULL },
  { "exclam",                            600, NULL },
  { "period",                            600, NULL },
  { "Rcaron",                            600, NULL },
  { "Kcommaaccent",                      600, NULL },
  { "greater",                           600, NULL },
  { "atilde",                            600, NULL },
  { "brokenbar",                         600, NULL },
  { "quoteleft",                         600, NULL },
  { "Edotaccent",                        600, NULL },
  { "onesuperior",                       600, NULL }
};

static BuiltinFontWidth courierBoldWidthsTab[] = {
  { "Ntilde",                            600, NULL },
  { "rcaron",                            600, NULL },
  { "kcommaaccent",                      600, NULL },
  { "Ncommaaccent",                      600, NULL },
  { "Zacute",                            600, NULL },
  { "comma",                             600, NULL },
  { "cedilla",                           600, NULL },
  { "plusminus",                         600, NULL },
  { "circumflex",                        600, NULL },
  { "dotaccent",                         600, NULL },
  { "edotaccent",                        600, NULL },
  { "asciitilde",                        600, NULL },
  { "colon",                             600, NULL },
  { "onehalf",                           600, NULL },
  { "dollar",                            600, NULL },
  { "Lcaron",                            600, NULL },
  { "ntilde",                            600, NULL },
  { "Aogonek",                           600, NULL },
  { "ncommaaccent",                      600, NULL },
  { "minus",                             600, NULL },
  { "Iogonek",                           600, NULL },
  { "zacute",                            600, NULL },
  { "yen",                               600, NULL },
  { "space",                             600, NULL },
  { "Omacron",                           600, NULL },
  { "questiondown",                      600, NULL },
  { "emdash",                            600, NULL },
  { "Agrave",                            600, NULL },
  { "three",                             600, NULL },
  { "numbersign",                        600, NULL },
  { "lcaron",                            600, NULL },
  { "A",                                 600, NULL },
  { "B",                                 600, NULL },
  { "C",                                 600, NULL },
  { "aogonek",                           600, NULL },
  { "D",                                 600, NULL },
  { "E",                                 600, NULL },
  { "onequarter",                        600, NULL },
  { "F",                                 600, NULL },
  { "G",                                 600, NULL },
  { "H",                                 600, NULL },
  { "I",                                 600, NULL },
  { "J",                                 600, NULL },
  { "K",                                 600, NULL },
  { "iogonek",                           600, NULL },
  { "backslash",                         600, NULL },
  { "L",                                 600, NULL },
  { "periodcentered",                    600, NULL },
  { "M",                                 600, NULL },
  { "N",                                 600, NULL },
  { "omacron",                           600, NULL },
  { "Tcommaaccent",                      600, NULL },
  { "O",                                 600, NULL },
  { "P",                                 600, NULL },
  { "Q",                                 600, NULL },
  { "Uhungarumlaut",                     600, NULL },
  { "R",                                 600, NULL },
  { "Aacute",                            600, NULL },
  { "caron",                             600, NULL },
  { "S",                                 600, NULL },
  { "T",                                 600, NULL },
  { "U",                                 600, NULL },
  { "agrave",                            600, NULL },
  { "V",                                 600, NULL },
  { "W",                                 600, NULL },
  { "X",                                 600, NULL },
  { "question",                          600, NULL },
  { "equal",                             600, NULL },
  { "Y",                                 600, NULL },
  { "Z",                                 600, NULL },
  { "four",                              600, NULL },
  { "a",                                 600, NULL },
  { "Gcommaaccent",                      600, NULL },
  { "b",                                 600, NULL },
  { "c",                                 600, NULL },
  { "d",                                 600, NULL },
  { "e",                                 600, NULL },
  { "f",                                 600, NULL },
  { "g",                                 600, NULL },
  { "bullet",                            600, NULL },
  { "h",                                 600, NULL },
  { "i",                                 600, NULL },
  { "Oslash",                            600, NULL },
  { "dagger",                            600, NULL },
  { "j",                                 600, NULL },
  { "k",                                 600, NULL },
  { "l",                                 600, NULL },
  { "m",                                 600, NULL },
  { "n",                                 600, NULL },
  { "tcommaaccent",                      600, NULL },
  { "o",                                 600, NULL },
  { "ordfeminine",                       600, NULL },
  { "ring",                              600, NULL },
  { "p",                                 600, NULL },
  { "q",                                 600, NULL },
  { "uhungarumlaut",                     600, NULL },
  { "r",                                 600, NULL },
  { "twosuperior",                       600, NULL },
  { "aacute",                            600, NULL },
  { "s",                                 600, NULL },
  { "OE",                                600, NULL },
  { "t",                                 600, NULL },
  { "divide",                            600, NULL },
  { "u",                                 600, NULL },
  { "Ccaron",                            600, NULL },
  { "v",                                 600, NULL },
  { "w",                                 600, NULL },
  { "x",                                 600, NULL },
  { "y",                                 600, NULL },
  { "z",                                 600, NULL },
  { "Gbreve",                            600, NULL },
  { "commaaccent",                       600, NULL },
  { "hungarumlaut",                      600, NULL },
  { "Idotaccent",                        600, NULL },
  { "Nacute",                            600, NULL },
  { "quotedbl",                          600, NULL },
  { "gcommaaccent",                      600, NULL },
  { "mu",                                600, NULL },
  { "greaterequal",                      600, NULL },
  { "Scaron",                            600, NULL },
  { "Lslash",                            600, NULL },
  { "semicolon",                         600, NULL },
  { "oslash",                            600, NULL },
  { "lessequal",                         600, NULL },
  { "lozenge",                           600, NULL },
  { "parenright",                        600, NULL },
  { "ccaron",                            600, NULL },
  { "Ecircumflex",                       600, NULL },
  { "gbreve",                            600, NULL },
  { "trademark",                         600, NULL },
  { "daggerdbl",                         600, NULL },
  { "nacute",                            600, NULL },
  { "macron",                            600, NULL },
  { "Otilde",                            600, NULL },
  { "Emacron",                           600, NULL },
  { "ellipsis",                          600, NULL },
  { "scaron",                            600, NULL },
  { "AE",                                600, NULL },
  { "Ucircumflex",                       600, NULL },
  { "lslash",                            600, NULL },
  { "quotedblleft",                      600, NULL },
  { "guilsinglright",                    600, NULL },
  { "hyphen",                            600, NULL },
  { "quotesingle",                       600, NULL },
  { "eight",                             600, NULL },
  { "exclamdown",                        600, NULL },
  { "endash",                            600, NULL },
  { "oe",                                600, NULL },
  { "Abreve",                            600, NULL },
  { "Umacron",                           600, NULL },
  { "ecircumflex",                       600, NULL },
  { "Adieresis",                         600, NULL },
  { "copyright",                         600, NULL },
  { "Egrave",                            600, NULL },
  { "slash",                             600, NULL },
  { "Edieresis",                         600, NULL },
  { "otilde",                            600, NULL },
  { "Idieresis",                         600, NULL },
  { "parenleft",                         600, NULL },
  { "one",                               600, NULL },
  { "emacron",                           600, NULL },
  { "Odieresis",                         600, NULL },
  { "ucircumflex",                       600, NULL },
  { "bracketleft",                       600, NULL },
  { "Ugrave",                            600, NULL },
  { "quoteright",                        600, NULL },
  { "Udieresis",                         600, NULL },
  { "perthousand",                       600, NULL },
  { "Ydieresis",                         600, NULL },
  { "umacron",                           600, NULL },
  { "abreve",                            600, NULL },
  { "Eacute",                            600, NULL },
  { "adieresis",                         600, NULL },
  { "egrave",                            600, NULL },
  { "edieresis",                         600, NULL },
  { "idieresis",                         600, NULL },
  { "Eth",                               600, NULL },
  { "ae",                                600, NULL },
  { "asterisk",                          600, NULL },
  { "odieresis",                         600, NULL },
  { "Uacute",                            600, NULL },
  { "ugrave",                            600, NULL },
  { "nine",                              600, NULL },
  { "five",                              600, NULL },
  { "udieresis",                         600, NULL },
  { "Zcaron",                            600, NULL },
  { "Scommaaccent",                      600, NULL },
  { "threequarters",                     600, NULL },
  { "guillemotright",                    600, NULL },
  { "Ccedilla",                          600, NULL },
  { "ydieresis",                         600, NULL },
  { "tilde",                             600, NULL },
  { "at",                                600, NULL },
  { "eacute",                            600, NULL },
  { "underscore",                        600, NULL },
  { "Euro",                              600, NULL },
  { "Dcroat",                            600, NULL },
  { "multiply",                          600, NULL },
  { "zero",                              600, NULL },
  { "eth",                               600, NULL },
  { "Scedilla",                          600, NULL },
  { "Ograve",                            600, NULL },
  { "Racute",                            600, NULL },
  { "partialdiff",                       600, NULL },
  { "uacute",                            600, NULL },
  { "braceleft",                         600, NULL },
  { "Thorn",                             600, NULL },
  { "zcaron",                            600, NULL },
  { "scommaaccent",                      600, NULL },
  { "ccedilla",                          600, NULL },
  { "Dcaron",                            600, NULL },
  { "dcroat",                            600, NULL },
  { "Ocircumflex",                       600, NULL },
  { "Oacute",                            600, NULL },
  { "scedilla",                          600, NULL },
  { "ogonek",                            600, NULL },
  { "ograve",                            600, NULL },
  { "racute",                            600, NULL },
  { "Tcaron",                            600, NULL },
  { "Eogonek",                           600, NULL },
  { "thorn",                             600, NULL },
  { "degree",                            600, NULL },
  { "registered",                        600, NULL },
  { "radical",                           600, NULL },
  { "Aring",                             600, NULL },
  { "percent",                           600, NULL },
  { "six",                               600, NULL },
  { "paragraph",                         600, NULL },
  { "dcaron",                            600, NULL },
  { "Uogonek",                           600, NULL },
  { "two",                               600, NULL },
  { "summation",                         600, NULL },
  { "Igrave",                            600, NULL },
  { "Lacute",                            600, NULL },
  { "ocircumflex",                       600, NULL },
  { "oacute",                            600, NULL },
  { "Uring",                             600, NULL },
  { "Lcommaaccent",                      600, NULL },
  { "tcaron",                            600, NULL },
  { "eogonek",                           600, NULL },
  { "Delta",                             600, NULL },
  { "Ohungarumlaut",                     600, NULL },
  { "asciicircum",                       600, NULL },
  { "aring",                             600, NULL },
  { "grave",                             600, NULL },
  { "uogonek",                           600, NULL },
  { "bracketright",                      600, NULL },
  { "Iacute",                            600, NULL },
  { "ampersand",                         600, NULL },
  { "igrave",                            600, NULL },
  { "lacute",                            600, NULL },
  { "Ncaron",                            600, NULL },
  { "plus",                              600, NULL },
  { "uring",                             600, NULL },
  { "quotesinglbase",                    600, NULL },
  { "lcommaaccent",                      600, NULL },
  { "Yacute",                            600, NULL },
  { "ohungarumlaut",                     600, NULL },
  { "threesuperior",                     600, NULL },
  { "acute",                             600, NULL },
  { "section",                           600, NULL },
  { "dieresis",                          600, NULL },
  { "iacute",                            600, NULL },
  { "quotedblbase",                      600, NULL },
  { "ncaron",                            600, NULL },
  { "florin",                            600, NULL },
  { "yacute",                            600, NULL },
  { "Rcommaaccent",                      600, NULL },
  { "fi",                                600, NULL },
  { "fl",                                600, NULL },
  { "Acircumflex",                       600, NULL },
  { "Cacute",                            600, NULL },
  { "Icircumflex",                       600, NULL },
  { "guillemotleft",                     600, NULL },
  { "germandbls",                        600, NULL },
  { "Amacron",                           600, NULL },
  { "seven",                             600, NULL },
  { "Sacute",                            600, NULL },
  { "ordmasculine",                      600, NULL },
  { "dotlessi",                          600, NULL },
  { "sterling",                          600, NULL },
  { "notequal",                          600, NULL },
  { "Imacron",                           600, NULL },
  { "rcommaaccent",                      600, NULL },
  { "Zdotaccent",                        600, NULL },
  { "acircumflex",                       600, NULL },
  { "cacute",                            600, NULL },
  { "Ecaron",                            600, NULL },
  { "icircumflex",                       600, NULL },
  { "braceright",                        600, NULL },
  { "quotedblright",                     600, NULL },
  { "amacron",                           600, NULL },
  { "sacute",                            600, NULL },
  { "imacron",                           600, NULL },
  { "cent",                              600, NULL },
  { "currency",                          600, NULL },
  { "logicalnot",                        600, NULL },
  { "zdotaccent",                        600, NULL },
  { "Atilde",                            600, NULL },
  { "breve",                             600, NULL },
  { "bar",                               600, NULL },
  { "fraction",                          600, NULL },
  { "less",                              600, NULL },
  { "ecaron",                            600, NULL },
  { "guilsinglleft",                     600, NULL },
  { "exclam",                            600, NULL },
  { "period",                            600, NULL },
  { "Rcaron",                            600, NULL },
  { "Kcommaaccent",                      600, NULL },
  { "greater",                           600, NULL },
  { "atilde",                            600, NULL },
  { "brokenbar",                         600, NULL },
  { "quoteleft",                         600, NULL },
  { "Edotaccent",                        600, NULL },
  { "onesuperior",                       600, NULL }
};

static BuiltinFontWidth courierBoldObliqueWidthsTab[] = {
  { "Ntilde",                            600, NULL },
  { "rcaron",                            600, NULL },
  { "kcommaaccent",                      600, NULL },
  { "Ncommaaccent",                      600, NULL },
  { "Zacute",                            600, NULL },
  { "comma",                             600, NULL },
  { "cedilla",                           600, NULL },
  { "plusminus",                         600, NULL },
  { "circumflex",                        600, NULL },
  { "dotaccent",                         600, NULL },
  { "edotaccent",                        600, NULL },
  { "asciitilde",                        600, NULL },
  { "colon",                             600, NULL },
  { "onehalf",                           600, NULL },
  { "dollar",                            600, NULL },
  { "Lcaron",                            600, NULL },
  { "ntilde",                            600, NULL },
  { "Aogonek",                           600, NULL },
  { "ncommaaccent",                      600, NULL },
  { "minus",                             600, NULL },
  { "Iogonek",                           600, NULL },
  { "zacute",                            600, NULL },
  { "yen",                               600, NULL },
  { "space",                             600, NULL },
  { "Omacron",                           600, NULL },
  { "questiondown",                      600, NULL },
  { "emdash",                            600, NULL },
  { "Agrave",                            600, NULL },
  { "three",                             600, NULL },
  { "numbersign",                        600, NULL },
  { "lcaron",                            600, NULL },
  { "A",                                 600, NULL },
  { "B",                                 600, NULL },
  { "C",                                 600, NULL },
  { "aogonek",                           600, NULL },
  { "D",                                 600, NULL },
  { "E",                                 600, NULL },
  { "onequarter",                        600, NULL },
  { "F",                                 600, NULL },
  { "G",                                 600, NULL },
  { "H",                                 600, NULL },
  { "I",                                 600, NULL },
  { "J",                                 600, NULL },
  { "K",                                 600, NULL },
  { "iogonek",                           600, NULL },
  { "backslash",                         600, NULL },
  { "L",                                 600, NULL },
  { "periodcentered",                    600, NULL },
  { "M",                                 600, NULL },
  { "N",                                 600, NULL },
  { "omacron",                           600, NULL },
  { "Tcommaaccent",                      600, NULL },
  { "O",                                 600, NULL },
  { "P",                                 600, NULL },
  { "Q",                                 600, NULL },
  { "Uhungarumlaut",                     600, NULL },
  { "R",                                 600, NULL },
  { "Aacute",                            600, NULL },
  { "caron",                             600, NULL },
  { "S",                                 600, NULL },
  { "T",                                 600, NULL },
  { "U",                                 600, NULL },
  { "agrave",                            600, NULL },
  { "V",                                 600, NULL },
  { "W",                                 600, NULL },
  { "X",                                 600, NULL },
  { "question",                          600, NULL },
  { "equal",                             600, NULL },
  { "Y",                                 600, NULL },
  { "Z",                                 600, NULL },
  { "four",                              600, NULL },
  { "a",                                 600, NULL },
  { "Gcommaaccent",                      600, NULL },
  { "b",                                 600, NULL },
  { "c",                                 600, NULL },
  { "d",                                 600, NULL },
  { "e",                                 600, NULL },
  { "f",                                 600, NULL },
  { "g",                                 600, NULL },
  { "bullet",                            600, NULL },
  { "h",                                 600, NULL },
  { "i",                                 600, NULL },
  { "Oslash",                            600, NULL },
  { "dagger",                            600, NULL },
  { "j",                                 600, NULL },
  { "k",                                 600, NULL },
  { "l",                                 600, NULL },
  { "m",                                 600, NULL },
  { "n",                                 600, NULL },
  { "tcommaaccent",                      600, NULL },
  { "o",                                 600, NULL },
  { "ordfeminine",                       600, NULL },
  { "ring",                              600, NULL },
  { "p",                                 600, NULL },
  { "q",                                 600, NULL },
  { "uhungarumlaut",                     600, NULL },
  { "r",                                 600, NULL },
  { "twosuperior",                       600, NULL },
  { "aacute",                            600, NULL },
  { "s",                                 600, NULL },
  { "OE",                                600, NULL },
  { "t",                                 600, NULL },
  { "divide",                            600, NULL },
  { "u",                                 600, NULL },
  { "Ccaron",                            600, NULL },
  { "v",                                 600, NULL },
  { "w",                                 600, NULL },
  { "x",                                 600, NULL },
  { "y",                                 600, NULL },
  { "z",                                 600, NULL },
  { "Gbreve",                            600, NULL },
  { "commaaccent",                       600, NULL },
  { "hungarumlaut",                      600, NULL },
  { "Idotaccent",                        600, NULL },
  { "Nacute",                            600, NULL },
  { "quotedbl",                          600, NULL },
  { "gcommaaccent",                      600, NULL },
  { "mu",                                600, NULL },
  { "greaterequal",                      600, NULL },
  { "Scaron",                            600, NULL },
  { "Lslash",                            600, NULL },
  { "semicolon",                         600, NULL },
  { "oslash",                            600, NULL },
  { "lessequal",                         600, NULL },
  { "lozenge",                           600, NULL },
  { "parenright",                        600, NULL },
  { "ccaron",                            600, NULL },
  { "Ecircumflex",                       600, NULL },
  { "gbreve",                            600, NULL },
  { "trademark",                         600, NULL },
  { "daggerdbl",                         600, NULL },
  { "nacute",                            600, NULL },
  { "macron",                            600, NULL },
  { "Otilde",                            600, NULL },
  { "Emacron",                           600, NULL },
  { "ellipsis",                          600, NULL },
  { "scaron",                            600, NULL },
  { "AE",                                600, NULL },
  { "Ucircumflex",                       600, NULL },
  { "lslash",                            600, NULL },
  { "quotedblleft",                      600, NULL },
  { "guilsinglright",                    600, NULL },
  { "hyphen",                            600, NULL },
  { "quotesingle",                       600, NULL },
  { "eight",                             600, NULL },
  { "exclamdown",                        600, NULL },
  { "endash",                            600, NULL },
  { "oe",                                600, NULL },
  { "Abreve",                            600, NULL },
  { "Umacron",                           600, NULL },
  { "ecircumflex",                       600, NULL },
  { "Adieresis",                         600, NULL },
  { "copyright",                         600, NULL },
  { "Egrave",                            600, NULL },
  { "slash",                             600, NULL },
  { "Edieresis",                         600, NULL },
  { "otilde",                            600, NULL },
  { "Idieresis",                         600, NULL },
  { "parenleft",                         600, NULL },
  { "one",                               600, NULL },
  { "emacron",                           600, NULL },
  { "Odieresis",                         600, NULL },
  { "ucircumflex",                       600, NULL },
  { "bracketleft",                       600, NULL },
  { "Ugrave",                            600, NULL },
  { "quoteright",                        600, NULL },
  { "Udieresis",                         600, NULL },
  { "perthousand",                       600, NULL },
  { "Ydieresis",                         600, NULL },
  { "umacron",                           600, NULL },
  { "abreve",                            600, NULL },
  { "Eacute",                            600, NULL },
  { "adieresis",                         600, NULL },
  { "egrave",                            600, NULL },
  { "edieresis",                         600, NULL },
  { "idieresis",                         600, NULL },
  { "Eth",                               600, NULL },
  { "ae",                                600, NULL },
  { "asterisk",                          600, NULL },
  { "odieresis",                         600, NULL },
  { "Uacute",                            600, NULL },
  { "ugrave",                            600, NULL },
  { "nine",                              600, NULL },
  { "five",                              600, NULL },
  { "udieresis",                         600, NULL },
  { "Zcaron",                            600, NULL },
  { "Scommaaccent",                      600, NULL },
  { "threequarters",                     600, NULL },
  { "guillemotright",                    600, NULL },
  { "Ccedilla",                          600, NULL },
  { "ydieresis",                         600, NULL },
  { "tilde",                             600, NULL },
  { "at",                                600, NULL },
  { "eacute",                            600, NULL },
  { "underscore",                        600, NULL },
  { "Euro",                              600, NULL },
  { "Dcroat",                            600, NULL },
  { "multiply",                          600, NULL },
  { "zero",                              600, NULL },
  { "eth",                               600, NULL },
  { "Scedilla",                          600, NULL },
  { "Ograve",                            600, NULL },
  { "Racute",                            600, NULL },
  { "partialdiff",                       600, NULL },
  { "uacute",                            600, NULL },
  { "braceleft",                         600, NULL },
  { "Thorn",                             600, NULL },
  { "zcaron",                            600, NULL },
  { "scommaaccent",                      600, NULL },
  { "ccedilla",                          600, NULL },
  { "Dcaron",                            600, NULL },
  { "dcroat",                            600, NULL },
  { "Ocircumflex",                       600, NULL },
  { "Oacute",                            600, NULL },
  { "scedilla",                          600, NULL },
  { "ogonek",                            600, NULL },
  { "ograve",                            600, NULL },
  { "racute",                            600, NULL },
  { "Tcaron",                            600, NULL },
  { "Eogonek",                           600, NULL },
  { "thorn",                             600, NULL },
  { "degree",                            600, NULL },
  { "registered",                        600, NULL },
  { "radical",                           600, NULL },
  { "Aring",                             600, NULL },
  { "percent",                           600, NULL },
  { "six",                               600, NULL },
  { "paragraph",                         600, NULL },
  { "dcaron",                            600, NULL },
  { "Uogonek",                           600, NULL },
  { "two",                               600, NULL },
  { "summation",                         600, NULL },
  { "Igrave",                            600, NULL },
  { "Lacute",                            600, NULL },
  { "ocircumflex",                       600, NULL },
  { "oacute",                            600, NULL },
  { "Uring",                             600, NULL },
  { "Lcommaaccent",                      600, NULL },
  { "tcaron",                            600, NULL },
  { "eogonek",                           600, NULL },
  { "Delta",                             600, NULL },
  { "Ohungarumlaut",                     600, NULL },
  { "asciicircum",                       600, NULL },
  { "aring",                             600, NULL },
  { "grave",                             600, NULL },
  { "uogonek",                           600, NULL },
  { "bracketright",                      600, NULL },
  { "Iacute",                            600, NULL },
  { "ampersand",                         600, NULL },
  { "igrave",                            600, NULL },
  { "lacute",                            600, NULL },
  { "Ncaron",                            600, NULL },
  { "plus",                              600, NULL },
  { "uring",                             600, NULL },
  { "quotesinglbase",                    600, NULL },
  { "lcommaaccent",                      600, NULL },
  { "Yacute",                            600, NULL },
  { "ohungarumlaut",                     600, NULL },
  { "threesuperior",                     600, NULL },
  { "acute",                             600, NULL },
  { "section",                           600, NULL },
  { "dieresis",                          600, NULL },
  { "iacute",                            600, NULL },
  { "quotedblbase",                      600, NULL },
  { "ncaron",                            600, NULL },
  { "florin",                            600, NULL },
  { "yacute",                            600, NULL },
  { "Rcommaaccent",                      600, NULL },
  { "fi",                                600, NULL },
  { "fl",                                600, NULL },
  { "Acircumflex",                       600, NULL },
  { "Cacute",                            600, NULL },
  { "Icircumflex",                       600, NULL },
  { "guillemotleft",                     600, NULL },
  { "germandbls",                        600, NULL },
  { "Amacron",                           600, NULL },
  { "seven",                             600, NULL },
  { "Sacute",                            600, NULL },
  { "ordmasculine",                      600, NULL },
  { "dotlessi",                          600, NULL },
  { "sterling",                          600, NULL },
  { "notequal",                          600, NULL },
  { "Imacron",                           600, NULL },
  { "rcommaaccent",                      600, NULL },
  { "Zdotaccent",                        600, NULL },
  { "acircumflex",                       600, NULL },
  { "cacute",                            600, NULL },
  { "Ecaron",                            600, NULL },
  { "icircumflex",                       600, NULL },
  { "braceright",                        600, NULL },
  { "quotedblright",                     600, NULL },
  { "amacron",                           600, NULL },
  { "sacute",                            600, NULL },
  { "imacron",                           600, NULL },
  { "cent",                              600, NULL },
  { "currency",                          600, NULL },
  { "logicalnot",                        600, NULL },
  { "zdotaccent",                        600, NULL },
  { "Atilde",                            600, NULL },
  { "breve",                             600, NULL },
  { "bar",                               600, NULL },
  { "fraction",                          600, NULL },
  { "less",                              600, NULL },
  { "ecaron",                            600, NULL },
  { "guilsinglleft",                     600, NULL },
  { "exclam",                            600, NULL },
  { "period",                            600, NULL },
  { "Rcaron",                            600, NULL },
  { "Kcommaaccent",                      600, NULL },
  { "greater",                           600, NULL },
  { "atilde",                            600, NULL },
  { "brokenbar",                         600, NULL },
  { "quoteleft",                         600, NULL },
  { "Edotaccent",                        600, NULL },
  { "onesuperior",                       600, NULL }
};

static BuiltinFontWidth courierObliqueWidthsTab[] = {
  { "Ntilde",                            600, NULL },
  { "rcaron",                            600, NULL },
  { "kcommaaccent",                      600, NULL },
  { "Ncommaaccent",                      600, NULL },
  { "Zacute",                            600, NULL },
  { "comma",                             600, NULL },
  { "cedilla",                           600, NULL },
  { "plusminus",                         600, NULL },
  { "circumflex",                        600, NULL },
  { "dotaccent",                         600, NULL },
  { "edotaccent",                        600, NULL },
  { "asciitilde",                        600, NULL },
  { "colon",                             600, NULL },
  { "onehalf",                           600, NULL },
  { "dollar",                            600, NULL },
  { "Lcaron",                            600, NULL },
  { "ntilde",                            600, NULL },
  { "Aogonek",                           600, NULL },
  { "ncommaaccent",                      600, NULL },
  { "minus",                             600, NULL },
  { "Iogonek",                           600, NULL },
  { "zacute",                            600, NULL },
  { "yen",                               600, NULL },
  { "space",                             600, NULL },
  { "Omacron",                           600, NULL },
  { "questiondown",                      600, NULL },
  { "emdash",                            600, NULL },
  { "Agrave",                            600, NULL },
  { "three",                             600, NULL },
  { "numbersign",                        600, NULL },
  { "lcaron",                            600, NULL },
  { "A",                                 600, NULL },
  { "B",                                 600, NULL },
  { "C",                                 600, NULL },
  { "aogonek",                           600, NULL },
  { "D",                                 600, NULL },
  { "E",                                 600, NULL },
  { "onequarter",                        600, NULL },
  { "F",                                 600, NULL },
  { "G",                                 600, NULL },
  { "H",                                 600, NULL },
  { "I",                                 600, NULL },
  { "J",                                 600, NULL },
  { "K",                                 600, NULL },
  { "iogonek",                           600, NULL },
  { "backslash",                         600, NULL },
  { "L",                                 600, NULL },
  { "periodcentered",                    600, NULL },
  { "M",                                 600, NULL },
  { "N",                                 600, NULL },
  { "omacron",                           600, NULL },
  { "Tcommaaccent",                      600, NULL },
  { "O",                                 600, NULL },
  { "P",                                 600, NULL },
  { "Q",                                 600, NULL },
  { "Uhungarumlaut",                     600, NULL },
  { "R",                                 600, NULL },
  { "Aacute",                            600, NULL },
  { "caron",                             600, NULL },
  { "S",                                 600, NULL },
  { "T",                                 600, NULL },
  { "U",                                 600, NULL },
  { "agrave",                            600, NULL },
  { "V",                                 600, NULL },
  { "W",                                 600, NULL },
  { "X",                                 600, NULL },
  { "question",                          600, NULL },
  { "equal",                             600, NULL },
  { "Y",                                 600, NULL },
  { "Z",                                 600, NULL },
  { "four",                              600, NULL },
  { "a",                                 600, NULL },
  { "Gcommaaccent",                      600, NULL },
  { "b",                                 600, NULL },
  { "c",                                 600, NULL },
  { "d",                                 600, NULL },
  { "e",                                 600, NULL },
  { "f",                                 600, NULL },
  { "g",                                 600, NULL },
  { "bullet",                            600, NULL },
  { "h",                                 600, NULL },
  { "i",                                 600, NULL },
  { "Oslash",                            600, NULL },
  { "dagger",                            600, NULL },
  { "j",                                 600, NULL },
  { "k",                                 600, NULL },
  { "l",                                 600, NULL },
  { "m",                                 600, NULL },
  { "n",                                 600, NULL },
  { "tcommaaccent",                      600, NULL },
  { "o",                                 600, NULL },
  { "ordfeminine",                       600, NULL },
  { "ring",                              600, NULL },
  { "p",                                 600, NULL },
  { "q",                                 600, NULL },
  { "uhungarumlaut",                     600, NULL },
  { "r",                                 600, NULL },
  { "twosuperior",                       600, NULL },
  { "aacute",                            600, NULL },
  { "s",                                 600, NULL },
  { "OE",                                600, NULL },
  { "t",                                 600, NULL },
  { "divide",                            600, NULL },
  { "u",                                 600, NULL },
  { "Ccaron",                            600, NULL },
  { "v",                                 600, NULL },
  { "w",                                 600, NULL },
  { "x",                                 600, NULL },
  { "y",                                 600, NULL },
  { "z",                                 600, NULL },
  { "Gbreve",                            600, NULL },
  { "commaaccent",                       600, NULL },
  { "hungarumlaut",                      600, NULL },
  { "Idotaccent",                        600, NULL },
  { "Nacute",                            600, NULL },
  { "quotedbl",                          600, NULL },
  { "gcommaaccent",                      600, NULL },
  { "mu",                                600, NULL },
  { "greaterequal",                      600, NULL },
  { "Scaron",                            600, NULL },
  { "Lslash",                            600, NULL },
  { "semicolon",                         600, NULL },
  { "oslash",                            600, NULL },
  { "lessequal",                         600, NULL },
  { "lozenge",                           600, NULL },
  { "parenright",                        600, NULL },
  { "ccaron",                            600, NULL },
  { "Ecircumflex",                       600, NULL },
  { "gbreve",                            600, NULL },
  { "trademark",                         600, NULL },
  { "daggerdbl",                         600, NULL },
  { "nacute",                            600, NULL },
  { "macron",                            600, NULL },
  { "Otilde",                            600, NULL },
  { "Emacron",                           600, NULL },
  { "ellipsis",                          600, NULL },
  { "scaron",                            600, NULL },
  { "AE",                                600, NULL },
  { "Ucircumflex",                       600, NULL },
  { "lslash",                            600, NULL },
  { "quotedblleft",                      600, NULL },
  { "guilsinglright",                    600, NULL },
  { "hyphen",                            600, NULL },
  { "quotesingle",                       600, NULL },
  { "eight",                             600, NULL },
  { "exclamdown",                        600, NULL },
  { "endash",                            600, NULL },
  { "oe",                                600, NULL },
  { "Abreve",                            600, NULL },
  { "Umacron",                           600, NULL },
  { "ecircumflex",                       600, NULL },
  { "Adieresis",                         600, NULL },
  { "copyright",                         600, NULL },
  { "Egrave",                            600, NULL },
  { "slash",                             600, NULL },
  { "Edieresis",                         600, NULL },
  { "otilde",                            600, NULL },
  { "Idieresis",                         600, NULL },
  { "parenleft",                         600, NULL },
  { "one",                               600, NULL },
  { "emacron",                           600, NULL },
  { "Odieresis",                         600, NULL },
  { "ucircumflex",                       600, NULL },
  { "bracketleft",                       600, NULL },
  { "Ugrave",                            600, NULL },
  { "quoteright",                        600, NULL },
  { "Udieresis",                         600, NULL },
  { "perthousand",                       600, NULL },
  { "Ydieresis",                         600, NULL },
  { "umacron",                           600, NULL },
  { "abreve",                            600, NULL },
  { "Eacute",                            600, NULL },
  { "adieresis",                         600, NULL },
  { "egrave",                            600, NULL },
  { "edieresis",                         600, NULL },
  { "idieresis",                         600, NULL },
  { "Eth",                               600, NULL },
  { "ae",                                600, NULL },
  { "asterisk",                          600, NULL },
  { "odieresis",                         600, NULL },
  { "Uacute",                            600, NULL },
  { "ugrave",                            600, NULL },
  { "nine",                              600, NULL },
  { "five",                              600, NULL },
  { "udieresis",                         600, NULL },
  { "Zcaron",                            600, NULL },
  { "Scommaaccent",                      600, NULL },
  { "threequarters",                     600, NULL },
  { "guillemotright",                    600, NULL },
  { "Ccedilla",                          600, NULL },
  { "ydieresis",                         600, NULL },
  { "tilde",                             600, NULL },
  { "at",                                600, NULL },
  { "eacute",                            600, NULL },
  { "underscore",                        600, NULL },
  { "Euro",                              600, NULL },
  { "Dcroat",                            600, NULL },
  { "multiply",                          600, NULL },
  { "zero",                              600, NULL },
  { "eth",                               600, NULL },
  { "Scedilla",                          600, NULL },
  { "Ograve",                            600, NULL },
  { "Racute",                            600, NULL },
  { "partialdiff",                       600, NULL },
  { "uacute",                            600, NULL },
  { "braceleft",                         600, NULL },
  { "Thorn",                             600, NULL },
  { "zcaron",                            600, NULL },
  { "scommaaccent",                      600, NULL },
  { "ccedilla",                          600, NULL },
  { "Dcaron",                            600, NULL },
  { "dcroat",                            600, NULL },
  { "Ocircumflex",                       600, NULL },
  { "Oacute",                            600, NULL },
  { "scedilla",                          600, NULL },
  { "ogonek",                            600, NULL },
  { "ograve",                            600, NULL },
  { "racute",                            600, NULL },
  { "Tcaron",                            600, NULL },
  { "Eogonek",                           600, NULL },
  { "thorn",                             600, NULL },
  { "degree",                            600, NULL },
  { "registered",                        600, NULL },
  { "radical",                           600, NULL },
  { "Aring",                             600, NULL },
  { "percent",                           600, NULL },
  { "six",                               600, NULL },
  { "paragraph",                         600, NULL },
  { "dcaron",                            600, NULL },
  { "Uogonek",                           600, NULL },
  { "two",                               600, NULL },
  { "summation",                         600, NULL },
  { "Igrave",                            600, NULL },
  { "Lacute",                            600, NULL },
  { "ocircumflex",                       600, NULL },
  { "oacute",                            600, NULL },
  { "Uring",                             600, NULL },
  { "Lcommaaccent",                      600, NULL },
  { "tcaron",                            600, NULL },
  { "eogonek",                           600, NULL },
  { "Delta",                             600, NULL },
  { "Ohungarumlaut",                     600, NULL },
  { "asciicircum",                       600, NULL },
  { "aring",                             600, NULL },
  { "grave",                             600, NULL },
  { "uogonek",                           600, NULL },
  { "bracketright",                      600, NULL },
  { "Iacute",                            600, NULL },
  { "ampersand",                         600, NULL },
  { "igrave",                            600, NULL },
  { "lacute",                            600, NULL },
  { "Ncaron",                            600, NULL },
  { "plus",                              600, NULL },
  { "uring",                             600, NULL },
  { "quotesinglbase",                    600, NULL },
  { "lcommaaccent",                      600, NULL },
  { "Yacute",                            600, NULL },
  { "ohungarumlaut",                     600, NULL },
  { "threesuperior",                     600, NULL },
  { "acute",                             600, NULL },
  { "section",                           600, NULL },
  { "dieresis",                          600, NULL },
  { "iacute",                            600, NULL },
  { "quotedblbase",                      600, NULL },
  { "ncaron",                            600, NULL },
  { "florin",                            600, NULL },
  { "yacute",                            600, NULL },
  { "Rcommaaccent",                      600, NULL },
  { "fi",                                600, NULL },
  { "fl",                                600, NULL },
  { "Acircumflex",                       600, NULL },
  { "Cacute",                            600, NULL },
  { "Icircumflex",                       600, NULL },
  { "guillemotleft",                     600, NULL },
  { "germandbls",                        600, NULL },
  { "Amacron",                           600, NULL },
  { "seven",                             600, NULL },
  { "Sacute",                            600, NULL },
  { "ordmasculine",                      600, NULL },
  { "dotlessi",                          600, NULL },
  { "sterling",                          600, NULL },
  { "notequal",                          600, NULL },
  { "Imacron",                           600, NULL },
  { "rcommaaccent",                      600, NULL },
  { "Zdotaccent",                        600, NULL },
  { "acircumflex",                       600, NULL },
  { "cacute",                            600, NULL },
  { "Ecaron",                            600, NULL },
  { "icircumflex",                       600, NULL },
  { "braceright",                        600, NULL },
  { "quotedblright",                     600, NULL },
  { "amacron",                           600, NULL },
  { "sacute",                            600, NULL },
  { "imacron",                           600, NULL },
  { "cent",                              600, NULL },
  { "currency",                          600, NULL },
  { "logicalnot",                        600, NULL },
  { "zdotaccent",                        600, NULL },
  { "Atilde",                            600, NULL },
  { "breve",                             600, NULL },
  { "bar",                               600, NULL },
  { "fraction",                          600, NULL },
  { "less",                              600, NULL },
  { "ecaron",                            600, NULL },
  { "guilsinglleft",                     600, NULL },
  { "exclam",                            600, NULL },
  { "period",                            600, NULL },
  { "Rcaron",                            600, NULL },
  { "Kcommaaccent",                      600, NULL },
  { "greater",                           600, NULL },
  { "atilde",                            600, NULL },
  { "brokenbar",                         600, NULL },
  { "quoteleft",                         600, NULL },
  { "Edotaccent",                        600, NULL },
  { "onesuperior",                       600, NULL }
};

static BuiltinFontWidth helveticaWidthsTab[] = {
  { "Ntilde",                            722, NULL },
  { "rcaron",                            333, NULL },
  { "kcommaaccent",                      500, NULL },
  { "Ncommaaccent",                      722, NULL },
  { "Zacute",                            611, NULL },
  { "comma",                             278, NULL },
  { "cedilla",                           333, NULL },
  { "plusminus",                         584, NULL },
  { "circumflex",                        333, NULL },
  { "dotaccent",                         333, NULL },
  { "edotaccent",                        556, NULL },
  { "asciitilde",                        584, NULL },
  { "colon",                             278, NULL },
  { "onehalf",                           834, NULL },
  { "dollar",                            556, NULL },
  { "Lcaron",                            556, NULL },
  { "ntilde",                            556, NULL },
  { "Aogonek",                           667, NULL },
  { "ncommaaccent",                      556, NULL },
  { "minus",                             584, NULL },
  { "Iogonek",                           278, NULL },
  { "zacute",                            500, NULL },
  { "yen",                               556, NULL },
  { "space",                             278, NULL },
  { "Omacron",                           778, NULL },
  { "questiondown",                      611, NULL },
  { "emdash",                           1000, NULL },
  { "Agrave",                            667, NULL },
  { "three",                             556, NULL },
  { "numbersign",                        556, NULL },
  { "lcaron",                            299, NULL },
  { "A",                                 667, NULL },
  { "B",                                 667, NULL },
  { "C",                                 722, NULL },
  { "aogonek",                           556, NULL },
  { "D",                                 722, NULL },
  { "E",                                 667, NULL },
  { "onequarter",                        834, NULL },
  { "F",                                 611, NULL },
  { "G",                                 778, NULL },
  { "H",                                 722, NULL },
  { "I",                                 278, NULL },
  { "J",                                 500, NULL },
  { "K",                                 667, NULL },
  { "iogonek",                           222, NULL },
  { "backslash",                         278, NULL },
  { "L",                                 556, NULL },
  { "periodcentered",                    278, NULL },
  { "M",                                 833, NULL },
  { "N",                                 722, NULL },
  { "omacron",                           556, NULL },
  { "Tcommaaccent",                      611, NULL },
  { "O",                                 778, NULL },
  { "P",                                 667, NULL },
  { "Q",                                 778, NULL },
  { "Uhungarumlaut",                     722, NULL },
  { "R",                                 722, NULL },
  { "Aacute",                            667, NULL },
  { "caron",                             333, NULL },
  { "S",                                 667, NULL },
  { "T",                                 611, NULL },
  { "U",                                 722, NULL },
  { "agrave",                            556, NULL },
  { "V",                                 667, NULL },
  { "W",                                 944, NULL },
  { "X",                                 667, NULL },
  { "question",                          556, NULL },
  { "equal",                             584, NULL },
  { "Y",                                 667, NULL },
  { "Z",                                 611, NULL },
  { "four",                              556, NULL },
  { "a",                                 556, NULL },
  { "Gcommaaccent",                      778, NULL },
  { "b",                                 556, NULL },
  { "c",                                 500, NULL },
  { "d",                                 556, NULL },
  { "e",                                 556, NULL },
  { "f",                                 278, NULL },
  { "g",                                 556, NULL },
  { "bullet",                            350, NULL },
  { "h",                                 556, NULL },
  { "i",                                 222, NULL },
  { "Oslash",                            778, NULL },
  { "dagger",                            556, NULL },
  { "j",                                 222, NULL },
  { "k",                                 500, NULL },
  { "l",                                 222, NULL },
  { "m",                                 833, NULL },
  { "n",                                 556, NULL },
  { "tcommaaccent",                      278, NULL },
  { "o",                                 556, NULL },
  { "ordfeminine",                       370, NULL },
  { "ring",                              333, NULL },
  { "p",                                 556, NULL },
  { "q",                                 556, NULL },
  { "uhungarumlaut",                     556, NULL },
  { "r",                                 333, NULL },
  { "twosuperior",                       333, NULL },
  { "aacute",                            556, NULL },
  { "s",                                 500, NULL },
  { "OE",                               1000, NULL },
  { "t",                                 278, NULL },
  { "divide",                            584, NULL },
  { "u",                                 556, NULL },
  { "Ccaron",                            722, NULL },
  { "v",                                 500, NULL },
  { "w",                                 722, NULL },
  { "x",                                 500, NULL },
  { "y",                                 500, NULL },
  { "z",                                 500, NULL },
  { "Gbreve",                            778, NULL },
  { "commaaccent",                       250, NULL },
  { "hungarumlaut",                      333, NULL },
  { "Idotaccent",                        278, NULL },
  { "Nacute",                            722, NULL },
  { "quotedbl",                          355, NULL },
  { "gcommaaccent",                      556, NULL },
  { "mu",                                556, NULL },
  { "greaterequal",                      549, NULL },
  { "Scaron",                            667, NULL },
  { "Lslash",                            556, NULL },
  { "semicolon",                         278, NULL },
  { "oslash",                            611, NULL },
  { "lessequal",                         549, NULL },
  { "lozenge",                           471, NULL },
  { "parenright",                        333, NULL },
  { "ccaron",                            500, NULL },
  { "Ecircumflex",                       667, NULL },
  { "gbreve",                            556, NULL },
  { "trademark",                        1000, NULL },
  { "daggerdbl",                         556, NULL },
  { "nacute",                            556, NULL },
  { "macron",                            333, NULL },
  { "Otilde",                            778, NULL },
  { "Emacron",                           667, NULL },
  { "ellipsis",                         1000, NULL },
  { "scaron",                            500, NULL },
  { "AE",                               1000, NULL },
  { "Ucircumflex",                       722, NULL },
  { "lslash",                            222, NULL },
  { "quotedblleft",                      333, NULL },
  { "guilsinglright",                    333, NULL },
  { "hyphen",                            333, NULL },
  { "quotesingle",                       191, NULL },
  { "eight",                             556, NULL },
  { "exclamdown",                        333, NULL },
  { "endash",                            556, NULL },
  { "oe",                                944, NULL },
  { "Abreve",                            667, NULL },
  { "Umacron",                           722, NULL },
  { "ecircumflex",                       556, NULL },
  { "Adieresis",                         667, NULL },
  { "copyright",                         737, NULL },
  { "Egrave",                            667, NULL },
  { "slash",                             278, NULL },
  { "Edieresis",                         667, NULL },
  { "otilde",                            556, NULL },
  { "Idieresis",                         278, NULL },
  { "parenleft",                         333, NULL },
  { "one",                               556, NULL },
  { "emacron",                           556, NULL },
  { "Odieresis",                         778, NULL },
  { "ucircumflex",                       556, NULL },
  { "bracketleft",                       278, NULL },
  { "Ugrave",                            722, NULL },
  { "quoteright",                        222, NULL },
  { "Udieresis",                         722, NULL },
  { "perthousand",                      1000, NULL },
  { "Ydieresis",                         667, NULL },
  { "umacron",                           556, NULL },
  { "abreve",                            556, NULL },
  { "Eacute",                            667, NULL },
  { "adieresis",                         556, NULL },
  { "egrave",                            556, NULL },
  { "edieresis",                         556, NULL },
  { "idieresis",                         278, NULL },
  { "Eth",                               722, NULL },
  { "ae",                                889, NULL },
  { "asterisk",                          389, NULL },
  { "odieresis",                         556, NULL },
  { "Uacute",                            722, NULL },
  { "ugrave",                            556, NULL },
  { "nine",                              556, NULL },
  { "five",                              556, NULL },
  { "udieresis",                         556, NULL },
  { "Zcaron",                            611, NULL },
  { "Scommaaccent",                      667, NULL },
  { "threequarters",                     834, NULL },
  { "guillemotright",                    556, NULL },
  { "Ccedilla",                          722, NULL },
  { "ydieresis",                         500, NULL },
  { "tilde",                             333, NULL },
  { "at",                               1015, NULL },
  { "eacute",                            556, NULL },
  { "underscore",                        556, NULL },
  { "Euro",                              556, NULL },
  { "Dcroat",                            722, NULL },
  { "multiply",                          584, NULL },
  { "zero",                              556, NULL },
  { "eth",                               556, NULL },
  { "Scedilla",                          667, NULL },
  { "Ograve",                            778, NULL },
  { "Racute",                            722, NULL },
  { "partialdiff",                       476, NULL },
  { "uacute",                            556, NULL },
  { "braceleft",                         334, NULL },
  { "Thorn",                             667, NULL },
  { "zcaron",                            500, NULL },
  { "scommaaccent",                      500, NULL },
  { "ccedilla",                          500, NULL },
  { "Dcaron",                            722, NULL },
  { "dcroat",                            556, NULL },
  { "Ocircumflex",                       778, NULL },
  { "Oacute",                            778, NULL },
  { "scedilla",                          500, NULL },
  { "ogonek",                            333, NULL },
  { "ograve",                            556, NULL },
  { "racute",                            333, NULL },
  { "Tcaron",                            611, NULL },
  { "Eogonek",                           667, NULL },
  { "thorn",                             556, NULL },
  { "degree",                            400, NULL },
  { "registered",                        737, NULL },
  { "radical",                           453, NULL },
  { "Aring",                             667, NULL },
  { "percent",                           889, NULL },
  { "six",                               556, NULL },
  { "paragraph",                         537, NULL },
  { "dcaron",                            643, NULL },
  { "Uogonek",                           722, NULL },
  { "two",                               556, NULL },
  { "summation",                         600, NULL },
  { "Igrave",                            278, NULL },
  { "Lacute",                            556, NULL },
  { "ocircumflex",                       556, NULL },
  { "oacute",                            556, NULL },
  { "Uring",                             722, NULL },
  { "Lcommaaccent",                      556, NULL },
  { "tcaron",                            317, NULL },
  { "eogonek",                           556, NULL },
  { "Delta",                             612, NULL },
  { "Ohungarumlaut",                     778, NULL },
  { "asciicircum",                       469, NULL },
  { "aring",                             556, NULL },
  { "grave",                             333, NULL },
  { "uogonek",                           556, NULL },
  { "bracketright",                      278, NULL },
  { "Iacute",                            278, NULL },
  { "ampersand",                         667, NULL },
  { "igrave",                            278, NULL },
  { "lacute",                            222, NULL },
  { "Ncaron",                            722, NULL },
  { "plus",                              584, NULL },
  { "uring",                             556, NULL },
  { "quotesinglbase",                    222, NULL },
  { "lcommaaccent",                      222, NULL },
  { "Yacute",                            667, NULL },
  { "ohungarumlaut",                     556, NULL },
  { "threesuperior",                     333, NULL },
  { "acute",                             333, NULL },
  { "section",                           556, NULL },
  { "dieresis",                          333, NULL },
  { "iacute",                            278, NULL },
  { "quotedblbase",                      333, NULL },
  { "ncaron",                            556, NULL },
  { "florin",                            556, NULL },
  { "yacute",                            500, NULL },
  { "Rcommaaccent",                      722, NULL },
  { "fi",                                500, NULL },
  { "fl",                                500, NULL },
  { "Acircumflex",                       667, NULL },
  { "Cacute",                            722, NULL },
  { "Icircumflex",                       278, NULL },
  { "guillemotleft",                     556, NULL },
  { "germandbls",                        611, NULL },
  { "Amacron",                           667, NULL },
  { "seven",                             556, NULL },
  { "Sacute",                            667, NULL },
  { "ordmasculine",                      365, NULL },
  { "dotlessi",                          278, NULL },
  { "sterling",                          556, NULL },
  { "notequal",                          549, NULL },
  { "Imacron",                           278, NULL },
  { "rcommaaccent",                      333, NULL },
  { "Zdotaccent",                        611, NULL },
  { "acircumflex",                       556, NULL },
  { "cacute",                            500, NULL },
  { "Ecaron",                            667, NULL },
  { "icircumflex",                       278, NULL },
  { "braceright",                        334, NULL },
  { "quotedblright",                     333, NULL },
  { "amacron",                           556, NULL },
  { "sacute",                            500, NULL },
  { "imacron",                           278, NULL },
  { "cent",                              556, NULL },
  { "currency",                          556, NULL },
  { "logicalnot",                        584, NULL },
  { "zdotaccent",                        500, NULL },
  { "Atilde",                            667, NULL },
  { "breve",                             333, NULL },
  { "bar",                               260, NULL },
  { "fraction",                          167, NULL },
  { "less",                              584, NULL },
  { "ecaron",                            556, NULL },
  { "guilsinglleft",                     333, NULL },
  { "exclam",                            278, NULL },
  { "period",                            278, NULL },
  { "Rcaron",                            722, NULL },
  { "Kcommaaccent",                      667, NULL },
  { "greater",                           584, NULL },
  { "atilde",                            556, NULL },
  { "brokenbar",                         260, NULL },
  { "quoteleft",                         222, NULL },
  { "Edotaccent",                        667, NULL },
  { "onesuperior",                       333, NULL }
};

static BuiltinFontWidth helveticaBoldWidthsTab[] = {
  { "Ntilde",                            722, NULL },
  { "rcaron",                            389, NULL },
  { "kcommaaccent",                      556, NULL },
  { "Ncommaaccent",                      722, NULL },
  { "Zacute",                            611, NULL },
  { "comma",                             278, NULL },
  { "cedilla",                           333, NULL },
  { "plusminus",                         584, NULL },
  { "circumflex",                        333, NULL },
  { "dotaccent",                         333, NULL },
  { "edotaccent",                        556, NULL },
  { "asciitilde",                        584, NULL },
  { "colon",                             333, NULL },
  { "onehalf",                           834, NULL },
  { "dollar",                            556, NULL },
  { "Lcaron",                            611, NULL },
  { "ntilde",                            611, NULL },
  { "Aogonek",                           722, NULL },
  { "ncommaaccent",                      611, NULL },
  { "minus",                             584, NULL },
  { "Iogonek",                           278, NULL },
  { "zacute",                            500, NULL },
  { "yen",                               556, NULL },
  { "space",                             278, NULL },
  { "Omacron",                           778, NULL },
  { "questiondown",                      611, NULL },
  { "emdash",                           1000, NULL },
  { "Agrave",                            722, NULL },
  { "three",                             556, NULL },
  { "numbersign",                        556, NULL },
  { "lcaron",                            400, NULL },
  { "A",                                 722, NULL },
  { "B",                                 722, NULL },
  { "C",                                 722, NULL },
  { "aogonek",                           556, NULL },
  { "D",                                 722, NULL },
  { "E",                                 667, NULL },
  { "onequarter",                        834, NULL },
  { "F",                                 611, NULL },
  { "G",                                 778, NULL },
  { "H",                                 722, NULL },
  { "I",                                 278, NULL },
  { "J",                                 556, NULL },
  { "K",                                 722, NULL },
  { "iogonek",                           278, NULL },
  { "backslash",                         278, NULL },
  { "L",                                 611, NULL },
  { "periodcentered",                    278, NULL },
  { "M",                                 833, NULL },
  { "N",                                 722, NULL },
  { "omacron",                           611, NULL },
  { "Tcommaaccent",                      611, NULL },
  { "O",                                 778, NULL },
  { "P",                                 667, NULL },
  { "Q",                                 778, NULL },
  { "Uhungarumlaut",                     722, NULL },
  { "R",                                 722, NULL },
  { "Aacute",                            722, NULL },
  { "caron",                             333, NULL },
  { "S",                                 667, NULL },
  { "T",                                 611, NULL },
  { "U",                                 722, NULL },
  { "agrave",                            556, NULL },
  { "V",                                 667, NULL },
  { "W",                                 944, NULL },
  { "X",                                 667, NULL },
  { "question",                          611, NULL },
  { "equal",                             584, NULL },
  { "Y",                                 667, NULL },
  { "Z",                                 611, NULL },
  { "four",                              556, NULL },
  { "a",                                 556, NULL },
  { "Gcommaaccent",                      778, NULL },
  { "b",                                 611, NULL },
  { "c",                                 556, NULL },
  { "d",                                 611, NULL },
  { "e",                                 556, NULL },
  { "f",                                 333, NULL },
  { "g",                                 611, NULL },
  { "bullet",                            350, NULL },
  { "h",                                 611, NULL },
  { "i",                                 278, NULL },
  { "Oslash",                            778, NULL },
  { "dagger",                            556, NULL },
  { "j",                                 278, NULL },
  { "k",                                 556, NULL },
  { "l",                                 278, NULL },
  { "m",                                 889, NULL },
  { "n",                                 611, NULL },
  { "tcommaaccent",                      333, NULL },
  { "o",                                 611, NULL },
  { "ordfeminine",                       370, NULL },
  { "ring",                              333, NULL },
  { "p",                                 611, NULL },
  { "q",                                 611, NULL },
  { "uhungarumlaut",                     611, NULL },
  { "r",                                 389, NULL },
  { "twosuperior",                       333, NULL },
  { "aacute",                            556, NULL },
  { "s",                                 556, NULL },
  { "OE",                               1000, NULL },
  { "t",                                 333, NULL },
  { "divide",                            584, NULL },
  { "u",                                 611, NULL },
  { "Ccaron",                            722, NULL },
  { "v",                                 556, NULL },
  { "w",                                 778, NULL },
  { "x",                                 556, NULL },
  { "y",                                 556, NULL },
  { "z",                                 500, NULL },
  { "Gbreve",                            778, NULL },
  { "commaaccent",                       250, NULL },
  { "hungarumlaut",                      333, NULL },
  { "Idotaccent",                        278, NULL },
  { "Nacute",                            722, NULL },
  { "quotedbl",                          474, NULL },
  { "gcommaaccent",                      611, NULL },
  { "mu",                                611, NULL },
  { "greaterequal",                      549, NULL },
  { "Scaron",                            667, NULL },
  { "Lslash",                            611, NULL },
  { "semicolon",                         333, NULL },
  { "oslash",                            611, NULL },
  { "lessequal",                         549, NULL },
  { "lozenge",                           494, NULL },
  { "parenright",                        333, NULL },
  { "ccaron",                            556, NULL },
  { "Ecircumflex",                       667, NULL },
  { "gbreve",                            611, NULL },
  { "trademark",                        1000, NULL },
  { "daggerdbl",                         556, NULL },
  { "nacute",                            611, NULL },
  { "macron",                            333, NULL },
  { "Otilde",                            778, NULL },
  { "Emacron",                           667, NULL },
  { "ellipsis",                         1000, NULL },
  { "scaron",                            556, NULL },
  { "AE",                               1000, NULL },
  { "Ucircumflex",                       722, NULL },
  { "lslash",                            278, NULL },
  { "quotedblleft",                      500, NULL },
  { "guilsinglright",                    333, NULL },
  { "hyphen",                            333, NULL },
  { "quotesingle",                       238, NULL },
  { "eight",                             556, NULL },
  { "exclamdown",                        333, NULL },
  { "endash",                            556, NULL },
  { "oe",                                944, NULL },
  { "Abreve",                            722, NULL },
  { "Umacron",                           722, NULL },
  { "ecircumflex",                       556, NULL },
  { "Adieresis",                         722, NULL },
  { "copyright",                         737, NULL },
  { "Egrave",                            667, NULL },
  { "slash",                             278, NULL },
  { "Edieresis",                         667, NULL },
  { "otilde",                            611, NULL },
  { "Idieresis",                         278, NULL },
  { "parenleft",                         333, NULL },
  { "one",                               556, NULL },
  { "emacron",                           556, NULL },
  { "Odieresis",                         778, NULL },
  { "ucircumflex",                       611, NULL },
  { "bracketleft",                       333, NULL },
  { "Ugrave",                            722, NULL },
  { "quoteright",                        278, NULL },
  { "Udieresis",                         722, NULL },
  { "perthousand",                      1000, NULL },
  { "Ydieresis",                         667, NULL },
  { "umacron",                           611, NULL },
  { "abreve",                            556, NULL },
  { "Eacute",                            667, NULL },
  { "adieresis",                         556, NULL },
  { "egrave",                            556, NULL },
  { "edieresis",                         556, NULL },
  { "idieresis",                         278, NULL },
  { "Eth",                               722, NULL },
  { "ae",                                889, NULL },
  { "asterisk",                          389, NULL },
  { "odieresis",                         611, NULL },
  { "Uacute",                            722, NULL },
  { "ugrave",                            611, NULL },
  { "nine",                              556, NULL },
  { "five",                              556, NULL },
  { "udieresis",                         611, NULL },
  { "Zcaron",                            611, NULL },
  { "Scommaaccent",                      667, NULL },
  { "threequarters",                     834, NULL },
  { "guillemotright",                    556, NULL },
  { "Ccedilla",                          722, NULL },
  { "ydieresis",                         556, NULL },
  { "tilde",                             333, NULL },
  { "dbldaggerumlaut",                   556, NULL },
  { "at",                                975, NULL },
  { "eacute",                            556, NULL },
  { "underscore",                        556, NULL },
  { "Euro",                              556, NULL },
  { "Dcroat",                            722, NULL },
  { "multiply",                          584, NULL },
  { "zero",                              556, NULL },
  { "eth",                               611, NULL },
  { "Scedilla",                          667, NULL },
  { "Ograve",                            778, NULL },
  { "Racute",                            722, NULL },
  { "partialdiff",                       494, NULL },
  { "uacute",                            611, NULL },
  { "braceleft",                         389, NULL },
  { "Thorn",                             667, NULL },
  { "zcaron",                            500, NULL },
  { "scommaaccent",                      556, NULL },
  { "ccedilla",                          556, NULL },
  { "Dcaron",                            722, NULL },
  { "dcroat",                            611, NULL },
  { "Ocircumflex",                       778, NULL },
  { "Oacute",                            778, NULL },
  { "scedilla",                          556, NULL },
  { "ogonek",                            333, NULL },
  { "ograve",                            611, NULL },
  { "racute",                            389, NULL },
  { "Tcaron",                            611, NULL },
  { "Eogonek",                           667, NULL },
  { "thorn",                             611, NULL },
  { "degree",                            400, NULL },
  { "registered",                        737, NULL },
  { "radical",                           549, NULL },
  { "Aring",                             722, NULL },
  { "percent",                           889, NULL },
  { "six",                               556, NULL },
  { "paragraph",                         556, NULL },
  { "dcaron",                            743, NULL },
  { "Uogonek",                           722, NULL },
  { "two",                               556, NULL },
  { "summation",                         600, NULL },
  { "Igrave",                            278, NULL },
  { "Lacute",                            611, NULL },
  { "ocircumflex",                       611, NULL },
  { "oacute",                            611, NULL },
  { "Uring",                             722, NULL },
  { "Lcommaaccent",                      611, NULL },
  { "tcaron",                            389, NULL },
  { "eogonek",                           556, NULL },
  { "Delta",                             612, NULL },
  { "Ohungarumlaut",                     778, NULL },
  { "asciicircum",                       584, NULL },
  { "aring",                             556, NULL },
  { "grave",                             333, NULL },
  { "uogonek",                           611, NULL },
  { "bracketright",                      333, NULL },
  { "Iacute",                            278, NULL },
  { "ampersand",                         722, NULL },
  { "igrave",                            278, NULL },
  { "lacute",                            278, NULL },
  { "Ncaron",                            722, NULL },
  { "plus",                              584, NULL },
  { "uring",                             611, NULL },
  { "quotesinglbase",                    278, NULL },
  { "lcommaaccent",                      278, NULL },
  { "Yacute",                            667, NULL },
  { "ohungarumlaut",                     611, NULL },
  { "threesuperior",                     333, NULL },
  { "acute",                             333, NULL },
  { "section",                           556, NULL },
  { "dieresis",                          333, NULL },
  { "iacute",                            278, NULL },
  { "quotedblbase",                      500, NULL },
  { "ncaron",                            611, NULL },
  { "florin",                            556, NULL },
  { "yacute",                            556, NULL },
  { "Rcommaaccent",                      722, NULL },
  { "fi",                                611, NULL },
  { "fl",                                611, NULL },
  { "Acircumflex",                       722, NULL },
  { "Cacute",                            722, NULL },
  { "Icircumflex",                       278, NULL },
  { "guillemotleft",                     556, NULL },
  { "germandbls",                        611, NULL },
  { "Amacron",                           722, NULL },
  { "seven",                             556, NULL },
  { "Sacute",                            667, NULL },
  { "ordmasculine",                      365, NULL },
  { "dotlessi",                          278, NULL },
  { "sterling",                          556, NULL },
  { "notequal",                          549, NULL },
  { "Imacron",                           278, NULL },
  { "rcommaaccent",                      389, NULL },
  { "Zdotaccent",                        611, NULL },
  { "acircumflex",                       556, NULL },
  { "cacute",                            556, NULL },
  { "Ecaron",                            667, NULL },
  { "icircumflex",                       278, NULL },
  { "braceright",                        389, NULL },
  { "quotedblright",                     500, NULL },
  { "amacron",                           556, NULL },
  { "sacute",                            556, NULL },
  { "imacron",                           278, NULL },
  { "cent",                              556, NULL },
  { "currency",                          556, NULL },
  { "logicalnot",                        584, NULL },
  { "zdotaccent",                        500, NULL },
  { "Atilde",                            722, NULL },
  { "breve",                             333, NULL },
  { "bar",                               280, NULL },
  { "fraction",                          167, NULL },
  { "less",                              584, NULL },
  { "ecaron",                            556, NULL },
  { "guilsinglleft",                     333, NULL },
  { "exclam",                            333, NULL },
  { "period",                            278, NULL },
  { "Rcaron",                            722, NULL },
  { "Kcommaaccent",                      722, NULL },
  { "greater",                           584, NULL },
  { "atilde",                            556, NULL },
  { "brokenbar",                         280, NULL },
  { "quoteleft",                         278, NULL },
  { "Edotaccent",                        667, NULL },
  { "onesuperior",                       333, NULL }
};

static BuiltinFontWidth helveticaBoldObliqueWidthsTab[] = {
  { "Ntilde",                            722, NULL },
  { "rcaron",                            389, NULL },
  { "kcommaaccent",                      556, NULL },
  { "Ncommaaccent",                      722, NULL },
  { "Zacute",                            611, NULL },
  { "comma",                             278, NULL },
  { "cedilla",                           333, NULL },
  { "plusminus",                         584, NULL },
  { "circumflex",                        333, NULL },
  { "dotaccent",                         333, NULL },
  { "edotaccent",                        556, NULL },
  { "asciitilde",                        584, NULL },
  { "colon",                             333, NULL },
  { "onehalf",                           834, NULL },
  { "dollar",                            556, NULL },
  { "Lcaron",                            611, NULL },
  { "ntilde",                            611, NULL },
  { "Aogonek",                           722, NULL },
  { "ncommaaccent",                      611, NULL },
  { "minus",                             584, NULL },
  { "Iogonek",                           278, NULL },
  { "zacute",                            500, NULL },
  { "yen",                               556, NULL },
  { "space",                             278, NULL },
  { "Omacron",                           778, NULL },
  { "questiondown",                      611, NULL },
  { "emdash",                           1000, NULL },
  { "Agrave",                            722, NULL },
  { "three",                             556, NULL },
  { "numbersign",                        556, NULL },
  { "lcaron",                            400, NULL },
  { "A",                                 722, NULL },
  { "B",                                 722, NULL },
  { "C",                                 722, NULL },
  { "aogonek",                           556, NULL },
  { "D",                                 722, NULL },
  { "E",                                 667, NULL },
  { "onequarter",                        834, NULL },
  { "F",                                 611, NULL },
  { "G",                                 778, NULL },
  { "H",                                 722, NULL },
  { "I",                                 278, NULL },
  { "J",                                 556, NULL },
  { "K",                                 722, NULL },
  { "iogonek",                           278, NULL },
  { "backslash",                         278, NULL },
  { "L",                                 611, NULL },
  { "periodcentered",                    278, NULL },
  { "M",                                 833, NULL },
  { "N",                                 722, NULL },
  { "omacron",                           611, NULL },
  { "Tcommaaccent",                      611, NULL },
  { "O",                                 778, NULL },
  { "P",                                 667, NULL },
  { "Q",                                 778, NULL },
  { "Uhungarumlaut",                     722, NULL },
  { "R",                                 722, NULL },
  { "Aacute",                            722, NULL },
  { "caron",                             333, NULL },
  { "S",                                 667, NULL },
  { "T",                                 611, NULL },
  { "U",                                 722, NULL },
  { "agrave",                            556, NULL },
  { "V",                                 667, NULL },
  { "W",                                 944, NULL },
  { "X",                                 667, NULL },
  { "question",                          611, NULL },
  { "equal",                             584, NULL },
  { "Y",                                 667, NULL },
  { "Z",                                 611, NULL },
  { "four",                              556, NULL },
  { "a",                                 556, NULL },
  { "Gcommaaccent",                      778, NULL },
  { "b",                                 611, NULL },
  { "c",                                 556, NULL },
  { "d",                                 611, NULL },
  { "e",                                 556, NULL },
  { "f",                                 333, NULL },
  { "g",                                 611, NULL },
  { "bullet",                            350, NULL },
  { "h",                                 611, NULL },
  { "i",                                 278, NULL },
  { "Oslash",                            778, NULL },
  { "dagger",                            556, NULL },
  { "j",                                 278, NULL },
  { "k",                                 556, NULL },
  { "l",                                 278, NULL },
  { "m",                                 889, NULL },
  { "n",                                 611, NULL },
  { "tcommaaccent",                      333, NULL },
  { "o",                                 611, NULL },
  { "ordfeminine",                       370, NULL },
  { "ring",                              333, NULL },
  { "p",                                 611, NULL },
  { "q",                                 611, NULL },
  { "uhungarumlaut",                     611, NULL },
  { "r",                                 389, NULL },
  { "twosuperior",                       333, NULL },
  { "aacute",                            556, NULL },
  { "s",                                 556, NULL },
  { "OE",                               1000, NULL },
  { "t",                                 333, NULL },
  { "divide",                            584, NULL },
  { "u",                                 611, NULL },
  { "Ccaron",                            722, NULL },
  { "v",                                 556, NULL },
  { "w",                                 778, NULL },
  { "x",                                 556, NULL },
  { "y",                                 556, NULL },
  { "z",                                 500, NULL },
  { "Gbreve",                            778, NULL },
  { "commaaccent",                       250, NULL },
  { "hungarumlaut",                      333, NULL },
  { "Idotaccent",                        278, NULL },
  { "Nacute",                            722, NULL },
  { "quotedbl",                          474, NULL },
  { "gcommaaccent",                      611, NULL },
  { "mu",                                611, NULL },
  { "greaterequal",                      549, NULL },
  { "Scaron",                            667, NULL },
  { "Lslash",                            611, NULL },
  { "semicolon",                         333, NULL },
  { "oslash",                            611, NULL },
  { "lessequal",                         549, NULL },
  { "lozenge",                           494, NULL },
  { "parenright",                        333, NULL },
  { "ccaron",                            556, NULL },
  { "Ecircumflex",                       667, NULL },
  { "gbreve",                            611, NULL },
  { "trademark",                        1000, NULL },
  { "daggerdbl",                         556, NULL },
  { "nacute",                            611, NULL },
  { "macron",                            333, NULL },
  { "Otilde",                            778, NULL },
  { "Emacron",                           667, NULL },
  { "ellipsis",                         1000, NULL },
  { "scaron",                            556, NULL },
  { "AE",                               1000, NULL },
  { "Ucircumflex",                       722, NULL },
  { "lslash",                            278, NULL },
  { "quotedblleft",                      500, NULL },
  { "guilsinglright",                    333, NULL },
  { "hyphen",                            333, NULL },
  { "quotesingle",                       238, NULL },
  { "eight",                             556, NULL },
  { "exclamdown",                        333, NULL },
  { "endash",                            556, NULL },
  { "oe",                                944, NULL },
  { "Abreve",                            722, NULL },
  { "Umacron",                           722, NULL },
  { "ecircumflex",                       556, NULL },
  { "Adieresis",                         722, NULL },
  { "copyright",                         737, NULL },
  { "Egrave",                            667, NULL },
  { "slash",                             278, NULL },
  { "Edieresis",                         667, NULL },
  { "otilde",                            611, NULL },
  { "Idieresis",                         278, NULL },
  { "parenleft",                         333, NULL },
  { "one",                               556, NULL },
  { "emacron",                           556, NULL },
  { "Odieresis",                         778, NULL },
  { "ucircumflex",                       611, NULL },
  { "bracketleft",                       333, NULL },
  { "Ugrave",                            722, NULL },
  { "quoteright",                        278, NULL },
  { "Udieresis",                         722, NULL },
  { "perthousand",                      1000, NULL },
  { "Ydieresis",                         667, NULL },
  { "umacron",                           611, NULL },
  { "abreve",                            556, NULL },
  { "Eacute",                            667, NULL },
  { "adieresis",                         556, NULL },
  { "egrave",                            556, NULL },
  { "edieresis",                         556, NULL },
  { "idieresis",                         278, NULL },
  { "Eth",                               722, NULL },
  { "ae",                                889, NULL },
  { "asterisk",                          389, NULL },
  { "odieresis",                         611, NULL },
  { "Uacute",                            722, NULL },
  { "ugrave",                            611, NULL },
  { "nine",                              556, NULL },
  { "five",                              556, NULL },
  { "udieresis",                         611, NULL },
  { "Zcaron",                            611, NULL },
  { "Scommaaccent",                      667, NULL },
  { "threequarters",                     834, NULL },
  { "guillemotright",                    556, NULL },
  { "Ccedilla",                          722, NULL },
  { "ydieresis",                         556, NULL },
  { "tilde",                             333, NULL },
  { "at",                                975, NULL },
  { "eacute",                            556, NULL },
  { "underscore",                        556, NULL },
  { "Euro",                              556, NULL },
  { "Dcroat",                            722, NULL },
  { "multiply",                          584, NULL },
  { "zero",                              556, NULL },
  { "eth",                               611, NULL },
  { "Scedilla",                          667, NULL },
  { "Ograve",                            778, NULL },
  { "Racute",                            722, NULL },
  { "partialdiff",                       494, NULL },
  { "uacute",                            611, NULL },
  { "braceleft",                         389, NULL },
  { "Thorn",                             667, NULL },
  { "zcaron",                            500, NULL },
  { "scommaaccent",                      556, NULL },
  { "ccedilla",                          556, NULL },
  { "Dcaron",                            722, NULL },
  { "dcroat",                            611, NULL },
  { "Ocircumflex",                       778, NULL },
  { "Oacute",                            778, NULL },
  { "scedilla",                          556, NULL },
  { "ogonek",                            333, NULL },
  { "ograve",                            611, NULL },
  { "racute",                            389, NULL },
  { "Tcaron",                            611, NULL },
  { "Eogonek",                           667, NULL },
  { "thorn",                             611, NULL },
  { "degree",                            400, NULL },
  { "registered",                        737, NULL },
  { "radical",                           549, NULL },
  { "Aring",                             722, NULL },
  { "percent",                           889, NULL },
  { "six",                               556, NULL },
  { "paragraph",                         556, NULL },
  { "dcaron",                            743, NULL },
  { "Uogonek",                           722, NULL },
  { "two",                               556, NULL },
  { "summation",                         600, NULL },
  { "Igrave",                            278, NULL },
  { "Lacute",                            611, NULL },
  { "ocircumflex",                       611, NULL },
  { "oacute",                            611, NULL },
  { "Uring",                             722, NULL },
  { "Lcommaaccent",                      611, NULL },
  { "tcaron",                            389, NULL },
  { "eogonek",                           556, NULL },
  { "Delta",                             612, NULL },
  { "Ohungarumlaut",                     778, NULL },
  { "asciicircum",                       584, NULL },
  { "aring",                             556, NULL },
  { "grave",                             333, NULL },
  { "uogonek",                           611, NULL },
  { "bracketright",                      333, NULL },
  { "Iacute",                            278, NULL },
  { "ampersand",                         722, NULL },
  { "igrave",                            278, NULL },
  { "lacute",                            278, NULL },
  { "Ncaron",                            722, NULL },
  { "plus",                              584, NULL },
  { "uring",                             611, NULL },
  { "quotesinglbase",                    278, NULL },
  { "lcommaaccent",                      278, NULL },
  { "Yacute",                            667, NULL },
  { "ohungarumlaut",                     611, NULL },
  { "threesuperior",                     333, NULL },
  { "acute",                             333, NULL },
  { "section",                           556, NULL },
  { "dieresis",                          333, NULL },
  { "iacute",                            278, NULL },
  { "quotedblbase",                      500, NULL },
  { "ncaron",                            611, NULL },
  { "florin",                            556, NULL },
  { "yacute",                            556, NULL },
  { "Rcommaaccent",                      722, NULL },
  { "fi",                                611, NULL },
  { "fl",                                611, NULL },
  { "Acircumflex",                       722, NULL },
  { "Cacute",                            722, NULL },
  { "Icircumflex",                       278, NULL },
  { "guillemotleft",                     556, NULL },
  { "germandbls",                        611, NULL },
  { "Amacron",                           722, NULL },
  { "seven",                             556, NULL },
  { "Sacute",                            667, NULL },
  { "ordmasculine",                      365, NULL },
  { "dotlessi",                          278, NULL },
  { "sterling",                          556, NULL },
  { "notequal",                          549, NULL },
  { "Imacron",                           278, NULL },
  { "rcommaaccent",                      389, NULL },
  { "Zdotaccent",                        611, NULL },
  { "acircumflex",                       556, NULL },
  { "cacute",                            556, NULL },
  { "Ecaron",                            667, NULL },
  { "icircumflex",                       278, NULL },
  { "braceright",                        389, NULL },
  { "quotedblright",                     500, NULL },
  { "amacron",                           556, NULL },
  { "sacute",                            556, NULL },
  { "imacron",                           278, NULL },
  { "cent",                              556, NULL },
  { "currency",                          556, NULL },
  { "logicalnot",                        584, NULL },
  { "zdotaccent",                        500, NULL },
  { "Atilde",                            722, NULL },
  { "breve",                             333, NULL },
  { "bar",                               280, NULL },
  { "fraction",                          167, NULL },
  { "less",                              584, NULL },
  { "ecaron",                            556, NULL },
  { "guilsinglleft",                     333, NULL },
  { "exclam",                            333, NULL },
  { "period",                            278, NULL },
  { "Rcaron",                            722, NULL },
  { "Kcommaaccent",                      722, NULL },
  { "greater",                           584, NULL },
  { "atilde",                            556, NULL },
  { "brokenbar",                         280, NULL },
  { "quoteleft",                         278, NULL },
  { "Edotaccent",                        667, NULL },
  { "onesuperior",                       333, NULL }
};

static BuiltinFontWidth helveticaObliqueWidthsTab[] = {
  { "Ntilde",                            722, NULL },
  { "rcaron",                            333, NULL },
  { "kcommaaccent",                      500, NULL },
  { "Ncommaaccent",                      722, NULL },
  { "Zacute",                            611, NULL },
  { "comma",                             278, NULL },
  { "cedilla",                           333, NULL },
  { "plusminus",                         584, NULL },
  { "circumflex",                        333, NULL },
  { "dotaccent",                         333, NULL },
  { "edotaccent",                        556, NULL },
  { "asciitilde",                        584, NULL },
  { "colon",                             278, NULL },
  { "onehalf",                           834, NULL },
  { "dollar",                            556, NULL },
  { "Lcaron",                            556, NULL },
  { "ntilde",                            556, NULL },
  { "Aogonek",                           667, NULL },
  { "ncommaaccent",                      556, NULL },
  { "minus",                             584, NULL },
  { "Iogonek",                           278, NULL },
  { "zacute",                            500, NULL },
  { "yen",                               556, NULL },
  { "space",                             278, NULL },
  { "Omacron",                           778, NULL },
  { "questiondown",                      611, NULL },
  { "emdash",                           1000, NULL },
  { "Agrave",                            667, NULL },
  { "three",                             556, NULL },
  { "numbersign",                        556, NULL },
  { "lcaron",                            299, NULL },
  { "A",                                 667, NULL },
  { "B",                                 667, NULL },
  { "C",                                 722, NULL },
  { "aogonek",                           556, NULL },
  { "D",                                 722, NULL },
  { "E",                                 667, NULL },
  { "onequarter",                        834, NULL },
  { "F",                                 611, NULL },
  { "G",                                 778, NULL },
  { "H",                                 722, NULL },
  { "I",                                 278, NULL },
  { "J",                                 500, NULL },
  { "K",                                 667, NULL },
  { "iogonek",                           222, NULL },
  { "backslash",                         278, NULL },
  { "L",                                 556, NULL },
  { "periodcentered",                    278, NULL },
  { "M",                                 833, NULL },
  { "N",                                 722, NULL },
  { "omacron",                           556, NULL },
  { "Tcommaaccent",                      611, NULL },
  { "O",                                 778, NULL },
  { "P",                                 667, NULL },
  { "Q",                                 778, NULL },
  { "Uhungarumlaut",                     722, NULL },
  { "R",                                 722, NULL },
  { "Aacute",                            667, NULL },
  { "caron",                             333, NULL },
  { "S",                                 667, NULL },
  { "T",                                 611, NULL },
  { "U",                                 722, NULL },
  { "agrave",                            556, NULL },
  { "V",                                 667, NULL },
  { "W",                                 944, NULL },
  { "X",                                 667, NULL },
  { "question",                          556, NULL },
  { "equal",                             584, NULL },
  { "Y",                                 667, NULL },
  { "Z",                                 611, NULL },
  { "four",                              556, NULL },
  { "a",                                 556, NULL },
  { "Gcommaaccent",                      778, NULL },
  { "b",                                 556, NULL },
  { "c",                                 500, NULL },
  { "d",                                 556, NULL },
  { "e",                                 556, NULL },
  { "f",                                 278, NULL },
  { "g",                                 556, NULL },
  { "bullet",                            350, NULL },
  { "h",                                 556, NULL },
  { "i",                                 222, NULL },
  { "Oslash",                            778, NULL },
  { "dagger",                            556, NULL },
  { "j",                                 222, NULL },
  { "k",                                 500, NULL },
  { "l",                                 222, NULL },
  { "m",                                 833, NULL },
  { "n",                                 556, NULL },
  { "tcommaaccent",                      278, NULL },
  { "o",                                 556, NULL },
  { "ordfeminine",                       370, NULL },
  { "ring",                              333, NULL },
  { "p",                                 556, NULL },
  { "q",                                 556, NULL },
  { "uhungarumlaut",                     556, NULL },
  { "r",                                 333, NULL },
  { "twosuperior",                       333, NULL },
  { "aacute",                            556, NULL },
  { "s",                                 500, NULL },
  { "OE",                               1000, NULL },
  { "t",                                 278, NULL },
  { "divide",                            584, NULL },
  { "u",                                 556, NULL },
  { "Ccaron",                            722, NULL },
  { "v",                                 500, NULL },
  { "w",                                 722, NULL },
  { "x",                                 500, NULL },
  { "y",                                 500, NULL },
  { "z",                                 500, NULL },
  { "Gbreve",                            778, NULL },
  { "commaaccent",                       250, NULL },
  { "hungarumlaut",                      333, NULL },
  { "Idotaccent",                        278, NULL },
  { "Nacute",                            722, NULL },
  { "quotedbl",                          355, NULL },
  { "gcommaaccent",                      556, NULL },
  { "mu",                                556, NULL },
  { "greaterequal",                      549, NULL },
  { "Scaron",                            667, NULL },
  { "Lslash",                            556, NULL },
  { "semicolon",                         278, NULL },
  { "oslash",                            611, NULL },
  { "lessequal",                         549, NULL },
  { "lozenge",                           471, NULL },
  { "parenright",                        333, NULL },
  { "ccaron",                            500, NULL },
  { "Ecircumflex",                       667, NULL },
  { "gbreve",                            556, NULL },
  { "trademark",                        1000, NULL },
  { "daggerdbl",                         556, NULL },
  { "nacute",                            556, NULL },
  { "macron",                            333, NULL },
  { "Otilde",                            778, NULL },
  { "Emacron",                           667, NULL },
  { "ellipsis",                         1000, NULL },
  { "scaron",                            500, NULL },
  { "AE",                               1000, NULL },
  { "Ucircumflex",                       722, NULL },
  { "lslash",                            222, NULL },
  { "quotedblleft",                      333, NULL },
  { "guilsinglright",                    333, NULL },
  { "hyphen",                            333, NULL },
  { "quotesingle",                       191, NULL },
  { "eight",                             556, NULL },
  { "exclamdown",                        333, NULL },
  { "endash",                            556, NULL },
  { "oe",                                944, NULL },
  { "Abreve",                            667, NULL },
  { "Umacron",                           722, NULL },
  { "ecircumflex",                       556, NULL },
  { "Adieresis",                         667, NULL },
  { "copyright",                         737, NULL },
  { "Egrave",                            667, NULL },
  { "slash",                             278, NULL },
  { "Edieresis",                         667, NULL },
  { "otilde",                            556, NULL },
  { "Idieresis",                         278, NULL },
  { "parenleft",                         333, NULL },
  { "one",                               556, NULL },
  { "emacron",                           556, NULL },
  { "Odieresis",                         778, NULL },
  { "ucircumflex",                       556, NULL },
  { "bracketleft",                       278, NULL },
  { "Ugrave",                            722, NULL },
  { "quoteright",                        222, NULL },
  { "Udieresis",                         722, NULL },
  { "perthousand",                      1000, NULL },
  { "Ydieresis",                         667, NULL },
  { "umacron",                           556, NULL },
  { "abreve",                            556, NULL },
  { "Eacute",                            667, NULL },
  { "adieresis",                         556, NULL },
  { "egrave",                            556, NULL },
  { "edieresis",                         556, NULL },
  { "idieresis",                         278, NULL },
  { "Eth",                               722, NULL },
  { "ae",                                889, NULL },
  { "asterisk",                          389, NULL },
  { "odieresis",                         556, NULL },
  { "Uacute",                            722, NULL },
  { "ugrave",                            556, NULL },
  { "nine",                              556, NULL },
  { "five",                              556, NULL },
  { "udieresis",                         556, NULL },
  { "Zcaron",                            611, NULL },
  { "Scommaaccent",                      667, NULL },
  { "threequarters",                     834, NULL },
  { "guillemotright",                    556, NULL },
  { "Ccedilla",                          722, NULL },
  { "ydieresis",                         500, NULL },
  { "tilde",                             333, NULL },
  { "at",                               1015, NULL },
  { "eacute",                            556, NULL },
  { "underscore",                        556, NULL },
  { "Euro",                              556, NULL },
  { "Dcroat",                            722, NULL },
  { "multiply",                          584, NULL },
  { "zero",                              556, NULL },
  { "eth",                               556, NULL },
  { "Scedilla",                          667, NULL },
  { "Ograve",                            778, NULL },
  { "Racute",                            722, NULL },
  { "partialdiff",                       476, NULL },
  { "uacute",                            556, NULL },
  { "braceleft",                         334, NULL },
  { "Thorn",                             667, NULL },
  { "zcaron",                            500, NULL },
  { "scommaaccent",                      500, NULL },
  { "ccedilla",                          500, NULL },
  { "Dcaron",                            722, NULL },
  { "dcroat",                            556, NULL },
  { "Ocircumflex",                       778, NULL },
  { "Oacute",                            778, NULL },
  { "scedilla",                          500, NULL },
  { "ogonek",                            333, NULL },
  { "ograve",                            556, NULL },
  { "racute",                            333, NULL },
  { "Tcaron",                            611, NULL },
  { "Eogonek",                           667, NULL },
  { "thorn",                             556, NULL },
  { "degree",                            400, NULL },
  { "registered",                        737, NULL },
  { "radical",                           453, NULL },
  { "Aring",                             667, NULL },
  { "percent",                           889, NULL },
  { "six",                               556, NULL },
  { "paragraph",                         537, NULL },
  { "dcaron",                            643, NULL },
  { "Uogonek",                           722, NULL },
  { "two",                               556, NULL },
  { "summation",                         600, NULL },
  { "Igrave",                            278, NULL },
  { "Lacute",                            556, NULL },
  { "ocircumflex",                       556, NULL },
  { "oacute",                            556, NULL },
  { "Uring",                             722, NULL },
  { "Lcommaaccent",                      556, NULL },
  { "tcaron",                            317, NULL },
  { "eogonek",                           556, NULL },
  { "Delta",                             612, NULL },
  { "Ohungarumlaut",                     778, NULL },
  { "asciicircum",                       469, NULL },
  { "aring",                             556, NULL },
  { "grave",                             333, NULL },
  { "uogonek",                           556, NULL },
  { "bracketright",                      278, NULL },
  { "Iacute",                            278, NULL },
  { "ampersand",                         667, NULL },
  { "igrave",                            278, NULL },
  { "lacute",                            222, NULL },
  { "Ncaron",                            722, NULL },
  { "plus",                              584, NULL },
  { "uring",                             556, NULL },
  { "quotesinglbase",                    222, NULL },
  { "lcommaaccent",                      222, NULL },
  { "Yacute",                            667, NULL },
  { "ohungarumlaut",                     556, NULL },
  { "threesuperior",                     333, NULL },
  { "acute",                             333, NULL },
  { "section",                           556, NULL },
  { "dieresis",                          333, NULL },
  { "iacute",                            278, NULL },
  { "quotedblbase",                      333, NULL },
  { "ncaron",                            556, NULL },
  { "florin",                            556, NULL },
  { "yacute",                            500, NULL },
  { "Rcommaaccent",                      722, NULL },
  { "fi",                                500, NULL },
  { "fl",                                500, NULL },
  { "Acircumflex",                       667, NULL },
  { "Cacute",                            722, NULL },
  { "Icircumflex",                       278, NULL },
  { "guillemotleft",                     556, NULL },
  { "germandbls",                        611, NULL },
  { "Amacron",                           667, NULL },
  { "seven",                             556, NULL },
  { "Sacute",                            667, NULL },
  { "ordmasculine",                      365, NULL },
  { "dotlessi",                          278, NULL },
  { "sterling",                          556, NULL },
  { "notequal",                          549, NULL },
  { "Imacron",                           278, NULL },
  { "rcommaaccent",                      333, NULL },
  { "Zdotaccent",                        611, NULL },
  { "acircumflex",                       556, NULL },
  { "cacute",                            500, NULL },
  { "Ecaron",                            667, NULL },
  { "icircumflex",                       278, NULL },
  { "braceright",                        334, NULL },
  { "quotedblright",                     333, NULL },
  { "amacron",                           556, NULL },
  { "sacute",                            500, NULL },
  { "imacron",                           278, NULL },
  { "cent",                              556, NULL },
  { "currency",                          556, NULL },
  { "logicalnot",                        584, NULL },
  { "zdotaccent",                        500, NULL },
  { "Atilde",                            667, NULL },
  { "breve",                             333, NULL },
  { "bar",                               260, NULL },
  { "fraction",                          167, NULL },
  { "less",                              584, NULL },
  { "ecaron",                            556, NULL },
  { "guilsinglleft",                     333, NULL },
  { "exclam",                            278, NULL },
  { "period",                            278, NULL },
  { "Rcaron",                            722, NULL },
  { "Kcommaaccent",                      667, NULL },
  { "greater",                           584, NULL },
  { "atilde",                            556, NULL },
  { "brokenbar",                         260, NULL },
  { "quoteleft",                         222, NULL },
  { "Edotaccent",                        667, NULL },
  { "onesuperior",                       333, NULL }
};

static BuiltinFontWidth symbolWidthsTab[] = {
  { "bracketleftex",                     384, NULL },
  { "alpha",                             631, NULL },
  { "union",                             768, NULL },
  { "infinity",                          713, NULL },
  { "comma",                             250, NULL },
  { "copyrightsans",                     790, NULL },
  { "plusminus",                         549, NULL },
  { "arrowup",                           603, NULL },
  { "apple",                             790, NULL },
  { "parenleftbt",                       384, NULL },
  { "notelement",                        713, NULL },
  { "colon",                             278, NULL },
  { "beta",                              549, NULL },
  { "braceleftbt",                       494, NULL },
  { "Lambda",                            686, NULL },
  { "Phi",                               763, NULL },
  { "minus",                             549, NULL },
  { "space",                             250, NULL },
  { "Sigma",                             592, NULL },
  { "approxequal",                       549, NULL },
  { "minute",                            247, NULL },
  { "circleplus",                        768, NULL },
  { "Omicron",                           722, NULL },
  { "three",                             500, NULL },
  { "numbersign",                        500, NULL },
  { "lambda",                            549, NULL },
  { "phi",                               521, NULL },
  { "aleph",                             823, NULL },
  { "Tau",                               611, NULL },
  { "spade",                             753, NULL },
  { "logicaland",                        603, NULL },
  { "sigma",                             603, NULL },
  { "propersuperset",                    713, NULL },
  { "omicron",                           549, NULL },
  { "question",                          444, NULL },
  { "equal",                             549, NULL },
  { "Epsilon",                           611, NULL },
  { "emptyset",                          823, NULL },
  { "diamond",                           753, NULL },
  { "four",                              500, NULL },
  { "Mu",                                889, NULL },
  { "parenlefttp",                       384, NULL },
  { "club",                              753, NULL },
  { "bullet",                            460, NULL },
  { "Omega",                             768, NULL },
  { "tau",                               439, NULL },
  { "Upsilon",                           690, NULL },
  { "bracelefttp",                       494, NULL },
  { "heart",                             753, NULL },
  { "divide",                            549, NULL },
  { "epsilon",                           439, NULL },
  { "logicalor",                         603, NULL },
  { "parenleftex",                       384, NULL },
  { "greaterequal",                      549, NULL },
  { "mu",                                576, NULL },
  { "Nu",                                722, NULL },
  { "therefore",                         863, NULL },
  { "notsubset",                         713, NULL },
  { "omega",                             686, NULL },
  { "semicolon",                         278, NULL },
  { "element",                           713, NULL },
  { "upsilon",                           576, NULL },
  { "existential",                       549, NULL },
  { "integralbt",                        686, NULL },
  { "lessequal",                         549, NULL },
  { "phi1",                              603, NULL },
  { "lozenge",                           494, NULL },
  { "trademarkserif",                    890, NULL },
  { "parenright",                        333, NULL },
  { "reflexsuperset",                    713, NULL },
  { "sigma1",                            439, NULL },
  { "nu",                                521, NULL },
  { "Gamma",                             603, NULL },
  { "angleright",                        329, NULL },
  { "ellipsis",                         1000, NULL },
  { "Rho",                               556, NULL },
  { "parenrightbt",                      384, NULL },
  { "radicalex",                         500, NULL },
  { "eight",                             500, NULL },
  { "angleleft",                         329, NULL },
  { "arrowdbldown",                      603, NULL },
  { "congruent",                         549, NULL },
  { "Theta",                             741, NULL },
  { "intersection",                      768, NULL },
  { "Pi",                                768, NULL },
  { "slash",                             278, NULL },
  { "registerserif",                     790, NULL },
  { "parenleft",                         333, NULL },
  { "one",                               500, NULL },
  { "gamma",                             411, NULL },
  { "bracketleft",                       333, NULL },
  { "rho",                               549, NULL },
  { "circlemultiply",                    768, NULL },
  { "Chi",                               722, NULL },
  { "theta",                             521, NULL },
  { "pi",                                549, NULL },
  { "integraltp",                        686, NULL },
  { "Eta",                               722, NULL },
  { "product",                           823, NULL },
  { "nine",                              500, NULL },
  { "five",                              500, NULL },
  { "propersubset",                      713, NULL },
  { "bracketrightbt",                    384, NULL },
  { "trademarksans",                     786, NULL },
  { "dotmath",                           250, NULL },
  { "integralex",                        686, NULL },
  { "chi",                               549, NULL },
  { "parenrighttp",                      384, NULL },
  { "eta",                               603, NULL },
  { "underscore",                        500, NULL },
  { "Euro",                              750, NULL },
  { "multiply",                          549, NULL },
  { "zero",                              500, NULL },
  { "partialdiff",                       494, NULL },
  { "angle",                             768, NULL },
  { "arrowdblleft",                      987, NULL },
  { "braceleft",                         480, NULL },
  { "parenrightex",                      384, NULL },
  { "Rfraktur",                          795, NULL },
  { "Zeta",                              611, NULL },
  { "braceex",                           494, NULL },
  { "arrowdblup",                        603, NULL },
  { "arrowdown",                         603, NULL },
  { "Ifraktur",                          686, NULL },
  { "degree",                            400, NULL },
  { "Iota",                              333, NULL },
  { "perpendicular",                     658, NULL },
  { "radical",                           549, NULL },
  { "asteriskmath",                      500, NULL },
  { "percent",                           833, NULL },
  { "zeta",                              494, NULL },
  { "six",                               500, NULL },
  { "two",                               500, NULL },
  { "weierstrass",                       987, NULL },
  { "summation",                         713, NULL },
  { "bracketrighttp",                    384, NULL },
  { "carriagereturn",                    658, NULL },
  { "suchthat",                          439, NULL },
  { "arrowvertex",                       603, NULL },
  { "Delta",                             612, NULL },
  { "iota",                              329, NULL },
  { "arrowhorizex",                     1000, NULL },
  { "bracketrightex",                    384, NULL },
  { "bracketright",                      333, NULL },
  { "ampersand",                         778, NULL },
  { "plus",                              549, NULL },
  { "proportional",                      713, NULL },
  { "delta",                             494, NULL },
  { "copyrightserif",                    790, NULL },
  { "bracerightmid",                     494, NULL },
  { "arrowleft",                         987, NULL },
  { "second",                            411, NULL },
  { "arrowdblboth",                     1042, NULL },
  { "florin",                            500, NULL },
  { "Psi",                               795, NULL },
  { "bracerightbt",                      494, NULL },
  { "bracketleftbt",                     384, NULL },
  { "seven",                             500, NULL },
  { "braceleftmid",                      494, NULL },
  { "notequal",                          549, NULL },
  { "psi",                               686, NULL },
  { "equivalence",                       549, NULL },
  { "universal",                         713, NULL },
  { "arrowdblright",                     987, NULL },
  { "braceright",                        480, NULL },
  { "reflexsubset",                      713, NULL },
  { "Xi",                                645, NULL },
  { "theta1",                            631, NULL },
  { "logicalnot",                        713, NULL },
  { "Kappa",                             722, NULL },
  { "similar",                           549, NULL },
  { "bar",                               200, NULL },
  { "fraction",                          167, NULL },
  { "less",                              549, NULL },
  { "registersans",                      790, NULL },
  { "omega1",                            713, NULL },
  { "exclam",                            333, NULL },
  { "Upsilon1",                          620, NULL },
  { "bracerighttp",                      494, NULL },
  { "xi",                                493, NULL },
  { "period",                            250, NULL },
  { "Alpha",                             722, NULL },
  { "arrowright",                        987, NULL },
  { "greater",                           549, NULL },
  { "bracketlefttp",                     384, NULL },
  { "kappa",                             549, NULL },
  { "gradient",                          713, NULL },
  { "integral",                          274, NULL },
  { "arrowboth",                        1042, NULL },
  { "Beta",                              667, NULL }
};

static BuiltinFontWidth timesBoldWidthsTab[] = {
  { "Ntilde",                            722, NULL },
  { "rcaron",                            444, NULL },
  { "kcommaaccent",                      556, NULL },
  { "Ncommaaccent",                      722, NULL },
  { "Zacute",                            667, NULL },
  { "comma",                             250, NULL },
  { "cedilla",                           333, NULL },
  { "plusminus",                         570, NULL },
  { "circumflex",                        333, NULL },
  { "dotaccent",                         333, NULL },
  { "edotaccent",                        444, NULL },
  { "asciitilde",                        520, NULL },
  { "colon",                             333, NULL },
  { "onehalf",                           750, NULL },
  { "dollar",                            500, NULL },
  { "Lcaron",                            667, NULL },
  { "ntilde",                            556, NULL },
  { "Aogonek",                           722, NULL },
  { "ncommaaccent",                      556, NULL },
  { "minus",                             570, NULL },
  { "Iogonek",                           389, NULL },
  { "zacute",                            444, NULL },
  { "yen",                               500, NULL },
  { "space",                             250, NULL },
  { "Omacron",                           778, NULL },
  { "questiondown",                      500, NULL },
  { "emdash",                           1000, NULL },
  { "Agrave",                            722, NULL },
  { "three",                             500, NULL },
  { "numbersign",                        500, NULL },
  { "lcaron",                            394, NULL },
  { "A",                                 722, NULL },
  { "B",                                 667, NULL },
  { "C",                                 722, NULL },
  { "aogonek",                           500, NULL },
  { "D",                                 722, NULL },
  { "E",                                 667, NULL },
  { "onequarter",                        750, NULL },
  { "F",                                 611, NULL },
  { "G",                                 778, NULL },
  { "H",                                 778, NULL },
  { "I",                                 389, NULL },
  { "J",                                 500, NULL },
  { "K",                                 778, NULL },
  { "iogonek",                           278, NULL },
  { "backslash",                         278, NULL },
  { "L",                                 667, NULL },
  { "periodcentered",                    250, NULL },
  { "M",                                 944, NULL },
  { "N",                                 722, NULL },
  { "omacron",                           500, NULL },
  { "Tcommaaccent",                      667, NULL },
  { "O",                                 778, NULL },
  { "P",                                 611, NULL },
  { "Q",                                 778, NULL },
  { "Uhungarumlaut",                     722, NULL },
  { "R",                                 722, NULL },
  { "Aacute",                            722, NULL },
  { "caron",                             333, NULL },
  { "S",                                 556, NULL },
  { "T",                                 667, NULL },
  { "U",                                 722, NULL },
  { "agrave",                            500, NULL },
  { "V",                                 722, NULL },
  { "W",                                1000, NULL },
  { "X",                                 722, NULL },
  { "question",                          500, NULL },
  { "equal",                             570, NULL },
  { "Y",                                 722, NULL },
  { "Z",                                 667, NULL },
  { "four",                              500, NULL },
  { "a",                                 500, NULL },
  { "Gcommaaccent",                      778, NULL },
  { "b",                                 556, NULL },
  { "c",                                 444, NULL },
  { "d",                                 556, NULL },
  { "e",                                 444, NULL },
  { "f",                                 333, NULL },
  { "g",                                 500, NULL },
  { "bullet",                            350, NULL },
  { "h",                                 556, NULL },
  { "i",                                 278, NULL },
  { "Oslash",                            778, NULL },
  { "dagger",                            500, NULL },
  { "j",                                 333, NULL },
  { "k",                                 556, NULL },
  { "l",                                 278, NULL },
  { "m",                                 833, NULL },
  { "n",                                 556, NULL },
  { "tcommaaccent",                      333, NULL },
  { "o",                                 500, NULL },
  { "ordfeminine",                       300, NULL },
  { "ring",                              333, NULL },
  { "p",                                 556, NULL },
  { "q",                                 556, NULL },
  { "uhungarumlaut",                     556, NULL },
  { "r",                                 444, NULL },
  { "twosuperior",                       300, NULL },
  { "aacute",                            500, NULL },
  { "s",                                 389, NULL },
  { "OE",                               1000, NULL },
  { "t",                                 333, NULL },
  { "divide",                            570, NULL },
  { "u",                                 556, NULL },
  { "Ccaron",                            722, NULL },
  { "v",                                 500, NULL },
  { "w",                                 722, NULL },
  { "x",                                 500, NULL },
  { "y",                                 500, NULL },
  { "z",                                 444, NULL },
  { "Gbreve",                            778, NULL },
  { "commaaccent",                       250, NULL },
  { "hungarumlaut",                      333, NULL },
  { "Idotaccent",                        389, NULL },
  { "Nacute",                            722, NULL },
  { "quotedbl",                          555, NULL },
  { "gcommaaccent",                      500, NULL },
  { "mu",                                556, NULL },
  { "greaterequal",                      549, NULL },
  { "Scaron",                            556, NULL },
  { "Lslash",                            667, NULL },
  { "semicolon",                         333, NULL },
  { "oslash",                            500, NULL },
  { "lessequal",                         549, NULL },
  { "lozenge",                           494, NULL },
  { "parenright",                        333, NULL },
  { "ccaron",                            444, NULL },
  { "Ecircumflex",                       667, NULL },
  { "gbreve",                            500, NULL },
  { "trademark",                        1000, NULL },
  { "daggerdbl",                         500, NULL },
  { "nacute",                            556, NULL },
  { "macron",                            333, NULL },
  { "Otilde",                            778, NULL },
  { "Emacron",                           667, NULL },
  { "ellipsis",                         1000, NULL },
  { "scaron",                            389, NULL },
  { "AE",                               1000, NULL },
  { "Ucircumflex",                       722, NULL },
  { "lslash",                            278, NULL },
  { "quotedblleft",                      500, NULL },
  { "guilsinglright",                    333, NULL },
  { "hyphen",                            333, NULL },
  { "quotesingle",                       278, NULL },
  { "eight",                             500, NULL },
  { "exclamdown",                        333, NULL },
  { "endash",                            500, NULL },
  { "oe",                                722, NULL },
  { "Abreve",                            722, NULL },
  { "Umacron",                           722, NULL },
  { "ecircumflex",                       444, NULL },
  { "Adieresis",                         722, NULL },
  { "copyright",                         747, NULL },
  { "Egrave",                            667, NULL },
  { "slash",                             278, NULL },
  { "Edieresis",                         667, NULL },
  { "otilde",                            500, NULL },
  { "Idieresis",                         389, NULL },
  { "parenleft",                         333, NULL },
  { "one",                               500, NULL },
  { "emacron",                           444, NULL },
  { "Odieresis",                         778, NULL },
  { "ucircumflex",                       556, NULL },
  { "bracketleft",                       333, NULL },
  { "Ugrave",                            722, NULL },
  { "quoteright",                        333, NULL },
  { "Udieresis",                         722, NULL },
  { "perthousand",                      1000, NULL },
  { "Ydieresis",                         722, NULL },
  { "umacron",                           556, NULL },
  { "abreve",                            500, NULL },
  { "Eacute",                            667, NULL },
  { "adieresis",                         500, NULL },
  { "egrave",                            444, NULL },
  { "edieresis",                         444, NULL },
  { "idieresis",                         278, NULL },
  { "Eth",                               722, NULL },
  { "ae",                                722, NULL },
  { "asterisk",                          500, NULL },
  { "odieresis",                         500, NULL },
  { "Uacute",                            722, NULL },
  { "ugrave",                            556, NULL },
  { "nine",                              500, NULL },
  { "five",                              500, NULL },
  { "udieresis",                         556, NULL },
  { "Zcaron",                            667, NULL },
  { "Scommaaccent",                      556, NULL },
  { "threequarters",                     750, NULL },
  { "guillemotright",                    500, NULL },
  { "Ccedilla",                          722, NULL },
  { "ydieresis",                         500, NULL },
  { "tilde",                             333, NULL },
  { "at",                                930, NULL },
  { "eacute",                            444, NULL },
  { "underscore",                        500, NULL },
  { "Euro",                              500, NULL },
  { "Dcroat",                            722, NULL },
  { "multiply",                          570, NULL },
  { "zero",                              500, NULL },
  { "eth",                               500, NULL },
  { "Scedilla",                          556, NULL },
  { "Ograve",                            778, NULL },
  { "Racute",                            722, NULL },
  { "partialdiff",                       494, NULL },
  { "uacute",                            556, NULL },
  { "braceleft",                         394, NULL },
  { "Thorn",                             611, NULL },
  { "zcaron",                            444, NULL },
  { "scommaaccent",                      389, NULL },
  { "ccedilla",                          444, NULL },
  { "Dcaron",                            722, NULL },
  { "dcroat",                            556, NULL },
  { "Ocircumflex",                       778, NULL },
  { "Oacute",                            778, NULL },
  { "scedilla",                          389, NULL },
  { "ogonek",                            333, NULL },
  { "ograve",                            500, NULL },
  { "racute",                            444, NULL },
  { "Tcaron",                            667, NULL },
  { "Eogonek",                           667, NULL },
  { "thorn",                             556, NULL },
  { "degree",                            400, NULL },
  { "registered",                        747, NULL },
  { "radical",                           549, NULL },
  { "Aring",                             722, NULL },
  { "percent",                          1000, NULL },
  { "six",                               500, NULL },
  { "paragraph",                         540, NULL },
  { "dcaron",                            672, NULL },
  { "Uogonek",                           722, NULL },
  { "two",                               500, NULL },
  { "summation",                         600, NULL },
  { "Igrave",                            389, NULL },
  { "Lacute",                            667, NULL },
  { "ocircumflex",                       500, NULL },
  { "oacute",                            500, NULL },
  { "Uring",                             722, NULL },
  { "Lcommaaccent",                      667, NULL },
  { "tcaron",                            416, NULL },
  { "eogonek",                           444, NULL },
  { "Delta",                             612, NULL },
  { "Ohungarumlaut",                     778, NULL },
  { "asciicircum",                       581, NULL },
  { "aring",                             500, NULL },
  { "grave",                             333, NULL },
  { "uogonek",                           556, NULL },
  { "bracketright",                      333, NULL },
  { "Iacute",                            389, NULL },
  { "ampersand",                         833, NULL },
  { "igrave",                            278, NULL },
  { "lacute",                            278, NULL },
  { "Ncaron",                            722, NULL },
  { "plus",                              570, NULL },
  { "uring",                             556, NULL },
  { "quotesinglbase",                    333, NULL },
  { "lcommaaccent",                      278, NULL },
  { "Yacute",                            722, NULL },
  { "ohungarumlaut",                     500, NULL },
  { "threesuperior",                     300, NULL },
  { "acute",                             333, NULL },
  { "section",                           500, NULL },
  { "dieresis",                          333, NULL },
  { "iacute",                            278, NULL },
  { "quotedblbase",                      500, NULL },
  { "ncaron",                            556, NULL },
  { "florin",                            500, NULL },
  { "yacute",                            500, NULL },
  { "Rcommaaccent",                      722, NULL },
  { "fi",                                556, NULL },
  { "fl",                                556, NULL },
  { "Acircumflex",                       722, NULL },
  { "Cacute",                            722, NULL },
  { "Icircumflex",                       389, NULL },
  { "guillemotleft",                     500, NULL },
  { "germandbls",                        556, NULL },
  { "Amacron",                           722, NULL },
  { "seven",                             500, NULL },
  { "Sacute",                            556, NULL },
  { "ordmasculine",                      330, NULL },
  { "dotlessi",                          278, NULL },
  { "sterling",                          500, NULL },
  { "notequal",                          549, NULL },
  { "Imacron",                           389, NULL },
  { "rcommaaccent",                      444, NULL },
  { "Zdotaccent",                        667, NULL },
  { "acircumflex",                       500, NULL },
  { "cacute",                            444, NULL },
  { "Ecaron",                            667, NULL },
  { "icircumflex",                       278, NULL },
  { "braceright",                        394, NULL },
  { "quotedblright",                     500, NULL },
  { "amacron",                           500, NULL },
  { "sacute",                            389, NULL },
  { "imacron",                           278, NULL },
  { "cent",                              500, NULL },
  { "currency",                          500, NULL },
  { "logicalnot",                        570, NULL },
  { "zdotaccent",                        444, NULL },
  { "Atilde",                            722, NULL },
  { "breve",                             333, NULL },
  { "bar",                               220, NULL },
  { "fraction",                          167, NULL },
  { "less",                              570, NULL },
  { "ecaron",                            444, NULL },
  { "guilsinglleft",                     333, NULL },
  { "exclam",                            333, NULL },
  { "period",                            250, NULL },
  { "Rcaron",                            722, NULL },
  { "Kcommaaccent",                      778, NULL },
  { "greater",                           570, NULL },
  { "atilde",                            500, NULL },
  { "brokenbar",                         220, NULL },
  { "quoteleft",                         333, NULL },
  { "Edotaccent",                        667, NULL },
  { "onesuperior",                       300, NULL }
};

static BuiltinFontWidth timesBoldItalicWidthsTab[] = {
  { "Ntilde",                            722, NULL },
  { "rcaron",                            389, NULL },
  { "kcommaaccent",                      500, NULL },
  { "Ncommaaccent",                      722, NULL },
  { "Zacute",                            611, NULL },
  { "comma",                             250, NULL },
  { "cedilla",                           333, NULL },
  { "plusminus",                         570, NULL },
  { "circumflex",                        333, NULL },
  { "dotaccent",                         333, NULL },
  { "edotaccent",                        444, NULL },
  { "asciitilde",                        570, NULL },
  { "colon",                             333, NULL },
  { "onehalf",                           750, NULL },
  { "dollar",                            500, NULL },
  { "Lcaron",                            611, NULL },
  { "ntilde",                            556, NULL },
  { "Aogonek",                           667, NULL },
  { "ncommaaccent",                      556, NULL },
  { "minus",                             606, NULL },
  { "Iogonek",                           389, NULL },
  { "zacute",                            389, NULL },
  { "yen",                               500, NULL },
  { "space",                             250, NULL },
  { "Omacron",                           722, NULL },
  { "questiondown",                      500, NULL },
  { "emdash",                           1000, NULL },
  { "Agrave",                            667, NULL },
  { "three",                             500, NULL },
  { "numbersign",                        500, NULL },
  { "lcaron",                            382, NULL },
  { "A",                                 667, NULL },
  { "B",                                 667, NULL },
  { "C",                                 667, NULL },
  { "aogonek",                           500, NULL },
  { "D",                                 722, NULL },
  { "E",                                 667, NULL },
  { "onequarter",                        750, NULL },
  { "F",                                 667, NULL },
  { "G",                                 722, NULL },
  { "H",                                 778, NULL },
  { "I",                                 389, NULL },
  { "J",                                 500, NULL },
  { "K",                                 667, NULL },
  { "iogonek",                           278, NULL },
  { "backslash",                         278, NULL },
  { "L",                                 611, NULL },
  { "periodcentered",                    250, NULL },
  { "M",                                 889, NULL },
  { "N",                                 722, NULL },
  { "omacron",                           500, NULL },
  { "Tcommaaccent",                      611, NULL },
  { "O",                                 722, NULL },
  { "P",                                 611, NULL },
  { "Q",                                 722, NULL },
  { "Uhungarumlaut",                     722, NULL },
  { "R",                                 667, NULL },
  { "Aacute",                            667, NULL },
  { "caron",                             333, NULL },
  { "S",                                 556, NULL },
  { "T",                                 611, NULL },
  { "U",                                 722, NULL },
  { "agrave",                            500, NULL },
  { "V",                                 667, NULL },
  { "W",                                 889, NULL },
  { "X",                                 667, NULL },
  { "question",                          500, NULL },
  { "equal",                             570, NULL },
  { "Y",                                 611, NULL },
  { "Z",                                 611, NULL },
  { "four",                              500, NULL },
  { "a",                                 500, NULL },
  { "Gcommaaccent",                      722, NULL },
  { "b",                                 500, NULL },
  { "c",                                 444, NULL },
  { "d",                                 500, NULL },
  { "e",                                 444, NULL },
  { "f",                                 333, NULL },
  { "g",                                 500, NULL },
  { "bullet",                            350, NULL },
  { "h",                                 556, NULL },
  { "i",                                 278, NULL },
  { "Oslash",                            722, NULL },
  { "dagger",                            500, NULL },
  { "j",                                 278, NULL },
  { "k",                                 500, NULL },
  { "l",                                 278, NULL },
  { "m",                                 778, NULL },
  { "n",                                 556, NULL },
  { "tcommaaccent",                      278, NULL },
  { "o",                                 500, NULL },
  { "ordfeminine",                       266, NULL },
  { "ring",                              333, NULL },
  { "p",                                 500, NULL },
  { "q",                                 500, NULL },
  { "uhungarumlaut",                     556, NULL },
  { "r",                                 389, NULL },
  { "twosuperior",                       300, NULL },
  { "aacute",                            500, NULL },
  { "s",                                 389, NULL },
  { "OE",                                944, NULL },
  { "t",                                 278, NULL },
  { "divide",                            570, NULL },
  { "u",                                 556, NULL },
  { "Ccaron",                            667, NULL },
  { "v",                                 444, NULL },
  { "w",                                 667, NULL },
  { "x",                                 500, NULL },
  { "y",                                 444, NULL },
  { "z",                                 389, NULL },
  { "Gbreve",                            722, NULL },
  { "commaaccent",                       250, NULL },
  { "hungarumlaut",                      333, NULL },
  { "Idotaccent",                        389, NULL },
  { "Nacute",                            722, NULL },
  { "quotedbl",                          555, NULL },
  { "gcommaaccent",                      500, NULL },
  { "mu",                                576, NULL },
  { "greaterequal",                      549, NULL },
  { "Scaron",                            556, NULL },
  { "Lslash",                            611, NULL },
  { "semicolon",                         333, NULL },
  { "oslash",                            500, NULL },
  { "lessequal",                         549, NULL },
  { "lozenge",                           494, NULL },
  { "parenright",                        333, NULL },
  { "ccaron",                            444, NULL },
  { "Ecircumflex",                       667, NULL },
  { "gbreve",                            500, NULL },
  { "trademark",                        1000, NULL },
  { "daggerdbl",                         500, NULL },
  { "nacute",                            556, NULL },
  { "macron",                            333, NULL },
  { "Otilde",                            722, NULL },
  { "Emacron",                           667, NULL },
  { "ellipsis",                         1000, NULL },
  { "scaron",                            389, NULL },
  { "AE",                                944, NULL },
  { "Ucircumflex",                       722, NULL },
  { "lslash",                            278, NULL },
  { "quotedblleft",                      500, NULL },
  { "guilsinglright",                    333, NULL },
  { "hyphen",                            333, NULL },
  { "quotesingle",                       278, NULL },
  { "eight",                             500, NULL },
  { "exclamdown",                        389, NULL },
  { "endash",                            500, NULL },
  { "oe",                                722, NULL },
  { "Abreve",                            667, NULL },
  { "Umacron",                           722, NULL },
  { "ecircumflex",                       444, NULL },
  { "Adieresis",                         667, NULL },
  { "copyright",                         747, NULL },
  { "Egrave",                            667, NULL },
  { "slash",                             278, NULL },
  { "Edieresis",                         667, NULL },
  { "otilde",                            500, NULL },
  { "Idieresis",                         389, NULL },
  { "parenleft",                         333, NULL },
  { "one",                               500, NULL },
  { "emacron",                           444, NULL },
  { "Odieresis",                         722, NULL },
  { "ucircumflex",                       556, NULL },
  { "bracketleft",                       333, NULL },
  { "Ugrave",                            722, NULL },
  { "quoteright",                        333, NULL },
  { "Udieresis",                         722, NULL },
  { "perthousand",                      1000, NULL },
  { "Ydieresis",                         611, NULL },
  { "umacron",                           556, NULL },
  { "abreve",                            500, NULL },
  { "Eacute",                            667, NULL },
  { "adieresis",                         500, NULL },
  { "egrave",                            444, NULL },
  { "edieresis",                         444, NULL },
  { "idieresis",                         278, NULL },
  { "Eth",                               722, NULL },
  { "ae",                                722, NULL },
  { "asterisk",                          500, NULL },
  { "odieresis",                         500, NULL },
  { "Uacute",                            722, NULL },
  { "ugrave",                            556, NULL },
  { "nine",                              500, NULL },
  { "five",                              500, NULL },
  { "udieresis",                         556, NULL },
  { "Zcaron",                            611, NULL },
  { "Scommaaccent",                      556, NULL },
  { "threequarters",                     750, NULL },
  { "guillemotright",                    500, NULL },
  { "Ccedilla",                          667, NULL },
  { "ydieresis",                         444, NULL },
  { "tilde",                             333, NULL },
  { "at",                                832, NULL },
  { "eacute",                            444, NULL },
  { "underscore",                        500, NULL },
  { "Euro",                              500, NULL },
  { "Dcroat",                            722, NULL },
  { "multiply",                          570, NULL },
  { "zero",                              500, NULL },
  { "eth",                               500, NULL },
  { "Scedilla",                          556, NULL },
  { "Ograve",                            722, NULL },
  { "Racute",                            667, NULL },
  { "partialdiff",                       494, NULL },
  { "uacute",                            556, NULL },
  { "braceleft",                         348, NULL },
  { "Thorn",                             611, NULL },
  { "zcaron",                            389, NULL },
  { "scommaaccent",                      389, NULL },
  { "ccedilla",                          444, NULL },
  { "Dcaron",                            722, NULL },
  { "dcroat",                            500, NULL },
  { "Ocircumflex",                       722, NULL },
  { "Oacute",                            722, NULL },
  { "scedilla",                          389, NULL },
  { "ogonek",                            333, NULL },
  { "ograve",                            500, NULL },
  { "racute",                            389, NULL },
  { "Tcaron",                            611, NULL },
  { "Eogonek",                           667, NULL },
  { "thorn",                             500, NULL },
  { "degree",                            400, NULL },
  { "registered",                        747, NULL },
  { "radical",                           549, NULL },
  { "Aring",                             667, NULL },
  { "percent",                           833, NULL },
  { "six",                               500, NULL },
  { "paragraph",                         500, NULL },
  { "dcaron",                            608, NULL },
  { "Uogonek",                           722, NULL },
  { "two",                               500, NULL },
  { "summation",                         600, NULL },
  { "Igrave",                            389, NULL },
  { "Lacute",                            611, NULL },
  { "ocircumflex",                       500, NULL },
  { "oacute",                            500, NULL },
  { "Uring",                             722, NULL },
  { "Lcommaaccent",                      611, NULL },
  { "tcaron",                            366, NULL },
  { "eogonek",                           444, NULL },
  { "Delta",                             612, NULL },
  { "Ohungarumlaut",                     722, NULL },
  { "asciicircum",                       570, NULL },
  { "aring",                             500, NULL },
  { "grave",                             333, NULL },
  { "uogonek",                           556, NULL },
  { "bracketright",                      333, NULL },
  { "Iacute",                            389, NULL },
  { "ampersand",                         778, NULL },
  { "igrave",                            278, NULL },
  { "lacute",                            278, NULL },
  { "Ncaron",                            722, NULL },
  { "plus",                              570, NULL },
  { "uring",                             556, NULL },
  { "quotesinglbase",                    333, NULL },
  { "lcommaaccent",                      278, NULL },
  { "Yacute",                            611, NULL },
  { "ohungarumlaut",                     500, NULL },
  { "threesuperior",                     300, NULL },
  { "acute",                             333, NULL },
  { "section",                           500, NULL },
  { "dieresis",                          333, NULL },
  { "iacute",                            278, NULL },
  { "quotedblbase",                      500, NULL },
  { "ncaron",                            556, NULL },
  { "florin",                            500, NULL },
  { "yacute",                            444, NULL },
  { "Rcommaaccent",                      667, NULL },
  { "fi",                                556, NULL },
  { "fl",                                556, NULL },
  { "Acircumflex",                       667, NULL },
  { "Cacute",                            667, NULL },
  { "Icircumflex",                       389, NULL },
  { "guillemotleft",                     500, NULL },
  { "germandbls",                        500, NULL },
  { "Amacron",                           667, NULL },
  { "seven",                             500, NULL },
  { "Sacute",                            556, NULL },
  { "ordmasculine",                      300, NULL },
  { "dotlessi",                          278, NULL },
  { "sterling",                          500, NULL },
  { "notequal",                          549, NULL },
  { "Imacron",                           389, NULL },
  { "rcommaaccent",                      389, NULL },
  { "Zdotaccent",                        611, NULL },
  { "acircumflex",                       500, NULL },
  { "cacute",                            444, NULL },
  { "Ecaron",                            667, NULL },
  { "icircumflex",                       278, NULL },
  { "braceright",                        348, NULL },
  { "quotedblright",                     500, NULL },
  { "amacron",                           500, NULL },
  { "sacute",                            389, NULL },
  { "imacron",                           278, NULL },
  { "cent",                              500, NULL },
  { "currency",                          500, NULL },
  { "logicalnot",                        606, NULL },
  { "zdotaccent",                        389, NULL },
  { "Atilde",                            667, NULL },
  { "breve",                             333, NULL },
  { "bar",                               220, NULL },
  { "fraction",                          167, NULL },
  { "less",                              570, NULL },
  { "ecaron",                            444, NULL },
  { "guilsinglleft",                     333, NULL },
  { "exclam",                            389, NULL },
  { "period",                            250, NULL },
  { "Rcaron",                            667, NULL },
  { "Kcommaaccent",                      667, NULL },
  { "greater",                           570, NULL },
  { "atilde",                            500, NULL },
  { "brokenbar",                         220, NULL },
  { "quoteleft",                         333, NULL },
  { "Edotaccent",                        667, NULL },
  { "onesuperior",                       300, NULL }
};

static BuiltinFontWidth timesItalicWidthsTab[] = {
  { "Ntilde",                            667, NULL },
  { "rcaron",                            389, NULL },
  { "kcommaaccent",                      444, NULL },
  { "Ncommaaccent",                      667, NULL },
  { "Zacute",                            556, NULL },
  { "comma",                             250, NULL },
  { "cedilla",                           333, NULL },
  { "plusminus",                         675, NULL },
  { "circumflex",                        333, NULL },
  { "dotaccent",                         333, NULL },
  { "edotaccent",                        444, NULL },
  { "asciitilde",                        541, NULL },
  { "colon",                             333, NULL },
  { "onehalf",                           750, NULL },
  { "dollar",                            500, NULL },
  { "Lcaron",                            611, NULL },
  { "ntilde",                            500, NULL },
  { "Aogonek",                           611, NULL },
  { "ncommaaccent",                      500, NULL },
  { "minus",                             675, NULL },
  { "Iogonek",                           333, NULL },
  { "zacute",                            389, NULL },
  { "yen",                               500, NULL },
  { "space",                             250, NULL },
  { "Omacron",                           722, NULL },
  { "questiondown",                      500, NULL },
  { "emdash",                            889, NULL },
  { "Agrave",                            611, NULL },
  { "three",                             500, NULL },
  { "numbersign",                        500, NULL },
  { "lcaron",                            300, NULL },
  { "A",                                 611, NULL },
  { "B",                                 611, NULL },
  { "C",                                 667, NULL },
  { "aogonek",                           500, NULL },
  { "D",                                 722, NULL },
  { "E",                                 611, NULL },
  { "onequarter",                        750, NULL },
  { "F",                                 611, NULL },
  { "G",                                 722, NULL },
  { "H",                                 722, NULL },
  { "I",                                 333, NULL },
  { "J",                                 444, NULL },
  { "K",                                 667, NULL },
  { "iogonek",                           278, NULL },
  { "backslash",                         278, NULL },
  { "L",                                 556, NULL },
  { "periodcentered",                    250, NULL },
  { "M",                                 833, NULL },
  { "N",                                 667, NULL },
  { "omacron",                           500, NULL },
  { "Tcommaaccent",                      556, NULL },
  { "O",                                 722, NULL },
  { "P",                                 611, NULL },
  { "Q",                                 722, NULL },
  { "Uhungarumlaut",                     722, NULL },
  { "R",                                 611, NULL },
  { "Aacute",                            611, NULL },
  { "caron",                             333, NULL },
  { "S",                                 500, NULL },
  { "T",                                 556, NULL },
  { "U",                                 722, NULL },
  { "agrave",                            500, NULL },
  { "V",                                 611, NULL },
  { "W",                                 833, NULL },
  { "X",                                 611, NULL },
  { "question",                          500, NULL },
  { "equal",                             675, NULL },
  { "Y",                                 556, NULL },
  { "Z",                                 556, NULL },
  { "four",                              500, NULL },
  { "a",                                 500, NULL },
  { "Gcommaaccent",                      722, NULL },
  { "b",                                 500, NULL },
  { "c",                                 444, NULL },
  { "d",                                 500, NULL },
  { "e",                                 444, NULL },
  { "f",                                 278, NULL },
  { "g",                                 500, NULL },
  { "bullet",                            350, NULL },
  { "h",                                 500, NULL },
  { "i",                                 278, NULL },
  { "Oslash",                            722, NULL },
  { "dagger",                            500, NULL },
  { "j",                                 278, NULL },
  { "k",                                 444, NULL },
  { "l",                                 278, NULL },
  { "m",                                 722, NULL },
  { "n",                                 500, NULL },
  { "tcommaaccent",                      278, NULL },
  { "o",                                 500, NULL },
  { "ordfeminine",                       276, NULL },
  { "ring",                              333, NULL },
  { "p",                                 500, NULL },
  { "q",                                 500, NULL },
  { "uhungarumlaut",                     500, NULL },
  { "r",                                 389, NULL },
  { "twosuperior",                       300, NULL },
  { "aacute",                            500, NULL },
  { "s",                                 389, NULL },
  { "OE",                                944, NULL },
  { "t",                                 278, NULL },
  { "divide",                            675, NULL },
  { "u",                                 500, NULL },
  { "Ccaron",                            667, NULL },
  { "v",                                 444, NULL },
  { "w",                                 667, NULL },
  { "x",                                 444, NULL },
  { "y",                                 444, NULL },
  { "z",                                 389, NULL },
  { "Gbreve",                            722, NULL },
  { "commaaccent",                       250, NULL },
  { "hungarumlaut",                      333, NULL },
  { "Idotaccent",                        333, NULL },
  { "Nacute",                            667, NULL },
  { "quotedbl",                          420, NULL },
  { "gcommaaccent",                      500, NULL },
  { "mu",                                500, NULL },
  { "greaterequal",                      549, NULL },
  { "Scaron",                            500, NULL },
  { "Lslash",                            556, NULL },
  { "semicolon",                         333, NULL },
  { "oslash",                            500, NULL },
  { "lessequal",                         549, NULL },
  { "lozenge",                           471, NULL },
  { "parenright",                        333, NULL },
  { "ccaron",                            444, NULL },
  { "Ecircumflex",                       611, NULL },
  { "gbreve",                            500, NULL },
  { "trademark",                         980, NULL },
  { "daggerdbl",                         500, NULL },
  { "nacute",                            500, NULL },
  { "macron",                            333, NULL },
  { "Otilde",                            722, NULL },
  { "Emacron",                           611, NULL },
  { "ellipsis",                          889, NULL },
  { "scaron",                            389, NULL },
  { "AE",                                889, NULL },
  { "Ucircumflex",                       722, NULL },
  { "lslash",                            278, NULL },
  { "quotedblleft",                      556, NULL },
  { "guilsinglright",                    333, NULL },
  { "hyphen",                            333, NULL },
  { "quotesingle",                       214, NULL },
  { "eight",                             500, NULL },
  { "exclamdown",                        389, NULL },
  { "endash",                            500, NULL },
  { "oe",                                667, NULL },
  { "Abreve",                            611, NULL },
  { "Umacron",                           722, NULL },
  { "ecircumflex",                       444, NULL },
  { "Adieresis",                         611, NULL },
  { "copyright",                         760, NULL },
  { "Egrave",                            611, NULL },
  { "slash",                             278, NULL },
  { "Edieresis",                         611, NULL },
  { "otilde",                            500, NULL },
  { "Idieresis",                         333, NULL },
  { "parenleft",                         333, NULL },
  { "one",                               500, NULL },
  { "emacron",                           444, NULL },
  { "Odieresis",                         722, NULL },
  { "ucircumflex",                       500, NULL },
  { "bracketleft",                       389, NULL },
  { "Ugrave",                            722, NULL },
  { "quoteright",                        333, NULL },
  { "Udieresis",                         722, NULL },
  { "perthousand",                      1000, NULL },
  { "Ydieresis",                         556, NULL },
  { "umacron",                           500, NULL },
  { "abreve",                            500, NULL },
  { "Eacute",                            611, NULL },
  { "adieresis",                         500, NULL },
  { "egrave",                            444, NULL },
  { "edieresis",                         444, NULL },
  { "idieresis",                         278, NULL },
  { "Eth",                               722, NULL },
  { "ae",                                667, NULL },
  { "asterisk",                          500, NULL },
  { "odieresis",                         500, NULL },
  { "Uacute",                            722, NULL },
  { "ugrave",                            500, NULL },
  { "nine",                              500, NULL },
  { "five",                              500, NULL },
  { "udieresis",                         500, NULL },
  { "Zcaron",                            556, NULL },
  { "Scommaaccent",                      500, NULL },
  { "threequarters",                     750, NULL },
  { "guillemotright",                    500, NULL },
  { "Ccedilla",                          667, NULL },
  { "ydieresis",                         444, NULL },
  { "tilde",                             333, NULL },
  { "at",                                920, NULL },
  { "eacute",                            444, NULL },
  { "underscore",                        500, NULL },
  { "Euro",                              500, NULL },
  { "Dcroat",                            722, NULL },
  { "multiply",                          675, NULL },
  { "zero",                              500, NULL },
  { "eth",                               500, NULL },
  { "Scedilla",                          500, NULL },
  { "Ograve",                            722, NULL },
  { "Racute",                            611, NULL },
  { "partialdiff",                       476, NULL },
  { "uacute",                            500, NULL },
  { "braceleft",                         400, NULL },
  { "Thorn",                             611, NULL },
  { "zcaron",                            389, NULL },
  { "scommaaccent",                      389, NULL },
  { "ccedilla",                          444, NULL },
  { "Dcaron",                            722, NULL },
  { "dcroat",                            500, NULL },
  { "Ocircumflex",                       722, NULL },
  { "Oacute",                            722, NULL },
  { "scedilla",                          389, NULL },
  { "ogonek",                            333, NULL },
  { "ograve",                            500, NULL },
  { "racute",                            389, NULL },
  { "Tcaron",                            556, NULL },
  { "Eogonek",                           611, NULL },
  { "thorn",                             500, NULL },
  { "degree",                            400, NULL },
  { "registered",                        760, NULL },
  { "radical",                           453, NULL },
  { "Aring",                             611, NULL },
  { "percent",                           833, NULL },
  { "six",                               500, NULL },
  { "paragraph",                         523, NULL },
  { "dcaron",                            544, NULL },
  { "Uogonek",                           722, NULL },
  { "two",                               500, NULL },
  { "summation",                         600, NULL },
  { "Igrave",                            333, NULL },
  { "Lacute",                            556, NULL },
  { "ocircumflex",                       500, NULL },
  { "oacute",                            500, NULL },
  { "Uring",                             722, NULL },
  { "Lcommaaccent",                      556, NULL },
  { "tcaron",                            300, NULL },
  { "eogonek",                           444, NULL },
  { "Delta",                             612, NULL },
  { "Ohungarumlaut",                     722, NULL },
  { "asciicircum",                       422, NULL },
  { "aring",                             500, NULL },
  { "grave",                             333, NULL },
  { "uogonek",                           500, NULL },
  { "bracketright",                      389, NULL },
  { "Iacute",                            333, NULL },
  { "ampersand",                         778, NULL },
  { "igrave",                            278, NULL },
  { "lacute",                            278, NULL },
  { "Ncaron",                            667, NULL },
  { "plus",                              675, NULL },
  { "uring",                             500, NULL },
  { "quotesinglbase",                    333, NULL },
  { "lcommaaccent",                      278, NULL },
  { "Yacute",                            556, NULL },
  { "ohungarumlaut",                     500, NULL },
  { "threesuperior",                     300, NULL },
  { "acute",                             333, NULL },
  { "section",                           500, NULL },
  { "dieresis",                          333, NULL },
  { "iacute",                            278, NULL },
  { "quotedblbase",                      556, NULL },
  { "ncaron",                            500, NULL },
  { "florin",                            500, NULL },
  { "yacute",                            444, NULL },
  { "Rcommaaccent",                      611, NULL },
  { "fi",                                500, NULL },
  { "fl",                                500, NULL },
  { "Acircumflex",                       611, NULL },
  { "Cacute",                            667, NULL },
  { "Icircumflex",                       333, NULL },
  { "guillemotleft",                     500, NULL },
  { "germandbls",                        500, NULL },
  { "Amacron",                           611, NULL },
  { "seven",                             500, NULL },
  { "Sacute",                            500, NULL },
  { "ordmasculine",                      310, NULL },
  { "dotlessi",                          278, NULL },
  { "sterling",                          500, NULL },
  { "notequal",                          549, NULL },
  { "Imacron",                           333, NULL },
  { "rcommaaccent",                      389, NULL },
  { "Zdotaccent",                        556, NULL },
  { "acircumflex",                       500, NULL },
  { "cacute",                            444, NULL },
  { "Ecaron",                            611, NULL },
  { "icircumflex",                       278, NULL },
  { "braceright",                        400, NULL },
  { "quotedblright",                     556, NULL },
  { "amacron",                           500, NULL },
  { "sacute",                            389, NULL },
  { "imacron",                           278, NULL },
  { "cent",                              500, NULL },
  { "currency",                          500, NULL },
  { "logicalnot",                        675, NULL },
  { "zdotaccent",                        389, NULL },
  { "Atilde",                            611, NULL },
  { "breve",                             333, NULL },
  { "bar",                               275, NULL },
  { "fraction",                          167, NULL },
  { "less",                              675, NULL },
  { "ecaron",                            444, NULL },
  { "guilsinglleft",                     333, NULL },
  { "exclam",                            333, NULL },
  { "period",                            250, NULL },
  { "Rcaron",                            611, NULL },
  { "Kcommaaccent",                      667, NULL },
  { "greater",                           675, NULL },
  { "atilde",                            500, NULL },
  { "brokenbar",                         275, NULL },
  { "quoteleft",                         333, NULL },
  { "Edotaccent",                        611, NULL },
  { "onesuperior",                       300, NULL }
};

static BuiltinFontWidth timesRomanWidthsTab[] = {
  { "Ntilde",                            722, NULL },
  { "rcaron",                            333, NULL },
  { "kcommaaccent",                      500, NULL },
  { "Ncommaaccent",                      722, NULL },
  { "Zacute",                            611, NULL },
  { "comma",                             250, NULL },
  { "cedilla",                           333, NULL },
  { "plusminus",                         564, NULL },
  { "circumflex",                        333, NULL },
  { "dotaccent",                         333, NULL },
  { "edotaccent",                        444, NULL },
  { "asciitilde",                        541, NULL },
  { "colon",                             278, NULL },
  { "onehalf",                           750, NULL },
  { "dollar",                            500, NULL },
  { "Lcaron",                            611, NULL },
  { "ntilde",                            500, NULL },
  { "Aogonek",                           722, NULL },
  { "ncommaaccent",                      500, NULL },
  { "minus",                             564, NULL },
  { "Iogonek",                           333, NULL },
  { "zacute",                            444, NULL },
  { "yen",                               500, NULL },
  { "space",                             250, NULL },
  { "Omacron",                           722, NULL },
  { "questiondown",                      444, NULL },
  { "emdash",                           1000, NULL },
  { "Agrave",                            722, NULL },
  { "three",                             500, NULL },
  { "numbersign",                        500, NULL },
  { "lcaron",                            344, NULL },
  { "A",                                 722, NULL },
  { "B",                                 667, NULL },
  { "C",                                 667, NULL },
  { "aogonek",                           444, NULL },
  { "D",                                 722, NULL },
  { "E",                                 611, NULL },
  { "onequarter",                        750, NULL },
  { "F",                                 556, NULL },
  { "G",                                 722, NULL },
  { "H",                                 722, NULL },
  { "I",                                 333, NULL },
  { "J",                                 389, NULL },
  { "K",                                 722, NULL },
  { "iogonek",                           278, NULL },
  { "backslash",                         278, NULL },
  { "L",                                 611, NULL },
  { "periodcentered",                    250, NULL },
  { "M",                                 889, NULL },
  { "N",                                 722, NULL },
  { "omacron",                           500, NULL },
  { "Tcommaaccent",                      611, NULL },
  { "O",                                 722, NULL },
  { "P",                                 556, NULL },
  { "Q",                                 722, NULL },
  { "Uhungarumlaut",                     722, NULL },
  { "R",                                 667, NULL },
  { "Aacute",                            722, NULL },
  { "caron",                             333, NULL },
  { "S",                                 556, NULL },
  { "T",                                 611, NULL },
  { "U",                                 722, NULL },
  { "agrave",                            444, NULL },
  { "V",                                 722, NULL },
  { "W",                                 944, NULL },
  { "X",                                 722, NULL },
  { "question",                          444, NULL },
  { "equal",                             564, NULL },
  { "Y",                                 722, NULL },
  { "Z",                                 611, NULL },
  { "four",                              500, NULL },
  { "a",                                 444, NULL },
  { "Gcommaaccent",                      722, NULL },
  { "b",                                 500, NULL },
  { "c",                                 444, NULL },
  { "d",                                 500, NULL },
  { "e",                                 444, NULL },
  { "f",                                 333, NULL },
  { "g",                                 500, NULL },
  { "bullet",                            350, NULL },
  { "h",                                 500, NULL },
  { "i",                                 278, NULL },
  { "Oslash",                            722, NULL },
  { "dagger",                            500, NULL },
  { "j",                                 278, NULL },
  { "k",                                 500, NULL },
  { "l",                                 278, NULL },
  { "m",                                 778, NULL },
  { "n",                                 500, NULL },
  { "tcommaaccent",                      278, NULL },
  { "o",                                 500, NULL },
  { "ordfeminine",                       276, NULL },
  { "ring",                              333, NULL },
  { "p",                                 500, NULL },
  { "q",                                 500, NULL },
  { "uhungarumlaut",                     500, NULL },
  { "r",                                 333, NULL },
  { "twosuperior",                       300, NULL },
  { "aacute",                            444, NULL },
  { "s",                                 389, NULL },
  { "OE",                                889, NULL },
  { "t",                                 278, NULL },
  { "divide",                            564, NULL },
  { "u",                                 500, NULL },
  { "Ccaron",                            667, NULL },
  { "v",                                 500, NULL },
  { "w",                                 722, NULL },
  { "x",                                 500, NULL },
  { "y",                                 500, NULL },
  { "z",                                 444, NULL },
  { "Gbreve",                            722, NULL },
  { "commaaccent",                       250, NULL },
  { "hungarumlaut",                      333, NULL },
  { "Idotaccent",                        333, NULL },
  { "Nacute",                            722, NULL },
  { "quotedbl",                          408, NULL },
  { "gcommaaccent",                      500, NULL },
  { "mu",                                500, NULL },
  { "greaterequal",                      549, NULL },
  { "Scaron",                            556, NULL },
  { "Lslash",                            611, NULL },
  { "semicolon",                         278, NULL },
  { "oslash",                            500, NULL },
  { "lessequal",                         549, NULL },
  { "lozenge",                           471, NULL },
  { "parenright",                        333, NULL },
  { "ccaron",                            444, NULL },
  { "Ecircumflex",                       611, NULL },
  { "gbreve",                            500, NULL },
  { "trademark",                         980, NULL },
  { "daggerdbl",                         500, NULL },
  { "nacute",                            500, NULL },
  { "macron",                            333, NULL },
  { "Otilde",                            722, NULL },
  { "Emacron",                           611, NULL },
  { "ellipsis",                         1000, NULL },
  { "scaron",                            389, NULL },
  { "AE",                                889, NULL },
  { "Ucircumflex",                       722, NULL },
  { "lslash",                            278, NULL },
  { "quotedblleft",                      444, NULL },
  { "guilsinglright",                    333, NULL },
  { "hyphen",                            333, NULL },
  { "quotesingle",                       180, NULL },
  { "eight",                             500, NULL },
  { "exclamdown",                        333, NULL },
  { "endash",                            500, NULL },
  { "oe",                                722, NULL },
  { "Abreve",                            722, NULL },
  { "Umacron",                           722, NULL },
  { "ecircumflex",                       444, NULL },
  { "Adieresis",                         722, NULL },
  { "copyright",                         760, NULL },
  { "Egrave",                            611, NULL },
  { "slash",                             278, NULL },
  { "Edieresis",                         611, NULL },
  { "otilde",                            500, NULL },
  { "Idieresis",                         333, NULL },
  { "parenleft",                         333, NULL },
  { "one",                               500, NULL },
  { "emacron",                           444, NULL },
  { "Odieresis",                         722, NULL },
  { "ucircumflex",                       500, NULL },
  { "bracketleft",                       333, NULL },
  { "Ugrave",                            722, NULL },
  { "quoteright",                        333, NULL },
  { "Udieresis",                         722, NULL },
  { "perthousand",                      1000, NULL },
  { "Ydieresis",                         722, NULL },
  { "umacron",                           500, NULL },
  { "abreve",                            444, NULL },
  { "Eacute",                            611, NULL },
  { "adieresis",                         444, NULL },
  { "egrave",                            444, NULL },
  { "edieresis",                         444, NULL },
  { "idieresis",                         278, NULL },
  { "Eth",                               722, NULL },
  { "ae",                                667, NULL },
  { "asterisk",                          500, NULL },
  { "odieresis",                         500, NULL },
  { "Uacute",                            722, NULL },
  { "ugrave",                            500, NULL },
  { "nine",                              500, NULL },
  { "five",                              500, NULL },
  { "udieresis",                         500, NULL },
  { "Zcaron",                            611, NULL },
  { "Scommaaccent",                      556, NULL },
  { "threequarters",                     750, NULL },
  { "guillemotright",                    500, NULL },
  { "Ccedilla",                          667, NULL },
  { "ydieresis",                         500, NULL },
  { "tilde",                             333, NULL },
  { "at",                                921, NULL },
  { "eacute",                            444, NULL },
  { "underscore",                        500, NULL },
  { "Euro",                              500, NULL },
  { "Dcroat",                            722, NULL },
  { "multiply",                          564, NULL },
  { "zero",                              500, NULL },
  { "eth",                               500, NULL },
  { "Scedilla",                          556, NULL },
  { "Ograve",                            722, NULL },
  { "Racute",                            667, NULL },
  { "partialdiff",                       476, NULL },
  { "uacute",                            500, NULL },
  { "braceleft",                         480, NULL },
  { "Thorn",                             556, NULL },
  { "zcaron",                            444, NULL },
  { "scommaaccent",                      389, NULL },
  { "ccedilla",                          444, NULL },
  { "Dcaron",                            722, NULL },
  { "dcroat",                            500, NULL },
  { "Ocircumflex",                       722, NULL },
  { "Oacute",                            722, NULL },
  { "scedilla",                          389, NULL },
  { "ogonek",                            333, NULL },
  { "ograve",                            500, NULL },
  { "racute",                            333, NULL },
  { "Tcaron",                            611, NULL },
  { "Eogonek",                           611, NULL },
  { "thorn",                             500, NULL },
  { "degree",                            400, NULL },
  { "registered",                        760, NULL },
  { "radical",                           453, NULL },
  { "Aring",                             722, NULL },
  { "percent",                           833, NULL },
  { "six",                               500, NULL },
  { "paragraph",                         453, NULL },
  { "dcaron",                            588, NULL },
  { "Uogonek",                           722, NULL },
  { "two",                               500, NULL },
  { "summation",                         600, NULL },
  { "Igrave",                            333, NULL },
  { "Lacute",                            611, NULL },
  { "ocircumflex",                       500, NULL },
  { "oacute",                            500, NULL },
  { "Uring",                             722, NULL },
  { "Lcommaaccent",                      611, NULL },
  { "tcaron",                            326, NULL },
  { "eogonek",                           444, NULL },
  { "Delta",                             612, NULL },
  { "Ohungarumlaut",                     722, NULL },
  { "asciicircum",                       469, NULL },
  { "aring",                             444, NULL },
  { "grave",                             333, NULL },
  { "uogonek",                           500, NULL },
  { "bracketright",                      333, NULL },
  { "Iacute",                            333, NULL },
  { "ampersand",                         778, NULL },
  { "igrave",                            278, NULL },
  { "lacute",                            278, NULL },
  { "Ncaron",                            722, NULL },
  { "plus",                              564, NULL },
  { "uring",                             500, NULL },
  { "quotesinglbase",                    333, NULL },
  { "lcommaaccent",                      278, NULL },
  { "Yacute",                            722, NULL },
  { "ohungarumlaut",                     500, NULL },
  { "threesuperior",                     300, NULL },
  { "acute",                             333, NULL },
  { "section",                           500, NULL },
  { "dieresis",                          333, NULL },
  { "iacute",                            278, NULL },
  { "quotedblbase",                      444, NULL },
  { "ncaron",                            500, NULL },
  { "florin",                            500, NULL },
  { "yacute",                            500, NULL },
  { "Rcommaaccent",                      667, NULL },
  { "fi",                                556, NULL },
  { "fl",                                556, NULL },
  { "Acircumflex",                       722, NULL },
  { "Cacute",                            667, NULL },
  { "Icircumflex",                       333, NULL },
  { "guillemotleft",                     500, NULL },
  { "germandbls",                        500, NULL },
  { "Amacron",                           722, NULL },
  { "seven",                             500, NULL },
  { "Sacute",                            556, NULL },
  { "ordmasculine",                      310, NULL },
  { "dotlessi",                          278, NULL },
  { "sterling",                          500, NULL },
  { "notequal",                          549, NULL },
  { "Imacron",                           333, NULL },
  { "rcommaaccent",                      333, NULL },
  { "Zdotaccent",                        611, NULL },
  { "acircumflex",                       444, NULL },
  { "cacute",                            444, NULL },
  { "Ecaron",                            611, NULL },
  { "icircumflex",                       278, NULL },
  { "braceright",                        480, NULL },
  { "quotedblright",                     444, NULL },
  { "amacron",                           444, NULL },
  { "sacute",                            389, NULL },
  { "imacron",                           278, NULL },
  { "cent",                              500, NULL },
  { "currency",                          500, NULL },
  { "logicalnot",                        564, NULL },
  { "zdotaccent",                        444, NULL },
  { "Atilde",                            722, NULL },
  { "breve",                             333, NULL },
  { "bar",                               200, NULL },
  { "fraction",                          167, NULL },
  { "less",                              564, NULL },
  { "ecaron",                            444, NULL },
  { "guilsinglleft",                     333, NULL },
  { "exclam",                            333, NULL },
  { "period",                            250, NULL },
  { "Rcaron",                            667, NULL },
  { "Kcommaaccent",                      722, NULL },
  { "greater",                           564, NULL },
  { "atilde",                            444, NULL },
  { "brokenbar",                         200, NULL },
  { "quoteleft",                         333, NULL },
  { "Edotaccent",                        611, NULL },
  { "onesuperior",                       300, NULL }
};

static BuiltinFontWidth zapfDingbatsWidthsTab[] = {
  { "a81",                               438, NULL },
  { "a82",                               138, NULL },
  { "a83",                               277, NULL },
  { "a84",                               415, NULL },
  { "a85",                               509, NULL },
  { "a86",                               410, NULL },
  { "a87",                               234, NULL },
  { "a88",                               234, NULL },
  { "a89",                               390, NULL },
  { "a140",                              788, NULL },
  { "a141",                              788, NULL },
  { "a142",                              788, NULL },
  { "a143",                              788, NULL },
  { "a144",                              788, NULL },
  { "a145",                              788, NULL },
  { "a146",                              788, NULL },
  { "a147",                              788, NULL },
  { "a148",                              788, NULL },
  { "a149",                              788, NULL },
  { "a90",                               390, NULL },
  { "a91",                               276, NULL },
  { "a92",                               276, NULL },
  { "space",                             278, NULL },
  { "a93",                               317, NULL },
  { "a94",                               317, NULL },
  { "a95",                               334, NULL },
  { "a96",                               334, NULL },
  { "a97",                               392, NULL },
  { "a98",                               392, NULL },
  { "a99",                               668, NULL },
  { "a150",                              788, NULL },
  { "a151",                              788, NULL },
  { "a152",                              788, NULL },
  { "a153",                              788, NULL },
  { "a154",                              788, NULL },
  { "a155",                              788, NULL },
  { "a156",                              788, NULL },
  { "a157",                              788, NULL },
  { "a158",                              788, NULL },
  { "a159",                              788, NULL },
  { "a160",                              894, NULL },
  { "a161",                              838, NULL },
  { "a162",                              924, NULL },
  { "a163",                             1016, NULL },
  { "a164",                              458, NULL },
  { "a165",                              924, NULL },
  { "a166",                              918, NULL },
  { "a167",                              927, NULL },
  { "a168",                              928, NULL },
  { "a169",                              928, NULL },
  { "a170",                              834, NULL },
  { "a171",                              873, NULL },
  { "a172",                              828, NULL },
  { "a173",                              924, NULL },
  { "a174",                              917, NULL },
  { "a175",                              930, NULL },
  { "a176",                              931, NULL },
  { "a177",                              463, NULL },
  { "a178",                              883, NULL },
  { "a179",                              836, NULL },
  { "a180",                              867, NULL },
  { "a181",                              696, NULL },
  { "a182",                              874, NULL },
  { "a183",                              760, NULL },
  { "a184",                              946, NULL },
  { "a185",                              865, NULL },
  { "a186",                              967, NULL },
  { "a187",                              831, NULL },
  { "a188",                              873, NULL },
  { "a189",                              927, NULL },
  { "a1",                                974, NULL },
  { "a2",                                961, NULL },
  { "a3",                                980, NULL },
  { "a4",                                719, NULL },
  { "a5",                                789, NULL },
  { "a6",                                494, NULL },
  { "a7",                                552, NULL },
  { "a8",                                537, NULL },
  { "a9",                                577, NULL },
  { "a190",                              970, NULL },
  { "a191",                              918, NULL },
  { "a192",                              748, NULL },
  { "a193",                              836, NULL },
  { "a194",                              771, NULL },
  { "a195",                              888, NULL },
  { "a196",                              748, NULL },
  { "a197",                              771, NULL },
  { "a198",                              888, NULL },
  { "a199",                              867, NULL },
  { "a10",                               692, NULL },
  { "a11",                               960, NULL },
  { "a12",                               939, NULL },
  { "a13",                               549, NULL },
  { "a14",                               855, NULL },
  { "a15",                               911, NULL },
  { "a16",                               933, NULL },
  { "a17",                               945, NULL },
  { "a18",                               974, NULL },
  { "a19",                               755, NULL },
  { "a20",                               846, NULL },
  { "a21",                               762, NULL },
  { "a22",                               761, NULL },
  { "a23",                               571, NULL },
  { "a24",                               677, NULL },
  { "a25",                               763, NULL },
  { "a26",                               760, NULL },
  { "a27",                               759, NULL },
  { "a28",                               754, NULL },
  { "a29",                               786, NULL },
  { "a30",                               788, NULL },
  { "a31",                               788, NULL },
  { "a32",                               790, NULL },
  { "a33",                               793, NULL },
  { "a34",                               794, NULL },
  { "a35",                               816, NULL },
  { "a36",                               823, NULL },
  { "a37",                               789, NULL },
  { "a38",                               841, NULL },
  { "a39",                               823, NULL },
  { "a40",                               833, NULL },
  { "a41",                               816, NULL },
  { "a42",                               831, NULL },
  { "a43",                               923, NULL },
  { "a44",                               744, NULL },
  { "a45",                               723, NULL },
  { "a46",                               749, NULL },
  { "a47",                               790, NULL },
  { "a48",                               792, NULL },
  { "a49",                               695, NULL },
  { "a100",                              668, NULL },
  { "a101",                              732, NULL },
  { "a102",                              544, NULL },
  { "a103",                              544, NULL },
  { "a104",                              910, NULL },
  { "a105",                              911, NULL },
  { "a106",                              667, NULL },
  { "a107",                              760, NULL },
  { "a108",                              760, NULL },
  { "a109",                              626, NULL },
  { "a50",                               776, NULL },
  { "a51",                               768, NULL },
  { "a52",                               792, NULL },
  { "a53",                               759, NULL },
  { "a54",                               707, NULL },
  { "a55",                               708, NULL },
  { "a56",                               682, NULL },
  { "a57",                               701, NULL },
  { "a58",                               826, NULL },
  { "a59",                               815, NULL },
  { "a110",                              694, NULL },
  { "a111",                              595, NULL },
  { "a112",                              776, NULL },
  { "a117",                              690, NULL },
  { "a118",                              791, NULL },
  { "a119",                              790, NULL },
  { "a60",                               789, NULL },
  { "a61",                               789, NULL },
  { "a62",                               707, NULL },
  { "a63",                               687, NULL },
  { "a64",                               696, NULL },
  { "a65",                               689, NULL },
  { "a66",                               786, NULL },
  { "a67",                               787, NULL },
  { "a68",                               713, NULL },
  { "a69",                               791, NULL },
  { "a200",                              696, NULL },
  { "a201",                              874, NULL },
  { "a120",                              788, NULL },
  { "a121",                              788, NULL },
  { "a202",                              974, NULL },
  { "a122",                              788, NULL },
  { "a203",                              762, NULL },
  { "a123",                              788, NULL },
  { "a204",                              759, NULL },
  { "a124",                              788, NULL },
  { "a205",                              509, NULL },
  { "a125",                              788, NULL },
  { "a206",                              410, NULL },
  { "a126",                              788, NULL },
  { "a127",                              788, NULL },
  { "a128",                              788, NULL },
  { "a129",                              788, NULL },
  { "a70",                               785, NULL },
  { "a71",                               791, NULL },
  { "a72",                               873, NULL },
  { "a73",                               761, NULL },
  { "a74",                               762, NULL },
  { "a75",                               759, NULL },
  { "a76",                               892, NULL },
  { "a77",                               892, NULL },
  { "a78",                               788, NULL },
  { "a79",                               784, NULL },
  { "a130",                              788, NULL },
  { "a131",                              788, NULL },
  { "a132",                              788, NULL },
  { "a133",                              788, NULL },
  { "a134",                              788, NULL },
  { "a135",                              788, NULL },
  { "a136",                              788, NULL },
  { "a137",                              788, NULL },
  { "a138",                              788, NULL },
  { "a139",                              788, NULL }
};

BuiltinFont builtinFonts[] = {
  { "Courier",               standardEncoding,            629, -157, { -23, -250,  715,  805}, NULL },
  { "Courier-Bold",          standardEncoding,            629, -157, {-113, -250,  749,  801}, NULL },
  { "Courier-BoldOblique",   standardEncoding,            629, -157, { -57, -250,  869,  801}, NULL },
  { "Courier-Oblique",       standardEncoding,            629, -157, { -27, -250,  849,  805}, NULL },
  { "Helvetica",             standardEncoding,            718, -207, {-166, -225, 1000,  931}, NULL },
  { "Helvetica-Bold",        standardEncoding,            718, -207, {-170, -228, 1003,  962}, NULL },
  { "Helvetica-BoldOblique", standardEncoding,            718, -207, {-174, -228, 1114,  962}, NULL },
  { "Helvetica-Oblique",     standardEncoding,            718, -207, {-170, -225, 1116,  931}, NULL },
  { "Symbol",                symbolEncoding,             1010, -293, {-180, -293, 1090, 1010}, NULL },
  { "Times-Bold",            standardEncoding,            683, -217, {-168, -218, 1000,  935}, NULL },
  { "Times-BoldItalic",      standardEncoding,            683, -217, {-200, -218,  996,  921}, NULL },
  { "Times-Italic",          standardEncoding,            683, -217, {-169, -217, 1010,  883}, NULL },
  { "Times-Roman",           standardEncoding,            683, -217, {-168, -218, 1000,  898}, NULL },
  { "ZapfDingbats",          zapfDingbatsEncoding,        820, -143, {  -1, -143,  981,  820}, NULL }
};

BuiltinFont *builtinFontSubst[] = {
  &builtinFonts[0],
  &builtinFonts[3],
  &builtinFonts[1],
  &builtinFonts[2],
  &builtinFonts[4],
  &builtinFonts[7],
  &builtinFonts[5],
  &builtinFonts[6],
  &builtinFonts[12],
  &builtinFonts[11],
  &builtinFonts[9],
  &builtinFonts[10]
};

void initBuiltinFontTables() {
  builtinFonts[0].widths = new BuiltinFontWidths(courierWidthsTab, 315);
  builtinFonts[1].widths = new BuiltinFontWidths(courierBoldWidthsTab, 315);
  builtinFonts[2].widths = new BuiltinFontWidths(courierBoldObliqueWidthsTab, 315);
  builtinFonts[3].widths = new BuiltinFontWidths(courierObliqueWidthsTab, 315);
  builtinFonts[4].widths = new BuiltinFontWidths(helveticaWidthsTab, 315);
  builtinFonts[5].widths = new BuiltinFontWidths(helveticaBoldWidthsTab, 316);
  builtinFonts[6].widths = new BuiltinFontWidths(helveticaBoldObliqueWidthsTab, 315);
  builtinFonts[7].widths = new BuiltinFontWidths(helveticaObliqueWidthsTab, 315);
  builtinFonts[8].widths = new BuiltinFontWidths(symbolWidthsTab, 190);
  builtinFonts[9].widths = new BuiltinFontWidths(timesBoldWidthsTab, 315);
  builtinFonts[10].widths = new BuiltinFontWidths(timesBoldItalicWidthsTab, 315);
  builtinFonts[11].widths = new BuiltinFontWidths(timesItalicWidthsTab, 315);
  builtinFonts[12].widths = new BuiltinFontWidths(timesRomanWidthsTab, 315);
  builtinFonts[13].widths = new BuiltinFontWidths(zapfDingbatsWidthsTab, 202);
}

void freeBuiltinFontTables() {
  int i;

  for (i = 0; i < 14; ++i) {
    delete builtinFonts[i].widths;
  }
}
//========================================================================
//
// BuiltinFontTables.h
//
// Copyright 2001-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef BUILTINFONTTABLES_H
#define BUILTINFONTTABLES_H

#include "BuiltinFont.h"

#define nBuiltinFonts      14
#define nBuiltinFontSubsts 12

extern BuiltinFont builtinFonts[nBuiltinFonts];
extern BuiltinFont *builtinFontSubst[nBuiltinFontSubsts];

extern void initBuiltinFontTables();
extern void freeBuiltinFontTables();

#endif
//========================================================================
//
// CMap.cc
//
// Copyright 2001-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "gmem.h"
#include "gfile.h"
#include "GString.h"
#include "Error.h"
#include "GlobalParams.h"
#include "PSTokenizer.h"
#include "CMap.h"

//------------------------------------------------------------------------

struct CMapVectorEntry {
  GBool isVector;
  union {
    CMapVectorEntry *vector;
    CID cid;
  };
};

//------------------------------------------------------------------------

static int getCharFromFile(void *data) {
  return fgetc((FILE *)data);
}

//------------------------------------------------------------------------

CMap *CMap::parse(CMapCache *cache, GString *collectionA,
		  GString *cMapNameA) {
  FILE *f;
  CMap *cmap;
  PSTokenizer *pst;
  char tok1[256], tok2[256], tok3[256];
  int n1, n2, n3;
  Guint start, end, code;

  if (!(f = globalParams->findCMapFile(collectionA, cMapNameA))) {

    // Check for an identity CMap.
    if (!cMapNameA->cmp("Identity") || !cMapNameA->cmp("Identity-H")) {
      return new CMap(collectionA->copy(), cMapNameA->copy(), 0);
    }
    if (!cMapNameA->cmp("Identity-V")) {
      return new CMap(collectionA->copy(), cMapNameA->copy(), 1);
    }

    error(-1, "Couldn't find '%s' CMap file for '%s' collection",
	  cMapNameA->getCString(), collectionA->getCString());
    return NULL;
  }

  cmap = new CMap(collectionA->copy(), cMapNameA->copy());

  pst = new PSTokenizer(&getCharFromFile, f);
  pst->getToken(tok1, sizeof(tok1), &n1);
  while (pst->getToken(tok2, sizeof(tok2), &n2)) {
    if (!strcmp(tok2, "usecmap")) {
      if (tok1[0] == '/') {
	cmap->useCMap(cache, tok1 + 1);
      }
      pst->getToken(tok1, sizeof(tok1), &n1);
    } else if (!strcmp(tok1, "/WMode")) {
      cmap->wMode = atoi(tok2);
      pst->getToken(tok1, sizeof(tok1), &n1);
    } else if (!strcmp(tok2, "begincodespacerange")) {
      while (pst->getToken(tok1, sizeof(tok1), &n1)) {
	if (!strcmp(tok1, "endcodespacerange")) {
	  break;
	}
	if (!pst->getToken(tok2, sizeof(tok2), &n2) ||
	    !strcmp(tok2, "endcodespacerange")) {
	  error(-1, "Illegal entry in codespacerange block in CMap");
	  break;
	}
	if (tok1[0] == '<' && tok2[0] == '<' &&
	    n1 == n2 && n1 >= 4 && (n1 & 1) == 0) {
	  tok1[n1 - 1] = tok2[n1 - 1] = '\0';
	  sscanf(tok1 + 1, "%x", &start);
	  sscanf(tok2 + 1, "%x", &end);
	  n1 = (n1 - 2) / 2;
	  cmap->addCodeSpace(cmap->vector, start, end, n1);
	}
      }
      pst->getToken(tok1, sizeof(tok1), &n1);
    } else if (!strcmp(tok2, "begincidchar")) {
      while (pst->getToken(tok1, sizeof(tok1), &n1)) {
	if (!strcmp(tok1, "endcidchar")) {
	  break;
	}
	if (!pst->getToken(tok2, sizeof(tok2), &n2) ||
	    !strcmp(tok2, "endcidchar")) {
	  error(-1, "Illegal entry in cidchar block in CMap");
	  break;
	}
	if (!(tok1[0] == '<' && tok1[n1 - 1] == '>' &&
	      n1 >= 4 && (n1 & 1) == 0)) {
	  error(-1, "Illegal entry in cidchar block in CMap");
	  continue;
	}
	tok1[n1 - 1] = '\0';
	if (sscanf(tok1 + 1, "%x", &code) != 1) {
	  error(-1, "Illegal entry in cidchar block in CMap");
	  continue;
	}
	n1 = (n1 - 2) / 2;
	cmap->addCIDs(code, code, n1, (CID)atoi(tok2));
      }
      pst->getToken(tok1, sizeof(tok1), &n1);
    } else if (!strcmp(tok2, "begincidrange")) {
      while (pst->getToken(tok1, sizeof(tok1), &n1)) {
	if (!strcmp(tok1, "endcidrange")) {
	  break;
	}
	if (!pst->getToken(tok2, sizeof(tok2), &n2) ||
	    !strcmp(tok2, "endcidrange") ||
	    !pst->getToken(tok3, sizeof(tok3), &n3) ||
	    !strcmp(tok3, "endcidrange")) {
	  error(-1, "Illegal entry in cidrange block in CMap");
	  break;
	}
	if (tok1[0] == '<' && tok2[0] == '<' &&
	    n1 == n2 && n1 >= 4 && (n1 & 1) == 0) {
	  tok1[n1 - 1] = tok2[n1 - 1] = '\0';
	  sscanf(tok1 + 1, "%x", &start);
	  sscanf(tok2 + 1, "%x", &end);
	  n1 = (n1 - 2) / 2;
	  cmap->addCIDs(start, end, n1, (CID)atoi(tok3));
	}
      }
      pst->getToken(tok1, sizeof(tok1), &n1);
    } else {
      strcpy(tok1, tok2);
    }
  }
  delete pst;

  fclose(f);

  return cmap;
}

CMap::CMap(GString *collectionA, GString *cMapNameA) {
  int i;

  collection = collectionA;
  cMapName = cMapNameA;
  wMode = 0;
  vector = (CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry));
  for (i = 0; i < 256; ++i) {
    vector[i].isVector = gFalse;
    vector[i].cid = 0;
  }
  refCnt = 1;
#if MULTITHREADED
  gInitMutex(&mutex);
#endif
}

CMap::CMap(GString *collectionA, GString *cMapNameA, int wModeA) {
  collection = collectionA;
  cMapName = cMapNameA;
  wMode = wModeA;
  vector = NULL;
  refCnt = 1;
#if MULTITHREADED
  gInitMutex(&mutex);
#endif
}

void CMap::useCMap(CMapCache *cache, char *useName) {
  GString *useNameStr;
  CMap *subCMap;

  useNameStr = new GString(useName);
  subCMap = cache->getCMap(collection, useNameStr);
  delete useNameStr;
  if (!subCMap) {
    return;
  }
  copyVector(vector, subCMap->vector);
  subCMap->decRefCnt();
}

void CMap::copyVector(CMapVectorEntry *dest, CMapVectorEntry *src) {
  int i, j;

  for (i = 0; i < 256; ++i) {
    if (src[i].isVector) {
      if (!dest[i].isVector) {
	dest[i].isVector = gTrue;
	dest[i].vector =
	  (CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry));
	for (j = 0; j < 256; ++j) {
	  dest[i].vector[j].isVector = gFalse;
	  dest[i].vector[j].cid = 0;
	}
      }
      copyVector(dest[i].vector, src[i].vector);
    } else {
      if (dest[i].isVector) {
	error(-1, "Collision in usecmap");
      } else {
	dest[i].cid = src[i].cid;
      }
    }
  }
}

void CMap::addCodeSpace(CMapVectorEntry *vec, Guint start, Guint end,
			Guint nBytes) {
  Guint start2, end2;
  int startByte, endByte, i, j;

  if (nBytes > 1) {
    startByte = (start >> (8 * (nBytes - 1))) & 0xff;
    endByte = (end >> (8 * (nBytes - 1))) & 0xff;
    start2 = start & ((1 << (8 * (nBytes - 1))) - 1);
    end2 = end & ((1 << (8 * (nBytes - 1))) - 1);
    for (i = startByte; i <= endByte; ++i) {
      if (!vec[i].isVector) {
	vec[i].isVector = gTrue;
	vec[i].vector =
	  (CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry));
	for (j = 0; j < 256; ++j) {
	  vec[i].vector[j].isVector = gFalse;
	  vec[i].vector[j].cid = 0;
	}
      }
      addCodeSpace(vec[i].vector, start2, end2, nBytes - 1);
    }
  }
}

void CMap::addCIDs(Guint start, Guint end, Guint nBytes, CID firstCID) {
  CMapVectorEntry *vec;
  CID cid;
  int byte;
  Guint i;

  vec = vector;
  for (i = nBytes - 1; i >= 1; --i) {
    byte = (start >> (8 * i)) & 0xff;
    if (!vec[byte].isVector) {
      error(-1, "Invalid CID (%0*x - %0*x) in CMap",
	    2*nBytes, start, 2*nBytes, end);
      return;
    }
    vec = vec[byte].vector;
  }
  cid = firstCID;
  for (byte = (int)(start & 0xff); byte <= (int)(end & 0xff); ++byte) {
    if (vec[byte].isVector) {
      error(-1, "Invalid CID (%0*x - %0*x) in CMap",
	    2*nBytes, start, 2*nBytes, end);
    } else {
      vec[byte].cid = cid;
    }
    ++cid;
  }
}

CMap::~CMap() {
  delete collection;
  delete cMapName;
  if (vector) {
    freeCMapVector(vector);
  }
#if MULTITHREADED
  gDestroyMutex(&mutex);
#endif
}

void CMap::freeCMapVector(CMapVectorEntry *vec) {
  int i;

  for (i = 0; i < 256; ++i) {
    if (vec[i].isVector) {
      freeCMapVector(vec[i].vector);
    }
  }
  gfree(vec);
}

void CMap::incRefCnt() {
#if MULTITHREADED
  gLockMutex(&mutex);
#endif
  ++refCnt;
#if MULTITHREADED
  gUnlockMutex(&mutex);
#endif
}

void CMap::decRefCnt() {
  GBool done;

#if MULTITHREADED
  gLockMutex(&mutex);
#endif
  done = --refCnt == 0;
#if MULTITHREADED
  gUnlockMutex(&mutex);
#endif
  if (done) {
    delete this;
  }
}

GBool CMap::match(GString *collectionA, GString *cMapNameA) {
  return !collection->cmp(collectionA) && !cMapName->cmp(cMapNameA);
}

CID CMap::getCID(char *s, int len, int *nUsed) {
  CMapVectorEntry *vec;
  int n, i;

  if (!(vec = vector)) {
    // identity CMap
    *nUsed = 2;
    if (len < 2) {
      return 0;
    }
    return ((s[0] & 0xff) << 8) + (s[1] & 0xff);
  }
  n = 0;
  while (1) {
    if (n >= len) {
      *nUsed = n;
      return 0;
    }
    i = s[n++] & 0xff;
    if (!vec[i].isVector) {
      *nUsed = n;
      return vec[i].cid;
    }
    vec = vec[i].vector;
  }
}

//------------------------------------------------------------------------

CMapCache::CMapCache() {
  int i;

  for (i = 0; i < cMapCacheSize; ++i) {
    cache[i] = NULL;
  }
}

CMapCache::~CMapCache() {
  int i;

  for (i = 0; i < cMapCacheSize; ++i) {
    if (cache[i]) {
      cache[i]->decRefCnt();
    }
  }
}

CMap *CMapCache::getCMap(GString *collection, GString *cMapName) {
  CMap *cmap;
  int i, j;

  if (cache[0] && cache[0]->match(collection, cMapName)) {
    cache[0]->incRefCnt();
    return cache[0];
  }
  for (i = 1; i < cMapCacheSize; ++i) {
    if (cache[i] && cache[i]->match(collection, cMapName)) {
      cmap = cache[i];
      for (j = i; j >= 1; --j) {
	cache[j] = cache[j - 1];
      }
      cache[0] = cmap;
      cmap->incRefCnt();
      return cmap;
    }
  }
  if ((cmap = CMap::parse(this, collection, cMapName))) {
    if (cache[cMapCacheSize - 1]) {
      cache[cMapCacheSize - 1]->decRefCnt();
    }
    for (j = cMapCacheSize - 1; j >= 1; --j) {
      cache[j] = cache[j - 1];
    }
    cache[0] = cmap;
    cmap->incRefCnt();
    return cmap;
  }
  return NULL;
}
//========================================================================
//
// CMap.h
//
// Copyright 2001-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef CMAP_H
#define CMAP_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "gtypes.h"
#include "CharTypes.h"

#if MULTITHREADED
#include "GMutex.h"
#endif

class GString;
struct CMapVectorEntry;
class CMapCache;

//------------------------------------------------------------------------

class CMap {
public:

  // Create the CMap specified by <collection> and <cMapName>.  Sets
  // the initial reference count to 1.  Returns NULL on failure.
  static CMap *parse(CMapCache *cache, GString *collectionA,
		     GString *cMapNameA);

  ~CMap();

  void incRefCnt();
  void decRefCnt();

  // Return collection name (<registry>-<ordering>).
  GString *getCollection() { return collection; }

  // Return true if this CMap matches the specified <collectionA>, and
  // <cMapNameA>.
  GBool match(GString *collectionA, GString *cMapNameA);

  // Return the CID corresponding to the character code starting at
  // 
, which contains <len> bytes.  Sets *<nUsed> to the number of
  // bytes used by the char code.
  CID getCID(char *s, int len, int *nUsed);

  // Return the writing mode (0=horizontal, 1=vertical).
  int getWMode() { return wMode; }

private:

  CMap(GString *collectionA, GString *cMapNameA);
  CMap(GString *collectionA, GString *cMapNameA, int wModeA);
  void useCMap(CMapCache *cache, char *useName);
  void copyVector(CMapVectorEntry *dest, CMapVectorEntry *src);
  void addCodeSpace(CMapVectorEntry *vec, Guint start, Guint end,
		    Guint nBytes);
  void addCIDs(Guint start, Guint end, Guint nBytes, CID firstCID);
  void freeCMapVector(CMapVectorEntry *vec);

  GString *collection;
  GString *cMapName;
  int wMode;			// writing mode (0=horizontal, 1=vertical)
  CMapVectorEntry *vector;	// vector for first byte (NULL for
				//   identity CMap)
  int refCnt;
#if MULTITHREADED
  GMutex mutex;
#endif
};

//------------------------------------------------------------------------

#define cMapCacheSize 4

class CMapCache {
public:

  CMapCache();
  ~CMapCache();

  // Get the <cMapName> CMap for the specified character collection.
  // Increments its reference count; there will be one reference for
  // the cache plus one for the caller of this function.  Returns NULL
  // on failure.
  CMap *getCMap(GString *collection, GString *cMapName);

private:

  CMap *cache[cMapCacheSize];
};

#endif
//========================================================================
//
// Catalog.cc
//
// Copyright 1996-2007 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stddef.h>
#include "gmem.h"
#include "Object.h"
#include "XRef.h"
#include "Array.h"
#include "Dict.h"
#include "Page.h"
#include "Error.h"
#include "Link.h"
#include "Catalog.h"

//------------------------------------------------------------------------
// Catalog
//------------------------------------------------------------------------

Catalog::Catalog(XRef *xrefA) {
  Object catDict, pagesDict, pagesDictRef;
  Object obj, obj2;
  char *alreadyRead;
  int numPages0;
  int i;

  ok = gTrue;
  xref = xrefA;
  pages = NULL;
  pageRefs = NULL;
  numPages = pagesSize = 0;
  baseURI = NULL;

  xref->getCatalog(&catDict);
  if (!catDict.isDict()) {
    error(-1, "Catalog object is wrong type (%s)", catDict.getTypeName());
    goto err1;
  }

  // read page tree
  catDict.dictLookup("Pages", &pagesDict);
  // This should really be isDict("Pages"), but I've seen at least one
  // PDF file where the /Type entry is missing.
  if (!pagesDict.isDict()) {
    error(-1, "Top-level pages object is wrong type (%s)",
	  pagesDict.getTypeName());
    goto err2;
  }
  pagesDict.dictLookup("Count", &obj);
  // some PDF files actually use real numbers here ("/Count 9.0")
  if (!obj.isNum()) {
    error(-1, "Page count in top-level pages object is wrong type (%s)",
	  obj.getTypeName());
    goto err3;
  }
  pagesSize = numPages0 = (int)obj.getNum();
  obj.free();
  pages = (Page **)gmallocn(pagesSize, sizeof(Page *));
  pageRefs = (Ref *)gmallocn(pagesSize, sizeof(Ref));
  for (i = 0; i < pagesSize; ++i) {
    pages[i] = NULL;
    pageRefs[i].num = -1;
    pageRefs[i].gen = -1;
  }
  alreadyRead = (char *)gmalloc(xref->getNumObjects());
  memset(alreadyRead, 0, xref->getNumObjects());
  if (catDict.dictLookupNF("Pages", &pagesDictRef)->isRef() &&
      pagesDictRef.getRefNum() >= 0 &&
      pagesDictRef.getRefNum() < xref->getNumObjects()) {
    alreadyRead[pagesDictRef.getRefNum()] = 1;
  }
  pagesDictRef.free();
  numPages = readPageTree(pagesDict.getDict(), NULL, 0, alreadyRead);
  gfree(alreadyRead);
  if (numPages != numPages0) {
    error(-1, "Page count in top-level pages object is incorrect");
  }
  pagesDict.free();

  // read named destination dictionary
  catDict.dictLookup("Dests", &dests);

  // read root of named destination tree
  if (catDict.dictLookup("Names", &obj)->isDict())
    obj.dictLookup("Dests", &nameTree);
  else
    nameTree.initNull();
  obj.free();

  // read base URI
  if (catDict.dictLookup("URI", &obj)->isDict()) {
    if (obj.dictLookup("Base", &obj2)->isString()) {
      baseURI = obj2.getString()->copy();
    }
    obj2.free();
  }
  obj.free();

  // get the metadata stream
  catDict.dictLookup("Metadata", &metadata);

  // get the structure tree root
  catDict.dictLookup("StructTreeRoot", &structTreeRoot);

  // get the outline dictionary
  catDict.dictLookup("Outlines", &outline);

  // get the AcroForm dictionary
  catDict.dictLookup("AcroForm", &acroForm);

  catDict.free();
  return;

 err3:
  obj.free();
 err2:
  pagesDict.free();
 err1:
  catDict.free();
  dests.initNull();
  nameTree.initNull();
  ok = gFalse;
}

Catalog::~Catalog() {
  int i;

  if (pages) {
    for (i = 0; i < pagesSize; ++i) {
      if (pages[i]) {
	delete pages[i];
      }
    }
    gfree(pages);
    gfree(pageRefs);
  }
  dests.free();
  nameTree.free();
  if (baseURI) {
    delete baseURI;
  }
  metadata.free();
  structTreeRoot.free();
  outline.free();
  acroForm.free();
}

GString *Catalog::readMetadata() {
  GString *s;
  Dict *dict;
  Object obj;
  int c;

  if (!metadata.isStream()) {
    return NULL;
  }
  dict = metadata.streamGetDict();
  if (!dict->lookup("Subtype", &obj)->isName("XML")) {
    error(-1, "Unknown Metadata type: '%s'",
	  obj.isName() ? obj.getName() : "???");
  }
  obj.free();
  s = new GString();
  metadata.streamReset();
  while ((c = metadata.streamGetChar()) != EOF) {
    s->append(c);
  }
  metadata.streamClose();
  return s;
}

int Catalog::readPageTree(Dict *pagesDict, PageAttrs *attrs, int start,
			  char *alreadyRead) {
  Object kids;
  Object kid;
  Object kidRef;
  PageAttrs *attrs1, *attrs2;
  Page *page;
  int i, j;

  attrs1 = new PageAttrs(attrs, pagesDict);
  pagesDict->lookup("Kids", &kids);
  if (!kids.isArray()) {
    error(-1, "Kids object (page %d) is wrong type (%s)",
	  start+1, kids.getTypeName());
    goto err1;
  }
  for (i = 0; i < kids.arrayGetLength(); ++i) {
    kids.arrayGetNF(i, &kidRef);
    if (kidRef.isRef() &&
	kidRef.getRefNum() >= 0 &&
	kidRef.getRefNum() < xref->getNumObjects()) {
      if (alreadyRead[kidRef.getRefNum()]) {
	error(-1, "Loop in Pages tree");
	kidRef.free();
	continue;
      }
      alreadyRead[kidRef.getRefNum()] = 1;
    }
    kids.arrayGet(i, &kid);
    if (kid.isDict("Page")) {
      attrs2 = new PageAttrs(attrs1, kid.getDict());
      page = new Page(xref, start+1, kid.getDict(), attrs2);
      if (!page->isOk()) {
	++start;
	goto err3;
      }
      if (start >= pagesSize) {
	pagesSize += 32;
	pages = (Page **)greallocn(pages, pagesSize, sizeof(Page *));
	pageRefs = (Ref *)greallocn(pageRefs, pagesSize, sizeof(Ref));
	for (j = pagesSize - 32; j < pagesSize; ++j) {
	  pages[j] = NULL;
	  pageRefs[j].num = -1;
	  pageRefs[j].gen = -1;
	}
      }
      pages[start] = page;
      if (kidRef.isRef()) {
	pageRefs[start].num = kidRef.getRefNum();
	pageRefs[start].gen = kidRef.getRefGen();
      }
      ++start;
    // This should really be isDict("Pages"), but I've seen at least one
    // PDF file where the /Type entry is missing.
    } else if (kid.isDict()) {
      if ((start = readPageTree(kid.getDict(), attrs1, start, alreadyRead))
	  < 0)
	goto err2;
    } else {
      error(-1, "Kid object (page %d) is wrong type (%s)",
	    start+1, kid.getTypeName());
    }
    kid.free();
    kidRef.free();
  }
  delete attrs1;
  kids.free();
  return start;

 err3:
  delete page;
 err2:
  kid.free();
 err1:
  kids.free();
  delete attrs1;
  ok = gFalse;
  return -1;
}

int Catalog::findPage(int num, int gen) {
  int i;

  for (i = 0; i < numPages; ++i) {
    if (pageRefs[i].num == num && pageRefs[i].gen == gen)
      return i + 1;
  }
  return 0;
}

LinkDest *Catalog::findDest(GString *name) {
  LinkDest *dest;
  Object obj1, obj2;
  GBool found;

  // try named destination dictionary then name tree
  found = gFalse;
  if (dests.isDict()) {
    if (!dests.dictLookup(name->getCString(), &obj1)->isNull())
      found = gTrue;
    else
      obj1.free();
  }
  if (!found && nameTree.isDict()) {
    if (!findDestInTree(&nameTree, name, &obj1)->isNull())
      found = gTrue;
    else
      obj1.free();
  }
  if (!found)
    return NULL;

  // construct LinkDest
  dest = NULL;
  if (obj1.isArray()) {
    dest = new LinkDest(obj1.getArray());
  } else if (obj1.isDict()) {
    if (obj1.dictLookup("D", &obj2)->isArray())
      dest = new LinkDest(obj2.getArray());
    else
      error(-1, "Bad named destination value");
    obj2.free();
  } else {
    error(-1, "Bad named destination value");
  }
  obj1.free();
  if (dest && !dest->isOk()) {
    delete dest;
    dest = NULL;
  }

  return dest;
}

Object *Catalog::findDestInTree(Object *tree, GString *name, Object *obj) {
  Object names, name1;
  Object kids, kid, limits, low, high;
  GBool done, found;
  int cmp, i;

  // leaf node
  if (tree->dictLookup("Names", &names)->isArray()) {
    done = found = gFalse;
    for (i = 0; !done && i < names.arrayGetLength(); i += 2) {
      if (names.arrayGet(i, &name1)->isString()) {
	cmp = name->cmp(name1.getString());
	if (cmp == 0) {
	  names.arrayGet(i+1, obj);
	  found = gTrue;
	  done = gTrue;
	} else if (cmp < 0) {
	  done = gTrue;
	}
      }
      name1.free();
    }
    names.free();
    if (!found)
      obj->initNull();
    return obj;
  }
  names.free();

  // root or intermediate node
  done = gFalse;
  if (tree->dictLookup("Kids", &kids)->isArray()) {
    for (i = 0; !done && i < kids.arrayGetLength(); ++i) {
      if (kids.arrayGet(i, &kid)->isDict()) {
	if (kid.dictLookup("Limits", &limits)->isArray()) {
	  if (limits.arrayGet(0, &low)->isString() &&
	      name->cmp(low.getString()) >= 0) {
	    if (limits.arrayGet(1, &high)->isString() &&
		name->cmp(high.getString()) <= 0) {
	      findDestInTree(&kid, name, obj);
	      done = gTrue;
	    }
	    high.free();
	  }
	  low.free();
	}
	limits.free();
      }
      kid.free();
    }
  }
  kids.free();

  // name was outside of ranges of all kids
  if (!done)
    obj->initNull();

  return obj;
}
//========================================================================
//
// Catalog.h
//
// Copyright 1996-2007 Glyph & Cog, LLC
//
//========================================================================

#ifndef CATALOG_H
#define CATALOG_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

class XRef;
class Object;
class Page;
class PageAttrs;
struct Ref;
class LinkDest;

//------------------------------------------------------------------------
// Catalog
//------------------------------------------------------------------------

class Catalog {
public:

  // Constructor.
  Catalog(XRef *xrefA);

  // Destructor.
  ~Catalog();

  // Is catalog valid?
  GBool isOk() { return ok; }

  // Get number of pages.
  int getNumPages() { return numPages; }

  // Get a page.
  Page *getPage(int i) { return pages[i-1]; }

  // Get the reference for a page object.
  Ref *getPageRef(int i) { return &pageRefs[i-1]; }

  // Return base URI, or NULL if none.
  GString *getBaseURI() { return baseURI; }

  // Return the contents of the metadata stream, or NULL if there is
  // no metadata.
  GString *readMetadata();

  // Return the structure tree root object.
  Object *getStructTreeRoot() { return &structTreeRoot; }

  // Find a page, given its object ID.  Returns page number, or 0 if
  // not found.
  int findPage(int num, int gen);

  // Find a named destination.  Returns the link destination, or
  // NULL if <name> is not a destination.
  LinkDest *findDest(GString *name);

  Object *getDests() { return &dests; }

  Object *getNameTree() { return &nameTree; }

  Object *getOutline() { return &outline; }

  Object *getAcroForm() { return &acroForm; }

private:

  XRef *xref;			// the xref table for this PDF file
  Page **pages;			// array of pages
  Ref *pageRefs;		// object ID for each page
  int numPages;			// number of pages
  int pagesSize;		// size of pages array
  Object dests;			// named destination dictionary
  Object nameTree;		// name tree
  GString *baseURI;		// base URI for URI-type links
  Object metadata;		// metadata stream
  Object structTreeRoot;	// structure tree root dictionary
  Object outline;		// outline dictionary
  Object acroForm;		// AcroForm dictionary
  GBool ok;			// true if catalog is valid

  int readPageTree(Dict *pages, PageAttrs *attrs, int start,
		   char *alreadyRead);
  Object *findDestInTree(Object *tree, GString *name, Object *obj);
};

#endif
//========================================================================
//
// CharCodeToUnicode.cc
//
// Copyright 2001-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stdio.h>
#include <string.h>
#include "gmem.h"
#include "gfile.h"
#include "GString.h"
#include "Error.h"
#include "GlobalParams.h"
#include "PSTokenizer.h"
#include "CharCodeToUnicode.h"
#include "GooLikely.h"

//------------------------------------------------------------------------

#define maxUnicodeString 8

struct CharCodeToUnicodeString {
  CharCode c;
  Unicode u[maxUnicodeString];
  int len;
};

//------------------------------------------------------------------------

static int getCharFromString(void *data) {
  char *p;
  int c;

  p = *(char **)data;
  if (*p) {
    c = *p++;
    *(char **)data = p;
  } else {
    c = EOF;
  }
  return c;
}

static int getCharFromFile(void *data) {
  return fgetc((FILE *)data);
}

//------------------------------------------------------------------------

CharCodeToUnicode *CharCodeToUnicode::parseCIDToUnicode(GString *fileName,
							GString *collection) {
  FILE *f;
  Unicode *mapA;
  CharCode size, mapLenA;
  char buf[64];
  Unicode u;
  CharCodeToUnicode *ctu;

  if (!(f = fopen(fileName->getCString(), "r"))) {
    error(-1, "Couldn't open cidToUnicode file '%s'",
	  fileName->getCString());
    return NULL;
  }

  size = 32768;
  mapA = (Unicode *)gmallocn(size, sizeof(Unicode));
  mapLenA = 0;

  while (getLine(buf, sizeof(buf), f)) {
    if (mapLenA == size) {
      size *= 2;
      mapA = (Unicode *)greallocn(mapA, size, sizeof(Unicode));
    }
    if (sscanf(buf, "%x", &u) == 1) {
      mapA[mapLenA] = u;
    } else {
      error(-1, "Bad line (%d) in cidToUnicode file '%s'",
	    (int)(mapLenA + 1), fileName->getCString());
      mapA[mapLenA] = 0;
    }
    ++mapLenA;
  }
  fclose(f);

  ctu = new CharCodeToUnicode(collection->copy(), mapA, mapLenA, gTrue,
			      NULL, 0, 0);
  gfree(mapA);
  return ctu;
}

CharCodeToUnicode *CharCodeToUnicode::parseUnicodeToUnicode(
						    GString *fileName) {
  FILE *f;
  Unicode *mapA;
  CharCodeToUnicodeString *sMapA;
  CharCode size, oldSize, len, sMapSizeA, sMapLenA;
  char buf[256];
  char *tok;
  Unicode u0;
  Unicode uBuf[maxUnicodeString];
  CharCodeToUnicode *ctu;
  int line, n, i;

  if (!(f = fopen(fileName->getCString(), "r"))) {
    error(-1, "Couldn't open unicodeToUnicode file '%s'",
	  fileName->getCString());
    return NULL;
  }

  size = 4096;
  mapA = (Unicode *)gmallocn(size, sizeof(Unicode));
  memset(mapA, 0, size * sizeof(Unicode));
  len = 0;
  sMapA = NULL;
  sMapSizeA = sMapLenA = 0;

  line = 0;
  while (getLine(buf, sizeof(buf), f)) {
    ++line;
    if (!(tok = strtok(buf, " \t\r\n")) ||
	sscanf(tok, "%x", &u0) != 1) {
      error(-1, "Bad line (%d) in unicodeToUnicode file '%s'",
	    line, fileName->getCString());
      continue;
    }
    n = 0;
    while (n < maxUnicodeString) {
      if (!(tok = strtok(NULL, " \t\r\n"))) {
	break;
      }
      if (sscanf(tok, "%x", &uBuf[n]) != 1) {
	error(-1, "Bad line (%d) in unicodeToUnicode file '%s'",
	      line, fileName->getCString());
	break;
      }
      ++n;
    }
    if (n < 1) {
      error(-1, "Bad line (%d) in unicodeToUnicode file '%s'",
	    line, fileName->getCString());
      continue;
    }
    if (u0 >= size) {
      oldSize = size;
      while (u0 >= size) {
	size *= 2;
      }
      mapA = (Unicode *)greallocn(mapA, size, sizeof(Unicode));
      memset(mapA + oldSize, 0, (size - oldSize) * sizeof(Unicode));
    }
    if (n == 1) {
      mapA[u0] = uBuf[0];
    } else {
      mapA[u0] = 0;
      if (sMapLenA == sMapSizeA) {
	sMapSizeA += 16;
	sMapA = (CharCodeToUnicodeString *)
	          greallocn(sMapA, sMapSizeA, sizeof(CharCodeToUnicodeString));
      }
      sMapA[sMapLenA].c = u0;
      for (i = 0; i < n; ++i) {
	sMapA[sMapLenA].u[i] = uBuf[i];
      }
      sMapA[sMapLenA].len = n;
      ++sMapLenA;
    }
    if (u0 >= len) {
      len = u0 + 1;
    }
  }
  fclose(f);

  ctu = new CharCodeToUnicode(fileName->copy(), mapA, len, gTrue,
			      sMapA, sMapLenA, sMapSizeA);
  gfree(mapA);
  return ctu;
}

CharCodeToUnicode *CharCodeToUnicode::make8BitToUnicode(Unicode *toUnicode) {
  return new CharCodeToUnicode(NULL, toUnicode, 256, gTrue, NULL, 0, 0);
}

CharCodeToUnicode *CharCodeToUnicode::parseCMap(GString *buf, int nBits) {
  CharCodeToUnicode *ctu;
  char *p;

  ctu = new CharCodeToUnicode(NULL);
  p = buf->getCString();
  ctu->parseCMap1(&getCharFromString, &p, nBits);
  return ctu;
}

void CharCodeToUnicode::mergeCMap(GString *buf, int nBits) {
  char *p;

  p = buf->getCString();
  parseCMap1(&getCharFromString, &p, nBits);
}

void CharCodeToUnicode::parseCMap1(int (*getCharFunc)(void *), void *data,
				   int nBits) {
  PSTokenizer *pst;
  char tok1[256], tok2[256], tok3[256];
  int nDigits, n1, n2, n3;
  CharCode i;
  CharCode code1, code2;
  GString *name;
  FILE *f;

  nDigits = nBits / 4;
  pst = new PSTokenizer(getCharFunc, data);
  pst->getToken(tok1, sizeof(tok1), &n1);
  while (pst->getToken(tok2, sizeof(tok2), &n2)) {
    if (!strcmp(tok2, "usecmap")) {
      if (tok1[0] == '/') {
	name = new GString(tok1 + 1);
	if ((f = globalParams->findToUnicodeFile(name))) {
	  parseCMap1(&getCharFromFile, f, nBits);
	  fclose(f);
	} else {
	  error(-1, "Couldn't find ToUnicode CMap file for '%s'",
		name->getCString());
	}
	delete name;
      }
      pst->getToken(tok1, sizeof(tok1), &n1);
    } else if (!strcmp(tok2, "beginbfchar")) {
      while (pst->getToken(tok1, sizeof(tok1), &n1)) {
	if (!strcmp(tok1, "endbfchar")) {
	  break;
	}
	if (!pst->getToken(tok2, sizeof(tok2), &n2) ||
	    !strcmp(tok2, "endbfchar")) {
	  error(-1, "Illegal entry in bfchar block in ToUnicode CMap");
	  break;
	}
	if (!(n1 == 2 + nDigits && tok1[0] == '<' && tok1[n1 - 1] == '>' &&
	      tok2[0] == '<' && tok2[n2 - 1] == '>')) {
	  error(-1, "Illegal entry in bfchar block in ToUnicode CMap");
	  continue;
	}
	tok1[n1 - 1] = tok2[n2 - 1] = '\0';
	if (sscanf(tok1 + 1, "%x", &code1) != 1) {
	  error(-1, "Illegal entry in bfchar block in ToUnicode CMap");
	  continue;
	}
	addMapping(code1, tok2 + 1, n2 - 2, 0);
      }
      pst->getToken(tok1, sizeof(tok1), &n1);
    } else if (!strcmp(tok2, "beginbfrange")) {
      while (pst->getToken(tok1, sizeof(tok1), &n1)) {
	if (!strcmp(tok1, "endbfrange")) {
	  break;
	}
	if (!pst->getToken(tok2, sizeof(tok2), &n2) ||
	    !strcmp(tok2, "endbfrange") ||
	    !pst->getToken(tok3, sizeof(tok3), &n3) ||
	    !strcmp(tok3, "endbfrange")) {
	  error(-1, "Illegal entry in bfrange block in ToUnicode CMap");
	  break;
	}
	if (!(n1 == 2 + nDigits && tok1[0] == '<' && tok1[n1 - 1] == '>' &&
	      n2 == 2 + nDigits && tok2[0] == '<' && tok2[n2 - 1] == '>')) {
	  error(-1, "Illegal entry in bfrange block in ToUnicode CMap");
	  continue;
	}
	tok1[n1 - 1] = tok2[n2 - 1] = '\0';
	if (sscanf(tok1 + 1, "%x", &code1) != 1 ||
	    sscanf(tok2 + 1, "%x", &code2) != 1) {
	  error(-1, "Illegal entry in bfrange block in ToUnicode CMap");
	  continue;
	}
	if (!strcmp(tok3, "[")) {
	  i = 0;
	  while (pst->getToken(tok1, sizeof(tok1), &n1) &&
		 code1 + i <= code2) {
	    if (!strcmp(tok1, "]")) {
	      break;
	    }
	    if (tok1[0] == '<' && tok1[n1 - 1] == '>') {
	      tok1[n1 - 1] = '\0';
	      addMapping(code1 + i, tok1 + 1, n1 - 2, 0);
	    } else {
	      error(-1, "Illegal entry in bfrange block in ToUnicode CMap");
	    }
	    ++i;
	  }
	} else if (tok3[0] == '<' && tok3[n3 - 1] == '>') {
	  tok3[n3 - 1] = '\0';
	  for (i = 0; code1 <= code2; ++code1, ++i) {
	    addMapping(code1, tok3 + 1, n3 - 2, i);
	  }

	} else {
	  error(-1, "Illegal entry in bfrange block in ToUnicode CMap");
	}
      }
      pst->getToken(tok1, sizeof(tok1), &n1);
    } else {
      strcpy(tok1, tok2);
    }
  }
  delete pst;
}

void CharCodeToUnicode::addMapping(CharCode code, char *uStr, int n,
				   int offset) {
  CharCode oldLen, i;
  Unicode u;
  char uHex[5];
  int j;

  if (code >= mapLen) {
    oldLen = mapLen;
    mapLen = (code + 256) & ~255;
    if (unlikely(code >= mapLen)) {
      error(-1, "Illegal code value in CharCodeToUnicode::addMapping");
      return;
    } else {
      map = (Unicode *)greallocn(map, mapLen, sizeof(Unicode));
      for (i = oldLen; i < mapLen; ++i) {
        map[i] = 0;
      }
    }
  }
  if (n <= 4) {
    if (sscanf(uStr, "%x", &u) != 1) {
      error(-1, "Illegal entry in ToUnicode CMap");
      return;
    }
    map[code] = u + offset;
  } else {
    if (sMapLen >= sMapSize) {
      sMapSize = sMapSize + 16;
      sMap = (CharCodeToUnicodeString *)
	       greallocn(sMap, sMapSize, sizeof(CharCodeToUnicodeString));
    }
    map[code] = 0;
    sMap[sMapLen].c = code;
    sMap[sMapLen].len = n / 4;
    for (j = 0; j < sMap[sMapLen].len && j < maxUnicodeString; ++j) {
      strncpy(uHex, uStr + j*4, 4);
      uHex[4] = '\0';
      if (sscanf(uHex, "%x", &sMap[sMapLen].u[j]) != 1) {
	error(-1, "Illegal entry in ToUnicode CMap");
      }
    }
    sMap[sMapLen].u[sMap[sMapLen].len - 1] += offset;
    ++sMapLen;
  }
}

CharCodeToUnicode::CharCodeToUnicode(GString *tagA) {
  CharCode i;

  tag = tagA;
  mapLen = 256;
  map = (Unicode *)gmallocn(mapLen, sizeof(Unicode));
  for (i = 0; i < mapLen; ++i) {
    map[i] = 0;
  }
  sMap = NULL;
  sMapLen = sMapSize = 0;
  refCnt = 1;
#if MULTITHREADED
  gInitMutex(&mutex);
#endif
}

CharCodeToUnicode::CharCodeToUnicode(GString *tagA, Unicode *mapA,
				     CharCode mapLenA, GBool copyMap,
				     CharCodeToUnicodeString *sMapA,
				     int sMapLenA, int sMapSizeA) {
  tag = tagA;
  mapLen = mapLenA;
  if (copyMap) {
    map = (Unicode *)gmallocn(mapLen, sizeof(Unicode));
    memcpy(map, mapA, mapLen * sizeof(Unicode));
  } else {
    map = mapA;
  }
  sMap = sMapA;
  sMapLen = sMapLenA;
  sMapSize = sMapSizeA;
  refCnt = 1;
#if MULTITHREADED
  gInitMutex(&mutex);
#endif
}

CharCodeToUnicode::~CharCodeToUnicode() {
  if (tag) {
    delete tag;
  }
  gfree(map);
  if (sMap) {
    gfree(sMap);
  }
#if MULTITHREADED
  gDestroyMutex(&mutex);
#endif
}

void CharCodeToUnicode::incRefCnt() {
#if MULTITHREADED
  gLockMutex(&mutex);
#endif
  ++refCnt;
#if MULTITHREADED
  gUnlockMutex(&mutex);
#endif
}

void CharCodeToUnicode::decRefCnt() {
  GBool done;

#if MULTITHREADED
  gLockMutex(&mutex);
#endif
  done = --refCnt == 0;
#if MULTITHREADED
  gUnlockMutex(&mutex);
#endif
  if (done) {
    delete this;
  }
}

GBool CharCodeToUnicode::match(GString *tagA) {
  return tag && !tag->cmp(tagA);
}

void CharCodeToUnicode::setMapping(CharCode c, Unicode *u, int len) {
  int i, j;

  if (len == 1) {
    map[c] = u[0];
  } else {
    for (i = 0; i < sMapLen; ++i) {
      if (sMap[i].c == c) {
	break;
      }
    }
    if (i == sMapLen) {
      if (sMapLen == sMapSize) {
	sMapSize += 8;
	sMap = (CharCodeToUnicodeString *)
	         greallocn(sMap, sMapSize, sizeof(CharCodeToUnicodeString));
      }
      ++sMapLen;
    }
    map[c] = 0;
    sMap[i].c = c;
    sMap[i].len = len;
    for (j = 0; j < len && j < maxUnicodeString; ++j) {
      sMap[i].u[j] = u[j];
    }
  }
}

int CharCodeToUnicode::mapToUnicode(CharCode c, Unicode *u, int size) {
  int i, j;

  if (c >= mapLen) {
    return 0;
  }
  if (map[c]) {
    u[0] = map[c];
    return 1;
  }
  for (i = 0; i < sMapLen; ++i) {
    if (sMap[i].c == c) {
      for (j = 0; j < sMap[i].len && j < size; ++j) {
	u[j] = sMap[i].u[j];
      }
      return j;
    }
  }
  return 0;
}

//------------------------------------------------------------------------

CharCodeToUnicodeCache::CharCodeToUnicodeCache(int sizeA) {
  int i;

  size = sizeA;
  cache = (CharCodeToUnicode **)gmallocn(size, sizeof(CharCodeToUnicode *));
  for (i = 0; i < size; ++i) {
    cache[i] = NULL;
  }
}

CharCodeToUnicodeCache::~CharCodeToUnicodeCache() {
  int i;

  for (i = 0; i < size; ++i) {
    if (cache[i]) {
      cache[i]->decRefCnt();
    }
  }
  gfree(cache);
}

CharCodeToUnicode *CharCodeToUnicodeCache::getCharCodeToUnicode(GString *tag) {
  CharCodeToUnicode *ctu;
  int i, j;

  if (cache[0] && cache[0]->match(tag)) {
    cache[0]->incRefCnt();
    return cache[0];
  }
  for (i = 1; i < size; ++i) {
    if (cache[i] && cache[i]->match(tag)) {
      ctu = cache[i];
      for (j = i; j >= 1; --j) {
	cache[j] = cache[j - 1];
      }
      cache[0] = ctu;
      ctu->incRefCnt();
      return ctu;
    }
  }
  return NULL;
}

void CharCodeToUnicodeCache::add(CharCodeToUnicode *ctu) {
  int i;

  if (cache[size - 1]) {
    cache[size - 1]->decRefCnt();
  }
  for (i = size - 1; i >= 1; --i) {
    cache[i] = cache[i - 1];
  }
  cache[0] = ctu;
  ctu->incRefCnt();
}
//========================================================================
//
// CharCodeToUnicode.h
//
// Mapping from character codes to Unicode.
//
// Copyright 2001-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef CHARCODETOUNICODE_H
#define CHARCODETOUNICODE_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "CharTypes.h"

#if MULTITHREADED
#include "GMutex.h"
#endif

struct CharCodeToUnicodeString;

//------------------------------------------------------------------------

class CharCodeToUnicode {
public:

  // Read the CID-to-Unicode mapping for <collection> from the file
  // specified by <fileName>.  Sets the initial reference count to 1.
  // Returns NULL on failure.
  static CharCodeToUnicode *parseCIDToUnicode(GString *fileName,
					      GString *collection);

  // Create a Unicode-to-Unicode mapping from the file specified by
  // <fileName>.  Sets the initial reference count to 1.  Returns NULL
  // on failure.
  static CharCodeToUnicode *parseUnicodeToUnicode(GString *fileName);

  // Create the CharCode-to-Unicode mapping for an 8-bit font.
  // <toUnicode> is an array of 256 Unicode indexes.  Sets the initial
  // reference count to 1.
  static CharCodeToUnicode *make8BitToUnicode(Unicode *toUnicode);

  // Parse a ToUnicode CMap for an 8- or 16-bit font.
  static CharCodeToUnicode *parseCMap(GString *buf, int nBits);

  // Parse a ToUnicode CMap for an 8- or 16-bit font, merging it into
  // <this>.
  void mergeCMap(GString *buf, int nBits);

  ~CharCodeToUnicode();

  void incRefCnt();
  void decRefCnt();

  // Return true if this mapping matches the specified <tagA>.
  GBool match(GString *tagA);

  // Set the mapping for <c>.
  void setMapping(CharCode c, Unicode *u, int len);

  // Map a CharCode to Unicode.
  int mapToUnicode(CharCode c, Unicode *u, int size);

  // Return the mapping's length, i.e., one more than the max char
  // code supported by the mapping.
  CharCode getLength() { return mapLen; }

private:

  void parseCMap1(int (*getCharFunc)(void *), void *data, int nBits);
  void addMapping(CharCode code, char *uStr, int n, int offset);
  CharCodeToUnicode(GString *tagA);
  CharCodeToUnicode(GString *tagA, Unicode *mapA,
		    CharCode mapLenA, GBool copyMap,
		    CharCodeToUnicodeString *sMapA,
		    int sMapLenA, int sMapSizeA);

  GString *tag;
  Unicode *map;
  CharCode mapLen;
  CharCodeToUnicodeString *sMap;
  int sMapLen, sMapSize;
  int refCnt;
#if MULTITHREADED
  GMutex mutex;
#endif
};

//------------------------------------------------------------------------

class CharCodeToUnicodeCache {
public:

  CharCodeToUnicodeCache(int sizeA);
  ~CharCodeToUnicodeCache();

  // Get the CharCodeToUnicode object for <tag>.  Increments its
  // reference count; there will be one reference for the cache plus
  // one for the caller of this function.  Returns NULL on failure.
  CharCodeToUnicode *getCharCodeToUnicode(GString *tag);

  // Insert <ctu> into the cache, in the most-recently-used position.
  void add(CharCodeToUnicode *ctu);

private:

  CharCodeToUnicode **cache;
  int size;
};

#endif
//========================================================================
//
// CharTypes.h
//
// Copyright 2001-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef CHARTYPES_H
#define CHARTYPES_H

// Unicode character.
typedef unsigned int Unicode;

// Character ID for CID character collections.
typedef unsigned int CID;

// This is large enough to hold any of the following:
// - 8-bit char code
// - 16-bit CID
// - Unicode
typedef unsigned int CharCode;

#endif
//========================================================================
//
// CompactFontTables.h
//
// Copyright 1999-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef COMPACTFONTINFO_H
#define COMPACTFONTINFO_H

static char *type1CStdStrings[391] = {
  ".notdef",
  "space",
  "exclam",
  "quotedbl",
  "numbersign",
  "dollar",
  "percent",
  "ampersand",
  "quoteright",
  "parenleft",
  "parenright",
  "asterisk",
  "plus",
  "comma",
  "hyphen",
  "period",
  "slash",
  "zero",
  "one",
  "two",
  "three",
  "four",
  "five",
  "six",
  "seven",
  "eight",
  "nine",
  "colon",
  "semicolon",
  "less",
  "equal",
  "greater",
  "question",
  "at",
  "A",
  "B",
  "C",
  "D",
  "E",
  "F",
  "G",
  "H",
  "I",
  "J",
  "K",
  "L",
  "M",
  "N",
  "O",
  "P",
  "Q",
  "R",
  "S",
  "T",
  "U",
  "V",
  "W",
  "X",
  "Y",
  "Z",
  "bracketleft",
  "backslash",
  "bracketright",
  "asciicircum",
  "underscore",
  "quoteleft",
  "a",
  "b",
  "c",
  "d",
  "e",
  "f",
  "g",
  "h",
  "i",
  "j",
  "k",
  "l",
  "m",
  "n",
  "o",
  "p",
  "q",
  "r",
  "s",
  "t",
  "u",
  "v",
  "w",
  "x",
  "y",
  "z",
  "braceleft",
  "bar",
  "braceright",
  "asciitilde",
  "exclamdown",
  "cent",
  "sterling",
  "fraction",
  "yen",
  "florin",
  "section",
  "currency",
  "quotesingle",
  "quotedblleft",
  "guillemotleft",
  "guilsinglleft",
  "guilsinglright",
  "fi",
  "fl",
  "endash",
  "dagger",
  "daggerdbl",
  "periodcentered",
  "paragraph",
  "bullet",
  "quotesinglbase",
  "quotedblbase",
  "quotedblright",
  "guillemotright",
  "ellipsis",
  "perthousand",
  "questiondown",
  "grave",
  "acute",
  "circumflex",
  "tilde",
  "macron",
  "breve",
  "dotaccent",
  "dieresis",
  "ring",
  "cedilla",
  "hungarumlaut",
  "ogonek",
  "caron",
  "emdash",
  "AE",
  "ordfeminine",
  "Lslash",
  "Oslash",
  "OE",
  "ordmasculine",
  "ae",
  "dotlessi",
  "lslash",
  "oslash",
  "oe",
  "germandbls",
  "onesuperior",
  "logicalnot",
  "mu",
  "trademark",
  "Eth",
  "onehalf",
  "plusminus",
  "Thorn",
  "onequarter",
  "divide",
  "brokenbar",
  "degree",
  "thorn",
  "threequarters",
  "twosuperior",
  "registered",
  "minus",
  "eth",
  "multiply",
  "threesuperior",
  "copyright",
  "Aacute",
  "Acircumflex",
  "Adieresis",
  "Agrave",
  "Aring",
  "Atilde",
  "Ccedilla",
  "Eacute",
  "Ecircumflex",
  "Edieresis",
  "Egrave",
  "Iacute",
  "Icircumflex",
  "Idieresis",
  "Igrave",
  "Ntilde",
  "Oacute",
  "Ocircumflex",
  "Odieresis",
  "Ograve",
  "Otilde",
  "Scaron",
  "Uacute",
  "Ucircumflex",
  "Udieresis",
  "Ugrave",
  "Yacute",
  "Ydieresis",
  "Zcaron",
  "aacute",
  "acircumflex",
  "adieresis",
  "agrave",
  "aring",
  "atilde",
  "ccedilla",
  "eacute",
  "ecircumflex",
  "edieresis",
  "egrave",
  "iacute",
  "icircumflex",
  "idieresis",
  "igrave",
  "ntilde",
  "oacute",
  "ocircumflex",
  "odieresis",
  "ograve",
  "otilde",
  "scaron",
  "uacute",
  "ucircumflex",
  "udieresis",
  "ugrave",
  "yacute",
  "ydieresis",
  "zcaron",
  "exclamsmall",
  "Hungarumlautsmall",
  "dollaroldstyle",
  "dollarsuperior",
  "ampersandsmall",
  "Acutesmall",
  "parenleftsuperior",
  "parenrightsuperior",
  "twodotenleader",
  "onedotenleader",
  "zerooldstyle",
  "oneoldstyle",
  "twooldstyle",
  "threeoldstyle",
  "fouroldstyle",
  "fiveoldstyle",
  "sixoldstyle",
  "sevenoldstyle",
  "eightoldstyle",
  "nineoldstyle",
  "commasuperior",
  "threequartersemdash",
  "periodsuperior",
  "questionsmall",
  "asuperior",
  "bsuperior",
  "centsuperior",
  "dsuperior",
  "esuperior",
  "isuperior",
  "lsuperior",
  "msuperior",
  "nsuperior",
  "osuperior",
  "rsuperior",
  "ssuperior",
  "tsuperior",
  "ff",
  "ffi",
  "ffl",
  "parenleftinferior",
  "parenrightinferior",
  "Circumflexsmall",
  "hyphensuperior",
  "Gravesmall",
  "Asmall",
  "Bsmall",
  "Csmall",
  "Dsmall",
  "Esmall",
  "Fsmall",
  "Gsmall",
  "Hsmall",
  "Ismall",
  "Jsmall",
  "Ksmall",
  "Lsmall",
  "Msmall",
  "Nsmall",
  "Osmall",
  "Psmall",
  "Qsmall",
  "Rsmall",
  "Ssmall",
  "Tsmall",
  "Usmall",
  "Vsmall",
  "Wsmall",
  "Xsmall",
  "Ysmall",
  "Zsmall",
  "colonmonetary",
  "onefitted",
  "rupiah",
  "Tildesmall",
  "exclamdownsmall",
  "centoldstyle",
  "Lslashsmall",
  "Scaronsmall",
  "Zcaronsmall",
  "Dieresissmall",
  "Brevesmall",
  "Caronsmall",
  "Dotaccentsmall",
  "Macronsmall",
  "figuredash",
  "hypheninferior",
  "Ogoneksmall",
  "Ringsmall",
  "Cedillasmall",
  "questiondownsmall",
  "oneeighth",
  "threeeighths",
  "fiveeighths",
  "seveneighths",
  "onethird",
  "twothirds",
  "zerosuperior",
  "foursuperior",
  "fivesuperior",
  "sixsuperior",
  "sevensuperior",
  "eightsuperior",
  "ninesuperior",
  "zeroinferior",
  "oneinferior",
  "twoinferior",
  "threeinferior",
  "fourinferior",
  "fiveinferior",
  "sixinferior",
  "seveninferior",
  "eightinferior",
  "nineinferior",
  "centinferior",
  "dollarinferior",
  "periodinferior",
  "commainferior",
  "Agravesmall",
  "Aacutesmall",
  "Acircumflexsmall",
  "Atildesmall",
  "Adieresissmall",
  "Aringsmall",
  "AEsmall",
  "Ccedillasmall",
  "Egravesmall",
  "Eacutesmall",
  "Ecircumflexsmall",
  "Edieresissmall",
  "Igravesmall",
  "Iacutesmall",
  "Icircumflexsmall",
  "Idieresissmall",
  "Ethsmall",
  "Ntildesmall",
  "Ogravesmall",
  "Oacutesmall",
  "Ocircumflexsmall",
  "Otildesmall",
  "Odieresissmall",
  "OEsmall",
  "Oslashsmall",
  "Ugravesmall",
  "Uacutesmall",
  "Ucircumflexsmall",
  "Udieresissmall",
  "Yacutesmall",
  "Thornsmall",
  "Ydieresissmall",
  "001.000",
  "001.001",
  "001.002",
  "001.003",
  "Black",
  "Bold",
  "Book",
  "Light",
  "Medium",
  "Regular",
  "Roman",
  "Semibold"
};

static Gushort type1CISOAdobeCharset[229] = {
    0,   1,   2,   3,   4,   5,   6,   7,   8,   9,
   10,  11,  12,  13,  14,  15,  16,  17,  18,  19,
   20,  21,  22,  23,  24,  25,  26,  27,  28,  29,
   30,  31,  32,  33,  34,  35,  36,  37,  38,  39,
   40,  41,  42,  43,  44,  45,  46,  47,  48,  49,
   50,  51,  52,  53,  54,  55,  56,  57,  58,  59,
   60,  61,  62,  63,  64,  65,  66,  67,  68,  69,
   70,  71,  72,  73,  74,  75,  76,  77,  78,  79,
   80,  81,  82,  83,  84,  85,  86,  87,  88,  89,
   90,  91,  92,  93,  94,  95,  96,  97,  98,  99,
  100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
  110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
  120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
  130, 131, 132, 133, 134, 135, 136, 137, 138, 139,
  140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
  150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
  160, 161, 162, 163, 164, 165, 166, 167, 168, 169,
  170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
  180, 181, 182, 183, 184, 185, 186, 187, 188, 189,
  190, 191, 192, 193, 194, 195, 196, 197, 198, 199,
  200, 201, 202, 203, 204, 205, 206, 207, 208, 209,
  210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
  220, 221, 222, 223, 224, 225, 226, 227, 228
};

static Gushort type1CExpertCharset[166] = {
    0,   1, 229, 230, 231, 232, 233, 234, 235, 236,
  237, 238,  13,  14,  15,  99, 239, 240, 241, 242,
  243, 244, 245, 246, 247, 248,  27,  28, 249, 250,
  251, 252, 253, 254, 255, 256, 257, 258, 259, 260,
  261, 262, 263, 264, 265, 266, 109, 110, 267, 268,
  269, 270, 271, 272, 273, 274, 275, 276, 277, 278,
  279, 280, 281, 282, 283, 284, 285, 286, 287, 288,
  289, 290, 291, 292, 293, 294, 295, 296, 297, 298,
  299, 300, 301, 302, 303, 304, 305, 306, 307, 308,
  309, 310, 311, 312, 313, 314, 315, 316, 317, 318,
  158, 155, 163, 319, 320, 321, 322, 323, 324, 325,
  326, 150, 164, 169, 327, 328, 329, 330, 331, 332,
  333, 334, 335, 336, 337, 338, 339, 340, 341, 342,
  343, 344, 345, 346, 347, 348, 349, 350, 351, 352,
  353, 354, 355, 356, 357, 358, 359, 360, 361, 362,
  363, 364, 365, 366, 367, 368, 369, 370, 371, 372,
  373, 374, 375, 376, 377, 378
};

static Gushort type1CExpertSubsetCharset[87] = {
    0,   1, 231, 232, 235, 236, 237, 238,  13,  14,
   15,  99, 239, 240, 241, 242, 243, 244, 245, 246,
  247, 248,  27,  28, 249, 250, 251, 253, 254, 255,
  256, 257, 258, 259, 260, 261, 262, 263, 264, 265,
  266, 109, 110, 267, 268, 269, 270, 272, 300, 301,
  302, 305, 314, 315, 158, 155, 163, 320, 321, 322,
  323, 324, 325, 326, 150, 164, 169, 327, 328, 329,
  330, 331, 332, 333, 334, 335, 336, 337, 338, 339,
  340, 341, 342, 343, 344, 345, 346
};

#endif
//========================================================================
//
// CoreOutputDev.cc
//
// Copyright 2004 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include "Object.h"
#include "TextOutputDev.h"
#include "CoreOutputDev.h"

//------------------------------------------------------------------------
// CoreOutputDev
//------------------------------------------------------------------------

CoreOutputDev::CoreOutputDev(SplashColorMode colorModeA, int bitmapRowPadA,
			     GBool reverseVideoA, SplashColorPtr paperColorA,
			     GBool incrementalUpdateA,
			     CoreOutRedrawCbk redrawCbkA,
			     void *redrawCbkDataA):
  SplashOutputDev(colorModeA, bitmapRowPadA, reverseVideoA, paperColorA)
{
  incrementalUpdate = incrementalUpdateA;
  redrawCbk = redrawCbkA;
  redrawCbkData = redrawCbkDataA;
}

CoreOutputDev::~CoreOutputDev() {
}

void CoreOutputDev::endPage() {
  SplashOutputDev::endPage();
  if (!incrementalUpdate) {
    (*redrawCbk)(redrawCbkData, 0, 0, getBitmapWidth(), getBitmapHeight(),
		 gTrue);
  }
}

void CoreOutputDev::dump() {
  int x0, y0, x1, y1;

  if (incrementalUpdate) {
    getModRegion(&x0, &y0, &x1, &y1);
    clearModRegion();
    if (x1 >= x0 && y1 >= y0) {
      (*redrawCbk)(redrawCbkData, x0, y0, x1, y1, gFalse);
    }
  }
}

void CoreOutputDev::clear() {
  startDoc(NULL);
  startPage(0, NULL);
}
//========================================================================
//
// CoreOutputDev.h
//
// Copyright 2004 Glyph & Cog, LLC
//
//========================================================================

#ifndef COREOUTPUTDEV_H
#define COREOUTPUTDEV_H

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "SplashTypes.h"
#include "SplashOutputDev.h"

class TextPage;

//------------------------------------------------------------------------

typedef void (*CoreOutRedrawCbk)(void *data, int x0, int y0, int x1, int y1,
				 GBool composited);

//------------------------------------------------------------------------
// CoreOutputDev
//------------------------------------------------------------------------

class CoreOutputDev: public SplashOutputDev {
public:

  CoreOutputDev(SplashColorMode colorModeA, int bitmapRowPadA,
		GBool reverseVideoA, SplashColorPtr paperColorA,
		GBool incrementalUpdateA,
		CoreOutRedrawCbk redrawCbkA,
		void *redrawCbkDataA);

  virtual ~CoreOutputDev();

  //----- initialization and control

  // End a page.
  virtual void endPage();

  // Dump page contents to display.
  virtual void dump();

  //----- special access

  // Clear out the document (used when displaying an empty window).
  void clear();

private:

  GBool incrementalUpdate;      // incrementally update the display?
  CoreOutRedrawCbk redrawCbk;
  void *redrawCbkData;
};

#endif
//========================================================================
//
// Decrypt.cc
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <string.h>
#include "gmem.h"
#include "Decrypt.h"

static void rc4InitKey(Guchar *key, int keyLen, Guchar *state);
static Guchar rc4DecryptByte(Guchar *state, Guchar *x, Guchar *y, Guchar c);
static void aesKeyExpansion(DecryptAESState *s,
			    Guchar *objKey, int objKeyLen);
static void aesDecryptBlock(DecryptAESState *s, Guchar *in, GBool last);
static void md5(Guchar *msg, int msgLen, Guchar *digest);

static Guchar passwordPad[32] = {
  0x28, 0xbf, 0x4e, 0x5e, 0x4e, 0x75, 0x8a, 0x41,
  0x64, 0x00, 0x4e, 0x56, 0xff, 0xfa, 0x01, 0x08, 
  0x2e, 0x2e, 0x00, 0xb6, 0xd0, 0x68, 0x3e, 0x80, 
  0x2f, 0x0c, 0xa9, 0xfe, 0x64, 0x53, 0x69, 0x7a
};

//------------------------------------------------------------------------
// Decrypt
//------------------------------------------------------------------------

GBool Decrypt::makeFileKey(int encVersion, int encRevision, int keyLength,
			   GString *ownerKey, GString *userKey,
			   int permissions, GString *fileID,
			   GString *ownerPassword, GString *userPassword,
			   Guchar *fileKey, GBool encryptMetadata,
			   GBool *ownerPasswordOk) {
  Guchar test[32], test2[32];
  GString *userPassword2;
  Guchar fState[256];
  Guchar tmpKey[16];
  Guchar fx, fy;
  int len, i, j;

  // try using the supplied owner password to generate the user password
  *ownerPasswordOk = gFalse;
  if (ownerPassword) {
    len = ownerPassword->getLength();
    if (len < 32) {
      memcpy(test, ownerPassword->getCString(), len);
      memcpy(test + len, passwordPad, 32 - len);
    } else {
      memcpy(test, ownerPassword->getCString(), 32);
    }
    md5(test, 32, test);
    if (encRevision == 3) {
      for (i = 0; i < 50; ++i) {
	md5(test, 16, test);
      }
    }
    if (encRevision == 2) {
      rc4InitKey(test, keyLength, fState);
      fx = fy = 0;
      for (i = 0; i < 32; ++i) {
	test2[i] = rc4DecryptByte(fState, &fx, &fy, ownerKey->getChar(i));
      }
    } else {
      memcpy(test2, ownerKey->getCString(), 32);
      for (i = 19; i >= 0; --i) {
	for (j = 0; j < keyLength; ++j) {
	  tmpKey[j] = test[j] ^ i;
	}
	rc4InitKey(tmpKey, keyLength, fState);
	fx = fy = 0;
	for (j = 0; j < 32; ++j) {
	  test2[j] = rc4DecryptByte(fState, &fx, &fy, test2[j]);
	}
      }
    }
    userPassword2 = new GString((char *)test2, 32);
    if (makeFileKey2(encVersion, encRevision, keyLength, ownerKey, userKey,
		     permissions, fileID, userPassword2, fileKey,
		     encryptMetadata)) {
      *ownerPasswordOk = gTrue;
      delete userPassword2;
      return gTrue;
    }
    delete userPassword2;
  }

  // try using the supplied user password
  return makeFileKey2(encVersion, encRevision, keyLength, ownerKey, userKey,
		      permissions, fileID, userPassword, fileKey,
		      encryptMetadata);
}

GBool Decrypt::makeFileKey2(int encVersion, int encRevision, int keyLength,
			    GString *ownerKey, GString *userKey,
			    int permissions, GString *fileID,
			    GString *userPassword, Guchar *fileKey,
			    GBool encryptMetadata) {
  Guchar *buf;
  Guchar test[32];
  Guchar fState[256];
  Guchar tmpKey[16];
  Guchar fx, fy;
  int len, i, j;
  GBool ok;

  // generate file key
  buf = (Guchar *)gmalloc(72 + fileID->getLength());
  if (userPassword) {
    len = userPassword->getLength();
    if (len < 32) {
      memcpy(buf, userPassword->getCString(), len);
      memcpy(buf + len, passwordPad, 32 - len);
    } else {
      memcpy(buf, userPassword->getCString(), 32);
    }
  } else {
    memcpy(buf, passwordPad, 32);
  }
  memcpy(buf + 32, ownerKey->getCString(), 32);
  buf[64] = permissions & 0xff;
  buf[65] = (permissions >> 8) & 0xff;
  buf[66] = (permissions >> 16) & 0xff;
  buf[67] = (permissions >> 24) & 0xff;
  memcpy(buf + 68, fileID->getCString(), fileID->getLength());
  len = 68 + fileID->getLength();
  if (!encryptMetadata) {
    buf[len++] = 0xff;
    buf[len++] = 0xff;
    buf[len++] = 0xff;
    buf[len++] = 0xff;
  }
  md5(buf, len, fileKey);
  if (encRevision == 3) {
    for (i = 0; i < 50; ++i) {
      md5(fileKey, keyLength, fileKey);
    }
  }

  // test user password
  if (encRevision == 2) {
    rc4InitKey(fileKey, keyLength, fState);
    fx = fy = 0;
    for (i = 0; i < 32; ++i) {
      test[i] = rc4DecryptByte(fState, &fx, &fy, userKey->getChar(i));
    }
    ok = memcmp(test, passwordPad, 32) == 0;
  } else if (encRevision == 3) {
    memcpy(test, userKey->getCString(), 32);
    for (i = 19; i >= 0; --i) {
      for (j = 0; j < keyLength; ++j) {
	tmpKey[j] = fileKey[j] ^ i;
      }
      rc4InitKey(tmpKey, keyLength, fState);
      fx = fy = 0;
      for (j = 0; j < 32; ++j) {
	test[j] = rc4DecryptByte(fState, &fx, &fy, test[j]);
      }
    }
    memcpy(buf, passwordPad, 32);
    memcpy(buf + 32, fileID->getCString(), fileID->getLength());
    md5(buf, 32 + fileID->getLength(), buf);
    ok = memcmp(test, buf, 16) == 0;
  } else {
    ok = gFalse;
  }

  gfree(buf);
  return ok;
}

//------------------------------------------------------------------------
// DecryptStream
//------------------------------------------------------------------------

DecryptStream::DecryptStream(Stream *strA, Guchar *fileKey,
			     CryptAlgorithm algoA, int keyLength,
			     int objNum, int objGen):
  FilterStream(strA)
{
  int n, i;

  algo = algoA;

  // construct object key
  for (i = 0; i < keyLength; ++i) {
    objKey[i] = fileKey[i];
  }
  objKey[keyLength] = objNum & 0xff;
  objKey[keyLength + 1] = (objNum >> 8) & 0xff;
  objKey[keyLength + 2] = (objNum >> 16) & 0xff;
  objKey[keyLength + 3] = objGen & 0xff;
  objKey[keyLength + 4] = (objGen >> 8) & 0xff;
  if (algo == cryptAES) {
    objKey[keyLength + 5] = 0x73; // 's'
    objKey[keyLength + 6] = 0x41; // 'A'
    objKey[keyLength + 7] = 0x6c; // 'l'
    objKey[keyLength + 8] = 0x54; // 'T'
    n = keyLength + 9;
  } else {
    n = keyLength + 5;
  }
  md5(objKey, n, objKey);
  if ((objKeyLength = keyLength + 5) > 16) {
    objKeyLength = 16;
  }
}

DecryptStream::~DecryptStream() {
  delete str;
}

void DecryptStream::reset() {
  int i;

  str->reset();
  switch (algo) {
  case cryptRC4:
    state.rc4.x = state.rc4.y = 0;
    rc4InitKey(objKey, objKeyLength, state.rc4.state);
    state.rc4.buf = EOF;
    break;
  case cryptAES:
    aesKeyExpansion(&state.aes, objKey, objKeyLength);
    for (i = 0; i < 16; ++i) {
      state.aes.cbc[i] = str->getChar();
    }
    state.aes.bufIdx = 16;
    break;
  }
}

int DecryptStream::getChar() {
  Guchar in[16];
  int c, i;

  c = EOF; // make gcc happy
  switch (algo) {
  case cryptRC4:
    if (state.rc4.buf == EOF) {
      c = str->getChar();
      if (c != EOF) {
	state.rc4.buf = rc4DecryptByte(state.rc4.state, &state.rc4.x,
				       &state.rc4.y, (Guchar)c);
      }
    }
    c = state.rc4.buf;
    state.rc4.buf = EOF;
    break;
  case cryptAES:
    if (state.aes.bufIdx == 16) {
      for (i = 0; i < 16; ++i) {
	if ((c = str->getChar()) == EOF) {
	  return EOF;
	}
	in[i] = (Guchar)c;
      }
      aesDecryptBlock(&state.aes, in, str->lookChar() == EOF);
    }
    if (state.aes.bufIdx == 16) {
      c = EOF;
    } else {
      c = state.aes.buf[state.aes.bufIdx++];
    }
    break;
  }
  return c;
}

int DecryptStream::lookChar() {
  Guchar in[16];
  int c, i;

  c = EOF; // make gcc happy
  switch (algo) {
  case cryptRC4:
    if (state.rc4.buf == EOF) {
      c = str->getChar();
      if (c != EOF) {
	state.rc4.buf = rc4DecryptByte(state.rc4.state, &state.rc4.x,
				       &state.rc4.y, (Guchar)c);
      }
    }
    c = state.rc4.buf;
    break;
  case cryptAES:
    if (state.aes.bufIdx == 16) {
      for (i = 0; i < 16; ++i) {
	if ((c = str->getChar()) == EOF) {
	  return EOF;
	}
	in[i] = c;
      }
      aesDecryptBlock(&state.aes, in, str->lookChar() == EOF);
    }
    if (state.aes.bufIdx == 16) {
      c = EOF;
    } else {
      c = state.aes.buf[state.aes.bufIdx];
    }
    break;
  }
  return c;
}

GBool DecryptStream::isBinary(GBool last) {
  return str->isBinary(last);
}

//------------------------------------------------------------------------
// RC4-compatible decryption
//------------------------------------------------------------------------

static void rc4InitKey(Guchar *key, int keyLen, Guchar *state) {
  Guchar index1, index2;
  Guchar t;
  int i;

  for (i = 0; i < 256; ++i)
    state[i] = i;
  index1 = index2 = 0;
  for (i = 0; i < 256; ++i) {
    index2 = (key[index1] + state[i] + index2) % 256;
    t = state[i];
    state[i] = state[index2];
    state[index2] = t;
    index1 = (index1 + 1) % keyLen;
  }
}

static Guchar rc4DecryptByte(Guchar *state, Guchar *x, Guchar *y, Guchar c) {
  Guchar x1, y1, tx, ty;

  x1 = *x = (*x + 1) % 256;
  y1 = *y = (state[*x] + *y) % 256;
  tx = state[x1];
  ty = state[y1];
  state[x1] = ty;
  state[y1] = tx;
  return c ^ state[(tx + ty) % 256];
}

//------------------------------------------------------------------------
// AES decryption
//------------------------------------------------------------------------

static Guchar sbox[256] = {
  0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
  0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
  0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
  0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
  0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
  0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
  0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
  0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
  0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
  0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
  0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
  0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
  0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
  0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
  0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
  0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
};

static Guchar invSbox[256] = {
  0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
  0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
  0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
  0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
  0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
  0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
  0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
  0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
  0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
  0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
  0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
  0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
  0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
  0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
  0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
  0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
};

static Guint rcon[11] = {
  0x00000000, // unused
  0x01000000,
  0x02000000,
  0x04000000,
  0x08000000,
  0x10000000,
  0x20000000,
  0x40000000,
  0x80000000,
  0x1b000000,
  0x36000000
};

static inline Guint subWord(Guint x) {
  return (sbox[x >> 24] << 24)
         | (sbox[(x >> 16) & 0xff] << 16)
         | (sbox[(x >> 8) & 0xff] << 8)
         | sbox[x & 0xff];
}

static inline Guint rotWord(Guint x) {
  return ((x << 8) & 0xffffffff) | (x >> 24);
}

static inline void invSubBytes(Guchar *state) {
  int i;

  for (i = 0; i < 16; ++i) {
    state[i] = invSbox[state[i]];
  }
}

static inline void invShiftRows(Guchar *state) {
  Guchar t;

  t = state[7];
  state[7] = state[6];
  state[6] = state[5];
  state[5] = state[4];
  state[4] = t;

  t = state[8];
  state[8] = state[10];
  state[10] = t;
  t = state[9];
  state[9] = state[11];
  state[11] = t;

  t = state[12];
  state[12] = state[13];
  state[13] = state[14];
  state[14] = state[15];
  state[15] = t;
}

// {09} \cdot s
static inline Guchar mul09(Guchar s) {
  Guchar s2, s4, s8;

  s2 = (s & 0x80) ? ((s << 1) ^ 0x1b) : (s << 1);
  s4 = (s2 & 0x80) ? ((s2 << 1) ^ 0x1b) : (s2 << 1);
  s8 = (s4 & 0x80) ? ((s4 << 1) ^ 0x1b) : (s4 << 1);
  return s ^ s8;
}

// {0b} \cdot s
static inline Guchar mul0b(Guchar s) {
  Guchar s2, s4, s8;

  s2 = (s & 0x80) ? ((s << 1) ^ 0x1b) : (s << 1);
  s4 = (s2 & 0x80) ? ((s2 << 1) ^ 0x1b) : (s2 << 1);
  s8 = (s4 & 0x80) ? ((s4 << 1) ^ 0x1b) : (s4 << 1);
  return s ^ s2 ^ s8;
}

// {0d} \cdot s
static inline Guchar mul0d(Guchar s) {
  Guchar s2, s4, s8;

  s2 = (s & 0x80) ? ((s << 1) ^ 0x1b) : (s << 1);
  s4 = (s2 & 0x80) ? ((s2 << 1) ^ 0x1b) : (s2 << 1);
  s8 = (s4 & 0x80) ? ((s4 << 1) ^ 0x1b) : (s4 << 1);
  return s ^ s4 ^ s8;
}

// {0e} \cdot s
static inline Guchar mul0e(Guchar s) {
  Guchar s2, s4, s8;

  s2 = (s & 0x80) ? ((s << 1) ^ 0x1b) : (s << 1);
  s4 = (s2 & 0x80) ? ((s2 << 1) ^ 0x1b) : (s2 << 1);
  s8 = (s4 & 0x80) ? ((s4 << 1) ^ 0x1b) : (s4 << 1);
  return s2 ^ s4 ^ s8;
}

static inline void invMixColumns(Guchar *state) {
  int c;
  Guchar s0, s1, s2, s3;

  for (c = 0; c < 4; ++c) {
    s0 = state[c];
    s1 = state[4+c];
    s2 = state[8+c];
    s3 = state[12+c];
    state[c] =    mul0e(s0) ^ mul0b(s1) ^ mul0d(s2) ^ mul09(s3);
    state[4+c] =  mul09(s0) ^ mul0e(s1) ^ mul0b(s2) ^ mul0d(s3);
    state[8+c] =  mul0d(s0) ^ mul09(s1) ^ mul0e(s2) ^ mul0b(s3);
    state[12+c] = mul0b(s0) ^ mul0d(s1) ^ mul09(s2) ^ mul0e(s3);
  }
}

static inline void invMixColumnsW(Guint *w) {
  int c;
  Guchar s0, s1, s2, s3;

  for (c = 0; c < 4; ++c) {
    s0 = w[c] >> 24;
    s1 = w[c] >> 16;
    s2 = w[c] >> 8;
    s3 = w[c];
    w[c] = ((mul0e(s0) ^ mul0b(s1) ^ mul0d(s2) ^ mul09(s3)) << 24)
           | ((mul09(s0) ^ mul0e(s1) ^ mul0b(s2) ^ mul0d(s3)) << 16)
           | ((mul0d(s0) ^ mul09(s1) ^ mul0e(s2) ^ mul0b(s3)) << 8)
           | (mul0b(s0) ^ mul0d(s1) ^ mul09(s2) ^ mul0e(s3));
  }
}

static inline void addRoundKey(Guchar *state, Guint *w) {
  int c;

  for (c = 0; c < 4; ++c) {
    state[c] ^= w[c] >> 24;
    state[4+c] ^= w[c] >> 16;
    state[8+c] ^= w[c] >> 8;
    state[12+c] ^= w[c];
  }
}

static void aesKeyExpansion(DecryptAESState *s,
			    Guchar *objKey, int objKeyLen) {
  Guint temp;
  int i, round;

  //~ this assumes objKeyLen == 16

  for (i = 0; i < 4; ++i) {
    s->w[i] = (objKey[4*i] << 24) + (objKey[4*i+1] << 16) +
              (objKey[4*i+2] << 8) + objKey[4*i+3];
  }
  for (i = 4; i < 44; ++i) {
    temp = s->w[i-1];
    if (!(i & 3)) {
      temp = subWord(rotWord(temp)) ^ rcon[i/4];
    }
    s->w[i] = s->w[i-4] ^ temp;
  }
  for (round = 1; round <= 9; ++round) {
    invMixColumnsW(&s->w[round * 4]);
  }
}

static void aesDecryptBlock(DecryptAESState *s, Guchar *in, GBool last) {
  int c, round, n, i;

  // initial state
  for (c = 0; c < 4; ++c) {
    s->state[c] = in[4*c];
    s->state[4+c] = in[4*c+1];
    s->state[8+c] = in[4*c+2];
    s->state[12+c] = in[4*c+3];
  }

  // round 0
  addRoundKey(s->state, &s->w[10 * 4]);

  // rounds 1-9
  for (round = 9; round >= 1; --round) {
    invSubBytes(s->state);
    invShiftRows(s->state);
    invMixColumns(s->state);
    addRoundKey(s->state, &s->w[round * 4]);
  }

  // round 10
  invSubBytes(s->state);
  invShiftRows(s->state);
  addRoundKey(s->state, &s->w[0]);

  // CBC
  for (c = 0; c < 4; ++c) {
    s->buf[4*c] = s->state[c] ^ s->cbc[4*c];
    s->buf[4*c+1] = s->state[4+c] ^ s->cbc[4*c+1];
    s->buf[4*c+2] = s->state[8+c] ^ s->cbc[4*c+2];
    s->buf[4*c+3] = s->state[12+c] ^ s->cbc[4*c+3];
  }

  // save the input block for the next CBC
  for (i = 0; i < 16; ++i) {
    s->cbc[i] = in[i];
  }

  // remove padding
  s->bufIdx = 0;
  if (last) {
    n = s->buf[15];
    for (i = 15; i >= n; --i) {
      s->buf[i] = s->buf[i-n];
    }
    s->bufIdx = n;
  }
}

//------------------------------------------------------------------------
// MD5 message digest
//------------------------------------------------------------------------

// this works around a bug in older Sun compilers
static inline Gulong rotateLeft(Gulong x, int r) {
  x &= 0xffffffff;
  return ((x << r) | (x >> (32 - r))) & 0xffffffff;
}

static inline Gulong md5Round1(Gulong a, Gulong b, Gulong c, Gulong d,
			       Gulong Xk,  Gulong s, Gulong Ti) {
  return b + rotateLeft((a + ((b & c) | (~b & d)) + Xk + Ti), s);
}

static inline Gulong md5Round2(Gulong a, Gulong b, Gulong c, Gulong d,
			       Gulong Xk,  Gulong s, Gulong Ti) {
  return b + rotateLeft((a + ((b & d) | (c & ~d)) + Xk + Ti), s);
}

static inline Gulong md5Round3(Gulong a, Gulong b, Gulong c, Gulong d,
			       Gulong Xk,  Gulong s, Gulong Ti) {
  return b + rotateLeft((a + (b ^ c ^ d) + Xk + Ti), s);
}

static inline Gulong md5Round4(Gulong a, Gulong b, Gulong c, Gulong d,
			       Gulong Xk,  Gulong s, Gulong Ti) {
  return b + rotateLeft((a + (c ^ (b | ~d)) + Xk + Ti), s);
}

static void md5(Guchar *msg, int msgLen, Guchar *digest) {
  Gulong x[16];
  Gulong a, b, c, d, aa, bb, cc, dd;
  int n64;
  int i, j, k;

  // compute number of 64-byte blocks
  // (length + pad byte (0x80) + 8 bytes for length)
  n64 = (msgLen + 1 + 8 + 63) / 64;

  // initialize a, b, c, d
  a = 0x67452301;
  b = 0xefcdab89;
  c = 0x98badcfe;
  d = 0x10325476;

  // loop through blocks
  k = 0;
  for (i = 0; i < n64; ++i) {

    // grab a 64-byte block
    for (j = 0; j < 16 && k < msgLen - 3; ++j, k += 4)
      x[j] = (((((msg[k+3] << 8) + msg[k+2]) << 8) + msg[k+1]) << 8) + msg[k];
    if (i == n64 - 1) {
      if (k == msgLen - 3)
	x[j] = 0x80000000 + (((msg[k+2] << 8) + msg[k+1]) << 8) + msg[k];
      else if (k == msgLen - 2)
	x[j] = 0x800000 + (msg[k+1] << 8) + msg[k];
      else if (k == msgLen - 1)
	x[j] = 0x8000 + msg[k];
      else
	x[j] = 0x80;
      ++j;
      while (j < 16)
	x[j++] = 0;
      x[14] = msgLen << 3;
    }

    // save a, b, c, d
    aa = a;
    bb = b;
    cc = c;
    dd = d;

    // round 1
    a = md5Round1(a, b, c, d, x[0],   7, 0xd76aa478);
    d = md5Round1(d, a, b, c, x[1],  12, 0xe8c7b756);
    c = md5Round1(c, d, a, b, x[2],  17, 0x242070db);
    b = md5Round1(b, c, d, a, x[3],  22, 0xc1bdceee);
    a = md5Round1(a, b, c, d, x[4],   7, 0xf57c0faf);
    d = md5Round1(d, a, b, c, x[5],  12, 0x4787c62a);
    c = md5Round1(c, d, a, b, x[6],  17, 0xa8304613);
    b = md5Round1(b, c, d, a, x[7],  22, 0xfd469501);
    a = md5Round1(a, b, c, d, x[8],   7, 0x698098d8);
    d = md5Round1(d, a, b, c, x[9],  12, 0x8b44f7af);
    c = md5Round1(c, d, a, b, x[10], 17, 0xffff5bb1);
    b = md5Round1(b, c, d, a, x[11], 22, 0x895cd7be);
    a = md5Round1(a, b, c, d, x[12],  7, 0x6b901122);
    d = md5Round1(d, a, b, c, x[13], 12, 0xfd987193);
    c = md5Round1(c, d, a, b, x[14], 17, 0xa679438e);
    b = md5Round1(b, c, d, a, x[15], 22, 0x49b40821);

    // round 2
    a = md5Round2(a, b, c, d, x[1],   5, 0xf61e2562);
    d = md5Round2(d, a, b, c, x[6],   9, 0xc040b340);
    c = md5Round2(c, d, a, b, x[11], 14, 0x265e5a51);
    b = md5Round2(b, c, d, a, x[0],  20, 0xe9b6c7aa);
    a = md5Round2(a, b, c, d, x[5],   5, 0xd62f105d);
    d = md5Round2(d, a, b, c, x[10],  9, 0x02441453);
    c = md5Round2(c, d, a, b, x[15], 14, 0xd8a1e681);
    b = md5Round2(b, c, d, a, x[4],  20, 0xe7d3fbc8);
    a = md5Round2(a, b, c, d, x[9],   5, 0x21e1cde6);
    d = md5Round2(d, a, b, c, x[14],  9, 0xc33707d6);
    c = md5Round2(c, d, a, b, x[3],  14, 0xf4d50d87);
    b = md5Round2(b, c, d, a, x[8],  20, 0x455a14ed);
    a = md5Round2(a, b, c, d, x[13],  5, 0xa9e3e905);
    d = md5Round2(d, a, b, c, x[2],   9, 0xfcefa3f8);
    c = md5Round2(c, d, a, b, x[7],  14, 0x676f02d9);
    b = md5Round2(b, c, d, a, x[12], 20, 0x8d2a4c8a);

    // round 3
    a = md5Round3(a, b, c, d, x[5],   4, 0xfffa3942);
    d = md5Round3(d, a, b, c, x[8],  11, 0x8771f681);
    c = md5Round3(c, d, a, b, x[11], 16, 0x6d9d6122);
    b = md5Round3(b, c, d, a, x[14], 23, 0xfde5380c);
    a = md5Round3(a, b, c, d, x[1],   4, 0xa4beea44);
    d = md5Round3(d, a, b, c, x[4],  11, 0x4bdecfa9);
    c = md5Round3(c, d, a, b, x[7],  16, 0xf6bb4b60);
    b = md5Round3(b, c, d, a, x[10], 23, 0xbebfbc70);
    a = md5Round3(a, b, c, d, x[13],  4, 0x289b7ec6);
    d = md5Round3(d, a, b, c, x[0],  11, 0xeaa127fa);
    c = md5Round3(c, d, a, b, x[3],  16, 0xd4ef3085);
    b = md5Round3(b, c, d, a, x[6],  23, 0x04881d05);
    a = md5Round3(a, b, c, d, x[9],   4, 0xd9d4d039);
    d = md5Round3(d, a, b, c, x[12], 11, 0xe6db99e5);
    c = md5Round3(c, d, a, b, x[15], 16, 0x1fa27cf8);
    b = md5Round3(b, c, d, a, x[2],  23, 0xc4ac5665);

    // round 4
    a = md5Round4(a, b, c, d, x[0],   6, 0xf4292244);
    d = md5Round4(d, a, b, c, x[7],  10, 0x432aff97);
    c = md5Round4(c, d, a, b, x[14], 15, 0xab9423a7);
    b = md5Round4(b, c, d, a, x[5],  21, 0xfc93a039);
    a = md5Round4(a, b, c, d, x[12],  6, 0x655b59c3);
    d = md5Round4(d, a, b, c, x[3],  10, 0x8f0ccc92);
    c = md5Round4(c, d, a, b, x[10], 15, 0xffeff47d);
    b = md5Round4(b, c, d, a, x[1],  21, 0x85845dd1);
    a = md5Round4(a, b, c, d, x[8],   6, 0x6fa87e4f);
    d = md5Round4(d, a, b, c, x[15], 10, 0xfe2ce6e0);
    c = md5Round4(c, d, a, b, x[6],  15, 0xa3014314);
    b = md5Round4(b, c, d, a, x[13], 21, 0x4e0811a1);
    a = md5Round4(a, b, c, d, x[4],   6, 0xf7537e82);
    d = md5Round4(d, a, b, c, x[11], 10, 0xbd3af235);
    c = md5Round4(c, d, a, b, x[2],  15, 0x2ad7d2bb);
    b = md5Round4(b, c, d, a, x[9],  21, 0xeb86d391);

    // increment a, b, c, d
    a += aa;
    b += bb;
    c += cc;
    d += dd;
  }

  // break digest into bytes
  digest[0] = (Guchar)(a & 0xff);
  digest[1] = (Guchar)((a >>= 8) & 0xff);
  digest[2] = (Guchar)((a >>= 8) & 0xff);
  digest[3] = (Guchar)((a >>= 8) & 0xff);
  digest[4] = (Guchar)(b & 0xff);
  digest[5] = (Guchar)((b >>= 8) & 0xff);
  digest[6] = (Guchar)((b >>= 8) & 0xff);
  digest[7] = (Guchar)((b >>= 8) & 0xff);
  digest[8] = (Guchar)(c & 0xff);
  digest[9] = (Guchar)((c >>= 8) & 0xff);
  digest[10] = (Guchar)((c >>= 8) & 0xff);
  digest[11] = (Guchar)((c >>= 8) & 0xff);
  digest[12] = (Guchar)(d & 0xff);
  digest[13] = (Guchar)((d >>= 8) & 0xff);
  digest[14] = (Guchar)((d >>= 8) & 0xff);
  digest[15] = (Guchar)((d >>= 8) & 0xff);
}
//========================================================================
//
// Decrypt.h
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef DECRYPT_H
#define DECRYPT_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "gtypes.h"
#include "GString.h"
#include "Object.h"
#include "Stream.h"

//------------------------------------------------------------------------
// Decrypt
//------------------------------------------------------------------------

class Decrypt {
public:

  // Generate a file key.  The <fileKey> buffer must have space for at
  // least 16 bytes.  Checks <ownerPassword> and then <userPassword>
  // and returns true if either is correct.  Sets <ownerPasswordOk> if
  // the owner password was correct.  Either or both of the passwords
  // may be NULL, which is treated as an empty string.
  static GBool makeFileKey(int encVersion, int encRevision, int keyLength,
			   GString *ownerKey, GString *userKey,
			   int permissions, GString *fileID,
			   GString *ownerPassword, GString *userPassword,
			   Guchar *fileKey, GBool encryptMetadata,
			   GBool *ownerPasswordOk);

private:

  static GBool makeFileKey2(int encVersion, int encRevision, int keyLength,
			    GString *ownerKey, GString *userKey,
			    int permissions, GString *fileID,
			    GString *userPassword, Guchar *fileKey,
			    GBool encryptMetadata);
};

//------------------------------------------------------------------------
// DecryptStream
//------------------------------------------------------------------------

struct DecryptRC4State {
  Guchar state[256];
  Guchar x, y;
  int buf;
};

struct DecryptAESState {
  Guint w[44];
  Guchar state[16];
  Guchar cbc[16];
  Guchar buf[16];
  int bufIdx;
};

class DecryptStream: public FilterStream {
public:

  DecryptStream(Stream *strA, Guchar *fileKey,
		CryptAlgorithm algoA, int keyLength,
		int objNum, int objGen);
  virtual ~DecryptStream();
  virtual StreamKind getKind() { return strWeird; }
  virtual void reset();
  virtual int getChar();
  virtual int lookChar();
  virtual GBool isBinary(GBool last);
  virtual Stream *getUndecodedStream() { return this; }

private:

  CryptAlgorithm algo;
  int objKeyLength;
  Guchar objKey[16 + 9];

  union {
    DecryptRC4State rc4;
    DecryptAESState aes;
  } state;
};

#endif
//========================================================================
//
// Dict.cc
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stddef.h>
#include <string.h>
#include "gmem.h"
#include "Object.h"
#include "XRef.h"
#include "Dict.h"

//------------------------------------------------------------------------
// Dict
//------------------------------------------------------------------------

Dict::Dict(XRef *xrefA) {
  xref = xrefA;
  entries = NULL;
  size = length = 0;
  ref = 1;
}

Dict::~Dict() {
  int i;

  for (i = 0; i < length; ++i) {
    gfree(entries[i].key);
    entries[i].val.free();
  }
  gfree(entries);
}

void Dict::add(char *key, Object *val) {
  if (length == size) {
    if (length == 0) {
      size = 8;
    } else {
      size *= 2;
    }
    entries = (DictEntry *)greallocn(entries, size, sizeof(DictEntry));
  }
  entries[length].key = key;
  entries[length].val = *val;
  ++length;
}

inline DictEntry *Dict::find(char *key) {
  int i;

  for (i = 0; i < length; ++i) {
    if (!strcmp(key, entries[i].key))
      return &entries[i];
  }
  return NULL;
}

GBool Dict::is(char *type) {
  DictEntry *e;

  return (e = find("Type")) && e->val.isName(type);
}

Object *Dict::lookup(char *key, Object *obj) {
  DictEntry *e;

  return (e = find(key)) ? e->val.fetch(xref, obj) : obj->initNull();
}

Object *Dict::lookupNF(char *key, Object *obj) {
  DictEntry *e;

  return (e = find(key)) ? e->val.copy(obj) : obj->initNull();
}

char *Dict::getKey(int i) {
  return entries[i].key;
}

Object *Dict::getVal(int i, Object *obj) {
  return entries[i].val.fetch(xref, obj);
}

Object *Dict::getValNF(int i, Object *obj) {
  return entries[i].val.copy(obj);
}
//========================================================================
//
// Dict.h
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef DICT_H
#define DICT_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "Object.h"

//------------------------------------------------------------------------
// Dict
//------------------------------------------------------------------------

struct DictEntry {
  char *key;
  Object val;
};

class Dict {
public:

  // Constructor.
  Dict(XRef *xrefA);

  // Destructor.
  ~Dict();

  // Reference counting.
  int incRef() { return ++ref; }
  int decRef() { return --ref; }

  // Get number of entries.
  int getLength() { return length; }

  // Add an entry.  NB: does not copy key.
  void add(char *key, Object *val);

  // Check if dictionary is of specified type.
  GBool is(char *type);

  // Look up an entry and return the value.  Returns a null object
  // if <key> is not in the dictionary.
  Object *lookup(char *key, Object *obj);
  Object *lookupNF(char *key, Object *obj);

  // Iterative accessors.
  char *getKey(int i);
  Object *getVal(int i, Object *obj);
  Object *getValNF(int i, Object *obj);

  // Set the xref pointer.  This is only used in one special case: the
  // trailer dictionary, which is read before the xref table is
  // parsed.
  void setXRef(XRef *xrefA) { xref = xrefA; }

private:

  XRef *xref;			// the xref table for this PDF file
  DictEntry *entries;		// array of entries
  int size;			// size of <entries> array
  int length;			// number of entries in dictionary
  int ref;			// reference count

  DictEntry *find(char *key);
};

#endif
//========================================================================
//
// Error.cc
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stdio.h>
#include <stddef.h>
#include <stdarg.h>
#include "GlobalParams.h"
#include "Error.h"

void CDECL error(int pos, char *msg, ...) {
  va_list args;

  // NB: this can be called before the globalParams object is created
  if (globalParams && globalParams->getErrQuiet()) {
    return;
  }
  if (pos >= 0) {
    fprintf(stderr, "Error (%d): ", pos);
  } else {
    fprintf(stderr, "Error: ");
  }
  va_start(args, msg);
  vfprintf(stderr, msg, args);
  va_end(args);
  fprintf(stderr, "\n");
  fflush(stderr);
}
//========================================================================
//
// Error.h
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef ERROR_H
#define ERROR_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include <stdio.h>
#include "config.h"

extern void CDECL error(int pos, char *msg, ...);

#endif
//========================================================================
//
// ErrorCodes.h
//
// Copyright 2002-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef ERRORCODES_H
#define ERRORCODES_H

#define errNone             0	// no error

#define errOpenFile         1	// couldn't open the PDF file

#define errBadCatalog       2	// couldn't read the page catalog

#define errDamaged          3	// PDF file was damaged and couldn't be
				// repaired

#define errEncrypted        4	// file was encrypted and password was
				// incorrect or not supplied

#define errHighlightFile    5	// nonexistent or invalid highlight file

#define errBadPrinter       6   // invalid printer

#define errPrinting         7   // error during printing

#define errPermission       8	// PDF file doesn't allow that operation

#define errBadPageNum       9	// invalid page number

#define errFileIO          10   // file I/O error

#endif
//========================================================================
//
// FontEncodingTables.cc
//
// Copyright 2001-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>
#include <stdlib.h>
#include "FontEncodingTables.h"

char *macRomanEncoding[256] = {
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  "space",
  "exclam",
  "quotedbl",
  "numbersign",
  "dollar",
  "percent",
  "ampersand",
  "quotesingle",
  "parenleft",
  "parenright",
  "asterisk",
  "plus",
  "comma",
  "hyphen",
  "period",
  "slash",
  "zero",
  "one",
  "two",
  "three",
  "four",
  "five",
  "six",
  "seven",
  "eight",
  "nine",
  "colon",
  "semicolon",
  "less",
  "equal",
  "greater",
  "question",
  "at",
  "A",
  "B",
  "C",
  "D",
  "E",
  "F",
  "G",
  "H",
  "I",
  "J",
  "K",
  "L",
  "M",
  "N",
  "O",
  "P",
  "Q",
  "R",
  "S",
  "T",
  "U",
  "V",
  "W",
  "X",
  "Y",
  "Z",
  "bracketleft",
  "backslash",
  "bracketright",
  "asciicircum",
  "underscore",
  "grave",
  "a",
  "b",
  "c",
  "d",
  "e",
  "f",
  "g",
  "h",
  "i",
  "j",
  "k",
  "l",
  "m",
  "n",
  "o",
  "p",
  "q",
  "r",
  "s",
  "t",
  "u",
  "v",
  "w",
  "x",
  "y",
  "z",
  "braceleft",
  "bar",
  "braceright",
  "asciitilde",
  NULL,
  "Adieresis",
  "Aring",
  "Ccedilla",
  "Eacute",
  "Ntilde",
  "Odieresis",
  "Udieresis",
  "aacute",
  "agrave",
  "acircumflex",
  "adieresis",
  "atilde",
  "aring",
  "ccedilla",
  "eacute",
  "egrave",
  "ecircumflex",
  "edieresis",
  "iacute",
  "igrave",
  "icircumflex",
  "idieresis",
  "ntilde",
  "oacute",
  "ograve",
  "ocircumflex",
  "odieresis",
  "otilde",
  "uacute",
  "ugrave",
  "ucircumflex",
  "udieresis",
  "dagger",
  "degree",
  "cent",
  "sterling",
  "section",
  "bullet",
  "paragraph",
  "germandbls",
  "registered",
  "copyright",
  "trademark",
  "acute",
  "dieresis",
  "notequal",
  "AE",
  "Oslash",
  "infinity",
  "plusminus",
  "lessequal",
  "greaterequal",
  "yen",
  "mu",
  "partialdiff",
  "summation",
  "product",
  "pi",
  "integral",
  "ordfeminine",
  "ordmasculine",
  "Omega",
  "ae",
  "oslash",
  "questiondown",
  "exclamdown",
  "logicalnot",
  "radical",
  "florin",
  "approxequal",
  "Delta",
  "guillemotleft",
  "guillemotright",
  "ellipsis",
  "space",
  "Agrave",
  "Atilde",
  "Otilde",
  "OE",
  "oe",
  "endash",
  "emdash",
  "quotedblleft",
  "quotedblright",
  "quoteleft",
  "quoteright",
  "divide",
  "lozenge",
  "ydieresis",
  "Ydieresis",
  "fraction",
  "currency",
  "guilsinglleft",
  "guilsinglright",
  "fi",
  "fl",
  "daggerdbl",
  "periodcentered",
  "quotesinglbase",
  "quotedblbase",
  "perthousand",
  "Acircumflex",
  "Ecircumflex",
  "Aacute",
  "Edieresis",
  "Egrave",
  "Iacute",
  "Icircumflex",
  "Idieresis",
  "Igrave",
  "Oacute",
  "Ocircumflex",
  "apple",
  "Ograve",
  "Uacute",
  "Ucircumflex",
  "Ugrave",
  "dotlessi",
  "circumflex",
  "tilde",
  "macron",
  "breve",
  "dotaccent",
  "ring",
  "cedilla",
  "hungarumlaut",
  "ogonek",
  "caron"
};

char *macExpertEncoding[256] = {
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  "space",
  "exclamsmall",
  "Hungarumlautsmall",
  "centoldstyle",
  "dollaroldstyle",
  "dollarsuperior",
  "ampersandsmall",
  "Acutesmall",
  "parenleftsuperior",
  "parenrightsuperior",
  "twodotenleader",
  "onedotenleader",
  "comma",
  "hyphen",
  "period",
  "fraction",
  "zerooldstyle",
  "oneoldstyle",
  "twooldstyle",
  "threeoldstyle",
  "fouroldstyle",
  "fiveoldstyle",
  "sixoldstyle",
  "sevenoldstyle",
  "eightoldstyle",
  "nineoldstyle",
  "colon",
  "semicolon",
  NULL,
  "threequartersemdash",
  NULL,
  "questionsmall",
  NULL,
  NULL,
  NULL,
  NULL,
  "Ethsmall",
  NULL,
  NULL,
  "onequarter",
  "onehalf",
  "threequarters",
  "oneeighth",
  "threeeighths",
  "fiveeighths",
  "seveneighths",
  "onethird",
  "twothirds",
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  "ff",
  "fi",
  "fl",
  "ffi",
  "ffl",
  "parenleftinferior",
  NULL,
  "parenrightinferior",
  "Circumflexsmall",
  "hypheninferior",
  "Gravesmall",
  "Asmall",
  "Bsmall",
  "Csmall",
  "Dsmall",
  "Esmall",
  "Fsmall",
  "Gsmall",
  "Hsmall",
  "Ismall",
  "Jsmall",
  "Ksmall",
  "Lsmall",
  "Msmall",
  "Nsmall",
  "Osmall",
  "Psmall",
  "Qsmall",
  "Rsmall",
  "Ssmall",
  "Tsmall",
  "Usmall",
  "Vsmall",
  "Wsmall",
  "Xsmall",
  "Ysmall",
  "Zsmall",
  "colonmonetary",
  "onefitted",
  "rupiah",
  "Tildesmall",
  NULL,
  NULL,
  "asuperior",
  "centsuperior",
  NULL,
  NULL,
  NULL,
  NULL,
  "Aacutesmall",
  "Agravesmall",
  "Acircumflexsmall",
  "Adieresissmall",
  "Atildesmall",
  "Aringsmall",
  "Ccedillasmall",
  "Eacutesmall",
  "Egravesmall",
  "Ecircumflexsmall",
  "Edieresissmall",
  "Iacutesmall",
  "Igravesmall",
  "Icircumflexsmall",
  "Idieresissmall",
  "Ntildesmall",
  "Oacutesmall",
  "Ogravesmall",
  "Ocircumflexsmall",
  "Odieresissmall",
  "Otildesmall",
  "Uacutesmall",
  "Ugravesmall",
  "Ucircumflexsmall",
  "Udieresissmall",
  NULL,
  "eightsuperior",
  "fourinferior",
  "threeinferior",
  "sixinferior",
  "eightinferior",
  "seveninferior",
  "Scaronsmall",
  NULL,
  "centinferior",
  "twoinferior",
  NULL,
  "Dieresissmall",
  NULL,
  "Caronsmall",
  "osuperior",
  "fiveinferior",
  NULL,
  "commainferior",
  "periodinferior",
  "Yacutesmall",
  NULL,
  "dollarinferior",
  NULL,
  NULL,
  "Thornsmall",
  NULL,
  "nineinferior",
  "zeroinferior",
  "Zcaronsmall",
  "AEsmall",
  "Oslashsmall",
  "questiondownsmall",
  "oneinferior",
  "Lslashsmall",
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  "Cedillasmall",
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  "OEsmall",
  "figuredash",
  "hyphensuperior",
  NULL,
  NULL,
  NULL,
  NULL,
  "exclamdownsmall",
  NULL,
  "Ydieresissmall",
  NULL,
  "onesuperior",
  "twosuperior",
  "threesuperior",
  "foursuperior",
  "fivesuperior",
  "sixsuperior",
  "sevensuperior",
  "ninesuperior",
  "zerosuperior",
  NULL,
  "esuperior",
  "rsuperior",
  "tsuperior",
  NULL,
  NULL,
  "isuperior",
  "ssuperior",
  "dsuperior",
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  "lsuperior",
  "Ogoneksmall",
  "Brevesmall",
  "Macronsmall",
  "bsuperior",
  "nsuperior",
  "msuperior",
  "commasuperior",
  "periodsuperior",
  "Dotaccentsmall",
  "Ringsmall",
  NULL,
  NULL,
  NULL,
  NULL
};

char *winAnsiEncoding[256] = {
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  "space",
  "exclam",
  "quotedbl",
  "numbersign",
  "dollar",
  "percent",
  "ampersand",
  "quotesingle",
  "parenleft",
  "parenright",
  "asterisk",
  "plus",
  "comma",
  "hyphen",
  "period",
  "slash",
  "zero",
  "one",
  "two",
  "three",
  "four",
  "five",
  "six",
  "seven",
  "eight",
  "nine",
  "colon",
  "semicolon",
  "less",
  "equal",
  "greater",
  "question",
  "at",
  "A",
  "B",
  "C",
  "D",
  "E",
  "F",
  "G",
  "H",
  "I",
  "J",
  "K",
  "L",
  "M",
  "N",
  "O",
  "P",
  "Q",
  "R",
  "S",
  "T",
  "U",
  "V",
  "W",
  "X",
  "Y",
  "Z",
  "bracketleft",
  "backslash",
  "bracketright",
  "asciicircum",
  "underscore",
  "grave",
  "a",
  "b",
  "c",
  "d",
  "e",
  "f",
  "g",
  "h",
  "i",
  "j",
  "k",
  "l",
  "m",
  "n",
  "o",
  "p",
  "q",
  "r",
  "s",
  "t",
  "u",
  "v",
  "w",
  "x",
  "y",
  "z",
  "braceleft",
  "bar",
  "braceright",
  "asciitilde",
  "bullet",
  "Euro",
  "bullet",
  "quotesinglbase",
  "florin",
  "quotedblbase",
  "ellipsis",
  "dagger",
  "daggerdbl",
  "circumflex",
  "perthousand",
  "Scaron",
  "guilsinglleft",
  "OE",
  "bullet",
  "Zcaron",
  "bullet",
  "bullet",
  "quoteleft",
  "quoteright",
  "quotedblleft",
  "quotedblright",
  "bullet",
  "endash",
  "emdash",
  "tilde",
  "trademark",
  "scaron",
  "guilsinglright",
  "oe",
  "bullet",
  "zcaron",
  "Ydieresis",
  "space",
  "exclamdown",
  "cent",
  "sterling",
  "currency",
  "yen",
  "brokenbar",
  "section",
  "dieresis",
  "copyright",
  "ordfeminine",
  "guillemotleft",
  "logicalnot",
  "hyphen",
  "registered",
  "macron",
  "degree",
  "plusminus",
  "twosuperior",
  "threesuperior",
  "acute",
  "mu",
  "paragraph",
  "periodcentered",
  "cedilla",
  "onesuperior",
  "ordmasculine",
  "guillemotright",
  "onequarter",
  "onehalf",
  "threequarters",
  "questiondown",
  "Agrave",
  "Aacute",
  "Acircumflex",
  "Atilde",
  "Adieresis",
  "Aring",
  "AE",
  "Ccedilla",
  "Egrave",
  "Eacute",
  "Ecircumflex",
  "Edieresis",
  "Igrave",
  "Iacute",
  "Icircumflex",
  "Idieresis",
  "Eth",
  "Ntilde",
  "Ograve",
  "Oacute",
  "Ocircumflex",
  "Otilde",
  "Odieresis",
  "multiply",
  "Oslash",
  "Ugrave",
  "Uacute",
  "Ucircumflex",
  "Udieresis",
  "Yacute",
  "Thorn",
  "germandbls",
  "agrave",
  "aacute",
  "acircumflex",
  "atilde",
  "adieresis",
  "aring",
  "ae",
  "ccedilla",
  "egrave",
  "eacute",
  "ecircumflex",
  "edieresis",
  "igrave",
  "iacute",
  "icircumflex",
  "idieresis",
  "eth",
  "ntilde",
  "ograve",
  "oacute",
  "ocircumflex",
  "otilde",
  "odieresis",
  "divide",
  "oslash",
  "ugrave",
  "uacute",
  "ucircumflex",
  "udieresis",
  "yacute",
  "thorn",
  "ydieresis"
};

char *standardEncoding[256] = {
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  "space",
  "exclam",
  "quotedbl",
  "numbersign",
  "dollar",
  "percent",
  "ampersand",
  "quoteright",
  "parenleft",
  "parenright",
  "asterisk",
  "plus",
  "comma",
  "hyphen",
  "period",
  "slash",
  "zero",
  "one",
  "two",
  "three",
  "four",
  "five",
  "six",
  "seven",
  "eight",
  "nine",
  "colon",
  "semicolon",
  "less",
  "equal",
  "greater",
  "question",
  "at",
  "A",
  "B",
  "C",
  "D",
  "E",
  "F",
  "G",
  "H",
  "I",
  "J",
  "K",
  "L",
  "M",
  "N",
  "O",
  "P",
  "Q",
  "R",
  "S",
  "T",
  "U",
  "V",
  "W",
  "X",
  "Y",
  "Z",
  "bracketleft",
  "backslash",
  "bracketright",
  "asciicircum",
  "underscore",
  "quoteleft",
  "a",
  "b",
  "c",
  "d",
  "e",
  "f",
  "g",
  "h",
  "i",
  "j",
  "k",
  "l",
  "m",
  "n",
  "o",
  "p",
  "q",
  "r",
  "s",
  "t",
  "u",
  "v",
  "w",
  "x",
  "y",
  "z",
  "braceleft",
  "bar",
  "braceright",
  "asciitilde",
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  "exclamdown",
  "cent",
  "sterling",
  "fraction",
  "yen",
  "florin",
  "section",
  "currency",
  "quotesingle",
  "quotedblleft",
  "guillemotleft",
  "guilsinglleft",
  "guilsinglright",
  "fi",
  "fl",
  NULL,
  "endash",
  "dagger",
  "daggerdbl",
  "periodcentered",
  NULL,
  "paragraph",
  "bullet",
  "quotesinglbase",
  "quotedblbase",
  "quotedblright",
  "guillemotright",
  "ellipsis",
  "perthousand",
  NULL,
  "questiondown",
  NULL,
  "grave",
  "acute",
  "circumflex",
  "tilde",
  "macron",
  "breve",
  "dotaccent",
  "dieresis",
  NULL,
  "ring",
  "cedilla",
  NULL,
  "hungarumlaut",
  "ogonek",
  "caron",
  "emdash",
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  "AE",
  NULL,
  "ordfeminine",
  NULL,
  NULL,
  NULL,
  NULL,
  "Lslash",
  "Oslash",
  "OE",
  "ordmasculine",
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  "ae",
  NULL,
  NULL,
  NULL,
  "dotlessi",
  NULL,
  NULL,
  "lslash",
  "oslash",
  "oe",
  "germandbls",
  NULL,
  NULL,
  NULL,
  NULL
};

char *expertEncoding[256] = {
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  "space",
  "exclamsmall",
  "Hungarumlautsmall",
  NULL,
  "dollaroldstyle",
  "dollarsuperior",
  "ampersandsmall",
  "Acutesmall",
  "parenleftsuperior",
  "parenrightsuperior",
  "twodotenleader",
  "onedotenleader",
  "comma",
  "hyphen",
  "period",
  "fraction",
  "zerooldstyle",
  "oneoldstyle",
  "twooldstyle",
  "threeoldstyle",
  "fouroldstyle",
  "fiveoldstyle",
  "sixoldstyle",
  "sevenoldstyle",
  "eightoldstyle",
  "nineoldstyle",
  "colon",
  "semicolon",
  "commasuperior",
  "threequartersemdash",
  "periodsuperior",
  "questionsmall",
  NULL,
  "asuperior",
  "bsuperior",
  "centsuperior",
  "dsuperior",
  "esuperior",
  NULL,
  NULL,
  NULL,
  "isuperior",
  NULL,
  NULL,
  "lsuperior",
  "msuperior",
  "nsuperior",
  "osuperior",
  NULL,
  NULL,
  "rsuperior",
  "ssuperior",
  "tsuperior",
  NULL,
  "ff",
  "fi",
  "fl",
  "ffi",
  "ffl",
  "parenleftinferior",
  NULL,
  "parenrightinferior",
  "Circumflexsmall",
  "hyphensuperior",
  "Gravesmall",
  "Asmall",
  "Bsmall",
  "Csmall",
  "Dsmall",
  "Esmall",
  "Fsmall",
  "Gsmall",
  "Hsmall",
  "Ismall",
  "Jsmall",
  "Ksmall",
  "Lsmall",
  "Msmall",
  "Nsmall",
  "Osmall",
  "Psmall",
  "Qsmall",
  "Rsmall",
  "Ssmall",
  "Tsmall",
  "Usmall",
  "Vsmall",
  "Wsmall",
  "Xsmall",
  "Ysmall",
  "Zsmall",
  "colonmonetary",
  "onefitted",
  "rupiah",
  "Tildesmall",
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  "exclamdownsmall",
  "centoldstyle",
  "Lslashsmall",
  NULL,
  NULL,
  "Scaronsmall",
  "Zcaronsmall",
  "Dieresissmall",
  "Brevesmall",
  "Caronsmall",
  NULL,
  "Dotaccentsmall",
  NULL,
  NULL,
  "Macronsmall",
  NULL,
  NULL,
  "figuredash",
  "hypheninferior",
  NULL,
  NULL,
  "Ogoneksmall",
  "Ringsmall",
  "Cedillasmall",
  NULL,
  NULL,
  NULL,
  "onequarter",
  "onehalf",
  "threequarters",
  "questiondownsmall",
  "oneeighth",
  "threeeighths",
  "fiveeighths",
  "seveneighths",
  "onethird",
  "twothirds",
  NULL,
  NULL,
  "zerosuperior",
  "onesuperior",
  "twosuperior",
  "threesuperior",
  "foursuperior",
  "fivesuperior",
  "sixsuperior",
  "sevensuperior",
  "eightsuperior",
  "ninesuperior",
  "zeroinferior",
  "oneinferior",
  "twoinferior",
  "threeinferior",
  "fourinferior",
  "fiveinferior",
  "sixinferior",
  "seveninferior",
  "eightinferior",
  "nineinferior",
  "centinferior",
  "dollarinferior",
  "periodinferior",
  "commainferior",
  "Agravesmall",
  "Aacutesmall",
  "Acircumflexsmall",
  "Atildesmall",
  "Adieresissmall",
  "Aringsmall",
  "AEsmall",
  "Ccedillasmall",
  "Egravesmall",
  "Eacutesmall",
  "Ecircumflexsmall",
  "Edieresissmall",
  "Igravesmall",
  "Iacutesmall",
  "Icircumflexsmall",
  "Idieresissmall",
  "Ethsmall",
  "Ntildesmall",
  "Ogravesmall",
  "Oacutesmall",
  "Ocircumflexsmall",
  "Otildesmall",
  "Odieresissmall",
  "OEsmall",
  "Oslashsmall",
  "Ugravesmall",
  "Uacutesmall",
  "Ucircumflexsmall",
  "Udieresissmall",
  "Yacutesmall",
  "Thornsmall",
  "Ydieresissmall"
};

char *symbolEncoding[256] = {
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  "space",
  "exclam",
  "universal",
  "numbersign",
  "existential",
  "percent",
  "ampersand",
  "suchthat",
  "parenleft",
  "parenright",
  "asteriskmath",
  "plus",
  "comma",
  "minus",
  "period",
  "slash",
  "zero",
  "one",
  "two",
  "three",
  "four",
  "five",
  "six",
  "seven",
  "eight",
  "nine",
  "colon",
  "semicolon",
  "less",
  "equal",
  "greater",
  "question",
  "congruent",
  "Alpha",
  "Beta",
  "Chi",
  "Delta",
  "Epsilon",
  "Phi",
  "Gamma",
  "Eta",
  "Iota",
  "theta1",
  "Kappa",
  "Lambda",
  "Mu",
  "Nu",
  "Omicron",
  "Pi",
  "Theta",
  "Rho",
  "Sigma",
  "Tau",
  "Upsilon",
  "sigma1",
  "Omega",
  "Xi",
  "Psi",
  "Zeta",
  "bracketleft",
  "therefore",
  "bracketright",
  "perpendicular",
  "underscore",
  "radicalex",
  "alpha",
  "beta",
  "chi",
  "delta",
  "epsilon",
  "phi",
  "gamma",
  "eta",
  "iota",
  "phi1",
  "kappa",
  "lambda",
  "mu",
  "nu",
  "omicron",
  "pi",
  "theta",
  "rho",
  "sigma",
  "tau",
  "upsilon",
  "omega1",
  "omega",
  "xi",
  "psi",
  "zeta",
  "braceleft",
  "bar",
  "braceright",
  "similar",
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  "Upsilon1",
  "minute",
  "lessequal",
  "fraction",
  "infinity",
  "florin",
  "club",
  "diamond",
  "heart",
  "spade",
  "arrowboth",
  "arrowleft",
  "arrowup",
  "arrowright",
  "arrowdown",
  "degree",
  "plusminus",
  "second",
  "greaterequal",
  "multiply",
  "proportional",
  "partialdiff",
  "bullet",
  "divide",
  "notequal",
  "equivalence",
  "approxequal",
  "ellipsis",
  "arrowvertex",
  "arrowhorizex",
  "carriagereturn",
  "aleph",
  "Ifraktur",
  "Rfraktur",
  "weierstrass",
  "circlemultiply",
  "circleplus",
  "emptyset",
  "intersection",
  "union",
  "propersuperset",
  "reflexsuperset",
  "notsubset",
  "propersubset",
  "reflexsubset",
  "element",
  "notelement",
  "angle",
  "gradient",
  "registerserif",
  "copyrightserif",
  "trademarkserif",
  "product",
  "radical",
  "dotmath",
  "logicalnot",
  "logicaland",
  "logicalor",
  "arrowdblboth",
  "arrowdblleft",
  "arrowdblup",
  "arrowdblright",
  "arrowdbldown",
  "lozenge",
  "angleleft",
  "registersans",
  "copyrightsans",
  "trademarksans",
  "summation",
  "parenlefttp",
  "parenleftex",
  "parenleftbt",
  "bracketlefttp",
  "bracketleftex",
  "bracketleftbt",
  "bracelefttp",
  "braceleftmid",
  "braceleftbt",
  "braceex",
  NULL,
  "angleright",
  "integral",
  "integraltp",
  "integralex",
  "integralbt",
  "parenrighttp",
  "parenrightex",
  "parenrightbt",
  "bracketrighttp",
  "bracketrightex",
  "bracketrightbt",
  "bracerighttp",
  "bracerightmid",
  "bracerightbt",
  NULL
};

char *zapfDingbatsEncoding[256] = {
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  "space",
  "a1",
  "a2",
  "a202",
  "a3",
  "a4",
  "a5",
  "a119",
  "a118",
  "a117",
  "a11",
  "a12",
  "a13",
  "a14",
  "a15",
  "a16",
  "a105",
  "a17",
  "a18",
  "a19",
  "a20",
  "a21",
  "a22",
  "a23",
  "a24",
  "a25",
  "a26",
  "a27",
  "a28",
  "a6",
  "a7",
  "a8",
  "a9",
  "a10",
  "a29",
  "a30",
  "a31",
  "a32",
  "a33",
  "a34",
  "a35",
  "a36",
  "a37",
  "a38",
  "a39",
  "a40",
  "a41",
  "a42",
  "a43",
  "a44",
  "a45",
  "a46",
  "a47",
  "a48",
  "a49",
  "a50",
  "a51",
  "a52",
  "a53",
  "a54",
  "a55",
  "a56",
  "a57",
  "a58",
  "a59",
  "a60",
  "a61",
  "a62",
  "a63",
  "a64",
  "a65",
  "a66",
  "a67",
  "a68",
  "a69",
  "a70",
  "a71",
  "a72",
  "a73",
  "a74",
  "a203",
  "a75",
  "a204",
  "a76",
  "a77",
  "a78",
  "a79",
  "a81",
  "a82",
  "a83",
  "a84",
  "a97",
  "a98",
  "a99",
  "a100",
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  "a101",
  "a102",
  "a103",
  "a104",
  "a106",
  "a107",
  "a108",
  "a112",
  "a111",
  "a110",
  "a109",
  "a120",
  "a121",
  "a122",
  "a123",
  "a124",
  "a125",
  "a126",
  "a127",
  "a128",
  "a129",
  "a130",
  "a131",
  "a132",
  "a133",
  "a134",
  "a135",
  "a136",
  "a137",
  "a138",
  "a139",
  "a140",
  "a141",
  "a142",
  "a143",
  "a144",
  "a145",
  "a146",
  "a147",
  "a148",
  "a149",
  "a150",
  "a151",
  "a152",
  "a153",
  "a154",
  "a155",
  "a156",
  "a157",
  "a158",
  "a159",
  "a160",
  "a161",
  "a163",
  "a164",
  "a196",
  "a165",
  "a192",
  "a166",
  "a167",
  "a168",
  "a169",
  "a170",
  "a171",
  "a172",
  "a173",
  "a162",
  "a174",
  "a175",
  "a176",
  "a177",
  "a178",
  "a179",
  "a193",
  "a180",
  "a199",
  "a181",
  "a200",
  "a182",
  NULL,
  "a201",
  "a183",
  "a184",
  "a197",
  "a185",
  "a194",
  "a198",
  "a186",
  "a195",
  "a187",
  "a188",
  "a189",
  "a190",
  "a191",
  NULL
};
//========================================================================
//
// FontEncodingTables.h
//
// Copyright 2001-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef FONTENCODINGTABLES_H
#define FONTENCODINGTABLES_H

extern char *macRomanEncoding[];
extern char *macExpertEncoding[];
extern char *winAnsiEncoding[];
extern char *standardEncoding[];
extern char *expertEncoding[];
extern char *symbolEncoding[];
extern char *zapfDingbatsEncoding[];

#endif
//========================================================================
//
// Function.cc
//
// Copyright 2001-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include "gmem.h"
#include "Object.h"
#include "Dict.h"
#include "Stream.h"
#include "Error.h"
#include "Function.h"

//------------------------------------------------------------------------
// Function
//------------------------------------------------------------------------

Function::Function() {
}

Function::~Function() {
}

Function *Function::parse(Object *funcObj) {
  Function *func;
  Dict *dict;
  int funcType;
  Object obj1;

  if (funcObj->isStream()) {
    dict = funcObj->streamGetDict();
  } else if (funcObj->isDict()) {
    dict = funcObj->getDict();
  } else if (funcObj->isName("Identity")) {
    return new IdentityFunction();
  } else {
    error(-1, "Expected function dictionary or stream");
    return NULL;
  }

  if (!dict->lookup("FunctionType", &obj1)->isInt()) {
    error(-1, "Function type is missing or wrong type");
    obj1.free();
    return NULL;
  }
  funcType = obj1.getInt();
  obj1.free();

  if (funcType == 0) {
    func = new SampledFunction(funcObj, dict);
  } else if (funcType == 2) {
    func = new ExponentialFunction(funcObj, dict);
  } else if (funcType == 3) {
    func = new StitchingFunction(funcObj, dict);
  } else if (funcType == 4) {
    func = new PostScriptFunction(funcObj, dict);
  } else {
    error(-1, "Unimplemented function type (%d)", funcType);
    return NULL;
  }
  if (!func->isOk()) {
    delete func;
    return NULL;
  }

  return func;
}

GBool Function::init(Dict *dict) {
  Object obj1, obj2;
  int i;

  //----- Domain
  if (!dict->lookup("Domain", &obj1)->isArray()) {
    error(-1, "Function is missing domain");
    goto err2;
  }
  m = obj1.arrayGetLength() / 2;
  if (m > funcMaxInputs) {
    error(-1, "Functions with more than %d inputs are unsupported",
	  funcMaxInputs);
    goto err2;
  }
  for (i = 0; i < m; ++i) {
    obj1.arrayGet(2*i, &obj2);
    if (!obj2.isNum()) {
      error(-1, "Illegal value in function domain array");
      goto err1;
    }
    domain[i][0] = obj2.getNum();
    obj2.free();
    obj1.arrayGet(2*i+1, &obj2);
    if (!obj2.isNum()) {
      error(-1, "Illegal value in function domain array");
      goto err1;
    }
    domain[i][1] = obj2.getNum();
    obj2.free();
  }
  obj1.free();

  //----- Range
  hasRange = gFalse;
  n = 0;
  if (dict->lookup("Range", &obj1)->isArray()) {
    hasRange = gTrue;
    n = obj1.arrayGetLength() / 2;
    if (n > funcMaxOutputs) {
      error(-1, "Functions with more than %d outputs are unsupported",
	    funcMaxOutputs);
      goto err2;
    }
    for (i = 0; i < n; ++i) {
      obj1.arrayGet(2*i, &obj2);
      if (!obj2.isNum()) {
	error(-1, "Illegal value in function range array");
	goto err1;
      }
      range[i][0] = obj2.getNum();
      obj2.free();
      obj1.arrayGet(2*i+1, &obj2);
      if (!obj2.isNum()) {
	error(-1, "Illegal value in function range array");
	goto err1;
      }
      range[i][1] = obj2.getNum();
      obj2.free();
    }
  }
  obj1.free();

  return gTrue;

 err1:
  obj2.free();
 err2:
  obj1.free();
  return gFalse;
}

//------------------------------------------------------------------------
// IdentityFunction
//------------------------------------------------------------------------

IdentityFunction::IdentityFunction() {
  int i;

  // fill these in with arbitrary values just in case they get used
  // somewhere
  m = funcMaxInputs;
  n = funcMaxOutputs;
  for (i = 0; i < funcMaxInputs; ++i) {
    domain[i][0] = 0;
    domain[i][1] = 1;
  }
  hasRange = gFalse;
}

IdentityFunction::~IdentityFunction() {
}

void IdentityFunction::transform(double *in, double *out) {
  int i;

  for (i = 0; i < funcMaxOutputs; ++i) {
    out[i] = in[i];
  }
}

//------------------------------------------------------------------------
// SampledFunction
//------------------------------------------------------------------------

SampledFunction::SampledFunction(Object *funcObj, Dict *dict) {
  Stream *str;
  int sampleBits;
  double sampleMul;
  Object obj1, obj2;
  Guint buf, bitMask;
  int bits;
  Guint s;
  int i;

  samples = NULL;
  sBuf = NULL;
  ok = gFalse;

  //----- initialize the generic stuff
  if (!init(dict)) {
    goto err1;
  }
  if (!hasRange) {
    error(-1, "Type 0 function is missing range");
    goto err1;
  }
  if (m > sampledFuncMaxInputs) {
    error(-1, "Sampled functions with more than %d inputs are unsupported",
	  sampledFuncMaxInputs);
    goto err1;
  }

  //----- buffer
  sBuf = (double *)gmallocn(1 << m, sizeof(double));

  //----- get the stream
  if (!funcObj->isStream()) {
    error(-1, "Type 0 function isn't a stream");
    goto err1;
  }
  str = funcObj->getStream();

  //----- Size
  if (!dict->lookup("Size", &obj1)->isArray() ||
      obj1.arrayGetLength() != m) {
    error(-1, "Function has missing or invalid size array");
    goto err2;
  }
  for (i = 0; i < m; ++i) {
    obj1.arrayGet(i, &obj2);
    if (!obj2.isInt()) {
      error(-1, "Illegal value in function size array");
      goto err3;
    }
    sampleSize[i] = obj2.getInt();
    obj2.free();
  }
  obj1.free();
  idxMul[0] = n;
  for (i = 1; i < m; ++i) {
    idxMul[i] = idxMul[i-1] * sampleSize[i-1];
  }

  //----- BitsPerSample
  if (!dict->lookup("BitsPerSample", &obj1)->isInt()) {
    error(-1, "Function has missing or invalid BitsPerSample");
    goto err2;
  }
  sampleBits = obj1.getInt();
  sampleMul = 1.0 / (pow(2.0, (double)sampleBits) - 1);
  obj1.free();

  //----- Encode
  if (dict->lookup("Encode", &obj1)->isArray() &&
      obj1.arrayGetLength() == 2*m) {
    for (i = 0; i < m; ++i) {
      obj1.arrayGet(2*i, &obj2);
      if (!obj2.isNum()) {
	error(-1, "Illegal value in function encode array");
	goto err3;
      }
      encode[i][0] = obj2.getNum();
      obj2.free();
      obj1.arrayGet(2*i+1, &obj2);
      if (!obj2.isNum()) {
	error(-1, "Illegal value in function encode array");
	goto err3;
      }
      encode[i][1] = obj2.getNum();
      obj2.free();
    }
  } else {
    for (i = 0; i < m; ++i) {
      encode[i][0] = 0;
      encode[i][1] = sampleSize[i] - 1;
    }
  }
  obj1.free();
  for (i = 0; i < m; ++i) {
    inputMul[i] = (encode[i][1] - encode[i][0]) /
                  (domain[i][1] - domain[i][0]);
  }

  //----- Decode
  if (dict->lookup("Decode", &obj1)->isArray() &&
      obj1.arrayGetLength() == 2*n) {
    for (i = 0; i < n; ++i) {
      obj1.arrayGet(2*i, &obj2);
      if (!obj2.isNum()) {
	error(-1, "Illegal value in function decode array");
	goto err3;
      }
      decode[i][0] = obj2.getNum();
      obj2.free();
      obj1.arrayGet(2*i+1, &obj2);
      if (!obj2.isNum()) {
	error(-1, "Illegal value in function decode array");
	goto err3;
      }
      decode[i][1] = obj2.getNum();
      obj2.free();
    }
  } else {
    for (i = 0; i < n; ++i) {
      decode[i][0] = range[i][0];
      decode[i][1] = range[i][1];
    }
  }
  obj1.free();

  //----- samples
  nSamples = n;
  for (i = 0; i < m; ++i)
    nSamples *= sampleSize[i];
  samples = (double *)gmallocn(nSamples, sizeof(double));
  buf = 0;
  bits = 0;
  bitMask = (1 << sampleBits) - 1;
  str->reset();
  for (i = 0; i < nSamples; ++i) {
    if (sampleBits == 8) {
      s = str->getChar();
    } else if (sampleBits == 16) {
      s = str->getChar();
      s = (s << 8) + str->getChar();
    } else if (sampleBits == 32) {
      s = str->getChar();
      s = (s << 8) + str->getChar();
      s = (s << 8) + str->getChar();
      s = (s << 8) + str->getChar();
    } else {
      while (bits < sampleBits) {
	buf = (buf << 8) | (str->getChar() & 0xff);
	bits += 8;
      }
      s = (buf >> (bits - sampleBits)) & bitMask;
      bits -= sampleBits;
    }
    samples[i] = (double)s * sampleMul;
  }
  str->close();

  ok = gTrue;
  return;

 err3:
  obj2.free();
 err2:
  obj1.free();
 err1:
  return;
}

SampledFunction::~SampledFunction() {
  if (samples) {
    gfree(samples);
  }
  if (sBuf) {
    gfree(sBuf);
  }
}

SampledFunction::SampledFunction(SampledFunction *func) {
  memcpy(this, func, sizeof(SampledFunction));
  samples = (double *)gmallocn(nSamples, sizeof(double));
  memcpy(samples, func->samples, nSamples * sizeof(double));
  sBuf = (double *)gmallocn(1 << m, sizeof(double));
}

void SampledFunction::transform(double *in, double *out) {
  double x;
  int e[funcMaxInputs][2];
  double efrac0[funcMaxInputs];
  double efrac1[funcMaxInputs];
  int i, j, k, idx, t;

  // map input values into sample array
  for (i = 0; i < m; ++i) {
    x = (in[i] - domain[i][0]) * inputMul[i] + encode[i][0];
    if (x < 0) {
      x = 0;
    } else if (x > sampleSize[i] - 1) {
      x = sampleSize[i] - 1;
    }
    e[i][0] = (int)x;
    if ((e[i][1] = e[i][0] + 1) >= sampleSize[i]) {
      // this happens if in[i] = domain[i][1]
      e[i][1] = e[i][0];
    }
    efrac1[i] = x - e[i][0];
    efrac0[i] = 1 - efrac1[i];
  }

  // for each output, do m-linear interpolation
  for (i = 0; i < n; ++i) {

    // pull 2^m values out of the sample array
    for (j = 0; j < (1<<m); ++j) {
      idx = i;
      for (k = 0, t = j; k < m; ++k, t >>= 1) {
	idx += idxMul[k] * (e[k][t & 1]);
      }
      sBuf[j] = samples[idx];
    }

    // do m sets of interpolations
    for (j = 0, t = (1<<m); j < m; ++j, t >>= 1) {
      for (k = 0; k < t; k += 2) {
	sBuf[k >> 1] = efrac0[j] * sBuf[k] + efrac1[j] * sBuf[k+1];
      }
    }

    // map output value to range
    out[i] = sBuf[0] * (decode[i][1] - decode[i][0]) + decode[i][0];
    if (out[i] < range[i][0]) {
      out[i] = range[i][0];
    } else if (out[i] > range[i][1]) {
      out[i] = range[i][1];
    }
  }
}

//------------------------------------------------------------------------
// ExponentialFunction
//------------------------------------------------------------------------

ExponentialFunction::ExponentialFunction(Object *funcObj, Dict *dict) {
  Object obj1, obj2;
  int i;

  ok = gFalse;

  //----- initialize the generic stuff
  if (!init(dict)) {
    goto err1;
  }
  if (m != 1) {
    error(-1, "Exponential function with more than one input");
    goto err1;
  }

  //----- C0
  if (dict->lookup("C0", &obj1)->isArray()) {
    if (hasRange && obj1.arrayGetLength() != n) {
      error(-1, "Function's C0 array is wrong length");
      goto err2;
    }
    n = obj1.arrayGetLength();
    for (i = 0; i < n; ++i) {
      obj1.arrayGet(i, &obj2);
      if (!obj2.isNum()) {
	error(-1, "Illegal value in function C0 array");
	goto err3;
      }
      c0[i] = obj2.getNum();
      obj2.free();
    }
  } else {
    if (hasRange && n != 1) {
      error(-1, "Function's C0 array is wrong length");
      goto err2;
    }
    n = 1;
    c0[0] = 0;
  }
  obj1.free();

  //----- C1
  if (dict->lookup("C1", &obj1)->isArray()) {
    if (obj1.arrayGetLength() != n) {
      error(-1, "Function's C1 array is wrong length");
      goto err2;
    }
    for (i = 0; i < n; ++i) {
      obj1.arrayGet(i, &obj2);
      if (!obj2.isNum()) {
	error(-1, "Illegal value in function C1 array");
	goto err3;
      }
      c1[i] = obj2.getNum();
      obj2.free();
    }
  } else {
    if (n != 1) {
      error(-1, "Function's C1 array is wrong length");
      goto err2;
    }
    c1[0] = 1;
  }
  obj1.free();

  //----- N (exponent)
  if (!dict->lookup("N", &obj1)->isNum()) {
    error(-1, "Function has missing or invalid N");
    goto err2;
  }
  e = obj1.getNum();
  obj1.free();

  ok = gTrue;
  return;

 err3:
  obj2.free();
 err2:
  obj1.free();
 err1:
  return;
}

ExponentialFunction::~ExponentialFunction() {
}

ExponentialFunction::ExponentialFunction(ExponentialFunction *func) {
  memcpy(this, func, sizeof(ExponentialFunction));
}

void ExponentialFunction::transform(double *in, double *out) {
  double x;
  int i;

  if (in[0] < domain[0][0]) {
    x = domain[0][0];
  } else if (in[0] > domain[0][1]) {
    x = domain[0][1];
  } else {
    x = in[0];
  }
  for (i = 0; i < n; ++i) {
    out[i] = c0[i] + pow(x, e) * (c1[i] - c0[i]);
    if (hasRange) {
      if (out[i] < range[i][0]) {
	out[i] = range[i][0];
      } else if (out[i] > range[i][1]) {
	out[i] = range[i][1];
      }
    }
  }
  return;
}

//------------------------------------------------------------------------
// StitchingFunction
//------------------------------------------------------------------------

StitchingFunction::StitchingFunction(Object *funcObj, Dict *dict) {
  Object obj1, obj2;
  int i;

  ok = gFalse;
  funcs = NULL;
  bounds = NULL;
  encode = NULL;
  scale = NULL;

  //----- initialize the generic stuff
  if (!init(dict)) {
    goto err1;
  }
  if (m != 1) {
    error(-1, "Stitching function with more than one input");
    goto err1;
  }

  //----- Functions
  if (!dict->lookup("Functions", &obj1)->isArray()) {
    error(-1, "Missing 'Functions' entry in stitching function");
    goto err1;
  }
  k = obj1.arrayGetLength();
  funcs = (Function **)gmallocn(k, sizeof(Function *));
  bounds = (double *)gmallocn(k + 1, sizeof(double));
  encode = (double *)gmallocn(2 * k, sizeof(double));
  scale = (double *)gmallocn(k, sizeof(double));
  for (i = 0; i < k; ++i) {
    funcs[i] = NULL;
  }
  for (i = 0; i < k; ++i) {
    if (!(funcs[i] = Function::parse(obj1.arrayGet(i, &obj2)))) {
      goto err2;
    }
    if (i > 0 && (funcs[i]->getInputSize() != 1 ||
		  funcs[i]->getOutputSize() != funcs[0]->getOutputSize())) {
      error(-1, "Incompatible subfunctions in stitching function");
      goto err2;
    }
    obj2.free();
  }
  obj1.free();

  //----- Bounds
  if (!dict->lookup("Bounds", &obj1)->isArray() ||
      obj1.arrayGetLength() != k - 1) {
    error(-1, "Missing or invalid 'Bounds' entry in stitching function");
    goto err1;
  }
  bounds[0] = domain[0][0];
  for (i = 1; i < k; ++i) {
    if (!obj1.arrayGet(i - 1, &obj2)->isNum()) {
      error(-1, "Invalid type in 'Bounds' array in stitching function");
      goto err2;
    }
    bounds[i] = obj2.getNum();
    obj2.free();
  }
  bounds[k] = domain[0][1];
  obj1.free();

  //----- Encode
  if (!dict->lookup("Encode", &obj1)->isArray() ||
      obj1.arrayGetLength() != 2 * k) {
    error(-1, "Missing or invalid 'Encode' entry in stitching function");
    goto err1;
  }
  for (i = 0; i < 2 * k; ++i) {
    if (!obj1.arrayGet(i, &obj2)->isNum()) {
      error(-1, "Invalid type in 'Encode' array in stitching function");
      goto err2;
    }
    encode[i] = obj2.getNum();
    obj2.free();
  }
  obj1.free();

  //----- pre-compute the scale factors
  for (i = 0; i < k; ++i) {
    if (bounds[i] == bounds[i+1]) {
      // avoid a divide-by-zero -- in this situation, function i will
      // never be used anyway
      scale[i] = 0;
    } else {
      scale[i] = (encode[2*i+1] - encode[2*i]) / (bounds[i+1] - bounds[i]);
    }
  }

  ok = gTrue;
  return;

 err2:
  obj2.free();
 err1:
  obj1.free();
}

StitchingFunction::StitchingFunction(StitchingFunction *func) {
  int i;

  k = func->k;
  funcs = (Function **)gmallocn(k, sizeof(Function *));
  for (i = 0; i < k; ++i) {
    funcs[i] = func->funcs[i]->copy();
  }
  bounds = (double *)gmallocn(k + 1, sizeof(double));
  memcpy(bounds, func->bounds, (k + 1) * sizeof(double));
  encode = (double *)gmallocn(2 * k, sizeof(double));
  memcpy(encode, func->encode, 2 * k * sizeof(double));
  scale = (double *)gmallocn(k, sizeof(double));
  memcpy(scale, func->scale, k * sizeof(double));
  ok = gTrue;
}

StitchingFunction::~StitchingFunction() {
  int i;

  if (funcs) {
    for (i = 0; i < k; ++i) {
      if (funcs[i]) {
	delete funcs[i];
      }
    }
  }
  gfree(funcs);
  gfree(bounds);
  gfree(encode);
  gfree(scale);
}

void StitchingFunction::transform(double *in, double *out) {
  double x;
  int i;

  if (in[0] < domain[0][0]) {
    x = domain[0][0];
  } else if (in[0] > domain[0][1]) {
    x = domain[0][1];
  } else {
    x = in[0];
  }
  for (i = 0; i < k - 1; ++i) {
    if (x < bounds[i+1]) {
      break;
    }
  }
  x = encode[2*i] + (x - bounds[i]) * scale[i];
  funcs[i]->transform(&x, out);
}

//------------------------------------------------------------------------
// PostScriptFunction
//------------------------------------------------------------------------

enum PSOp {
  psOpAbs,
  psOpAdd,
  psOpAnd,
  psOpAtan,
  psOpBitshift,
  psOpCeiling,
  psOpCopy,
  psOpCos,
  psOpCvi,
  psOpCvr,
  psOpDiv,
  psOpDup,
  psOpEq,
  psOpExch,
  psOpExp,
  psOpFalse,
  psOpFloor,
  psOpGe,
  psOpGt,
  psOpIdiv,
  psOpIndex,
  psOpLe,
  psOpLn,
  psOpLog,
  psOpLt,
  psOpMod,
  psOpMul,
  psOpNe,
  psOpNeg,
  psOpNot,
  psOpOr,
  psOpPop,
  psOpRoll,
  psOpRound,
  psOpSin,
  psOpSqrt,
  psOpSub,
  psOpTrue,
  psOpTruncate,
  psOpXor,
  psOpIf,
  psOpIfelse,
  psOpReturn
};

// Note: 'if' and 'ifelse' are parsed separately.
// The rest are listed here in alphabetical order.
// The index in this table is equivalent to the entry in PSOp.
char *psOpNames[] = {
  "abs",
  "add",
  "and",
  "atan",
  "bitshift",
  "ceiling",
  "copy",
  "cos",
  "cvi",
  "cvr",
  "div",
  "dup",
  "eq",
  "exch",
  "exp",
  "false",
  "floor",
  "ge",
  "gt",
  "idiv",
  "index",
  "le",
  "ln",
  "log",
  "lt",
  "mod",
  "mul",
  "ne",
  "neg",
  "not",
  "or",
  "pop",
  "roll",
  "round",
  "sin",
  "sqrt",
  "sub",
  "true",
  "truncate",
  "xor"
};

#define nPSOps (sizeof(psOpNames) / sizeof(char *))

enum PSObjectType {
  psBool,
  psInt,
  psReal,
  psOperator,
  psBlock
};

// In the code array, 'if'/'ifelse' operators take up three slots
// plus space for the code in the subclause(s).
//
//         +---------------------------------+
//         | psOperator: psOpIf / psOpIfelse |
//         +---------------------------------+
//         | psBlock: ptr=<A>                |
//         +---------------------------------+
//         | psBlock: ptr=<B>                |
//         +---------------------------------+
//         | if clause                       |
//         | ...                             |
//         | psOperator: psOpReturn          |
//         +---------------------------------+
//     <A> | else clause                     |
//         | ...                             |
//         | psOperator: psOpReturn          |
//         +---------------------------------+
//     <B> | ...                             |
//
// For 'if', pointer <A> is present in the code stream but unused.

struct PSObject {
  PSObjectType type;
  union {
    GBool booln;		// boolean (stack only)
    int intg;			// integer (stack and code)
    double real;		// real (stack and code)
    PSOp op;			// operator (code only)
    int blk;			// if/ifelse block pointer (code only)
  };
};

#define psStackSize 100

class PSStack {
public:

  PSStack() { sp = psStackSize; }
  void pushBool(GBool booln);
  void pushInt(int intg);
  void pushReal(double real);
  GBool popBool();
  int popInt();
  double popNum();
  GBool empty() { return sp == psStackSize; }
  GBool topIsInt() { return sp < psStackSize && stack[sp].type == psInt; }
  GBool topTwoAreInts()
    { return sp < psStackSize - 1 &&
	     stack[sp].type == psInt &&
             stack[sp+1].type == psInt; }
  GBool topIsReal() { return sp < psStackSize && stack[sp].type == psReal; }
  GBool topTwoAreNums()
    { return sp < psStackSize - 1 &&
	     (stack[sp].type == psInt || stack[sp].type == psReal) &&
	     (stack[sp+1].type == psInt || stack[sp+1].type == psReal); }
  void copy(int n);
  void roll(int n, int j);
  void index(int i);
  void pop();

private:

  GBool checkOverflow(int n = 1);
  GBool checkUnderflow();
  GBool checkType(PSObjectType t1, PSObjectType t2);

  PSObject stack[psStackSize];
  int sp;
};

GBool PSStack::checkOverflow(int n) {
  if (sp - n < 0) {
    error(-1, "Stack overflow in PostScript function");
    return gFalse;
  }
  return gTrue;
}

GBool PSStack::checkUnderflow() {
  if (sp == psStackSize) {
    error(-1, "Stack underflow in PostScript function");
    return gFalse;
  }
  return gTrue;
}

GBool PSStack::checkType(PSObjectType t1, PSObjectType t2) {
  if (stack[sp].type != t1 && stack[sp].type != t2) {
    error(-1, "Type mismatch in PostScript function");
    return gFalse;
  }
  return gTrue;
}

void PSStack::pushBool(GBool booln) {
  if (checkOverflow()) {
    stack[--sp].type = psBool;
    stack[sp].booln = booln;
  }
}

void PSStack::pushInt(int intg) {
  if (checkOverflow()) {
    stack[--sp].type = psInt;
    stack[sp].intg = intg;
  }
}

void PSStack::pushReal(double real) {
  if (checkOverflow()) {
    stack[--sp].type = psReal;
    stack[sp].real = real;
  }
}

GBool PSStack::popBool() {
  if (checkUnderflow() && checkType(psBool, psBool)) {
    return stack[sp++].booln;
  }
  return gFalse;
}

int PSStack::popInt() {
  if (checkUnderflow() && checkType(psInt, psInt)) {
    return stack[sp++].intg;
  }
  return 0;
}

double PSStack::popNum() {
  double ret;

  if (checkUnderflow() && checkType(psInt, psReal)) {
    ret = (stack[sp].type == psInt) ? (double)stack[sp].intg : stack[sp].real;
    ++sp;
    return ret;
  }
  return 0;
}

void PSStack::copy(int n) {
  int i;

  if (sp + n > psStackSize) {
    error(-1, "Stack underflow in PostScript function");
    return;
  }
  if (!checkOverflow(n)) {
    return;
  }
  for (i = sp + n - 1; i >= sp; --i) {
    stack[i - n] = stack[i];
  }
  sp -= n;
}

void PSStack::roll(int n, int j) {
  PSObject obj;
  int i, k;

  if (j >= 0) {
    j %= n;
  } else {
    j = -j % n;
    if (j != 0) {
      j = n - j;
    }
  }
  if (n <= 0 || j == 0) {
    return;
  }
  for (i = 0; i < j; ++i) {
    obj = stack[sp];
    for (k = sp; k < sp + n - 1; ++k) {
      stack[k] = stack[k+1];
    }
    stack[sp + n - 1] = obj;
  }
}

void PSStack::index(int i) {
  if (!checkOverflow()) {
    return;
  }
  --sp;
  stack[sp] = stack[sp + 1 + i];
}

void PSStack::pop() {
  if (!checkUnderflow()) {
    return;
  }
  ++sp;
}

PostScriptFunction::PostScriptFunction(Object *funcObj, Dict *dict) {
  Stream *str;
  int codePtr;
  GString *tok;

  code = NULL;
  codeSize = 0;
  ok = gFalse;

  //----- initialize the generic stuff
  if (!init(dict)) {
    goto err1;
  }
  if (!hasRange) {
    error(-1, "Type 4 function is missing range");
    goto err1;
  }

  //----- get the stream
  if (!funcObj->isStream()) {
    error(-1, "Type 4 function isn't a stream");
    goto err1;
  }
  str = funcObj->getStream();

  //----- parse the function
  codeString = new GString();
  str->reset();
  if (!(tok = getToken(str)) || tok->cmp("{")) {
    error(-1, "Expected '{' at start of PostScript function");
    if (tok) {
      delete tok;
    }
    goto err1;
  }
  delete tok;
  codePtr = 0;
  if (!parseCode(str, &codePtr)) {
    goto err2;
  }
  str->close();

  ok = gTrue;

 err2:
  str->close();
 err1:
  return;
}

PostScriptFunction::PostScriptFunction(PostScriptFunction *func) {
  memcpy(this, func, sizeof(PostScriptFunction));
  code = (PSObject *)gmallocn(codeSize, sizeof(PSObject));
  memcpy(code, func->code, codeSize * sizeof(PSObject));
  codeString = func->codeString->copy();
}

PostScriptFunction::~PostScriptFunction() {
  gfree(code);
  delete codeString;
}

void PostScriptFunction::transform(double *in, double *out) {
  PSStack *stack;
  int i;

  stack = new PSStack();
  for (i = 0; i < m; ++i) {
    //~ may need to check for integers here
    stack->pushReal(in[i]);
  }
  exec(stack, 0);
  for (i = n - 1; i >= 0; --i) {
    out[i] = stack->popNum();
    if (out[i] < range[i][0]) {
      out[i] = range[i][0];
    } else if (out[i] > range[i][1]) {
      out[i] = range[i][1];
    }
  }
  // if (!stack->empty()) {
  //   error(-1, "Extra values on stack at end of PostScript function");
  // }
  delete stack;
}

GBool PostScriptFunction::parseCode(Stream *str, int *codePtr) {
  GString *tok;
  char *p;
  GBool isReal;
  int opPtr, elsePtr;
  int a, b, mid, cmp;

  while (1) {
    if (!(tok = getToken(str))) {
      error(-1, "Unexpected end of PostScript function stream");
      return gFalse;
    }
    p = tok->getCString();
    if (isdigit(*p) || *p == '.' || *p == '-') {
      isReal = gFalse;
      for (++p; *p; ++p) {
	if (*p == '.') {
	  isReal = gTrue;
	  break;
	}
      }
      resizeCode(*codePtr);
      if (isReal) {
	code[*codePtr].type = psReal;
	code[*codePtr].real = atof(tok->getCString());
      } else {
	code[*codePtr].type = psInt;
	code[*codePtr].intg = atoi(tok->getCString());
      }
      ++*codePtr;
      delete tok;
    } else if (!tok->cmp("{")) {
      delete tok;
      opPtr = *codePtr;
      *codePtr += 3;
      resizeCode(opPtr + 2);
      if (!parseCode(str, codePtr)) {
	return gFalse;
      }
      if (!(tok = getToken(str))) {
	error(-1, "Unexpected end of PostScript function stream");
	return gFalse;
      }
      if (!tok->cmp("{")) {
	elsePtr = *codePtr;
	if (!parseCode(str, codePtr)) {
	  return gFalse;
	}
	delete tok;
	if (!(tok = getToken(str))) {
	  error(-1, "Unexpected end of PostScript function stream");
	  return gFalse;
	}
      } else {
	elsePtr = -1;
      }
      if (!tok->cmp("if")) {
	if (elsePtr >= 0) {
	  error(-1, "Got 'if' operator with two blocks in PostScript function");
	  return gFalse;
	}
	code[opPtr].type = psOperator;
	code[opPtr].op = psOpIf;
	code[opPtr+2].type = psBlock;
	code[opPtr+2].blk = *codePtr;
      } else if (!tok->cmp("ifelse")) {
	if (elsePtr < 0) {
	  error(-1, "Got 'ifelse' operator with one blocks in PostScript function");
	  return gFalse;
	}
	code[opPtr].type = psOperator;
	code[opPtr].op = psOpIfelse;
	code[opPtr+1].type = psBlock;
	code[opPtr+1].blk = elsePtr;
	code[opPtr+2].type = psBlock;
	code[opPtr+2].blk = *codePtr;
      } else {
	error(-1, "Expected if/ifelse operator in PostScript function");
	delete tok;
	return gFalse;
      }
      delete tok;
    } else if (!tok->cmp("}")) {
      delete tok;
      resizeCode(*codePtr);
      code[*codePtr].type = psOperator;
      code[*codePtr].op = psOpReturn;
      ++*codePtr;
      break;
    } else {
      a = -1;
      b = nPSOps;
      // invariant: psOpNames[a] < tok < psOpNames[b]
      while (b - a > 1) {
	mid = (a + b) / 2;
	cmp = tok->cmp(psOpNames[mid]);
	if (cmp > 0) {
	  a = mid;
	} else if (cmp < 0) {
	  b = mid;
	} else {
	  a = b = mid;
	}
      }
      if (cmp != 0) {
	error(-1, "Unknown operator '%s' in PostScript function",
	      tok->getCString());
	delete tok;
	return gFalse;
      }
      delete tok;
      resizeCode(*codePtr);
      code[*codePtr].type = psOperator;
      code[*codePtr].op = (PSOp)a;
      ++*codePtr;
    }
  }
  return gTrue;
}

GString *PostScriptFunction::getToken(Stream *str) {
  GString *s;
  int c;
  GBool comment;

  s = new GString();
  comment = gFalse;
  while (1) {
    if ((c = str->getChar()) == EOF) {
      break;
    }
    codeString->append(c);
    if (comment) {
      if (c == '\x0a' || c == '\x0d') {
	comment = gFalse;
      }
    } else if (c == '%') {
      comment = gTrue;
    } else if (!isspace(c)) {
      break;
    }
  }
  if (c == '{' || c == '}') {
    s->append((char)c);
  } else if (isdigit(c) || c == '.' || c == '-') {
    while (1) {
      s->append((char)c);
      c = str->lookChar();
      if (c == EOF || !(isdigit(c) || c == '.' || c == '-')) {
	break;
      }
      str->getChar();
      codeString->append(c);
    }
  } else {
    while (1) {
      s->append((char)c);
      c = str->lookChar();
      if (c == EOF || !isalnum(c)) {
	break;
      }
      str->getChar();
      codeString->append(c);
    }
  }
  return s;
}

void PostScriptFunction::resizeCode(int newSize) {
  if (newSize >= codeSize) {
    codeSize += 64;
    code = (PSObject *)greallocn(code, codeSize, sizeof(PSObject));
  }
}

void PostScriptFunction::exec(PSStack *stack, int codePtr) {
  int i1, i2;
  double r1, r2;
  GBool b1, b2;

  while (1) {
    switch (code[codePtr].type) {
    case psInt:
      stack->pushInt(code[codePtr++].intg);
      break;
    case psReal:
      stack->pushReal(code[codePtr++].real);
      break;
    case psOperator:
      switch (code[codePtr++].op) {
      case psOpAbs:
	if (stack->topIsInt()) {
	  stack->pushInt(abs(stack->popInt()));
	} else {
	  stack->pushReal(fabs(stack->popNum()));
	}
	break;
      case psOpAdd:
	if (stack->topTwoAreInts()) {
	  i2 = stack->popInt();
	  i1 = stack->popInt();
	  stack->pushInt(i1 + i2);
	} else {
	  r2 = stack->popNum();
	  r1 = stack->popNum();
	  stack->pushReal(r1 + r2);
	}
	break;
      case psOpAnd:
	if (stack->topTwoAreInts()) {
	  i2 = stack->popInt();
	  i1 = stack->popInt();
	  stack->pushInt(i1 & i2);
	} else {
	  b2 = stack->popBool();
	  b1 = stack->popBool();
	  stack->pushBool(b1 && b2);
	}
	break;
      case psOpAtan:
	r2 = stack->popNum();
	r1 = stack->popNum();
	stack->pushReal(atan2(r1, r2));
	break;
      case psOpBitshift:
	i2 = stack->popInt();
	i1 = stack->popInt();
	if (i2 > 0) {
	  stack->pushInt(i1 << i2);
	} else if (i2 < 0) {
	  stack->pushInt((int)((Guint)i1 >> i2));
	} else {
	  stack->pushInt(i1);
	}
	break;
      case psOpCeiling:
	if (!stack->topIsInt()) {
	  stack->pushReal(ceil(stack->popNum()));
	}
	break;
      case psOpCopy:
	stack->copy(stack->popInt());
	break;
      case psOpCos:
	stack->pushReal(cos(stack->popNum()));
	break;
      case psOpCvi:
	if (!stack->topIsInt()) {
	  stack->pushInt((int)stack->popNum());
	}
	break;
      case psOpCvr:
	if (!stack->topIsReal()) {
	  stack->pushReal(stack->popNum());
	}
	break;
      case psOpDiv:
	r2 = stack->popNum();
	r1 = stack->popNum();
	stack->pushReal(r1 / r2);
	break;
      case psOpDup:
	stack->copy(1);
	break;
      case psOpEq:
	if (stack->topTwoAreInts()) {
	  i2 = stack->popInt();
	  i1 = stack->popInt();
	  stack->pushBool(i1 == i2);
	} else if (stack->topTwoAreNums()) {
	  r2 = stack->popNum();
	  r1 = stack->popNum();
	  stack->pushBool(r1 == r2);
	} else {
	  b2 = stack->popBool();
	  b1 = stack->popBool();
	  stack->pushBool(b1 == b2);
	}
	break;
      case psOpExch:
	stack->roll(2, 1);
	break;
      case psOpExp:
	r2 = stack->popNum();
	r1 = stack->popNum();
	stack->pushReal(pow(r1, r2));
	break;
      case psOpFalse:
	stack->pushBool(gFalse);
	break;
      case psOpFloor:
	if (!stack->topIsInt()) {
	  stack->pushReal(floor(stack->popNum()));
	}
	break;
      case psOpGe:
	if (stack->topTwoAreInts()) {
	  i2 = stack->popInt();
	  i1 = stack->popInt();
	  stack->pushBool(i1 >= i2);
	} else {
	  r2 = stack->popNum();
	  r1 = stack->popNum();
	  stack->pushBool(r1 >= r2);
	}
	break;
      case psOpGt:
	if (stack->topTwoAreInts()) {
	  i2 = stack->popInt();
	  i1 = stack->popInt();
	  stack->pushBool(i1 > i2);
	} else {
	  r2 = stack->popNum();
	  r1 = stack->popNum();
	  stack->pushBool(r1 > r2);
	}
	break;
      case psOpIdiv:
	i2 = stack->popInt();
	i1 = stack->popInt();
	stack->pushInt(i1 / i2);
	break;
      case psOpIndex:
	stack->index(stack->popInt());
	break;
      case psOpLe:
	if (stack->topTwoAreInts()) {
	  i2 = stack->popInt();
	  i1 = stack->popInt();
	  stack->pushBool(i1 <= i2);
	} else {
	  r2 = stack->popNum();
	  r1 = stack->popNum();
	  stack->pushBool(r1 <= r2);
	}
	break;
      case psOpLn:
	stack->pushReal(log(stack->popNum()));
	break;
      case psOpLog:
	stack->pushReal(log10(stack->popNum()));
	break;
      case psOpLt:
	if (stack->topTwoAreInts()) {
	  i2 = stack->popInt();
	  i1 = stack->popInt();
	  stack->pushBool(i1 < i2);
	} else {
	  r2 = stack->popNum();
	  r1 = stack->popNum();
	  stack->pushBool(r1 < r2);
	}
	break;
      case psOpMod:
	i2 = stack->popInt();
	i1 = stack->popInt();
	stack->pushInt(i1 % i2);
	break;
      case psOpMul:
	if (stack->topTwoAreInts()) {
	  i2 = stack->popInt();
	  i1 = stack->popInt();
	  //~ should check for out-of-range, and push a real instead
	  stack->pushInt(i1 * i2);
	} else {
	  r2 = stack->popNum();
	  r1 = stack->popNum();
	  stack->pushReal(r1 * r2);
	}
	break;
      case psOpNe:
	if (stack->topTwoAreInts()) {
	  i2 = stack->popInt();
	  i1 = stack->popInt();
	  stack->pushBool(i1 != i2);
	} else if (stack->topTwoAreNums()) {
	  r2 = stack->popNum();
	  r1 = stack->popNum();
	  stack->pushBool(r1 != r2);
	} else {
	  b2 = stack->popBool();
	  b1 = stack->popBool();
	  stack->pushBool(b1 != b2);
	}
	break;
      case psOpNeg:
	if (stack->topIsInt()) {
	  stack->pushInt(-stack->popInt());
	} else {
	  stack->pushReal(-stack->popNum());
	}
	break;
      case psOpNot:
	if (stack->topIsInt()) {
	  stack->pushInt(~stack->popInt());
	} else {
	  stack->pushBool(!stack->popBool());
	}
	break;
      case psOpOr:
	if (stack->topTwoAreInts()) {
	  i2 = stack->popInt();
	  i1 = stack->popInt();
	  stack->pushInt(i1 | i2);
	} else {
	  b2 = stack->popBool();
	  b1 = stack->popBool();
	  stack->pushBool(b1 || b2);
	}
	break;
      case psOpPop:
	stack->pop();
	break;
      case psOpRoll:
	i2 = stack->popInt();
	i1 = stack->popInt();
	stack->roll(i1, i2);
	break;
      case psOpRound:
	if (!stack->topIsInt()) {
	  r1 = stack->popNum();
	  stack->pushReal((r1 >= 0) ? floor(r1 + 0.5) : ceil(r1 - 0.5));
	}
	break;
      case psOpSin:
	stack->pushReal(sin(stack->popNum()));
	break;
      case psOpSqrt:
	stack->pushReal(sqrt(stack->popNum()));
	break;
      case psOpSub:
	if (stack->topTwoAreInts()) {
	  i2 = stack->popInt();
	  i1 = stack->popInt();
	  stack->pushInt(i1 - i2);
	} else {
	  r2 = stack->popNum();
	  r1 = stack->popNum();
	  stack->pushReal(r1 - r2);
	}
	break;
      case psOpTrue:
	stack->pushBool(gTrue);
	break;
      case psOpTruncate:
	if (!stack->topIsInt()) {
	  r1 = stack->popNum();
	  stack->pushReal((r1 >= 0) ? floor(r1) : ceil(r1));
	}
	break;
      case psOpXor:
	if (stack->topTwoAreInts()) {
	  i2 = stack->popInt();
	  i1 = stack->popInt();
	  stack->pushInt(i1 ^ i2);
	} else {
	  b2 = stack->popBool();
	  b1 = stack->popBool();
	  stack->pushBool(b1 ^ b2);
	}
	break;
      case psOpIf:
	b1 = stack->popBool();
	if (b1) {
	  exec(stack, codePtr + 2);
	}
	codePtr = code[codePtr + 1].blk;
	break;
      case psOpIfelse:
	b1 = stack->popBool();
	if (b1) {
	  exec(stack, codePtr + 2);
	} else {
	  exec(stack, code[codePtr].blk);
	}
	codePtr = code[codePtr + 1].blk;
	break;
      case psOpReturn:
	return;
      }
      break;
    default:
      error(-1, "Internal: bad object in PostScript function code");
      break;
    }
  }
}
//========================================================================
//
// Function.h
//
// Copyright 2001-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef FUNCTION_H
#define FUNCTION_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "gtypes.h"
#include "Object.h"

class Dict;
class Stream;
struct PSObject;
class PSStack;

//------------------------------------------------------------------------
// Function
//------------------------------------------------------------------------

#define funcMaxInputs        32
#define funcMaxOutputs       32
#define sampledFuncMaxInputs 16

class Function {
public:

  Function();

  virtual ~Function();

  // Construct a function.  Returns NULL if unsuccessful.
  static Function *parse(Object *funcObj);

  // Initialize the entries common to all function types.
  GBool init(Dict *dict);

  virtual Function *copy() = 0;

  // Return the function type:
  //   -1 : identity
  //    0 : sampled
  //    2 : exponential
  //    3 : stitching
  //    4 : PostScript
  virtual int getType() = 0;

  // Return size of input and output tuples.
  int getInputSize() { return m; }
  int getOutputSize() { return n; }

  double getDomainMin(int i) { return domain[i][0]; }
  double getDomainMax(int i) { return domain[i][1]; }
  double getRangeMin(int i) { return range[i][0]; }
  double getRangeMax(int i) { return range[i][1]; }
  GBool getHasRange() { return hasRange; }

  // Transform an input tuple into an output tuple.
  virtual void transform(double *in, double *out) = 0;

  virtual GBool isOk() = 0;

protected:

  int m, n;			// size of input and output tuples
  double			// min and max values for function domain
    domain[funcMaxInputs][2];
  double			// min and max values for function range
    range[funcMaxOutputs][2];
  GBool hasRange;		// set if range is defined
};

//------------------------------------------------------------------------
// IdentityFunction
//------------------------------------------------------------------------

class IdentityFunction: public Function {
public:

  IdentityFunction();
  virtual ~IdentityFunction();
  virtual Function *copy() { return new IdentityFunction(); }
  virtual int getType() { return -1; }
  virtual void transform(double *in, double *out);
  virtual GBool isOk() { return gTrue; }

private:
};

//------------------------------------------------------------------------
// SampledFunction
//------------------------------------------------------------------------

class SampledFunction: public Function {
public:

  SampledFunction(Object *funcObj, Dict *dict);
  virtual ~SampledFunction();
  virtual Function *copy() { return new SampledFunction(this); }
  virtual int getType() { return 0; }
  virtual void transform(double *in, double *out);
  virtual GBool isOk() { return ok; }

  int getSampleSize(int i) { return sampleSize[i]; }
  double getEncodeMin(int i) { return encode[i][0]; }
  double getEncodeMax(int i) { return encode[i][1]; }
  double getDecodeMin(int i) { return decode[i][0]; }
  double getDecodeMax(int i) { return decode[i][1]; }
  double *getSamples() { return samples; }

private:

  SampledFunction(SampledFunction *func);

  int				// number of samples for each domain element
    sampleSize[funcMaxInputs];
  double			// min and max values for domain encoder
    encode[funcMaxInputs][2];
  double			// min and max values for range decoder
    decode[funcMaxOutputs][2];
  double			// input multipliers
    inputMul[funcMaxInputs];
  int idxMul[funcMaxInputs];	// sample array index multipliers
  double *samples;		// the samples
  int nSamples;			// size of the samples array
  double *sBuf;			// buffer for the transform function
  GBool ok;
};

//------------------------------------------------------------------------
// ExponentialFunction
//------------------------------------------------------------------------

class ExponentialFunction: public Function {
public:

  ExponentialFunction(Object *funcObj, Dict *dict);
  virtual ~ExponentialFunction();
  virtual Function *copy() { return new ExponentialFunction(this); }
  virtual int getType() { return 2; }
  virtual void transform(double *in, double *out);
  virtual GBool isOk() { return ok; }

  double *getC0() { return c0; }
  double *getC1() { return c1; }
  double getE() { return e; }

private:

  ExponentialFunction(ExponentialFunction *func);

  double c0[funcMaxOutputs];
  double c1[funcMaxOutputs];
  double e;
  GBool ok;
};

//------------------------------------------------------------------------
// StitchingFunction
//------------------------------------------------------------------------

class StitchingFunction: public Function {
public:

  StitchingFunction(Object *funcObj, Dict *dict);
  virtual ~StitchingFunction();
  virtual Function *copy() { return new StitchingFunction(this); }
  virtual int getType() { return 3; }
  virtual void transform(double *in, double *out);
  virtual GBool isOk() { return ok; }

  int getNumFuncs() { return k; }
  Function *getFunc(int i) { return funcs[i]; }
  double *getBounds() { return bounds; }
  double *getEncode() { return encode; }
  double *getScale() { return scale; }

private:

  StitchingFunction(StitchingFunction *func);

  int k;
  Function **funcs;
  double *bounds;
  double *encode;
  double *scale;
  GBool ok;
};

//------------------------------------------------------------------------
// PostScriptFunction
//------------------------------------------------------------------------

class PostScriptFunction: public Function {
public:

  PostScriptFunction(Object *funcObj, Dict *dict);
  virtual ~PostScriptFunction();
  virtual Function *copy() { return new PostScriptFunction(this); }
  virtual int getType() { return 4; }
  virtual void transform(double *in, double *out);
  virtual GBool isOk() { return ok; }

  GString *getCodeString() { return codeString; }

private:

  PostScriptFunction(PostScriptFunction *func);
  GBool parseCode(Stream *str, int *codePtr);
  GString *getToken(Stream *str);
  void resizeCode(int newSize);
  void exec(PSStack *stack, int codePtr);

  GString *codeString;
  PSObject *code;
  int codeSize;
  GBool ok;
};

#endif
//========================================================================
//
// Gfx.cc
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <string.h>
#include <math.h>
#include "gmem.h"
#include "GlobalParams.h"
#include "CharTypes.h"
#include "Object.h"
#include "Array.h"
#include "Dict.h"
#include "Stream.h"
#include "Lexer.h"
#include "Parser.h"
#include "GfxFont.h"
#include "GfxState.h"
#include "OutputDev.h"
#include "Page.h"
#include "Annot.h"
#include "Error.h"
#include "Gfx.h"

// the MSVC math.h doesn't define this
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

//------------------------------------------------------------------------
// constants
//------------------------------------------------------------------------

// Max recursive depth for a function shading fill.
#define functionMaxDepth 6

// Max delta allowed in any color component for a function shading fill.
#define functionColorDelta (dblToCol(1 / 256.0))

// Max number of splits along the t axis for an axial shading fill.
#define axialMaxSplits 256

// Max delta allowed in any color component for an axial shading fill.
#define axialColorDelta (dblToCol(1 / 256.0))

// Max number of splits along the t axis for a radial shading fill.
#define radialMaxSplits 256

// Max delta allowed in any color component for a radial shading fill.
#define radialColorDelta (dblToCol(1 / 256.0))

// Max recursive depth for a Gouraud triangle shading fill.
#define gouraudMaxDepth 6

// Max delta allowed in any color component for a Gouraud triangle
// shading fill.
#define gouraudColorDelta (dblToCol(1 / 256.0))

// Max recursive depth for a patch mesh shading fill.
#define patchMaxDepth 6

// Max delta allowed in any color component for a patch mesh shading
// fill.
#define patchColorDelta (dblToCol(1 / 256.0))

//------------------------------------------------------------------------
// Operator table
//------------------------------------------------------------------------

#ifdef WIN32 // this works around a bug in the VC7 compiler
#  pragma optimize("",off)
#endif

Operator Gfx::opTab[] = {
  {"\"",  3, {tchkNum,    tchkNum,    tchkString},
          &Gfx::opMoveSetShowText},
  {"'",   1, {tchkString},
          &Gfx::opMoveShowText},
  {"B",   0, {tchkNone},
          &Gfx::opFillStroke},
  {"B*",  0, {tchkNone},
          &Gfx::opEOFillStroke},
  {"BDC", 2, {tchkName,   tchkProps},
          &Gfx::opBeginMarkedContent},
  {"BI",  0, {tchkNone},
          &Gfx::opBeginImage},
  {"BMC", 1, {tchkName},
          &Gfx::opBeginMarkedContent},
  {"BT",  0, {tchkNone},
          &Gfx::opBeginText},
  {"BX",  0, {tchkNone},
          &Gfx::opBeginIgnoreUndef},
  {"CS",  1, {tchkName},
          &Gfx::opSetStrokeColorSpace},
  {"DP",  2, {tchkName,   tchkProps},
          &Gfx::opMarkPoint},
  {"Do",  1, {tchkName},
          &Gfx::opXObject},
  {"EI",  0, {tchkNone},
          &Gfx::opEndImage},
  {"EMC", 0, {tchkNone},
          &Gfx::opEndMarkedContent},
  {"ET",  0, {tchkNone},
          &Gfx::opEndText},
  {"EX",  0, {tchkNone},
          &Gfx::opEndIgnoreUndef},
  {"F",   0, {tchkNone},
          &Gfx::opFill},
  {"G",   1, {tchkNum},
          &Gfx::opSetStrokeGray},
  {"ID",  0, {tchkNone},
          &Gfx::opImageData},
  {"J",   1, {tchkInt},
          &Gfx::opSetLineCap},
  {"K",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
          &Gfx::opSetStrokeCMYKColor},
  {"M",   1, {tchkNum},
          &Gfx::opSetMiterLimit},
  {"MP",  1, {tchkName},
          &Gfx::opMarkPoint},
  {"Q",   0, {tchkNone},
          &Gfx::opRestore},
  {"RG",  3, {tchkNum,    tchkNum,    tchkNum},
          &Gfx::opSetStrokeRGBColor},
  {"S",   0, {tchkNone},
          &Gfx::opStroke},
  {"SC",  -4, {tchkNum,   tchkNum,    tchkNum,    tchkNum},
          &Gfx::opSetStrokeColor},
  {"SCN", -33, {tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
	        tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
	        tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
	        tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
	        tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
	        tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
	        tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
	        tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
	        tchkSCN},
          &Gfx::opSetStrokeColorN},
  {"T*",  0, {tchkNone},
          &Gfx::opTextNextLine},
  {"TD",  2, {tchkNum,    tchkNum},
          &Gfx::opTextMoveSet},
  {"TJ",  1, {tchkArray},
          &Gfx::opShowSpaceText},
  {"TL",  1, {tchkNum},
          &Gfx::opSetTextLeading},
  {"Tc",  1, {tchkNum},
          &Gfx::opSetCharSpacing},
  {"Td",  2, {tchkNum,    tchkNum},
          &Gfx::opTextMove},
  {"Tf",  2, {tchkName,   tchkNum},
          &Gfx::opSetFont},
  {"Tj",  1, {tchkString},
          &Gfx::opShowText},
  {"Tm",  6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
	      tchkNum,    tchkNum},
          &Gfx::opSetTextMatrix},
  {"Tr",  1, {tchkInt},
          &Gfx::opSetTextRender},
  {"Ts",  1, {tchkNum},
          &Gfx::opSetTextRise},
  {"Tw",  1, {tchkNum},
          &Gfx::opSetWordSpacing},
  {"Tz",  1, {tchkNum},
          &Gfx::opSetHorizScaling},
  {"W",   0, {tchkNone},
          &Gfx::opClip},
  {"W*",  0, {tchkNone},
          &Gfx::opEOClip},
  {"b",   0, {tchkNone},
          &Gfx::opCloseFillStroke},
  {"b*",  0, {tchkNone},
          &Gfx::opCloseEOFillStroke},
  {"c",   6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
	      tchkNum,    tchkNum},
          &Gfx::opCurveTo},
  {"cm",  6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
	      tchkNum,    tchkNum},
          &Gfx::opConcat},
  {"cs",  1, {tchkName},
          &Gfx::opSetFillColorSpace},
  {"d",   2, {tchkArray,  tchkNum},
          &Gfx::opSetDash},
  {"d0",  2, {tchkNum,    tchkNum},
          &Gfx::opSetCharWidth},
  {"d1",  6, {tchkNum,    tchkNum,    tchkNum,    tchkNum,
	      tchkNum,    tchkNum},
          &Gfx::opSetCacheDevice},
  {"f",   0, {tchkNone},
          &Gfx::opFill},
  {"f*",  0, {tchkNone},
          &Gfx::opEOFill},
  {"g",   1, {tchkNum},
          &Gfx::opSetFillGray},
  {"gs",  1, {tchkName},
          &Gfx::opSetExtGState},
  {"h",   0, {tchkNone},
          &Gfx::opClosePath},
  {"i",   1, {tchkNum},
          &Gfx::opSetFlat},
  {"j",   1, {tchkInt},
          &Gfx::opSetLineJoin},
  {"k",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
          &Gfx::opSetFillCMYKColor},
  {"l",   2, {tchkNum,    tchkNum},
          &Gfx::opLineTo},
  {"m",   2, {tchkNum,    tchkNum},
          &Gfx::opMoveTo},
  {"n",   0, {tchkNone},
          &Gfx::opEndPath},
  {"q",   0, {tchkNone},
          &Gfx::opSave},
  {"re",  4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
          &Gfx::opRectangle},
  {"rg",  3, {tchkNum,    tchkNum,    tchkNum},
          &Gfx::opSetFillRGBColor},
  {"ri",  1, {tchkName},
          &Gfx::opSetRenderingIntent},
  {"s",   0, {tchkNone},
          &Gfx::opCloseStroke},
  {"sc",  -4, {tchkNum,   tchkNum,    tchkNum,    tchkNum},
          &Gfx::opSetFillColor},
  {"scn", -33, {tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
	        tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
	        tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
	        tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
	        tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
	        tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
	        tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
	        tchkSCN,   tchkSCN,    tchkSCN,    tchkSCN,
	        tchkSCN},
          &Gfx::opSetFillColorN},
  {"sh",  1, {tchkName},
          &Gfx::opShFill},
  {"v",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
          &Gfx::opCurveTo1},
  {"w",   1, {tchkNum},
          &Gfx::opSetLineWidth},
  {"y",   4, {tchkNum,    tchkNum,    tchkNum,    tchkNum},
          &Gfx::opCurveTo2},
};

#ifdef WIN32 // this works around a bug in the VC7 compiler
#  pragma optimize("",on)
#endif

#define numOps (sizeof(opTab) / sizeof(Operator))

//------------------------------------------------------------------------
// GfxResources
//------------------------------------------------------------------------

GfxResources::GfxResources(XRef *xref, Dict *resDict, GfxResources *nextA) {
  Object obj1, obj2;
  Ref r;

  if (resDict) {

    // build font dictionary
    fonts = NULL;
    resDict->lookupNF("Font", &obj1);
    if (obj1.isRef()) {
      obj1.fetch(xref, &obj2);
      if (obj2.isDict()) {
	r = obj1.getRef();
	fonts = new GfxFontDict(xref, &r, obj2.getDict());
      }
      obj2.free();
    } else if (obj1.isDict()) {
      fonts = new GfxFontDict(xref, NULL, obj1.getDict());
    }
    obj1.free();

    // get XObject dictionary
    resDict->lookup("XObject", &xObjDict);

    // get color space dictionary
    resDict->lookup("ColorSpace", &colorSpaceDict);

    // get pattern dictionary
    resDict->lookup("Pattern", &patternDict);

    // get shading dictionary
    resDict->lookup("Shading", &shadingDict);

    // get graphics state parameter dictionary
    resDict->lookup("ExtGState", &gStateDict);

  } else {
    fonts = NULL;
    xObjDict.initNull();
    colorSpaceDict.initNull();
    patternDict.initNull();
    shadingDict.initNull();
    gStateDict.initNull();
  }

  next = nextA;
}

GfxResources::~GfxResources() {
  if (fonts) {
    delete fonts;
  }
  xObjDict.free();
  colorSpaceDict.free();
  patternDict.free();
  shadingDict.free();
  gStateDict.free();
}

GfxFont *GfxResources::lookupFont(char *name) {
  GfxFont *font;
  GfxResources *resPtr;

  for (resPtr = this; resPtr; resPtr = resPtr->next) {
    if (resPtr->fonts) {
      if ((font = resPtr->fonts->lookup(name)))
	return font;
    }
  }
  error(-1, "Unknown font tag '%s'", name);
  return NULL;
}

GBool GfxResources::lookupXObject(char *name, Object *obj) {
  GfxResources *resPtr;

  for (resPtr = this; resPtr; resPtr = resPtr->next) {
    if (resPtr->xObjDict.isDict()) {
      if (!resPtr->xObjDict.dictLookup(name, obj)->isNull())
	return gTrue;
      obj->free();
    }
  }
  error(-1, "XObject '%s' is unknown", name);
  return gFalse;
}

GBool GfxResources::lookupXObjectNF(char *name, Object *obj) {
  GfxResources *resPtr;

  for (resPtr = this; resPtr; resPtr = resPtr->next) {
    if (resPtr->xObjDict.isDict()) {
      if (!resPtr->xObjDict.dictLookupNF(name, obj)->isNull())
	return gTrue;
      obj->free();
    }
  }
  error(-1, "XObject '%s' is unknown", name);
  return gFalse;
}

void GfxResources::lookupColorSpace(char *name, Object *obj) {
  GfxResources *resPtr;

  for (resPtr = this; resPtr; resPtr = resPtr->next) {
    if (resPtr->colorSpaceDict.isDict()) {
      if (!resPtr->colorSpaceDict.dictLookup(name, obj)->isNull()) {
	return;
      }
      obj->free();
    }
  }
  obj->initNull();
}

GfxPattern *GfxResources::lookupPattern(char *name) {
  GfxResources *resPtr;
  GfxPattern *pattern;
  Object obj;

  for (resPtr = this; resPtr; resPtr = resPtr->next) {
    if (resPtr->patternDict.isDict()) {
      if (!resPtr->patternDict.dictLookup(name, &obj)->isNull()) {
	pattern = GfxPattern::parse(&obj);
	obj.free();
	return pattern;
      }
      obj.free();
    }
  }
  error(-1, "Unknown pattern '%s'", name);
  return NULL;
}

GfxShading *GfxResources::lookupShading(char *name) {
  GfxResources *resPtr;
  GfxShading *shading;
  Object obj;

  for (resPtr = this; resPtr; resPtr = resPtr->next) {
    if (resPtr->shadingDict.isDict()) {
      if (!resPtr->shadingDict.dictLookup(name, &obj)->isNull()) {
	shading = GfxShading::parse(&obj);
	obj.free();
	return shading;
      }
      obj.free();
    }
  }
  error(-1, "Unknown shading '%s'", name);
  return NULL;
}

GBool GfxResources::lookupGState(char *name, Object *obj) {
  GfxResources *resPtr;

  for (resPtr = this; resPtr; resPtr = resPtr->next) {
    if (resPtr->gStateDict.isDict()) {
      if (!resPtr->gStateDict.dictLookup(name, obj)->isNull()) {
	return gTrue;
      }
      obj->free();
    }
  }
  error(-1, "ExtGState '%s' is unknown", name);
  return gFalse;
}

//------------------------------------------------------------------------
// Gfx
//------------------------------------------------------------------------

Gfx::Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict,
	 double hDPI, double vDPI, PDFRectangle *box,
	 PDFRectangle *cropBox, int rotate,
	 GBool (*abortCheckCbkA)(void *data),
	 void *abortCheckCbkDataA) {
  int i;

  xref = xrefA;
  subPage = gFalse;
  printCommands = globalParams->getPrintCommands();

  // start the resource stack
  res = new GfxResources(xref, resDict, NULL);

  // initialize
  out = outA;
  state = new GfxState(hDPI, vDPI, box, rotate, out->upsideDown());
  fontChanged = gFalse;
  clip = clipNone;
  ignoreUndef = 0;
//  out->startPage(pageNum, state);
    if(cropBox) {
        out->startPage(pageNum, state, cropBox->x1,cropBox->y1,cropBox->x2,cropBox->y2);
    } else {
        out->startPage(pageNum, state, 0,0,0,0);
    }
    
  out->setDefaultCTM(state->getCTM());
  out->updateAll(state);
  for (i = 0; i < 6; ++i) {
    baseMatrix[i] = state->getCTM()[i];
  }
  formDepth = 0;
  abortCheckCbk = abortCheckCbkA;
  abortCheckCbkData = abortCheckCbkDataA;

  // set crop box
  if (cropBox) {
    state->moveTo(cropBox->x1, cropBox->y1);
    state->lineTo(cropBox->x2, cropBox->y1);
    state->lineTo(cropBox->x2, cropBox->y2);
    state->lineTo(cropBox->x1, cropBox->y2);
    state->closePath();
    state->clip();
    out->clip(state);
    state->clearPath();
  }
}

Gfx::Gfx(XRef *xrefA, OutputDev *outA, Dict *resDict,
	 PDFRectangle *box, PDFRectangle *cropBox,
	 GBool (*abortCheckCbkA)(void *data),
	 void *abortCheckCbkDataA) {
  int i;

  xref = xrefA;
  subPage = gTrue;
  printCommands = globalParams->getPrintCommands();

  // start the resource stack
  res = new GfxResources(xref, resDict, NULL);

  // initialize
  out = outA;
  state = new GfxState(72, 72, box, 0, gFalse);
  fontChanged = gFalse;
  clip = clipNone;
  ignoreUndef = 0;
  for (i = 0; i < 6; ++i) {
    baseMatrix[i] = state->getCTM()[i];
  }
  formDepth = 0;
  abortCheckCbk = abortCheckCbkA;
  abortCheckCbkData = abortCheckCbkDataA;

  // set crop box
  if (cropBox) {
    state->moveTo(cropBox->x1, cropBox->y1);
    state->lineTo(cropBox->x2, cropBox->y1);
    state->lineTo(cropBox->x2, cropBox->y2);
    state->lineTo(cropBox->x1, cropBox->y2);
    state->closePath();
    state->clip();
    out->clip(state);
    state->clearPath();
  }
}

Gfx::~Gfx() {
  while (state->hasSaves()) {
    restoreState();
  }
  if (!subPage) {
    out->endPage();
  }
  while (res) {
    popResources();
  }
  if (state) {
    delete state;
  }
}

void Gfx::display(Object *obj, GBool topLevel) {
  Object obj2;
  int i;

  if (obj->isArray()) {
    for (i = 0; i < obj->arrayGetLength(); ++i) {
      obj->arrayGet(i, &obj2);
      if (!obj2.isStream()) {
	error(-1, "Weird page contents");
	obj2.free();
	return;
      }
      obj2.free();
    }
  } else if (!obj->isStream()) {
    error(-1, "Weird page contents");
    return;
  }
  parser = new Parser(xref, new Lexer(xref, obj), gFalse);
  go(topLevel);
  delete parser;
  parser = NULL;
}

void Gfx::go(GBool topLevel) {
  Object obj;
  Object args[maxArgs];
  int numArgs, i;
  int lastAbortCheck;

  // scan a sequence of objects
  updateLevel = lastAbortCheck = 0;
  numArgs = 0;
  parser->getObj(&obj);
  while (!obj.isEOF()) {

    // got a command - execute it
    if (obj.isCmd()) {
      if (printCommands) {
	obj.print(stdout);
	for (i = 0; i < numArgs; ++i) {
	  printf(" ");
	  args[i].print(stdout);
	}
	printf("\n");
	fflush(stdout);
      }
      execOp(&obj, args, numArgs);
      obj.free();
      for (i = 0; i < numArgs; ++i)
	args[i].free();
      numArgs = 0;

      // periodically update display
      if (++updateLevel >= 20000) {
	out->dump();
	updateLevel = 0;
      }

      // check for an abort
      if (abortCheckCbk) {
	if (updateLevel - lastAbortCheck > 10) {
	  if ((*abortCheckCbk)(abortCheckCbkData)) {
	    break;
	  }
	  lastAbortCheck = updateLevel;
	}
      }

    // got an argument - save it
    } else if (numArgs < maxArgs) {
      args[numArgs++] = obj;

    // too many arguments - something is wrong
    } else {
      error(getPos(), "Too many args in content stream");
      if (printCommands) {
	printf("throwing away arg: ");
	obj.print(stdout);
	printf("\n");
	fflush(stdout);
      }
      obj.free();
    }

    // grab the next object
    parser->getObj(&obj);
  }
  obj.free();

  // args at end with no command
  if (numArgs > 0) {
    error(getPos(), "Leftover args in content stream");
    if (printCommands) {
      printf("%d leftovers:", numArgs);
      for (i = 0; i < numArgs; ++i) {
	printf(" ");
	args[i].print(stdout);
      }
      printf("\n");
      fflush(stdout);
    }
    for (i = 0; i < numArgs; ++i)
      args[i].free();
  }

  // update display
  if (topLevel && updateLevel > 0) {
    out->dump();
  }
}

void Gfx::execOp(Object *cmd, Object args[], int numArgs) {
  Operator *op;
  char *name;
  Object *argPtr;
  int i;

  // find operator
  name = cmd->getCmd();
  if (!(op = findOp(name))) {
    if (ignoreUndef == 0)
      error(getPos(), "Unknown operator '%s'", name);
    return;
  }

  // type check args
  argPtr = args;
  if (op->numArgs >= 0) {
    if (numArgs < op->numArgs) {
      error(getPos(), "Too few (%d) args to '%s' operator", numArgs, name);
      return;
    }
    if (numArgs > op->numArgs) {
#if 0
      error(getPos(), "Too many (%d) args to '%s' operator", numArgs, name);
#endif
      argPtr += numArgs - op->numArgs;
      numArgs = op->numArgs;
    }
  } else {
    if (numArgs > -op->numArgs) {
      error(getPos(), "Too many (%d) args to '%s' operator",
	    numArgs, name);
      return;
    }
  }
  for (i = 0; i < numArgs; ++i) {
    if (!checkArg(&argPtr[i], op->tchk[i])) {
      error(getPos(), "Arg #%d to '%s' operator is wrong type (%s)",
	    i, name, argPtr[i].getTypeName());
      return;
    }
  }

  // do it
  (this->*op->func)(argPtr, numArgs);
}

Operator *Gfx::findOp(char *name) {
  int a, b, m, cmp;

  a = -1;
  b = numOps;
  // invariant: opTab[a] < name < opTab[b]
  while (b - a > 1) {
    m = (a + b) / 2;
    cmp = strcmp(opTab[m].name, name);
    if (cmp < 0)
      a = m;
    else if (cmp > 0)
      b = m;
    else
      a = b = m;
  }
  if (cmp != 0)
    return NULL;
  return &opTab[a];
}

GBool Gfx::checkArg(Object *arg, TchkType type) {
  switch (type) {
  case tchkBool:   return arg->isBool();
  case tchkInt:    return arg->isInt();
  case tchkNum:    return arg->isNum();
  case tchkString: return arg->isString();
  case tchkName:   return arg->isName();
  case tchkArray:  return arg->isArray();
  case tchkProps:  return arg->isDict() || arg->isName();
  case tchkSCN:    return arg->isNum() || arg->isName();
  case tchkNone:   return gFalse;
  }
  return gFalse;
}

int Gfx::getPos() {
  return parser ? parser->getPos() : -1;
}

//------------------------------------------------------------------------
// graphics state operators
//------------------------------------------------------------------------

void Gfx::opSave(Object args[], int numArgs) {
  saveState();
}

void Gfx::opRestore(Object args[], int numArgs) {
  restoreState();
}

void Gfx::opConcat(Object args[], int numArgs) {
  state->concatCTM(args[0].getNum(), args[1].getNum(),
		   args[2].getNum(), args[3].getNum(),
		   args[4].getNum(), args[5].getNum());
  out->updateCTM(state, args[0].getNum(), args[1].getNum(),
		 args[2].getNum(), args[3].getNum(),
		 args[4].getNum(), args[5].getNum());
  fontChanged = gTrue;
}

void Gfx::opSetDash(Object args[], int numArgs) {
  Array *a;
  int length;
  Object obj;
  double *dash;
  int i;

  a = args[0].getArray();
  length = a->getLength();
  if (length == 0) {
    dash = NULL;
  } else {
    dash = (double *)gmallocn(length, sizeof(double));
    for (i = 0; i < length; ++i) {
      dash[i] = a->get(i, &obj)->getNum();
      obj.free();
    }
  }
  state->setLineDash(dash, length, args[1].getNum());
  out->updateLineDash(state);
}

void Gfx::opSetFlat(Object args[], int numArgs) {
  state->setFlatness((int)args[0].getNum());
  out->updateFlatness(state);
}

void Gfx::opSetLineJoin(Object args[], int numArgs) {
  state->setLineJoin(args[0].getInt());
  out->updateLineJoin(state);
}

void Gfx::opSetLineCap(Object args[], int numArgs) {
  state->setLineCap(args[0].getInt());
  out->updateLineCap(state);
}

void Gfx::opSetMiterLimit(Object args[], int numArgs) {
  state->setMiterLimit(args[0].getNum());
  out->updateMiterLimit(state);
}

void Gfx::opSetLineWidth(Object args[], int numArgs) {
  state->setLineWidth(args[0].getNum());
  out->updateLineWidth(state);
}

void Gfx::opSetExtGState(Object args[], int numArgs) {
  Object obj1, obj2, obj3, obj4, obj5;
  GfxBlendMode mode;
  GBool haveFillOP;
  Function *funcs[4];
  GfxColor backdropColor;
  GBool haveBackdropColor;
  GfxColorSpace *blendingColorSpace;
  GBool alpha, isolated, knockout;
  int i;

  if (!res->lookupGState(args[0].getName(), &obj1)) {
    return;
  }
  if (!obj1.isDict()) {
    error(getPos(), "ExtGState '%s' is wrong type", args[0].getName());
    obj1.free();
    return;
  }
  if (printCommands) {
    printf("  gfx state dict: ");
    obj1.print();
    printf("\n");
  }

  // transparency support: blend mode, fill/stroke opacity
  if (!obj1.dictLookup("BM", &obj2)->isNull()) {
    if (state->parseBlendMode(&obj2, &mode)) {
      state->setBlendMode(mode);
      out->updateBlendMode(state);
    } else {
      error(getPos(), "Invalid blend mode in ExtGState");
    }
  }
  obj2.free();
  if (obj1.dictLookup("ca", &obj2)->isNum()) {
    state->setFillOpacity(obj2.getNum());
    out->updateFillOpacity(state);
  }
  obj2.free();
  if (obj1.dictLookup("CA", &obj2)->isNum()) {
    state->setStrokeOpacity(obj2.getNum());
    out->updateStrokeOpacity(state);
  }
  obj2.free();

  // fill/stroke overprint
  if ((haveFillOP = (obj1.dictLookup("op", &obj2)->isBool()))) {
    state->setFillOverprint(obj2.getBool());
    out->updateFillOverprint(state);
  }
  obj2.free();
  if (obj1.dictLookup("OP", &obj2)->isBool()) {
    state->setStrokeOverprint(obj2.getBool());
    out->updateStrokeOverprint(state);
    if (!haveFillOP) {
      state->setFillOverprint(obj2.getBool());
      out->updateFillOverprint(state);
    }
  }
  obj2.free();

  // stroke adjust
  if (obj1.dictLookup("SA", &obj2)->isBool()) {
    state->setStrokeAdjust(obj2.getBool());
    out->updateStrokeAdjust(state);
  }
  obj2.free();

  // transfer function
  if (obj1.dictLookup("TR2", &obj2)->isNull()) {
    obj2.free();
    obj1.dictLookup("TR", &obj2);
  }
  if (obj2.isName("Default") ||
      obj2.isName("Identity")) {
    funcs[0] = funcs[1] = funcs[2] = funcs[3] = NULL;
    state->setTransfer(funcs);
    out->updateTransfer(state);
  } else if (obj2.isArray() && obj2.arrayGetLength() == 4) {
    for (i = 0; i < 4; ++i) {
      obj2.arrayGet(i, &obj3);
      funcs[i] = Function::parse(&obj3);
      obj3.free();
      if (!funcs[i]) {
	break;
      }
    }
    if (i == 4) {
      state->setTransfer(funcs);
      out->updateTransfer(state);
    }
  } else if (obj2.isName() || obj2.isDict() || obj2.isStream()) {
    if ((funcs[0] = Function::parse(&obj2))) {
      funcs[1] = funcs[2] = funcs[3] = NULL;
      state->setTransfer(funcs);
      out->updateTransfer(state);
    }
  } else if (!obj2.isNull()) {
    error(getPos(), "Invalid transfer function in ExtGState");
  }
  obj2.free();

  // soft mask
  if (!obj1.dictLookup("SMask", &obj2)->isNull()) {
    if (obj2.isName("None")) {
      out->clearSoftMask(state);
    } else if (obj2.isDict()) {
      if (obj2.dictLookup("S", &obj3)->isName("Alpha")) {
	alpha = gTrue;
      } else { // "Luminosity"
	alpha = gFalse;
      }
      obj3.free();
      funcs[0] = NULL;
      if (!obj2.dictLookup("TR", &obj3)->isNull()) {
	funcs[0] = Function::parse(&obj3);
	if (funcs[0]->getInputSize() != 1 ||
	    funcs[0]->getOutputSize() != 1) {
	  error(getPos(),
		"Invalid transfer function in soft mask in ExtGState");
	  delete funcs[0];
	  funcs[0] = NULL;
	}
      }
      obj3.free();
      if ((haveBackdropColor = obj2.dictLookup("BC", &obj3)->isArray())) {
	for (i = 0; i < gfxColorMaxComps; ++i) {
	  backdropColor.c[i] = 0;
	}
	for (i = 0; i < obj3.arrayGetLength() && i < gfxColorMaxComps; ++i) {
	  obj3.arrayGet(i, &obj4);
	  if (obj4.isNum()) {
	    backdropColor.c[i] = dblToCol(obj4.getNum());
	  }
	  obj4.free();
	}
      }
      obj3.free();
      if (obj2.dictLookup("G", &obj3)->isStream()) {
	if (obj3.streamGetDict()->lookup("Group", &obj4)->isDict()) {
	  blendingColorSpace = NULL;
	  isolated = knockout = gFalse;
	  if (!obj4.dictLookup("CS", &obj5)->isNull()) {
	    blendingColorSpace = GfxColorSpace::parse(&obj5);
	  }
	  obj5.free();
	  if (obj4.dictLookup("I", &obj5)->isBool()) {
	    isolated = obj5.getBool();
	  }
	  obj5.free();
	  if (obj4.dictLookup("K", &obj5)->isBool()) {
	    knockout = obj5.getBool();
	  }
	  obj5.free();
	  if (!haveBackdropColor) {
	    if (blendingColorSpace) {
	      blendingColorSpace->getDefaultColor(&backdropColor);
	    } else {
	      //~ need to get the parent or default color space (?)
	      for (i = 0; i < gfxColorMaxComps; ++i) {
		backdropColor.c[i] = 0;
	      }
	    }
	  }
	  doSoftMask(&obj3, alpha, blendingColorSpace,
		     isolated, knockout, funcs[0], &backdropColor);
	  if (funcs[0]) {
	    delete funcs[0];
	  }
	} else {
	  error(getPos(), "Invalid soft mask in ExtGState - missing group");
	}
	obj4.free();
      } else {
	error(getPos(), "Invalid soft mask in ExtGState - missing group");
      }
      obj3.free();
    } else if (!obj2.isNull()) {
      error(getPos(), "Invalid soft mask in ExtGState");
    }
  }
  obj2.free();

  obj1.free();
}

void Gfx::doSoftMask(Object *str, GBool alpha,
		     GfxColorSpace *blendingColorSpace,
		     GBool isolated, GBool knockout,
		     Function *transferFunc, GfxColor *backdropColor) {
  Dict *dict, *resDict;
  double m[6], bbox[4];
  Object obj1, obj2;
  int i;

  // check for excessive recursion
  if (formDepth > 20) {
    return;
  }

  // get stream dict
  dict = str->streamGetDict();

  // check form type
  dict->lookup("FormType", &obj1);
  if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
    error(getPos(), "Unknown form type");
  }
  obj1.free();

  // get bounding box
  dict->lookup("BBox", &obj1);
  if (!obj1.isArray()) {
    obj1.free();
    error(getPos(), "Bad form bounding box");
    return;
  }
  for (i = 0; i < 4; ++i) {
    obj1.arrayGet(i, &obj2);
    bbox[i] = obj2.getNum();
    obj2.free();
  }
  obj1.free();

  // get matrix
  dict->lookup("Matrix", &obj1);
  if (obj1.isArray()) {
    for (i = 0; i < 6; ++i) {
      obj1.arrayGet(i, &obj2);
      m[i] = obj2.getNum();
      obj2.free();
    }
  } else {
    m[0] = 1; m[1] = 0;
    m[2] = 0; m[3] = 1;
    m[4] = 0; m[5] = 0;
  }
  obj1.free();

  // get resources
  dict->lookup("Resources", &obj1);
  resDict = obj1.isDict() ? obj1.getDict() : (Dict *)NULL;

  // draw it
  ++formDepth;
  doForm1(str, resDict, m, bbox, gTrue, gTrue,
	  blendingColorSpace, isolated, knockout,
	  alpha, transferFunc, backdropColor);
  --formDepth;

  if (blendingColorSpace) {
    delete blendingColorSpace;
  }
  obj1.free();
}

void Gfx::opSetRenderingIntent(Object args[], int numArgs) {
}

//------------------------------------------------------------------------
// color operators
//------------------------------------------------------------------------

void Gfx::opSetFillGray(Object args[], int numArgs) {
  GfxColor color;

  state->setFillPattern(NULL);
  state->setFillColorSpace(new GfxDeviceGrayColorSpace());
  out->updateFillColorSpace(state);
  color.c[0] = dblToCol(args[0].getNum());
  state->setFillColor(&color);
  out->updateFillColor(state);
}

void Gfx::opSetStrokeGray(Object args[], int numArgs) {
  GfxColor color;

  state->setStrokePattern(NULL);
  state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
  out->updateStrokeColorSpace(state);
  color.c[0] = dblToCol(args[0].getNum());
  state->setStrokeColor(&color);
  out->updateStrokeColor(state);
}

void Gfx::opSetFillCMYKColor(Object args[], int numArgs) {
  GfxColor color;
  int i;

  state->setFillPattern(NULL);
  state->setFillColorSpace(new GfxDeviceCMYKColorSpace());
  out->updateFillColorSpace(state);
  for (i = 0; i < 4; ++i) {
    color.c[i] = dblToCol(args[i].getNum());
  }
  state->setFillColor(&color);
  out->updateFillColor(state);
}

void Gfx::opSetStrokeCMYKColor(Object args[], int numArgs) {
  GfxColor color;
  int i;

  state->setStrokePattern(NULL);
  state->setStrokeColorSpace(new GfxDeviceCMYKColorSpace());
  out->updateStrokeColorSpace(state);
  for (i = 0; i < 4; ++i) {
    color.c[i] = dblToCol(args[i].getNum());
  }
  state->setStrokeColor(&color);
  out->updateStrokeColor(state);
}

void Gfx::opSetFillRGBColor(Object args[], int numArgs) {
  GfxColor color;
  int i;

  state->setFillPattern(NULL);
  state->setFillColorSpace(new GfxDeviceRGBColorSpace());
  out->updateFillColorSpace(state);
  for (i = 0; i < 3; ++i) {
    color.c[i] = dblToCol(args[i].getNum());
  }
  state->setFillColor(&color);
  out->updateFillColor(state);
}

void Gfx::opSetStrokeRGBColor(Object args[], int numArgs) {
  GfxColor color;
  int i;

  state->setStrokePattern(NULL);
  state->setStrokeColorSpace(new GfxDeviceRGBColorSpace());
  out->updateStrokeColorSpace(state);
  for (i = 0; i < 3; ++i) {
    color.c[i] = dblToCol(args[i].getNum());
  }
  state->setStrokeColor(&color);
  out->updateStrokeColor(state);
}

void Gfx::opSetFillColorSpace(Object args[], int numArgs) {
  Object obj;
  GfxColorSpace *colorSpace;
  GfxColor color;

  state->setFillPattern(NULL);
  res->lookupColorSpace(args[0].getName(), &obj);
  if (obj.isNull()) {
    colorSpace = GfxColorSpace::parse(&args[0]);
  } else {
    colorSpace = GfxColorSpace::parse(&obj);
  }
  obj.free();
  if (colorSpace) {
    state->setFillColorSpace(colorSpace);
    out->updateFillColorSpace(state);
    colorSpace->getDefaultColor(&color);
    state->setFillColor(&color);
    out->updateFillColor(state);
  } else {
    error(getPos(), "Bad color space (fill)");
  }
}

void Gfx::opSetStrokeColorSpace(Object args[], int numArgs) {
  Object obj;
  GfxColorSpace *colorSpace;
  GfxColor color;

  state->setStrokePattern(NULL);
  res->lookupColorSpace(args[0].getName(), &obj);
  if (obj.isNull()) {
    colorSpace = GfxColorSpace::parse(&args[0]);
  } else {
    colorSpace = GfxColorSpace::parse(&obj);
  }
  obj.free();
  if (colorSpace) {
    state->setStrokeColorSpace(colorSpace);
    out->updateStrokeColorSpace(state);
    colorSpace->getDefaultColor(&color);
    state->setStrokeColor(&color);
    out->updateStrokeColor(state);
  } else {
    error(getPos(), "Bad color space (stroke)");
  }
}

void Gfx::opSetFillColor(Object args[], int numArgs) {
  GfxColor color;
  int i;

  if (numArgs != state->getFillColorSpace()->getNComps()) {
    error(getPos(), "Incorrect number of arguments in 'sc' command");
    return;
  }
  state->setFillPattern(NULL);
  for (i = 0; i < numArgs; ++i) {
    color.c[i] = dblToCol(args[i].getNum());
  }
  state->setFillColor(&color);
  out->updateFillColor(state);
}

void Gfx::opSetStrokeColor(Object args[], int numArgs) {
  GfxColor color;
  int i;

  if (numArgs != state->getStrokeColorSpace()->getNComps()) {
    error(getPos(), "Incorrect number of arguments in 'SC' command");
    return;
  }
  state->setStrokePattern(NULL);
  for (i = 0; i < numArgs; ++i) {
    color.c[i] = dblToCol(args[i].getNum());
  }
  state->setStrokeColor(&color);
  out->updateStrokeColor(state);
}

void Gfx::opSetFillColorN(Object args[], int numArgs) {
  GfxColor color;
  GfxPattern *pattern;
  int i;

  if (state->getFillColorSpace()->getMode() == csPattern) {
    if (numArgs > 1) {
      if (!((GfxPatternColorSpace *)state->getFillColorSpace())->getUnder() ||
	  numArgs - 1 != ((GfxPatternColorSpace *)state->getFillColorSpace())
	                     ->getUnder()->getNComps()) {
	error(getPos(), "Incorrect number of arguments in 'scn' command");
	return;
      }
      for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) {
	if (args[i].isNum()) {
	  color.c[i] = dblToCol(args[i].getNum());
	}
      }
      state->setFillColor(&color);
      out->updateFillColor(state);
    }
    if (args[numArgs-1].isName() &&
	(pattern = res->lookupPattern(args[numArgs-1].getName()))) {
      state->setFillPattern(pattern);
    }

  } else {
    if (numArgs != state->getFillColorSpace()->getNComps()) {
      error(getPos(), "Incorrect number of arguments in 'scn' command");
      return;
    }
    state->setFillPattern(NULL);
    for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) {
      if (args[i].isNum()) {
	color.c[i] = dblToCol(args[i].getNum());
      }
    }
    state->setFillColor(&color);
    out->updateFillColor(state);
  }
}

void Gfx::opSetStrokeColorN(Object args[], int numArgs) {
  GfxColor color;
  GfxPattern *pattern;
  int i;

  if (state->getStrokeColorSpace()->getMode() == csPattern) {
    if (numArgs > 1) {
      if (!((GfxPatternColorSpace *)state->getStrokeColorSpace())
	       ->getUnder() ||
	  numArgs - 1 != ((GfxPatternColorSpace *)state->getStrokeColorSpace())
	                     ->getUnder()->getNComps()) {
	error(getPos(), "Incorrect number of arguments in 'SCN' command");
	return;
      }
      for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) {
	if (args[i].isNum()) {
	  color.c[i] = dblToCol(args[i].getNum());
	}
      }
      state->setStrokeColor(&color);
      out->updateStrokeColor(state);
    }
    if (args[numArgs-1].isName() &&
	(pattern = res->lookupPattern(args[numArgs-1].getName()))) {
      state->setStrokePattern(pattern);
    }

  } else {
    if (numArgs != state->getStrokeColorSpace()->getNComps()) {
      error(getPos(), "Incorrect number of arguments in 'SCN' command");
      return;
    }
    state->setStrokePattern(NULL);
    for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) {
      if (args[i].isNum()) {
	color.c[i] = dblToCol(args[i].getNum());
      }
    }
    state->setStrokeColor(&color);
    out->updateStrokeColor(state);
  }
}

//------------------------------------------------------------------------
// path segment operators
//------------------------------------------------------------------------

void Gfx::opMoveTo(Object args[], int numArgs) {
  state->moveTo(args[0].getNum(), args[1].getNum());
}

void Gfx::opLineTo(Object args[], int numArgs) {
  if (!state->isCurPt()) {
    error(getPos(), "No current point in lineto");
    return;
  }
  state->lineTo(args[0].getNum(), args[1].getNum());
}

void Gfx::opCurveTo(Object args[], int numArgs) {
  double x1, y1, x2, y2, x3, y3;

  if (!state->isCurPt()) {
    error(getPos(), "No current point in curveto");
    return;
  }
  x1 = args[0].getNum();
  y1 = args[1].getNum();
  x2 = args[2].getNum();
  y2 = args[3].getNum();
  x3 = args[4].getNum();
  y3 = args[5].getNum();
  state->curveTo(x1, y1, x2, y2, x3, y3);
}

void Gfx::opCurveTo1(Object args[], int numArgs) {
  double x1, y1, x2, y2, x3, y3;

  if (!state->isCurPt()) {
    error(getPos(), "No current point in curveto1");
    return;
  }
  x1 = state->getCurX();
  y1 = state->getCurY();
  x2 = args[0].getNum();
  y2 = args[1].getNum();
  x3 = args[2].getNum();
  y3 = args[3].getNum();
  state->curveTo(x1, y1, x2, y2, x3, y3);
}

void Gfx::opCurveTo2(Object args[], int numArgs) {
  double x1, y1, x2, y2, x3, y3;

  if (!state->isCurPt()) {
    error(getPos(), "No current point in curveto2");
    return;
  }
  x1 = args[0].getNum();
  y1 = args[1].getNum();
  x2 = args[2].getNum();
  y2 = args[3].getNum();
  x3 = x2;
  y3 = y2;
  state->curveTo(x1, y1, x2, y2, x3, y3);
}

void Gfx::opRectangle(Object args[], int numArgs) {
  double x, y, w, h;

  x = args[0].getNum();
  y = args[1].getNum();
  w = args[2].getNum();
  h = args[3].getNum();
  state->moveTo(x, y);
  state->lineTo(x + w, y);
  state->lineTo(x + w, y + h);
  state->lineTo(x, y + h);
  state->closePath();
}

void Gfx::opClosePath(Object args[], int numArgs) {
  if (!state->isCurPt()) {
    error(getPos(), "No current point in closepath");
    return;
  }
  state->closePath();
}

//------------------------------------------------------------------------
// path painting operators
//------------------------------------------------------------------------

void Gfx::opEndPath(Object args[], int numArgs) {
  doEndPath();
}

void Gfx::opStroke(Object args[], int numArgs) {
  if (!state->isCurPt()) {
    //error(getPos(), "No path in stroke");
    return;
  }
  if (state->isPath()) {
    if (state->getStrokeColorSpace()->getMode() == csPattern) {
      doPatternStroke();
    } else {
      out->stroke(state);
    }
  }
  doEndPath();
}

void Gfx::opCloseStroke(Object args[], int numArgs) {
  if (!state->isCurPt()) {
    //error(getPos(), "No path in closepath/stroke");
    return;
  }
  if (state->isPath()) {
    state->closePath();
    if (state->getStrokeColorSpace()->getMode() == csPattern) {
      doPatternStroke();
    } else {
      out->stroke(state);
    }
  }
  doEndPath();
}

void Gfx::opFill(Object args[], int numArgs) {
  if (!state->isCurPt()) {
    //error(getPos(), "No path in fill");
    return;
  }
  if (state->isPath()) {
    if (state->getFillColorSpace()->getMode() == csPattern) {
      doPatternFill(gFalse);
    } else {
      out->fill(state);
    }
  }
  doEndPath();
}

void Gfx::opEOFill(Object args[], int numArgs) {
  if (!state->isCurPt()) {
    //error(getPos(), "No path in eofill");
    return;
  }
  if (state->isPath()) {
    if (state->getFillColorSpace()->getMode() == csPattern) {
      doPatternFill(gTrue);
    } else {
      out->eoFill(state);
    }
  }
  doEndPath();
}

void Gfx::opFillStroke(Object args[], int numArgs) {
  if (!state->isCurPt()) {
    //error(getPos(), "No path in fill/stroke");
    return;
  }
  if (state->isPath()) {
    if (state->getFillColorSpace()->getMode() == csPattern) {
      doPatternFill(gFalse);
    } else {
      out->fill(state);
    }
    if (state->getStrokeColorSpace()->getMode() == csPattern) {
      doPatternStroke();
    } else {
      out->stroke(state);
    }
  }
  doEndPath();
}

void Gfx::opCloseFillStroke(Object args[], int numArgs) {
  if (!state->isCurPt()) {
    //error(getPos(), "No path in closepath/fill/stroke");
    return;
  }
  if (state->isPath()) {
    state->closePath();
    if (state->getFillColorSpace()->getMode() == csPattern) {
      doPatternFill(gFalse);
    } else {
      out->fill(state);
    }
    if (state->getStrokeColorSpace()->getMode() == csPattern) {
      doPatternStroke();
    } else {
      out->stroke(state);
    }
  }
  doEndPath();
}

void Gfx::opEOFillStroke(Object args[], int numArgs) {
  if (!state->isCurPt()) {
    //error(getPos(), "No path in eofill/stroke");
    return;
  }
  if (state->isPath()) {
    if (state->getFillColorSpace()->getMode() == csPattern) {
      doPatternFill(gTrue);
    } else {
      out->eoFill(state);
    }
    if (state->getStrokeColorSpace()->getMode() == csPattern) {
      doPatternStroke();
    } else {
      out->stroke(state);
    }
  }
  doEndPath();
}

void Gfx::opCloseEOFillStroke(Object args[], int numArgs) {
  if (!state->isCurPt()) {
    //error(getPos(), "No path in closepath/eofill/stroke");
    return;
  }
  if (state->isPath()) {
    state->closePath();
    if (state->getFillColorSpace()->getMode() == csPattern) {
      doPatternFill(gTrue);
    } else {
      out->eoFill(state);
    }
    if (state->getStrokeColorSpace()->getMode() == csPattern) {
      doPatternStroke();
    } else {
      out->stroke(state);
    }
  }
  doEndPath();
}

void Gfx::doPatternFill(GBool eoFill) {
  GfxPattern *pattern;

  // this is a bit of a kludge -- patterns can be really slow, so we
  // skip them if we're only doing text extraction, since they almost
  // certainly don't contain any text
  if (!out->needNonText()) {
    return;
  }

  if (!(pattern = state->getFillPattern())) {
    return;
  }
  switch (pattern->getType()) {
  case 1:
    doTilingPatternFill((GfxTilingPattern *)pattern, gFalse, eoFill);
    break;
  case 2:
    doShadingPatternFill((GfxShadingPattern *)pattern, gFalse, eoFill);
    break;
  default:
    error(getPos(), "Unimplemented pattern type (%d) in fill",
	  pattern->getType());
    break;
  }
}

void Gfx::doPatternStroke() {
  GfxPattern *pattern;

  // this is a bit of a kludge -- patterns can be really slow, so we
  // skip them if we're only doing text extraction, since they almost
  // certainly don't contain any text
  if (!out->needNonText()) {
    return;
  }

  if (!(pattern = state->getStrokePattern())) {
    return;
  }
  switch (pattern->getType()) {
  case 1:
    doTilingPatternFill((GfxTilingPattern *)pattern, gTrue, gFalse);
    break;
  case 2:
    doShadingPatternFill((GfxShadingPattern *)pattern, gTrue, gFalse);
    break;
  default:
    error(getPos(), "Unimplemented pattern type (%d) in stroke",
	  pattern->getType());
    break;
  }
}

void Gfx::doTilingPatternFill(GfxTilingPattern *tPat,
			      GBool stroke, GBool eoFill) {
  GfxPatternColorSpace *patCS;
  GfxColorSpace *cs;
  GfxPath *savedPath;
  double xMin, yMin, xMax, yMax, x, y, x1, y1;
  double cxMin, cyMin, cxMax, cyMax;
  int xi0, yi0, xi1, yi1, xi, yi;
  double *ctm, *btm, *ptm;
  double m[6], ictm[6], m1[6], imb[6];
  double det;
  double xstep, ystep;
  int i;

  // get color space
  patCS = (GfxPatternColorSpace *)(stroke ? state->getStrokeColorSpace()
				          : state->getFillColorSpace());

  // construct a (pattern space) -> (current space) transform matrix
  ctm = state->getCTM();
  btm = baseMatrix;
  ptm = tPat->getMatrix();
  // iCTM = invert CTM
  det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
  ictm[0] = ctm[3] * det;
  ictm[1] = -ctm[1] * det;
  ictm[2] = -ctm[2] * det;
  ictm[3] = ctm[0] * det;
  ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
  ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
  // m1 = PTM * BTM = PTM * base transform matrix
  m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
  m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
  m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
  m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
  m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
  m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
  // m = m1 * iCTM = (PTM * BTM) * (iCTM)
  m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
  m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
  m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
  m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
  m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
  m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];

  // construct a (device space) -> (pattern space) transform matrix
  det = 1 / (m1[0] * m1[3] - m1[1] * m1[2]);
  imb[0] = m1[3] * det;
  imb[1] = -m1[1] * det;
  imb[2] = -m1[2] * det;
  imb[3] = m1[0] * det;
  imb[4] = (m1[2] * m1[5] - m1[3] * m1[4]) * det;
  imb[5] = (m1[1] * m1[4] - m1[0] * m1[5]) * det;

  // save current graphics state
  savedPath = state->getPath()->copy();
  saveState();

  // set underlying color space (for uncolored tiling patterns); set
  // various other parameters (stroke color, line width) to match
  // Adobe's behavior
  if (tPat->getPaintType() == 2 && (cs = patCS->getUnder())) {
    state->setFillColorSpace(cs->copy());
    out->updateFillColorSpace(state);
    state->setStrokeColorSpace(cs->copy());
    out->updateStrokeColorSpace(state);
    state->setStrokeColor(state->getFillColor());
  } else {
    state->setFillColorSpace(new GfxDeviceGrayColorSpace());
    out->updateFillColorSpace(state);
    state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
    out->updateStrokeColorSpace(state);
  }
  state->setFillPattern(NULL);
  out->updateFillColor(state);
  state->setStrokePattern(NULL);
  out->updateStrokeColor(state);
  if (!stroke) {
    state->setLineWidth(0);
    out->updateLineWidth(state);
  }

  // clip to current path
  if (stroke) {
    state->clipToStrokePath();
    out->clipToStrokePath(state);
  } else {
    state->clip();
    if (eoFill) {
      out->eoClip(state);
    } else {
      out->clip(state);
    }
  }
  state->clearPath();

  // get the clip region, check for empty
  state->getClipBBox(&cxMin, &cyMin, &cxMax, &cyMax);
  if (cxMin > cxMax || cyMin > cyMax) {
    goto err;
  }

  // transform clip region bbox to pattern space
  xMin = xMax = cxMin * imb[0] + cyMin * imb[2] + imb[4];
  yMin = yMax = cxMin * imb[1] + cyMin * imb[3] + imb[5];
  x1 = cxMin * imb[0] + cyMax * imb[2] + imb[4];
  y1 = cxMin * imb[1] + cyMax * imb[3] + imb[5];
  if (x1 < xMin) {
    xMin = x1;
  } else if (x1 > xMax) {
    xMax = x1;
  }
  if (y1 < yMin) {
    yMin = y1;
  } else if (y1 > yMax) {
    yMax = y1;
  }
  x1 = cxMax * imb[0] + cyMin * imb[2] + imb[4];
  y1 = cxMax * imb[1] + cyMin * imb[3] + imb[5];
  if (x1 < xMin) {
    xMin = x1;
  } else if (x1 > xMax) {
    xMax = x1;
  }
  if (y1 < yMin) {
    yMin = y1;
  } else if (y1 > yMax) {
    yMax = y1;
  }
  x1 = cxMax * imb[0] + cyMax * imb[2] + imb[4];
  y1 = cxMax * imb[1] + cyMax * imb[3] + imb[5];
  if (x1 < xMin) {
    xMin = x1;
  } else if (x1 > xMax) {
    xMax = x1;
  }
  if (y1 < yMin) {
    yMin = y1;
  } else if (y1 > yMax) {
    yMax = y1;
  }

  // draw the pattern
  //~ this should treat negative steps differently -- start at right/top
  //~ edge instead of left/bottom (?)
  xstep = fabs(tPat->getXStep());
  ystep = fabs(tPat->getYStep());
  xi0 = (int)ceil((xMin - tPat->getBBox()[2]) / xstep);
  xi1 = (int)floor((xMax - tPat->getBBox()[0]) / xstep) + 1;
  yi0 = (int)ceil((yMin - tPat->getBBox()[3]) / ystep);
  yi1 = (int)floor((yMax - tPat->getBBox()[1]) / ystep) + 1;
  for (i = 0; i < 4; ++i) {
    m1[i] = m[i];
  }
  if (out->useTilingPatternFill()) {
    m1[4] = m[4];
    m1[5] = m[5];
    out->tilingPatternFill(state, tPat->getContentStream(),
			   tPat->getPaintType(), tPat->getResDict(),
			   m1, tPat->getBBox(),
			   xi0, yi0, xi1, yi1, xstep, ystep);
  } else {
    for (yi = yi0; yi < yi1; ++yi) {
      for (xi = xi0; xi < xi1; ++xi) {
	x = xi * xstep;
	y = yi * ystep;
	m1[4] = x * m[0] + y * m[2] + m[4];
	m1[5] = x * m[1] + y * m[3] + m[5];
	doForm1(tPat->getContentStream(), tPat->getResDict(),
		m1, tPat->getBBox());
      }
    }
  }

  // restore graphics state
 err:
  restoreState();
  state->setPath(savedPath);
}

void Gfx::doShadingPatternFill(GfxShadingPattern *sPat,
			       GBool stroke, GBool eoFill) {
  GfxShading *shading;
  GfxPath *savedPath;
  double *ctm, *btm, *ptm;
  double m[6], ictm[6], m1[6];
  double xMin, yMin, xMax, yMax;
  double det;

  shading = sPat->getShading();

  // save current graphics state
  savedPath = state->getPath()->copy();
  saveState();

  // clip to bbox
  if (shading->getHasBBox()) {
    shading->getBBox(&xMin, &yMin, &xMax, &yMax);
    state->moveTo(xMin, yMin);
    state->lineTo(xMax, yMin);
    state->lineTo(xMax, yMax);
    state->lineTo(xMin, yMax);
    state->closePath();
    state->clip();
    out->clip(state);
    state->setPath(savedPath->copy());
  }

  // clip to current path
  if (stroke) {
    state->clipToStrokePath();
    out->clipToStrokePath(state);
  } else {
    state->clip();
    if (eoFill) {
      out->eoClip(state);
    } else {
      out->clip(state);
    }
  }

  // set the color space
  state->setFillColorSpace(shading->getColorSpace()->copy());
  out->updateFillColorSpace(state);

  // background color fill
  if (shading->getHasBackground()) {
    state->setFillColor(shading->getBackground());
    out->updateFillColor(state);
    out->fill(state);
  }
  state->clearPath();

  // construct a (pattern space) -> (current space) transform matrix
  ctm = state->getCTM();
  btm = baseMatrix;
  ptm = sPat->getMatrix();
  // iCTM = invert CTM
  det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
  ictm[0] = ctm[3] * det;
  ictm[1] = -ctm[1] * det;
  ictm[2] = -ctm[2] * det;
  ictm[3] = ctm[0] * det;
  ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
  ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
  // m1 = PTM * BTM = PTM * base transform matrix
  m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
  m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
  m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
  m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
  m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
  m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
  // m = m1 * iCTM = (PTM * BTM) * (iCTM)
  m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
  m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
  m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
  m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
  m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
  m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];

  // set the new matrix
  state->concatCTM(m[0], m[1], m[2], m[3], m[4], m[5]);
  out->updateCTM(state, m[0], m[1], m[2], m[3], m[4], m[5]);

#if 1 //~tmp: turn off anti-aliasing temporarily
  GBool vaa = out->getVectorAntialias();
  if (vaa) {
    out->setVectorAntialias(gFalse);
  }
#endif

  // do shading type-specific operations
  switch (shading->getType()) {
  case 1:
    doFunctionShFill((GfxFunctionShading *)shading);
    break;
  case 2:
    doAxialShFill((GfxAxialShading *)shading);
    break;
  case 3:
    doRadialShFill((GfxRadialShading *)shading);
    break;
  case 4:
  case 5:
    doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
    break;
  case 6:
  case 7:
    doPatchMeshShFill((GfxPatchMeshShading *)shading);
    break;
  }

#if 1 //~tmp: turn off anti-aliasing temporarily
  if (vaa) {
    out->setVectorAntialias(gTrue);
  }
#endif

  // restore graphics state
  restoreState();
  state->setPath(savedPath);
}

void Gfx::opShFill(Object args[], int numArgs) {
  GfxShading *shading;
  GfxPath *savedPath;
  double xMin, yMin, xMax, yMax;

  if (!(shading = res->lookupShading(args[0].getName()))) {
    return;
  }

  // save current graphics state
  savedPath = state->getPath()->copy();
  saveState();

  // clip to bbox
  if (shading->getHasBBox()) {
    shading->getBBox(&xMin, &yMin, &xMax, &yMax);
    state->moveTo(xMin, yMin);
    state->lineTo(xMax, yMin);
    state->lineTo(xMax, yMax);
    state->lineTo(xMin, yMax);
    state->closePath();
    state->clip();
    out->clip(state);
    state->clearPath();
  }

  // set the color space
  state->setFillColorSpace(shading->getColorSpace()->copy());
  out->updateFillColorSpace(state);

#if 1 //~tmp: turn off anti-aliasing temporarily
  GBool vaa = out->getVectorAntialias();
  if (vaa) {
    out->setVectorAntialias(gFalse);
  }
#endif

  // do shading type-specific operations
  switch (shading->getType()) {
  case 1:
    doFunctionShFill((GfxFunctionShading *)shading);
    break;
  case 2:
    doAxialShFill((GfxAxialShading *)shading);
    break;
  case 3:
    doRadialShFill((GfxRadialShading *)shading);
    break;
  case 4:
  case 5:
    doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
    break;
  case 6:
  case 7:
    doPatchMeshShFill((GfxPatchMeshShading *)shading);
    break;
  }

#if 1 //~tmp: turn off anti-aliasing temporarily
  if (vaa) {
    out->setVectorAntialias(gTrue);
  }
#endif

  // restore graphics state
  restoreState();
  state->setPath(savedPath);

  delete shading;
}

void Gfx::doFunctionShFill(GfxFunctionShading *shading) {
  double x0, y0, x1, y1;
  GfxColor colors[4];

  if (out->useShadedFills() &&
      out->functionShadedFill(state, shading)) {
    return;
  }

  shading->getDomain(&x0, &y0, &x1, &y1);
  shading->getColor(x0, y0, &colors[0]);
  shading->getColor(x0, y1, &colors[1]);
  shading->getColor(x1, y0, &colors[2]);
  shading->getColor(x1, y1, &colors[3]);
  doFunctionShFill1(shading, x0, y0, x1, y1, colors, 0);
}

void Gfx::doFunctionShFill1(GfxFunctionShading *shading,
			    double x0, double y0,
			    double x1, double y1,
			    GfxColor *colors, int depth) {
  GfxColor fillColor;
  GfxColor color0M, color1M, colorM0, colorM1, colorMM;
  GfxColor colors2[4];
  double *matrix;
  double xM, yM;
  int nComps, i, j;

  nComps = shading->getColorSpace()->getNComps();
  matrix = shading->getMatrix();

  // compare the four corner colors
  for (i = 0; i < 4; ++i) {
    for (j = 0; j < nComps; ++j) {
      if (abs(colors[i].c[j] - colors[(i+1)&3].c[j]) > functionColorDelta) {
	break;
      }
    }
    if (j < nComps) {
      break;
    }
  }

  // center of the rectangle
  xM = 0.5 * (x0 + x1);
  yM = 0.5 * (y0 + y1);

  // the four corner colors are close (or we hit the recursive limit)
  // -- fill the rectangle; but require at least one subdivision
  // (depth==0) to avoid problems when the four outer corners of the
  // shaded region are the same color
  if ((i == 4 && depth > 0) || depth == functionMaxDepth) {

    // use the center color
    shading->getColor(xM, yM, &fillColor);
    state->setFillColor(&fillColor);
    out->updateFillColor(state);

    // fill the rectangle
    state->moveTo(x0 * matrix[0] + y0 * matrix[2] + matrix[4],
		  x0 * matrix[1] + y0 * matrix[3] + matrix[5]);
    state->lineTo(x1 * matrix[0] + y0 * matrix[2] + matrix[4],
		  x1 * matrix[1] + y0 * matrix[3] + matrix[5]);
    state->lineTo(x1 * matrix[0] + y1 * matrix[2] + matrix[4],
		  x1 * matrix[1] + y1 * matrix[3] + matrix[5]);
    state->lineTo(x0 * matrix[0] + y1 * matrix[2] + matrix[4],
		  x0 * matrix[1] + y1 * matrix[3] + matrix[5]);
    state->closePath();
    out->fill(state);
    state->clearPath();

  // the four corner colors are not close enough -- subdivide the
  // rectangle
  } else {

    // colors[0]       colorM0       colors[2]
    //   (x0,y0)       (xM,y0)       (x1,y0)
    //         +----------+----------+
    //         |          |          |
    //         |    UL    |    UR    |
    // color0M |       colorMM       | color1M
    // (x0,yM) +----------+----------+ (x1,yM)
    //         |       (xM,yM)       |
    //         |    LL    |    LR    |
    //         |          |          |
    //         +----------+----------+
    // colors[1]       colorM1       colors[3]
    //   (x0,y1)       (xM,y1)       (x1,y1)

    shading->getColor(x0, yM, &color0M);
    shading->getColor(x1, yM, &color1M);
    shading->getColor(xM, y0, &colorM0);
    shading->getColor(xM, y1, &colorM1);
    shading->getColor(xM, yM, &colorMM);

    // upper-left sub-rectangle
    colors2[0] = colors[0];
    colors2[1] = color0M;
    colors2[2] = colorM0;
    colors2[3] = colorMM;
    doFunctionShFill1(shading, x0, y0, xM, yM, colors2, depth + 1);
    
    // lower-left sub-rectangle
    colors2[0] = color0M;
    colors2[1] = colors[1];
    colors2[2] = colorMM;
    colors2[3] = colorM1;
    doFunctionShFill1(shading, x0, yM, xM, y1, colors2, depth + 1);
    
    // upper-right sub-rectangle
    colors2[0] = colorM0;
    colors2[1] = colorMM;
    colors2[2] = colors[2];
    colors2[3] = color1M;
    doFunctionShFill1(shading, xM, y0, x1, yM, colors2, depth + 1);

    // lower-right sub-rectangle
    colors2[0] = colorMM;
    colors2[1] = colorM1;
    colors2[2] = color1M;
    colors2[3] = colors[3];
    doFunctionShFill1(shading, xM, yM, x1, y1, colors2, depth + 1);
  }
}

void Gfx::doAxialShFill(GfxAxialShading *shading) {
  double xMin, yMin, xMax, yMax;
  double x0, y0, x1, y1;
  double dx, dy, mul;
  GBool dxZero, dyZero;
  double tMin, tMax, t, tx, ty;
  double s[4], sMin, sMax, tmp;
  double ux0, uy0, ux1, uy1, vx0, vy0, vx1, vy1;
  double t0, t1, tt;
  double ta[axialMaxSplits + 1];
  int next[axialMaxSplits + 1];
  GfxColor color0, color1;
  int nComps;
  int i, j, k, kk;

  if (out->useShadedFills() &&
      out->axialShadedFill(state, shading)) {
    return;
  }

  // get the clip region bbox
  state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);

  // compute min and max t values, based on the four corners of the
  // clip region bbox
  shading->getCoords(&x0, &y0, &x1, &y1);
  dx = x1 - x0;
  dy = y1 - y0;
  dxZero = fabs(dx) < 0.01;
  dyZero = fabs(dy) < 0.01;
  if (dxZero && dyZero) {
    tMin = tMax = 0;
  } else {
    mul = 1 / (dx * dx + dy * dy);
    tMin = tMax = ((xMin - x0) * dx + (yMin - y0) * dy) * mul;
    t = ((xMin - x0) * dx + (yMax - y0) * dy) * mul;
    if (t < tMin) {
      tMin = t;
    } else if (t > tMax) {
      tMax = t;
    }
    t = ((xMax - x0) * dx + (yMin - y0) * dy) * mul;
    if (t < tMin) {
      tMin = t;
    } else if (t > tMax) {
      tMax = t;
    }
    t = ((xMax - x0) * dx + (yMax - y0) * dy) * mul;
    if (t < tMin) {
      tMin = t;
    } else if (t > tMax) {
      tMax = t;
    }
    if (tMin < 0 && !shading->getExtend0()) {
      tMin = 0;
    }
    if (tMax > 1 && !shading->getExtend1()) {
      tMax = 1;
    }
  }

  // get the function domain
  t0 = shading->getDomain0();
  t1 = shading->getDomain1();

  // Traverse the t axis and do the shading.
  //
  // For each point (tx, ty) on the t axis, consider a line through
  // that point perpendicular to the t axis:
  //
  //     x(s) = tx + s * -dy   -->   s = (x - tx) / -dy
  //     y(s) = ty + s * dx    -->   s = (y - ty) / dx
  //
  // Then look at the intersection of this line with the bounding box
  // (xMin, yMin, xMax, yMax).  In the general case, there are four
  // intersection points:
  //
  //     s0 = (xMin - tx) / -dy
  //     s1 = (xMax - tx) / -dy
  //     s2 = (yMin - ty) / dx
  //     s3 = (yMax - ty) / dx
  //
  // and we want the middle two s values.
  //
  // In the case where dx = 0, take s0 and s1; in the case where dy =
  // 0, take s2 and s3.
  //
  // Each filled polygon is bounded by two of these line segments
  // perpdendicular to the t axis.
  //
  // The t axis is bisected into smaller regions until the color
  // difference across a region is small enough, and then the region
  // is painted with a single color.

  // set up: require at least one split to avoid problems when the two
  // ends of the t axis have the same color
  nComps = shading->getColorSpace()->getNComps();
  ta[0] = tMin;
  next[0] = axialMaxSplits / 2;
  ta[axialMaxSplits / 2] = 0.5 * (tMin + tMax);
  next[axialMaxSplits / 2] = axialMaxSplits;
  ta[axialMaxSplits] = tMax;

  // compute the color at t = tMin
  if (tMin < 0) {
    tt = t0;
  } else if (tMin > 1) {
    tt = t1;
  } else {
    tt = t0 + (t1 - t0) * tMin;
  }
  shading->getColor(tt, &color0);

  // compute the coordinates of the point on the t axis at t = tMin;
  // then compute the intersection of the perpendicular line with the
  // bounding box
  tx = x0 + tMin * dx;
  ty = y0 + tMin * dy;
  if (dxZero && dyZero) {
    sMin = sMax = 0;
  } else if (dxZero) {
    sMin = (xMin - tx) / -dy;
    sMax = (xMax - tx) / -dy;
    if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
  } else if (dyZero) {
    sMin = (yMin - ty) / dx;
    sMax = (yMax - ty) / dx;
    if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
  } else {
    s[0] = (yMin - ty) / dx;
    s[1] = (yMax - ty) / dx;
    s[2] = (xMin - tx) / -dy;
    s[3] = (xMax - tx) / -dy;
    for (j = 0; j < 3; ++j) {
      kk = j;
      for (k = j + 1; k < 4; ++k) {
	if (s[k] < s[kk]) {
	  kk = k;
	}
      }
      tmp = s[j]; s[j] = s[kk]; s[kk] = tmp;
    }
    sMin = s[1];
    sMax = s[2];
  }
  ux0 = tx - sMin * dy;
  uy0 = ty + sMin * dx;
  vx0 = tx - sMax * dy;
  vy0 = ty + sMax * dx;

  i = 0;
  while (i < axialMaxSplits) {

    // bisect until color difference is small enough or we hit the
    // bisection limit
    j = next[i];
    while (j > i + 1) {
      if (ta[j] < 0) {
	tt = t0;
      } else if (ta[j] > 1) {
	tt = t1;
      } else {
	tt = t0 + (t1 - t0) * ta[j];
      }
      shading->getColor(tt, &color1);
      for (k = 0; k < nComps; ++k) {
	if (abs(color1.c[k] - color0.c[k]) > axialColorDelta) {
	  break;
	}
      }
      if (k == nComps) {
	break;
      }
      k = (i + j) / 2;
      ta[k] = 0.5 * (ta[i] + ta[j]);
      next[i] = k;
      next[k] = j;
      j = k;
    }

    // use the average of the colors of the two sides of the region
    for (k = 0; k < nComps; ++k) {
      color0.c[k] = (color0.c[k] + color1.c[k]) / 2;
    }

    // compute the coordinates of the point on the t axis; then
    // compute the intersection of the perpendicular line with the
    // bounding box
    tx = x0 + ta[j] * dx;
    ty = y0 + ta[j] * dy;
    if (dxZero && dyZero) {
      sMin = sMax = 0;
    } else if (dxZero) {
      sMin = (xMin - tx) / -dy;
      sMax = (xMax - tx) / -dy;
      if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
    } else if (dyZero) {
      sMin = (yMin - ty) / dx;
      sMax = (yMax - ty) / dx;
      if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
    } else {
      s[0] = (yMin - ty) / dx;
      s[1] = (yMax - ty) / dx;
      s[2] = (xMin - tx) / -dy;
      s[3] = (xMax - tx) / -dy;
      for (j = 0; j < 3; ++j) {
	kk = j;
	for (k = j + 1; k < 4; ++k) {
	  if (s[k] < s[kk]) {
	    kk = k;
	  }
	}
	tmp = s[j]; s[j] = s[kk]; s[kk] = tmp;
      }
      sMin = s[1];
      sMax = s[2];
    }
    ux1 = tx - sMin * dy;
    uy1 = ty + sMin * dx;
    vx1 = tx - sMax * dy;
    vy1 = ty + sMax * dx;

    // set the color
    state->setFillColor(&color0);
    out->updateFillColor(state);

    // fill the region
    state->moveTo(ux0, uy0);
    state->lineTo(vx0, vy0);
    state->lineTo(vx1, vy1);
    state->lineTo(ux1, uy1);
    state->closePath();
    out->fill(state);
    state->clearPath();

    // set up for next region
    ux0 = ux1;
    uy0 = uy1;
    vx0 = vx1;
    vy0 = vy1;
    color0 = color1;
    i = next[i];
  }
}

void Gfx::doRadialShFill(GfxRadialShading *shading) {
  double xMin, yMin, xMax, yMax;
  double x0, y0, r0, x1, y1, r1, t0, t1;
  int nComps;
  GfxColor colorA, colorB;
  double xa, ya, xb, yb, ra, rb;
  double ta, tb, sa, sb;
  double sz, xz, yz, sMin, sMax;
  GBool enclosed;
  int ia, ib, k, n;
  double *ctm;
  double theta, alpha, angle, t;

  if (out->useShadedFills() &&
      out->radialShadedFill(state, shading)) {
    return;
  }

  // get the shading info
  shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1);
  t0 = shading->getDomain0();
  t1 = shading->getDomain1();
  nComps = shading->getColorSpace()->getNComps();

  // Compute the point at which r(s) = 0; check for the enclosed
  // circles case; and compute the angles for the tangent lines.
  if (x0 == x1 && y0 == y1) {
    enclosed = gTrue;
    theta = 0; // make gcc happy
    sz = 0; // make gcc happy
  } else if (r0 == r1) {
    enclosed = gFalse;
    theta = 0;
    sz = 0; // make gcc happy
  } else {
    sz = -r0 / (r1 - r0);
    xz = x0 + sz * (x1 - x0);
    yz = y0 + sz * (y1 - y0);
    enclosed = (xz - x0) * (xz - x0) + (yz - y0) * (yz - y0) <= r0 * r0;
    theta = asin(r0 / sqrt((x0 - xz) * (x0 - xz) + (y0 - yz) * (y0 - yz)));
    if (r0 > r1) {
      theta = -theta;
    }
  }
  if (enclosed) {
    alpha = 0;
  } else {
    alpha = atan2(y1 - y0, x1 - x0);
  }

  // compute the (possibly extended) s range
  state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
  if (enclosed) {
    sMin = 0;
    sMax = 1;
  } else {
    sMin = 1;
    sMax = 0;
    // solve for x(s) + r(s) = xMin
    if ((x1 + r1) - (x0 + r0) != 0) {
      sa = (xMin - (x0 + r0)) / ((x1 + r1) - (x0 + r0));
      if (sa < sMin) {
	sMin = sa;
      } else if (sa > sMax) {
	sMax = sa;
      }
    }
    // solve for x(s) - r(s) = xMax
    if ((x1 - r1) - (x0 - r0) != 0) {
      sa = (xMax - (x0 - r0)) / ((x1 - r1) - (x0 - r0));
      if (sa < sMin) {
	sMin = sa;
      } else if (sa > sMax) {
	sMax = sa;
      }
    }
    // solve for y(s) + r(s) = yMin
    if ((y1 + r1) - (y0 + r0) != 0) {
      sa = (yMin - (y0 + r0)) / ((y1 + r1) - (y0 + r0));
      if (sa < sMin) {
	sMin = sa;
      } else if (sa > sMax) {
	sMax = sa;
      }
    }
    // solve for y(s) - r(s) = yMax
    if ((y1 - r1) - (y0 - r0) != 0) {
      sa = (yMax - (y0 - r0)) / ((y1 - r1) - (y0 - r0));
      if (sa < sMin) {
	sMin = sa;
      } else if (sa > sMax) {
	sMax = sa;
      }
    }
    // check against sz
    if (r0 < r1) {
      if (sMin < sz) {
	sMin = sz;
      }
    } else if (r0 > r1) {
      if (sMax > sz) {
	sMax = sz;
      }
    }
    // check the 'extend' flags
    if (!shading->getExtend0() && sMin < 0) {
      sMin = 0;
    }
    if (!shading->getExtend1() && sMax > 1) {
      sMax = 1;
    }
  }

  // compute the number of steps into which circles must be divided to
  // achieve a curve flatness of 0.1 pixel in device space for the
  // largest circle (note that "device space" is 72 dpi when generating
  // PostScript, hence the relatively small 0.1 pixel accuracy)
  ctm = state->getCTM();
  t = fabs(ctm[0]);
  if (fabs(ctm[1]) > t) {
    t = fabs(ctm[1]);
  }
  if (fabs(ctm[2]) > t) {
    t = fabs(ctm[2]);
  }
  if (fabs(ctm[3]) > t) {
    t = fabs(ctm[3]);
  }
  if (r0 > r1) {
    t *= r0;
  } else {
    t *= r1;
  }
  if (t < 1) {
    n = 3;
  } else {
    n = (int)(M_PI / acos(1 - 0.1 / t));
    if (n < 3) {
      n = 3;
    } else if (n > 200) {
      n = 200;
    }
  }

  // setup for the start circle
  ia = 0;
  sa = sMin;
  ta = t0 + sa * (t1 - t0);
  xa = x0 + sa * (x1 - x0);
  ya = y0 + sa * (y1 - y0);
  ra = r0 + sa * (r1 - r0);
  if (ta < t0) {
    shading->getColor(t0, &colorA);
  } else if (ta > t1) {
    shading->getColor(t1, &colorA);
  } else {
    shading->getColor(ta, &colorA);
  }

  // fill the circles
  while (ia < radialMaxSplits) {

    // go as far along the t axis (toward t1) as we can, such that the
    // color difference is within the tolerance (radialColorDelta) --
    // this uses bisection (between the current value, t, and t1),
    // limited to radialMaxSplits points along the t axis; require at
    // least one split to avoid problems when the innermost and
    // outermost colors are the same
    ib = radialMaxSplits;
    sb = sMax;
    tb = t0 + sb * (t1 - t0);
    if (tb < t0) {
      shading->getColor(t0, &colorB);
    } else if (tb > t1) {
      shading->getColor(t1, &colorB);
    } else {
      shading->getColor(tb, &colorB);
    }
    while (ib - ia > 1) {
      for (k = 0; k < nComps; ++k) {
	if (abs(colorB.c[k] - colorA.c[k]) > radialColorDelta) {
	  break;
	}
      }
      if (k == nComps && ib < radialMaxSplits) {
	break;
      }
      ib = (ia + ib) / 2;
      sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin);
      tb = t0 + sb * (t1 - t0);
      if (tb < t0) {
	shading->getColor(t0, &colorB);
      } else if (tb > t1) {
	shading->getColor(t1, &colorB);
      } else {
	shading->getColor(tb, &colorB);
      }
    }

    // compute center and radius of the circle
    xb = x0 + sb * (x1 - x0);
    yb = y0 + sb * (y1 - y0);
    rb = r0 + sb * (r1 - r0);

    // use the average of the colors at the two circles
    for (k = 0; k < nComps; ++k) {
      colorA.c[k] = (colorA.c[k] + colorB.c[k]) / 2;
    }
    state->setFillColor(&colorA);
    out->updateFillColor(state);

    if (enclosed) {

      // construct path for first circle (counterclockwise)
      state->moveTo(xa + ra, ya);
      for (k = 1; k < n; ++k) {
	angle = ((double)k / (double)n) * 2 * M_PI;
	state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
      }
      state->closePath();

      // construct and append path for second circle (clockwise)
      state->moveTo(xb + rb, yb);
      for (k = 1; k < n; ++k) {
	angle = -((double)k / (double)n) * 2 * M_PI;
	state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle));
      }
      state->closePath();

    } else {

      // construct the first subpath (clockwise)
      state->moveTo(xa + ra * cos(alpha + theta + 0.5 * M_PI),
		    ya + ra * sin(alpha + theta + 0.5 * M_PI));
      for (k = 0; k < n; ++k) {
	angle = alpha + theta + 0.5 * M_PI
	  - ((double)k / (double)n) * (2 * theta + M_PI);
	state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle));
      }
      for (k = 0; k < n; ++k) {
	angle = alpha - theta - 0.5 * M_PI
	  + ((double)k / (double)n) * (2 * theta - M_PI);
	state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
      }
      state->closePath();

      // construct the second subpath (counterclockwise)
      state->moveTo(xa + ra * cos(alpha + theta + 0.5 * M_PI),
		    ya + ra * sin(alpha + theta + 0.5 * M_PI));
      for (k = 0; k < n; ++k) {
	angle = alpha + theta + 0.5 * M_PI
	        + ((double)k / (double)n) * (-2 * theta + M_PI);
	state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle));
      }
      for (k = 0; k < n; ++k) {
	angle = alpha - theta - 0.5 * M_PI
	        + ((double)k / (double)n) * (2 * theta + M_PI);
	state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
      }
      state->closePath();
    }

    // fill the path
    out->fill(state);
    state->clearPath();

    // step to the next value of t
    ia = ib;
    sa = sb;
    ta = tb;
    xa = xb;
    ya = yb;
    ra = rb;
    colorA = colorB;
  }

  if (enclosed) {
    // extend the smaller circle
    if ((shading->getExtend0() && r0 <= r1) ||
	(shading->getExtend1() && r1 < r0)) {
      if (r0 <= r1) {
	ta = t0;
	ra = r0;
	xa = x0;
	ya = y0;
      } else {
	ta = t1;
	ra = r1;
	xa = x1;
	ya = y1;
      }
      shading->getColor(ta, &colorA);
      state->setFillColor(&colorA);
      out->updateFillColor(state);
      state->moveTo(xa + ra, ya);
      for (k = 1; k < n; ++k) {
	angle = ((double)k / (double)n) * 2 * M_PI;
	state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
      }
      state->closePath();
      out->fill(state);
      state->clearPath();
    }

    // extend the larger circle
    if ((shading->getExtend0() && r0 > r1) ||
	(shading->getExtend1() && r1 >= r0)) {
      if (r0 > r1) {
	ta = t0;
	ra = r0;
	xa = x0;
	ya = y0;
      } else {
	ta = t1;
	ra = r1;
	xa = x1;
	ya = y1;
      }
      shading->getColor(ta, &colorA);
      state->setFillColor(&colorA);
      out->updateFillColor(state);
      state->moveTo(xMin, yMin);
      state->lineTo(xMin, yMax);
      state->lineTo(xMax, yMax);
      state->lineTo(xMax, yMin);
      state->closePath();
      state->moveTo(xa + ra, ya);
      for (k = 1; k < n; ++k) {
	angle = ((double)k / (double)n) * 2 * M_PI;
	state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
      }
      state->closePath();
      out->fill(state);
      state->clearPath();
    }
  }
}

void Gfx::doGouraudTriangleShFill(GfxGouraudTriangleShading *shading) {
  double x0, y0, x1, y1, x2, y2;
  GfxColor color0, color1, color2;
  int i;

  for (i = 0; i < shading->getNTriangles(); ++i) {
    shading->getTriangle(i, &x0, &y0, &color0,
			 &x1, &y1, &color1,
			 &x2, &y2, &color2);
    gouraudFillTriangle(x0, y0, &color0, x1, y1, &color1, x2, y2, &color2,
			shading->getColorSpace()->getNComps(), 0);
  }
}

void Gfx::gouraudFillTriangle(double x0, double y0, GfxColor *color0,
			      double x1, double y1, GfxColor *color1,
			      double x2, double y2, GfxColor *color2,
			      int nComps, int depth) {
  double x01, y01, x12, y12, x20, y20;
  GfxColor color01, color12, color20;
  int i;

  for (i = 0; i < nComps; ++i) {
    if (abs(color0->c[i] - color1->c[i]) > gouraudColorDelta ||
	abs(color1->c[i] - color2->c[i]) > gouraudColorDelta) {
      break;
    }
  }
  if (i == nComps || depth == gouraudMaxDepth) {
    state->setFillColor(color0);
    out->updateFillColor(state);
    state->moveTo(x0, y0);
    state->lineTo(x1, y1);
    state->lineTo(x2, y2);
    state->closePath();
    out->fill(state);
    state->clearPath();
  } else {
    x01 = 0.5 * (x0 + x1);
    y01 = 0.5 * (y0 + y1);
    x12 = 0.5 * (x1 + x2);
    y12 = 0.5 * (y1 + y2);
    x20 = 0.5 * (x2 + x0);
    y20 = 0.5 * (y2 + y0);
    //~ if the shading has a Function, this should interpolate on the
    //~ function parameter, not on the color components
    for (i = 0; i < nComps; ++i) {
      color01.c[i] = (color0->c[i] + color1->c[i]) / 2;
      color12.c[i] = (color1->c[i] + color2->c[i]) / 2;
      color20.c[i] = (color2->c[i] + color0->c[i]) / 2;
    }
    gouraudFillTriangle(x0, y0, color0, x01, y01, &color01,
			x20, y20, &color20, nComps, depth + 1);
    gouraudFillTriangle(x01, y01, &color01, x1, y1, color1,
			x12, y12, &color12, nComps, depth + 1);
    gouraudFillTriangle(x01, y01, &color01, x12, y12, &color12,
			x20, y20, &color20, nComps, depth + 1);
    gouraudFillTriangle(x20, y20, &color20, x12, y12, &color12,
			x2, y2, color2, nComps, depth + 1);
  }
}

void Gfx::doPatchMeshShFill(GfxPatchMeshShading *shading) {
  int start, i;

  if (shading->getNPatches() > 128) {
    start = 3;
  } else if (shading->getNPatches() > 64) {
    start = 2;
  } else if (shading->getNPatches() > 16) {
    start = 1;
  } else {
    start = 0;
  }
  for (i = 0; i < shading->getNPatches(); ++i) {
    fillPatch(shading->getPatch(i), shading->getColorSpace()->getNComps(),
	      start);
  }
}

void Gfx::fillPatch(GfxPatch *patch, int nComps, int depth) {
  GfxPatch patch00, patch01, patch10, patch11;
  double xx[4][8], yy[4][8];
  double xxm, yym;
  int i;

  for (i = 0; i < nComps; ++i) {
    if (abs(patch->color[0][0].c[i] - patch->color[0][1].c[i])
	  > patchColorDelta ||
	abs(patch->color[0][1].c[i] - patch->color[1][1].c[i])
	  > patchColorDelta ||
	abs(patch->color[1][1].c[i] - patch->color[1][0].c[i])
	  > patchColorDelta ||
	abs(patch->color[1][0].c[i] - patch->color[0][0].c[i])
	  > patchColorDelta) {
      break;
    }
  }
  if (i == nComps || depth == patchMaxDepth) {
    state->setFillColor(&patch->color[0][0]);
    out->updateFillColor(state);
    state->moveTo(patch->x[0][0], patch->y[0][0]);
    state->curveTo(patch->x[0][1], patch->y[0][1],
		   patch->x[0][2], patch->y[0][2],
		   patch->x[0][3], patch->y[0][3]);
    state->curveTo(patch->x[1][3], patch->y[1][3],
		   patch->x[2][3], patch->y[2][3],
		   patch->x[3][3], patch->y[3][3]);
    state->curveTo(patch->x[3][2], patch->y[3][2],
		   patch->x[3][1], patch->y[3][1],
		   patch->x[3][0], patch->y[3][0]);
    state->curveTo(patch->x[2][0], patch->y[2][0],
		   patch->x[1][0], patch->y[1][0],
		   patch->x[0][0], patch->y[0][0]);
    state->closePath();
    out->fill(state);
    state->clearPath();
  } else {
    for (i = 0; i < 4; ++i) {
      xx[i][0] = patch->x[i][0];
      yy[i][0] = patch->y[i][0];
      xx[i][1] = 0.5 * (patch->x[i][0] + patch->x[i][1]);
      yy[i][1] = 0.5 * (patch->y[i][0] + patch->y[i][1]);
      xxm = 0.5 * (patch->x[i][1] + patch->x[i][2]);
      yym = 0.5 * (patch->y[i][1] + patch->y[i][2]);
      xx[i][6] = 0.5 * (patch->x[i][2] + patch->x[i][3]);
      yy[i][6] = 0.5 * (patch->y[i][2] + patch->y[i][3]);
      xx[i][2] = 0.5 * (xx[i][1] + xxm);
      yy[i][2] = 0.5 * (yy[i][1] + yym);
      xx[i][5] = 0.5 * (xxm + xx[i][6]);
      yy[i][5] = 0.5 * (yym + yy[i][6]);
      xx[i][3] = xx[i][4] = 0.5 * (xx[i][2] + xx[i][5]);
      yy[i][3] = yy[i][4] = 0.5 * (yy[i][2] + yy[i][5]);
      xx[i][7] = patch->x[i][3];
      yy[i][7] = patch->y[i][3];
    }
    for (i = 0; i < 4; ++i) {
      patch00.x[0][i] = xx[0][i];
      patch00.y[0][i] = yy[0][i];
      patch00.x[1][i] = 0.5 * (xx[0][i] + xx[1][i]);
      patch00.y[1][i] = 0.5 * (yy[0][i] + yy[1][i]);
      xxm = 0.5 * (xx[1][i] + xx[2][i]);
      yym = 0.5 * (yy[1][i] + yy[2][i]);
      patch10.x[2][i] = 0.5 * (xx[2][i] + xx[3][i]);
      patch10.y[2][i] = 0.5 * (yy[2][i] + yy[3][i]);
      patch00.x[2][i] = 0.5 * (patch00.x[1][i] + xxm);
      patch00.y[2][i] = 0.5 * (patch00.y[1][i] + yym);
      patch10.x[1][i] = 0.5 * (xxm + patch10.x[2][i]);
      patch10.y[1][i] = 0.5 * (yym + patch10.y[2][i]);
      patch00.x[3][i] = 0.5 * (patch00.x[2][i] + patch10.x[1][i]);
      patch00.y[3][i] = 0.5 * (patch00.y[2][i] + patch10.y[1][i]);
      patch10.x[0][i] = patch00.x[3][i];
      patch10.y[0][i] = patch00.y[3][i];
      patch10.x[3][i] = xx[3][i];
      patch10.y[3][i] = yy[3][i];
    }
    for (i = 4; i < 8; ++i) {
      patch01.x[0][i-4] = xx[0][i];
      patch01.y[0][i-4] = yy[0][i];
      patch01.x[1][i-4] = 0.5 * (xx[0][i] + xx[1][i]);
      patch01.y[1][i-4] = 0.5 * (yy[0][i] + yy[1][i]);
      xxm = 0.5 * (xx[1][i] + xx[2][i]);
      yym = 0.5 * (yy[1][i] + yy[2][i]);
      patch11.x[2][i-4] = 0.5 * (xx[2][i] + xx[3][i]);
      patch11.y[2][i-4] = 0.5 * (yy[2][i] + yy[3][i]);
      patch01.x[2][i-4] = 0.5 * (patch01.x[1][i-4] + xxm);
      patch01.y[2][i-4] = 0.5 * (patch01.y[1][i-4] + yym);
      patch11.x[1][i-4] = 0.5 * (xxm + patch11.x[2][i-4]);
      patch11.y[1][i-4] = 0.5 * (yym + patch11.y[2][i-4]);
      patch01.x[3][i-4] = 0.5 * (patch01.x[2][i-4] + patch11.x[1][i-4]);
      patch01.y[3][i-4] = 0.5 * (patch01.y[2][i-4] + patch11.y[1][i-4]);
      patch11.x[0][i-4] = patch01.x[3][i-4];
      patch11.y[0][i-4] = patch01.y[3][i-4];
      patch11.x[3][i-4] = xx[3][i];
      patch11.y[3][i-4] = yy[3][i];
    }
    //~ if the shading has a Function, this should interpolate on the
    //~ function parameter, not on the color components
    for (i = 0; i < nComps; ++i) {
      patch00.color[0][0].c[i] = patch->color[0][0].c[i];
      patch00.color[0][1].c[i] = (patch->color[0][0].c[i] +
				  patch->color[0][1].c[i]) / 2;
      patch01.color[0][0].c[i] = patch00.color[0][1].c[i];
      patch01.color[0][1].c[i] = patch->color[0][1].c[i];
      patch01.color[1][1].c[i] = (patch->color[0][1].c[i] +
				  patch->color[1][1].c[i]) / 2;
      patch11.color[0][1].c[i] = patch01.color[1][1].c[i];
      patch11.color[1][1].c[i] = patch->color[1][1].c[i];
      patch11.color[1][0].c[i] = (patch->color[1][1].c[i] +
				  patch->color[1][0].c[i]) / 2;
      patch10.color[1][1].c[i] = patch11.color[1][0].c[i];
      patch10.color[1][0].c[i] = patch->color[1][0].c[i];
      patch10.color[0][0].c[i] = (patch->color[1][0].c[i] +
				  patch->color[0][0].c[i]) / 2;
      patch00.color[1][0].c[i] = patch10.color[0][0].c[i];
      patch00.color[1][1].c[i] = (patch00.color[1][0].c[i] +
				  patch01.color[1][1].c[i]) / 2;
      patch01.color[1][0].c[i] = patch00.color[1][1].c[i];
      patch11.color[0][0].c[i] = patch00.color[1][1].c[i];
      patch10.color[0][1].c[i] = patch00.color[1][1].c[i];
    }
    fillPatch(&patch00, nComps, depth + 1);
    fillPatch(&patch10, nComps, depth + 1);
    fillPatch(&patch01, nComps, depth + 1);
    fillPatch(&patch11, nComps, depth + 1);
  }
}

void Gfx::doEndPath() {
  if (state->isCurPt() && clip != clipNone) {
    state->clip();
    if (clip == clipNormal) {
      out->clip(state);
    } else {
      out->eoClip(state);
    }
  }
  clip = clipNone;
  state->clearPath();
}

//------------------------------------------------------------------------
// path clipping operators
//------------------------------------------------------------------------

void Gfx::opClip(Object args[], int numArgs) {
  clip = clipNormal;
}

void Gfx::opEOClip(Object args[], int numArgs) {
  clip = clipEO;
}

//------------------------------------------------------------------------
// text object operators
//------------------------------------------------------------------------

void Gfx::opBeginText(Object args[], int numArgs) {
  state->setTextMat(1, 0, 0, 1, 0, 0);
  state->textMoveTo(0, 0);
  out->updateTextMat(state);
  out->updateTextPos(state);
  fontChanged = gTrue;
}

void Gfx::opEndText(Object args[], int numArgs) {
  out->endTextObject(state);
}

//------------------------------------------------------------------------
// text state operators
//------------------------------------------------------------------------

void Gfx::opSetCharSpacing(Object args[], int numArgs) {
  state->setCharSpace(args[0].getNum());
  out->updateCharSpace(state);
}

void Gfx::opSetFont(Object args[], int numArgs) {
  GfxFont *font;

  if (!(font = res->lookupFont(args[0].getName()))) {
    return;
  }
  if (printCommands) {
    printf("  font: tag=%s name='%s' %g\n",
	   font->getTag()->getCString(),
	   font->getName() ? font->getName()->getCString() : "???",
	   args[1].getNum());
    fflush(stdout);
  }
  state->setFont(font, args[1].getNum());
  fontChanged = gTrue;
}

void Gfx::opSetTextLeading(Object args[], int numArgs) {
  state->setLeading(args[0].getNum());
}

void Gfx::opSetTextRender(Object args[], int numArgs) {
  state->setRender(args[0].getInt());
  out->updateRender(state);
}

void Gfx::opSetTextRise(Object args[], int numArgs) {
  state->setRise(args[0].getNum());
  out->updateRise(state);
}

void Gfx::opSetWordSpacing(Object args[], int numArgs) {
  state->setWordSpace(args[0].getNum());
  out->updateWordSpace(state);
}

void Gfx::opSetHorizScaling(Object args[], int numArgs) {
  state->setHorizScaling(args[0].getNum());
  out->updateHorizScaling(state);
  fontChanged = gTrue;
}

//------------------------------------------------------------------------
// text positioning operators
//------------------------------------------------------------------------

void Gfx::opTextMove(Object args[], int numArgs) {
  double tx, ty;

  tx = state->getLineX() + args[0].getNum();
  ty = state->getLineY() + args[1].getNum();
  state->textMoveTo(tx, ty);
  out->updateTextPos(state);
}

void Gfx::opTextMoveSet(Object args[], int numArgs) {
  double tx, ty;

  tx = state->getLineX() + args[0].getNum();
  ty = args[1].getNum();
  state->setLeading(-ty);
  ty += state->getLineY();
  state->textMoveTo(tx, ty);
  out->updateTextPos(state);
}

void Gfx::opSetTextMatrix(Object args[], int numArgs) {
  state->setTextMat(args[0].getNum(), args[1].getNum(),
		    args[2].getNum(), args[3].getNum(),
		    args[4].getNum(), args[5].getNum());
  state->textMoveTo(0, 0);
  out->updateTextMat(state);
  out->updateTextPos(state);
  fontChanged = gTrue;
}

void Gfx::opTextNextLine(Object args[], int numArgs) {
  double tx, ty;

  tx = state->getLineX();
  ty = state->getLineY() - state->getLeading();
  state->textMoveTo(tx, ty);
  out->updateTextPos(state);
}

//------------------------------------------------------------------------
// text string operators
//------------------------------------------------------------------------

void Gfx::opShowText(Object args[], int numArgs) {
  if (!state->getFont()) {
    error(getPos(), "No font in show");
    return;
  }
  if (fontChanged) {
    out->updateFont(state);
    fontChanged = gFalse;
  }
  out->beginStringOp(state);
  doShowText(args[0].getString());
  out->endStringOp(state);
}

void Gfx::opMoveShowText(Object args[], int numArgs) {
  double tx, ty;

  if (!state->getFont()) {
    error(getPos(), "No font in move/show");
    return;
  }
  if (fontChanged) {
    out->updateFont(state);
    fontChanged = gFalse;
  }
  tx = state->getLineX();
  ty = state->getLineY() - state->getLeading();
  state->textMoveTo(tx, ty);
  out->updateTextPos(state);
  out->beginStringOp(state);
  doShowText(args[0].getString());
  out->endStringOp(state);
}

void Gfx::opMoveSetShowText(Object args[], int numArgs) {
  double tx, ty;

  if (!state->getFont()) {
    error(getPos(), "No font in move/set/show");
    return;
  }
  if (fontChanged) {
    out->updateFont(state);
    fontChanged = gFalse;
  }
  state->setWordSpace(args[0].getNum());
  state->setCharSpace(args[1].getNum());
  tx = state->getLineX();
  ty = state->getLineY() - state->getLeading();
  state->textMoveTo(tx, ty);
  out->updateWordSpace(state);
  out->updateCharSpace(state);
  out->updateTextPos(state);
  out->beginStringOp(state);
  doShowText(args[2].getString());
  out->endStringOp(state);
}

void Gfx::opShowSpaceText(Object args[], int numArgs) {
  Array *a;
  Object obj;
  int wMode;
  int i;

  if (!state->getFont()) {
    error(getPos(), "No font in show/space");
    return;
  }
  if (fontChanged) {
    out->updateFont(state);
    fontChanged = gFalse;
  }
  out->beginStringOp(state);
  wMode = state->getFont()->getWMode();
  a = args[0].getArray();
  for (i = 0; i < a->getLength(); ++i) {
    a->get(i, &obj);
    if (obj.isNum()) {
      // this uses the absolute value of the font size to match
      // Acrobat's behavior
      if (wMode) {
	state->textShift(0, -obj.getNum() * 0.001 *
			    fabs(state->getFontSize()));
      } else {
	state->textShift(-obj.getNum() * 0.001 *
			 fabs(state->getFontSize()), 0);
      }
      out->updateTextShift(state, obj.getNum());
    } else if (obj.isString()) {
      doShowText(obj.getString());
    } else {
      error(getPos(), "Element of show/space array must be number or string");
    }
    obj.free();
  }
  out->endStringOp(state);
}

void Gfx::doShowText(GString *s) {
  GfxFont *font;
  int wMode;
  double riseX, riseY;
  CharCode code;
  Unicode u[8];
  double x, y, dx, dy, dx2, dy2, curX, curY, tdx, tdy, lineX, lineY;
  double originX, originY, tOriginX, tOriginY;
  double oldCTM[6], newCTM[6];
  double *mat;
  Object charProc;
  Dict *resDict;
  Parser *oldParser;
  char *p;
  int len, n, uLen, nChars, nSpaces, i;

  font = state->getFont();
  wMode = font->getWMode();

  if (out->useDrawChar()) {
    out->beginString(state, s);
  }

  // handle a Type 3 char
  if (font->getType() == fontType3 && out->interpretType3Chars()) {
    mat = state->getCTM();
    for (i = 0; i < 6; ++i) {
      oldCTM[i] = mat[i];
    }
    mat = state->getTextMat();
    newCTM[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2];
    newCTM[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3];
    newCTM[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2];
    newCTM[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3];
    mat = font->getFontMatrix();
    newCTM[0] = mat[0] * newCTM[0] + mat[1] * newCTM[2];
    newCTM[1] = mat[0] * newCTM[1] + mat[1] * newCTM[3];
    newCTM[2] = mat[2] * newCTM[0] + mat[3] * newCTM[2];
    newCTM[3] = mat[2] * newCTM[1] + mat[3] * newCTM[3];
    newCTM[0] *= state->getFontSize();
    newCTM[1] *= state->getFontSize();
    newCTM[2] *= state->getFontSize();
    newCTM[3] *= state->getFontSize();
    newCTM[0] *= state->getHorizScaling();
    newCTM[2] *= state->getHorizScaling();
    state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
    curX = state->getCurX();
    curY = state->getCurY();
    lineX = state->getLineX();
    lineY = state->getLineY();
    oldParser = parser;
    p = s->getCString();
    len = s->getLength();
    while (len > 0) {
      n = font->getNextChar(p, len, &code,
			    u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
			    &dx, &dy, &originX, &originY);
      dx = dx * state->getFontSize() + state->getCharSpace();
      if (n == 1 && *p == ' ') {
	dx += state->getWordSpace();
      }
      dx *= state->getHorizScaling();
      dy *= state->getFontSize();
      state->textTransformDelta(dx, dy, &tdx, &tdy);
      state->transform(curX + riseX, curY + riseY, &x, &y);
      saveState();
      state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
      //~ the CTM concat values here are wrong (but never used)
      out->updateCTM(state, 1, 0, 0, 1, 0, 0);
      if (!out->beginType3Char(state, curX + riseX, curY + riseY, tdx, tdy,
			       code, u, uLen)) {
	((Gfx8BitFont *)font)->getCharProc(code, &charProc);
	if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
	  pushResources(resDict);
	}
	if (charProc.isStream()) {
	  display(&charProc, gFalse);
	} else {
	  error(getPos(), "Missing or bad Type3 CharProc entry");
	}
	out->endType3Char(state);
	if (resDict) {
	  popResources();
	}
	charProc.free();
      }
      restoreState();
      // GfxState::restore() does *not* restore the current position,
      // so we deal with it here using (curX, curY) and (lineX, lineY)
      curX += tdx;
      curY += tdy;
      state->moveTo(curX, curY);
      state->textSetPos(lineX, lineY);
      p += n;
      len -= n;
    }
    parser = oldParser;

  } else if (out->useDrawChar()) {
    state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
    p = s->getCString();
    len = s->getLength();
    while (len > 0) {
      n = font->getNextChar(p, len, &code,
			    u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
			    &dx, &dy, &originX, &originY);
      if (wMode) {
	dx *= state->getFontSize();
	dy = dy * state->getFontSize() + state->getCharSpace();
	if (n == 1 && *p == ' ') {
	  dy += state->getWordSpace();
	}
      } else {
	dx = dx * state->getFontSize() + state->getCharSpace();
	if (n == 1 && *p == ' ') {
	  dx += state->getWordSpace();
	}
	dx *= state->getHorizScaling();
	dy *= state->getFontSize();
      }
      state->textTransformDelta(dx, dy, &tdx, &tdy);
      originX *= state->getFontSize();
      originY *= state->getFontSize();
      state->textTransformDelta(originX, originY, &tOriginX, &tOriginY);
      out->drawChar(state, state->getCurX() + riseX, state->getCurY() + riseY,
		    tdx, tdy, tOriginX, tOriginY, code, n, u, uLen);
      state->shift(tdx, tdy);
      p += n;
      len -= n;
    }

  } else {
    dx = dy = 0;
    p = s->getCString();
    len = s->getLength();
    nChars = nSpaces = 0;
    while (len > 0) {
      n = font->getNextChar(p, len, &code,
			    u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
			    &dx2, &dy2, &originX, &originY);
      dx += dx2;
      dy += dy2;
      if (n == 1 && *p == ' ') {
	++nSpaces;
      }
      ++nChars;
      p += n;
      len -= n;
    }
    if (wMode) {
      dx *= state->getFontSize();
      dy = dy * state->getFontSize()
	   + nChars * state->getCharSpace()
	   + nSpaces * state->getWordSpace();
    } else {
      dx = dx * state->getFontSize()
	   + nChars * state->getCharSpace()
	   + nSpaces * state->getWordSpace();
      dx *= state->getHorizScaling();
      dy *= state->getFontSize();
    }
    state->textTransformDelta(dx, dy, &tdx, &tdy);
    out->drawString(state, s);
    state->shift(tdx, tdy);
  }

  if (out->useDrawChar()) {
    out->endString(state);
  }

  updateLevel += 10 * s->getLength();
}

//------------------------------------------------------------------------
// XObject operators
//------------------------------------------------------------------------

void Gfx::opXObject(Object args[], int numArgs) {
  char *name;
  Object obj1, obj2, obj3, refObj;
#if OPI_SUPPORT
  Object opiDict;
#endif

  name = args[0].getName();
  if (!res->lookupXObject(name, &obj1)) {
    return;
  }
  if (!obj1.isStream()) {
    error(getPos(), "XObject '%s' is wrong type", name);
    obj1.free();
    return;
  }
#if OPI_SUPPORT
  obj1.streamGetDict()->lookup("OPI", &opiDict);
  if (opiDict.isDict()) {
    out->opiBegin(state, opiDict.getDict());
  }
#endif
  obj1.streamGetDict()->lookup("Subtype", &obj2);
  if (obj2.isName("Image")) {
    if (out->needNonText()) {
      res->lookupXObjectNF(name, &refObj);
      doImage(&refObj, obj1.getStream(), gFalse);
      refObj.free();
    }
  } else if (obj2.isName("Form")) {
    res->lookupXObjectNF(name, &refObj);
    if (out->useDrawForm() && refObj.isRef()) {
      out->drawForm(refObj.getRef());
    } else {
      doForm(&obj1);
    }
    refObj.free();
  } else if (obj2.isName("PS")) {
    obj1.streamGetDict()->lookup("Level1", &obj3);
    out->psXObject(obj1.getStream(),
		   obj3.isStream() ? obj3.getStream() : (Stream *)NULL);
  } else if (obj2.isName()) {
    error(getPos(), "Unknown XObject subtype '%s'", obj2.getName());
  } else {
    error(getPos(), "XObject subtype is missing or wrong type");
  }
  obj2.free();
#if OPI_SUPPORT
  if (opiDict.isDict()) {
    out->opiEnd(state, opiDict.getDict());
  }
  opiDict.free();
#endif
  obj1.free();
}

void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
  Dict *dict, *maskDict;
  int width, height;
  int bits, maskBits;
  StreamColorSpaceMode csMode;
  GBool mask;
  GBool invert;
  GfxColorSpace *colorSpace, *maskColorSpace;
  GfxImageColorMap *colorMap, *maskColorMap;
  Object maskObj, smaskObj;
  GBool haveColorKeyMask, haveExplicitMask, haveSoftMask;
  int maskColors[2*gfxColorMaxComps];
  int maskWidth, maskHeight;
  GBool maskInvert;
  Stream *maskStr;
  Object obj1, obj2;
  int i;

  // get info from the stream
  bits = 0;
  csMode = streamCSNone;
  str->getImageParams(&bits, &csMode);

  // get stream dict
  dict = str->getDict();

  // get size
  dict->lookup("Width", &obj1);
  if (obj1.isNull()) {
    obj1.free();
    dict->lookup("W", &obj1);
  }
  if (!obj1.isInt())
    goto err2;
  width = obj1.getInt();
  obj1.free();
  dict->lookup("Height", &obj1);
  if (obj1.isNull()) {
    obj1.free();
    dict->lookup("H", &obj1);
  }
  if (!obj1.isInt())
    goto err2;
  height = obj1.getInt();
  obj1.free();

  // image or mask?
  dict->lookup("ImageMask", &obj1);
  if (obj1.isNull()) {
    obj1.free();
    dict->lookup("IM", &obj1);
  }
  mask = gFalse;
  if (obj1.isBool())
    mask = obj1.getBool();
  else if (!obj1.isNull())
    goto err2;
  obj1.free();

  // bit depth
  if (bits == 0) {
    dict->lookup("BitsPerComponent", &obj1);
    if (obj1.isNull()) {
      obj1.free();
      dict->lookup("BPC", &obj1);
    }
    if (obj1.isInt()) {
      bits = obj1.getInt();
    } else if (mask) {
      bits = 1;
    } else {
      goto err2;
    }
    obj1.free();
  }

  // display a mask
  if (mask) {

    // check for inverted mask
    if (bits != 1)
      goto err1;
    invert = gFalse;
    dict->lookup("Decode", &obj1);
    if (obj1.isNull()) {
      obj1.free();
      dict->lookup("D", &obj1);
    }
    if (obj1.isArray()) {
      obj1.arrayGet(0, &obj2);
      if (obj2.isInt() && obj2.getInt() == 1)
	invert = gTrue;
      obj2.free();
    } else if (!obj1.isNull()) {
      goto err2;
    }
    obj1.free();

    // draw it
    out->drawImageMask(state, ref, str, width, height, invert, inlineImg);

  } else {

    // get color space and color map
    dict->lookup("ColorSpace", &obj1);
    if (obj1.isNull()) {
      obj1.free();
      dict->lookup("CS", &obj1);
    }
    if (obj1.isName()) {
      res->lookupColorSpace(obj1.getName(), &obj2);
      if (!obj2.isNull()) {
	obj1.free();
	obj1 = obj2;
      } else {
	obj2.free();
      }
    }
    if (!obj1.isNull()) {
      colorSpace = GfxColorSpace::parse(&obj1);
    } else if (csMode == streamCSDeviceGray) {
      colorSpace = new GfxDeviceGrayColorSpace();
    } else if (csMode == streamCSDeviceRGB) {
      colorSpace = new GfxDeviceRGBColorSpace();
    } else if (csMode == streamCSDeviceCMYK) {
      colorSpace = new GfxDeviceCMYKColorSpace();
    } else {
      colorSpace = NULL;
    }
    obj1.free();
    if (!colorSpace) {
      goto err1;
    }
    dict->lookup("Decode", &obj1);
    if (obj1.isNull()) {
      obj1.free();
      dict->lookup("D", &obj1);
    }
    colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
    obj1.free();
    if (!colorMap->isOk()) {
      delete colorMap;
      goto err1;
    }

    // get the mask
    haveColorKeyMask = haveExplicitMask = haveSoftMask = gFalse;
    maskStr = NULL; // make gcc happy
    maskWidth = maskHeight = 0; // make gcc happy
    maskInvert = gFalse; // make gcc happy
    maskColorMap = NULL; // make gcc happy
    dict->lookup("Mask", &maskObj);
    dict->lookup("SMask", &smaskObj);
    if (smaskObj.isStream()) {
      // soft mask
      if (inlineImg) {
	goto err1;
      }
      maskStr = smaskObj.getStream();
      maskDict = smaskObj.streamGetDict();
      maskDict->lookup("Width", &obj1);
      if (obj1.isNull()) {
	obj1.free();
	maskDict->lookup("W", &obj1);
      }
      if (!obj1.isInt()) {
	goto err2;
      }
      maskWidth = obj1.getInt();
      obj1.free();
      maskDict->lookup("Height", &obj1);
      if (obj1.isNull()) {
	obj1.free();
	maskDict->lookup("H", &obj1);
      }
      if (!obj1.isInt()) {
	goto err2;
      }
      maskHeight = obj1.getInt();
      obj1.free();
      maskDict->lookup("BitsPerComponent", &obj1);
      if (obj1.isNull()) {
	obj1.free();
	maskDict->lookup("BPC", &obj1);
      }
      if (!obj1.isInt()) {
	goto err2;
      }
      maskBits = obj1.getInt();
      obj1.free();
      maskDict->lookup("ColorSpace", &obj1);
      if (obj1.isNull()) {
	obj1.free();
	maskDict->lookup("CS", &obj1);
      }
      if (obj1.isName()) {
	res->lookupColorSpace(obj1.getName(), &obj2);
	if (!obj2.isNull()) {
	  obj1.free();
	  obj1 = obj2;
	} else {
	  obj2.free();
	}
      }
      maskColorSpace = GfxColorSpace::parse(&obj1);
      obj1.free();
      if (!maskColorSpace || maskColorSpace->getMode() != csDeviceGray) {
	goto err1;
      }
      maskDict->lookup("Decode", &obj1);
      if (obj1.isNull()) {
	obj1.free();
	maskDict->lookup("D", &obj1);
      }
      maskColorMap = new GfxImageColorMap(maskBits, &obj1, maskColorSpace);
      obj1.free();
      if (!maskColorMap->isOk()) {
	delete maskColorMap;
	goto err1;
      }
      //~ handle the Matte entry
      haveSoftMask = gTrue;
    } else if (maskObj.isArray()) {
      // color key mask
      for (i = 0;
	   i < maskObj.arrayGetLength() && i < 2*gfxColorMaxComps;
	   ++i) {
	maskObj.arrayGet(i, &obj1);
	maskColors[i] = obj1.getInt();
	obj1.free();
      }
      haveColorKeyMask = gTrue;
    } else if (maskObj.isStream()) {
      // explicit mask
      if (inlineImg) {
	goto err1;
      }
      maskStr = maskObj.getStream();
      maskDict = maskObj.streamGetDict();
      maskDict->lookup("Width", &obj1);
      if (obj1.isNull()) {
	obj1.free();
	maskDict->lookup("W", &obj1);
      }
      if (!obj1.isInt()) {
	goto err2;
      }
      maskWidth = obj1.getInt();
      obj1.free();
      maskDict->lookup("Height", &obj1);
      if (obj1.isNull()) {
	obj1.free();
	maskDict->lookup("H", &obj1);
      }
      if (!obj1.isInt()) {
	goto err2;
      }
      maskHeight = obj1.getInt();
      obj1.free();
      maskDict->lookup("ImageMask", &obj1);
      if (obj1.isNull()) {
	obj1.free();
	maskDict->lookup("IM", &obj1);
      }
      if (!obj1.isBool() || !obj1.getBool()) {
	goto err2;
      }
      obj1.free();
      maskInvert = gFalse;
      maskDict->lookup("Decode", &obj1);
      if (obj1.isNull()) {
	obj1.free();
	maskDict->lookup("D", &obj1);
      }
      if (obj1.isArray()) {
	obj1.arrayGet(0, &obj2);
	if (obj2.isInt() && obj2.getInt() == 1) {
	  maskInvert = gTrue;
	}
	obj2.free();
      } else if (!obj1.isNull()) {
	goto err2;
      }
      obj1.free();
      haveExplicitMask = gTrue;
    }

    // draw it
    if (haveSoftMask) {
      out->drawSoftMaskedImage(state, ref, str, width, height, colorMap,
			       maskStr, maskWidth, maskHeight, maskColorMap);
      delete maskColorMap;
    } else if (haveExplicitMask) {
      out->drawMaskedImage(state, ref, str, width, height, colorMap,
			   maskStr, maskWidth, maskHeight, maskInvert);
    } else {
      out->drawImage(state, ref, str, width, height, colorMap,
		     haveColorKeyMask ? maskColors : (int *)NULL, inlineImg);
    }
    delete colorMap;

    maskObj.free();
    smaskObj.free();
  }

  if ((i = width * height) > 1000) {
    i = 1000;
  }
  updateLevel += i;

  return;

 err2:
  obj1.free();
 err1:
  error(getPos(), "Bad image parameters");
}

void Gfx::doForm(Object *str) {
  Dict *dict;
  GBool transpGroup, isolated, knockout;
  GfxColorSpace *blendingColorSpace;
  Object matrixObj, bboxObj;
  double m[6], bbox[4];
  Object resObj;
  Dict *resDict;
  Object obj1, obj2, obj3;
  int i;

  // check for excessive recursion
  if (formDepth > 20) {
    return;
  }

  // get stream dict
  dict = str->streamGetDict();

  // check form type
  dict->lookup("FormType", &obj1);
  if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
    error(getPos(), "Unknown form type");
  }
  obj1.free();

  // get bounding box
  dict->lookup("BBox", &bboxObj);
  if (!bboxObj.isArray()) {
    bboxObj.free();
    error(getPos(), "Bad form bounding box");
    return;
  }
  for (i = 0; i < 4; ++i) {
    bboxObj.arrayGet(i, &obj1);
    bbox[i] = obj1.getNum();
    obj1.free();
  }
  bboxObj.free();

  // get matrix
  dict->lookup("Matrix", &matrixObj);
  if (matrixObj.isArray()) {
    for (i = 0; i < 6; ++i) {
      matrixObj.arrayGet(i, &obj1);
      m[i] = obj1.getNum();
      obj1.free();
    }
  } else {
    m[0] = 1; m[1] = 0;
    m[2] = 0; m[3] = 1;
    m[4] = 0; m[5] = 0;
  }
  matrixObj.free();

  // get resources
  dict->lookup("Resources", &resObj);
  resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;

  // check for a transparency group
  transpGroup = isolated = knockout = gFalse;
  blendingColorSpace = NULL;
  if (dict->lookup("Group", &obj1)->isDict()) {
    if (obj1.dictLookup("S", &obj2)->isName("Transparency")) {
      transpGroup = gTrue;
      if (!obj1.dictLookup("CS", &obj3)->isNull()) {
	blendingColorSpace = GfxColorSpace::parse(&obj3);
      }
      obj3.free();
      if (obj1.dictLookup("I", &obj3)->isBool()) {
	isolated = obj3.getBool();
      }
      obj3.free();
      if (obj1.dictLookup("K", &obj3)->isBool()) {
	knockout = obj3.getBool();
      }
      obj3.free();
    }
    obj2.free();
  }
  obj1.free();

  // draw it
  ++formDepth;
  doForm1(str, resDict, m, bbox,
	  transpGroup, gFalse, blendingColorSpace, isolated, knockout);
  --formDepth;

  if (blendingColorSpace) {
    delete blendingColorSpace;
  }
  resObj.free();
}

void Gfx::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox,
		  GBool transpGroup, GBool softMask,
		  GfxColorSpace *blendingColorSpace,
		  GBool isolated, GBool knockout,
		  GBool alpha, Function *transferFunc,
		  GfxColor *backdropColor) {
  Parser *oldParser;
  double oldBaseMatrix[6];
  int i;

  // push new resources on stack
  pushResources(resDict);

  // save current graphics state
  saveState();

  // kill any pre-existing path
  state->clearPath();

  // save current parser
  oldParser = parser;

  // set form transformation matrix
  state->concatCTM(matrix[0], matrix[1], matrix[2],
		   matrix[3], matrix[4], matrix[5]);
  out->updateCTM(state, matrix[0], matrix[1], matrix[2],
		 matrix[3], matrix[4], matrix[5]);

  // set form bounding box
  state->moveTo(bbox[0], bbox[1]);
  state->lineTo(bbox[2], bbox[1]);
  state->lineTo(bbox[2], bbox[3]);
  state->lineTo(bbox[0], bbox[3]);
  state->closePath();
  state->clip();
  out->clip(state);
  state->clearPath();

  if (softMask || transpGroup) {
    if (state->getBlendMode() != gfxBlendNormal) {
      state->setBlendMode(gfxBlendNormal);
      out->updateBlendMode(state);
    }
    if (state->getFillOpacity() != 1) {
      state->setFillOpacity(1);
      out->updateFillOpacity(state);
    }
    if (state->getStrokeOpacity() != 1) {
      state->setStrokeOpacity(1);
      out->updateStrokeOpacity(state);
    }
    out->clearSoftMask(state);
    out->beginTransparencyGroup(state, bbox, blendingColorSpace,
				isolated, knockout, softMask);
  }

  // set new base matrix
  for (i = 0; i < 6; ++i) {
    oldBaseMatrix[i] = baseMatrix[i];
    baseMatrix[i] = state->getCTM()[i];
  }

  // draw the form
  display(str, gFalse);

  if (softMask || transpGroup) {
    out->endTransparencyGroup(state);
  }

  // restore base matrix
  for (i = 0; i < 6; ++i) {
    baseMatrix[i] = oldBaseMatrix[i];
  }

  // restore parser
  parser = oldParser;

  // restore graphics state
  restoreState();

  // pop resource stack
  popResources();

  if (softMask) {
    out->setSoftMask(state, bbox, alpha, transferFunc, backdropColor);
  } else if (transpGroup) {
    out->paintTransparencyGroup(state, bbox);
  }

  return;
}

//------------------------------------------------------------------------
// in-line image operators
//------------------------------------------------------------------------

void Gfx::opBeginImage(Object args[], int numArgs) {
  Stream *str;
  int c1, c2;

  // build dict/stream
  str = buildImageStream();

  // display the image
  if (str) {
    doImage(NULL, str, gTrue);
  
    // skip 'EI' tag
    c1 = str->getUndecodedStream()->getChar();
    c2 = str->getUndecodedStream()->getChar();
    while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) {
      c1 = c2;
      c2 = str->getUndecodedStream()->getChar();
    }
    delete str;
  }
}

Stream *Gfx::buildImageStream() {
  Object dict;
  Object obj;
  char *key;
  Stream *str;

  // build dictionary
  dict.initDict(xref);
  parser->getObj(&obj);
  while (!obj.isCmd("ID") && !obj.isEOF()) {
    if (!obj.isName()) {
      error(getPos(), "Inline image dictionary key must be a name object");
      obj.free();
    } else {
      key = copyString(obj.getName());
      obj.free();
      parser->getObj(&obj);
      if (obj.isEOF() || obj.isError()) {
	gfree(key);
	break;
      }
      dict.dictAdd(key, &obj);
    }
    parser->getObj(&obj);
  }
  if (obj.isEOF()) {
    error(getPos(), "End of file in inline image");
    obj.free();
    dict.free();
    return NULL;
  }
  obj.free();

  // make stream
  str = new EmbedStream(parser->getStream(), &dict, gFalse, 0);
  str = str->addFilters(&dict);

  return str;
}

void Gfx::opImageData(Object args[], int numArgs) {
  error(getPos(), "Internal: got 'ID' operator");
}

void Gfx::opEndImage(Object args[], int numArgs) {
  error(getPos(), "Internal: got 'EI' operator");
}

//------------------------------------------------------------------------
// type 3 font operators
//------------------------------------------------------------------------

void Gfx::opSetCharWidth(Object args[], int numArgs) {
  out->type3D0(state, args[0].getNum(), args[1].getNum());
}

void Gfx::opSetCacheDevice(Object args[], int numArgs) {
  out->type3D1(state, args[0].getNum(), args[1].getNum(),
	       args[2].getNum(), args[3].getNum(),
	       args[4].getNum(), args[5].getNum());
}

//------------------------------------------------------------------------
// compatibility operators
//------------------------------------------------------------------------

void Gfx::opBeginIgnoreUndef(Object args[], int numArgs) {
  ++ignoreUndef;
}

void Gfx::opEndIgnoreUndef(Object args[], int numArgs) {
  if (ignoreUndef > 0)
    --ignoreUndef;
}

//------------------------------------------------------------------------
// marked content operators
//------------------------------------------------------------------------

void Gfx::opBeginMarkedContent(Object args[], int numArgs) {
  if (printCommands) {
    printf("  marked content: %s ", args[0].getName());
    if (numArgs == 2)
      args[2].print(stdout);
    printf("\n");
    fflush(stdout);
  }
}

void Gfx::opEndMarkedContent(Object args[], int numArgs) {
}

void Gfx::opMarkPoint(Object args[], int numArgs) {
  if (printCommands) {
    printf("  mark point: %s ", args[0].getName());
    if (numArgs == 2)
      args[2].print(stdout);
    printf("\n");
    fflush(stdout);
  }
}

//------------------------------------------------------------------------
// misc
//------------------------------------------------------------------------

void Gfx::drawAnnot(Object *str, AnnotBorderStyle *borderStyle,
		    double xMin, double yMin, double xMax, double yMax) {
  Dict *dict, *resDict;
  Object matrixObj, bboxObj, resObj;
  Object obj1;
  double m[6], bbox[4], ictm[6];
  double *ctm;
  double formX0, formY0, formX1, formY1;
  double annotX0, annotY0, annotX1, annotY1;
  double det, x, y, sx, sy;
  double r, g, b;
  GfxColor color;
  double *dash, *dash2;
  int dashLength;
  int i;

  //~ can we assume that we're in default user space?
  //~ (i.e., baseMatrix = ctm)

  // transform the annotation bbox from default user space to user
  // space: (bbox * baseMatrix) * iCTM
  ctm = state->getCTM();
  det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
  ictm[0] = ctm[3] * det;
  ictm[1] = -ctm[1] * det;
  ictm[2] = -ctm[2] * det;
  ictm[3] = ctm[0] * det;
  ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
  ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
  x = baseMatrix[0] * xMin + baseMatrix[2] * yMin + baseMatrix[4];
  y = baseMatrix[1] * xMin + baseMatrix[3] * yMin + baseMatrix[5];
  annotX0 = ictm[0] * x + ictm[2] * y + ictm[4];
  annotY0 = ictm[1] * x + ictm[3] * y + ictm[5];
  x = baseMatrix[0] * xMax + baseMatrix[2] * yMax + baseMatrix[4];
  y = baseMatrix[1] * xMax + baseMatrix[3] * yMax + baseMatrix[5];
  annotX1 = ictm[0] * x + ictm[2] * y + ictm[4];
  annotY1 = ictm[1] * x + ictm[3] * y + ictm[5];
  if (annotX0 > annotX1) {
    x = annotX0; annotX0 = annotX1; annotX1 = x;
  }
  if (annotY0 > annotY1) {
    y = annotY0; annotY0 = annotY1; annotY1 = y;
  }

  // draw the appearance stream (if there is one)
  if (str->isStream()) {

    // get stream dict
    dict = str->streamGetDict();

    // get the form bounding box
    dict->lookup("BBox", &bboxObj);
    if (!bboxObj.isArray()) {
      bboxObj.free();
      error(getPos(), "Bad form bounding box");
      return;
    }
    for (i = 0; i < 4; ++i) {
      bboxObj.arrayGet(i, &obj1);
      bbox[i] = obj1.getNum();
      obj1.free();
    }
    bboxObj.free();

    // get the form matrix
    dict->lookup("Matrix", &matrixObj);
    if (matrixObj.isArray()) {
      for (i = 0; i < 6; ++i) {
	matrixObj.arrayGet(i, &obj1);
	m[i] = obj1.getNum();
	obj1.free();
      }
    } else {
      m[0] = 1; m[1] = 0;
      m[2] = 0; m[3] = 1;
      m[4] = 0; m[5] = 0;
    }
    matrixObj.free();

    // transform the form bbox from form space to user space
    formX0 = bbox[0] * m[0] + bbox[1] * m[2] + m[4];
    formY0 = bbox[0] * m[1] + bbox[1] * m[3] + m[5];
    formX1 = bbox[2] * m[0] + bbox[3] * m[2] + m[4];
    formY1 = bbox[2] * m[1] + bbox[3] * m[3] + m[5];
    if (formX0 > formX1) {
      x = formX0; formX0 = formX1; formX1 = x;
    }
    if (formY0 > formY1) {
      y = formY0; formY0 = formY1; formY1 = y;
    }

    // scale the form to fit the annotation bbox
    if (formX1 == formX0) {
      // this shouldn't happen
      sx = 1;
    } else {
      sx = (annotX1 - annotX0) / (formX1 - formX0);
    }
    if (formY1 == formY0) {
      // this shouldn't happen
      sy = 1;
    } else {
      sy = (annotY1 - annotY0) / (formY1 - formY0);
    }
    m[0] *= sx;
    m[2] *= sx;
    m[4] = (m[4] - formX0) * sx + annotX0;
    m[1] *= sy;
    m[3] *= sy;
    m[5] = (m[5] - formY0) * sy + annotY0;

    // get resources
    dict->lookup("Resources", &resObj);
    resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;

    // draw it
    doForm1(str, resDict, m, bbox);

    resObj.free();
  }

  // draw the border
  if (borderStyle && borderStyle->getWidth() > 0) {
    if (state->getStrokeColorSpace()->getMode() != csDeviceRGB) {
      state->setStrokePattern(NULL);
      state->setStrokeColorSpace(new GfxDeviceRGBColorSpace());
      out->updateStrokeColorSpace(state);
    }
    borderStyle->getColor(&r, &g, &b);
    color.c[0] = dblToCol(r);
    color.c[1] = dblToCol(g);
    color.c[2] = dblToCol(b);
    state->setStrokeColor(&color);
    out->updateStrokeColor(state);
    // compute the width scale factor when going from default user
    // space to user space
    x = (baseMatrix[0] + baseMatrix[2]) * ictm[0] +
        (baseMatrix[1] + baseMatrix[3]) * ictm[2];
    y = (baseMatrix[0] + baseMatrix[2]) * ictm[1] +
        (baseMatrix[1] + baseMatrix[3]) * ictm[3];
    x = sqrt(0.5 * (x * x + y * y));
    state->setLineWidth(x * borderStyle->getWidth());
    out->updateLineWidth(state);
    borderStyle->getDash(&dash, &dashLength);
    if (borderStyle->getType() == annotBorderDashed && dashLength > 0) {
      dash2 = (double *)gmallocn(dashLength, sizeof(double));
      for (i = 0; i < dashLength; ++i) {
	dash2[i] = x * dash[i];
      }
      state->setLineDash(dash2, dashLength, 0);
      out->updateLineDash(state);
    }
    //~ this doesn't currently handle the beveled and engraved styles
    state->clearPath();
    state->moveTo(annotX0, out->upsideDown() ? annotY1 : annotY0);
    state->lineTo(annotX1, out->upsideDown() ? annotY1 : annotY0);
    if (borderStyle->getType() != annotBorderUnderlined) {
      state->lineTo(annotX1, out->upsideDown() ? annotY0 : annotY1);
      state->lineTo(annotX0, out->upsideDown() ? annotY0 : annotY1);
      state->closePath();
    }
    out->stroke(state);
  }
}

void Gfx::saveState() {
  out->saveState(state);
  state = state->save();
}

void Gfx::restoreState() {
  state = state->restore();
  out->restoreState(state);
}

void Gfx::pushResources(Dict *resDict) {
  res = new GfxResources(xref, resDict, res);
}

void Gfx::popResources() {
  GfxResources *resPtr;

  resPtr = res->getNext();
  delete res;
  res = resPtr;
}
//========================================================================
//
// Gfx.h
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef GFX_H
#define GFX_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "gtypes.h"

class GString;
class XRef;
class Array;
class Stream;
class Parser;
class Dict;
class Function;
class OutputDev;
class GfxFontDict;
class GfxFont;
class GfxPattern;
class GfxTilingPattern;
class GfxShadingPattern;
class GfxShading;
class GfxFunctionShading;
class GfxAxialShading;
class GfxRadialShading;
class GfxGouraudTriangleShading;
class GfxPatchMeshShading;
struct GfxPatch;
class GfxState;
struct GfxColor;
class GfxColorSpace;
class Gfx;
class PDFRectangle;
class AnnotBorderStyle;

//------------------------------------------------------------------------

enum GfxClipType {
  clipNone,
  clipNormal,
  clipEO
};

enum TchkType {
  tchkBool,			// boolean
  tchkInt,			// integer
  tchkNum,			// number (integer or real)
  tchkString,			// string
  tchkName,			// name
  tchkArray,			// array
  tchkProps,			// properties (dictionary or name)
  tchkSCN,			// scn/SCN args (number of name)
  tchkNone			// used to avoid empty initializer lists
};

#define maxArgs 33

struct Operator {
  char name[4];
  int numArgs;
  TchkType tchk[maxArgs];
  void (Gfx::*func)(Object args[], int numArgs);
};

//------------------------------------------------------------------------

class GfxResources {
public:

  GfxResources(XRef *xref, Dict *resDict, GfxResources *nextA);
  ~GfxResources();

  GfxFont *lookupFont(char *name);
  GBool lookupXObject(char *name, Object *obj);
  GBool lookupXObjectNF(char *name, Object *obj);
  void lookupColorSpace(char *name, Object *obj);
  GfxPattern *lookupPattern(char *name);
  GfxShading *lookupShading(char *name);
  GBool lookupGState(char *name, Object *obj);

  GfxResources *getNext() { return next; }

private:

  GfxFontDict *fonts;
  Object xObjDict;
  Object colorSpaceDict;
  Object patternDict;
  Object shadingDict;
  Object gStateDict;
  GfxResources *next;
};

//------------------------------------------------------------------------
// Gfx
//------------------------------------------------------------------------

class Gfx {
public:

  // Constructor for regular output.
  Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict,
      double hDPI, double vDPI, PDFRectangle *box,
      PDFRectangle *cropBox, int rotate,
      GBool (*abortCheckCbkA)(void *data) = NULL,
      void *abortCheckCbkDataA = NULL);

  // Constructor for a sub-page object.
  Gfx(XRef *xrefA, OutputDev *outA, Dict *resDict,
      PDFRectangle *box, PDFRectangle *cropBox,
      GBool (*abortCheckCbkA)(void *data) = NULL,
      void *abortCheckCbkDataA = NULL);

  ~Gfx();

  // Interpret a stream or array of streams.
  void display(Object *obj, GBool topLevel = gTrue);

  // Display an annotation, given its appearance (a Form XObject),
  // border style, and bounding box (in default user space).
  void drawAnnot(Object *str, AnnotBorderStyle *borderStyle,
		 double xMin, double yMin, double xMax, double yMax);

  // Save graphics state.
  void saveState();

  // Restore graphics state.
  void restoreState();

  // Get the current graphics state object.
  GfxState *getState() { return state; }

private:

  XRef *xref;			// the xref table for this PDF file
  OutputDev *out;		// output device
  GBool subPage;		// is this a sub-page object?
  GBool printCommands;		// print the drawing commands (for debugging)
  GfxResources *res;		// resource stack
  int updateLevel;

  GfxState *state;		// current graphics state
  GBool fontChanged;		// set if font or text matrix has changed
  GfxClipType clip;		// do a clip?
  int ignoreUndef;		// current BX/EX nesting level
  double baseMatrix[6];		// default matrix for most recent
				//   page/form/pattern
  int formDepth;

  Parser *parser;		// parser for page content stream(s)

  GBool				// callback to check for an abort
    (*abortCheckCbk)(void *data);
  void *abortCheckCbkData;

  static Operator opTab[];	// table of operators

  void go(GBool topLevel);
  void execOp(Object *cmd, Object args[], int numArgs);
  Operator *findOp(char *name);
  GBool checkArg(Object *arg, TchkType type);
  int getPos();

  // graphics state operators
  void opSave(Object args[], int numArgs);
  void opRestore(Object args[], int numArgs);
  void opConcat(Object args[], int numArgs);
  void opSetDash(Object args[], int numArgs);
  void opSetFlat(Object args[], int numArgs);
  void opSetLineJoin(Object args[], int numArgs);
  void opSetLineCap(Object args[], int numArgs);
  void opSetMiterLimit(Object args[], int numArgs);
  void opSetLineWidth(Object args[], int numArgs);
  void opSetExtGState(Object args[], int numArgs);
  void doSoftMask(Object *str, GBool alpha,
		  GfxColorSpace *blendingColorSpace,
		  GBool isolated, GBool knockout,
		  Function *transferFunc, GfxColor *backdropColor);
  void opSetRenderingIntent(Object args[], int numArgs);

  // color operators
  void opSetFillGray(Object args[], int numArgs);
  void opSetStrokeGray(Object args[], int numArgs);
  void opSetFillCMYKColor(Object args[], int numArgs);
  void opSetStrokeCMYKColor(Object args[], int numArgs);
  void opSetFillRGBColor(Object args[], int numArgs);
  void opSetStrokeRGBColor(Object args[], int numArgs);
  void opSetFillColorSpace(Object args[], int numArgs);
  void opSetStrokeColorSpace(Object args[], int numArgs);
  void opSetFillColor(Object args[], int numArgs);
  void opSetStrokeColor(Object args[], int numArgs);
  void opSetFillColorN(Object args[], int numArgs);
  void opSetStrokeColorN(Object args[], int numArgs);

  // path segment operators
  void opMoveTo(Object args[], int numArgs);
  void opLineTo(Object args[], int numArgs);
  void opCurveTo(Object args[], int numArgs);
  void opCurveTo1(Object args[], int numArgs);
  void opCurveTo2(Object args[], int numArgs);
  void opRectangle(Object args[], int numArgs);
  void opClosePath(Object args[], int numArgs);

  // path painting operators
  void opEndPath(Object args[], int numArgs);
  void opStroke(Object args[], int numArgs);
  void opCloseStroke(Object args[], int numArgs);
  void opFill(Object args[], int numArgs);
  void opEOFill(Object args[], int numArgs);
  void opFillStroke(Object args[], int numArgs);
  void opCloseFillStroke(Object args[], int numArgs);
  void opEOFillStroke(Object args[], int numArgs);
  void opCloseEOFillStroke(Object args[], int numArgs);
  void doPatternFill(GBool eoFill);
  void doPatternStroke();
  void doTilingPatternFill(GfxTilingPattern *tPat,
			   GBool stroke, GBool eoFill);
  void doShadingPatternFill(GfxShadingPattern *sPat,
			    GBool stroke, GBool eoFill);
  void opShFill(Object args[], int numArgs);
  void doFunctionShFill(GfxFunctionShading *shading);
  void doFunctionShFill1(GfxFunctionShading *shading,
			 double x0, double y0,
			 double x1, double y1,
			 GfxColor *colors, int depth);
  void doAxialShFill(GfxAxialShading *shading);
  void doRadialShFill(GfxRadialShading *shading);
  void doGouraudTriangleShFill(GfxGouraudTriangleShading *shading);
  void gouraudFillTriangle(double x0, double y0, GfxColor *color0,
			   double x1, double y1, GfxColor *color1,
			   double x2, double y2, GfxColor *color2,
			   int nComps, int depth);
  void doPatchMeshShFill(GfxPatchMeshShading *shading);
  void fillPatch(GfxPatch *patch, int nComps, int depth);
  void doEndPath();

  // path clipping operators
  void opClip(Object args[], int numArgs);
  void opEOClip(Object args[], int numArgs);

  // text object operators
  void opBeginText(Object args[], int numArgs);
  void opEndText(Object args[], int numArgs);

  // text state operators
  void opSetCharSpacing(Object args[], int numArgs);
  void opSetFont(Object args[], int numArgs);
  void opSetTextLeading(Object args[], int numArgs);
  void opSetTextRender(Object args[], int numArgs);
  void opSetTextRise(Object args[], int numArgs);
  void opSetWordSpacing(Object args[], int numArgs);
  void opSetHorizScaling(Object args[], int numArgs);

  // text positioning operators
  void opTextMove(Object args[], int numArgs);
  void opTextMoveSet(Object args[], int numArgs);
  void opSetTextMatrix(Object args[], int numArgs);
  void opTextNextLine(Object args[], int numArgs);

  // text string operators
  void opShowText(Object args[], int numArgs);
  void opMoveShowText(Object args[], int numArgs);
  void opMoveSetShowText(Object args[], int numArgs);
  void opShowSpaceText(Object args[], int numArgs);
  void doShowText(GString *s);

  // XObject operators
  void opXObject(Object args[], int numArgs);
  void doImage(Object *ref, Stream *str, GBool inlineImg);
  void doForm(Object *str);
  void doForm1(Object *str, Dict *resDict, double *matrix, double *bbox,
	       GBool transpGroup = gFalse, GBool softMask = gFalse,
	       GfxColorSpace *blendingColorSpace = NULL,
	       GBool isolated = gFalse, GBool knockout = gFalse,
	       GBool alpha = gFalse, Function *transferFunc = NULL,
	       GfxColor *backdropColor = NULL);

  // in-line image operators
  void opBeginImage(Object args[], int numArgs);
  Stream *buildImageStream();
  void opImageData(Object args[], int numArgs);
  void opEndImage(Object args[], int numArgs);

  // type 3 font operators
  void opSetCharWidth(Object args[], int numArgs);
  void opSetCacheDevice(Object args[], int numArgs);

  // compatibility operators
  void opBeginIgnoreUndef(Object args[], int numArgs);
  void opEndIgnoreUndef(Object args[], int numArgs);

  // marked content operators
  void opBeginMarkedContent(Object args[], int numArgs);
  void opEndMarkedContent(Object args[], int numArgs);
  void opMarkPoint(Object args[], int numArgs);

  void pushResources(Dict *resDict);
  void popResources();
};

#endif
//========================================================================
//
// GfxFont.cc
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "gmem.h"
#include "Error.h"
#include "Object.h"
#include "Dict.h"
#include "GlobalParams.h"
#include "CMap.h"
#include "CharCodeToUnicode.h"
#include "FontEncodingTables.h"
#include "BuiltinFontTables.h"
#include "FoFiType1.h"
#include "FoFiType1C.h"
#include "FoFiTrueType.h"
#include "GfxFont.h"

//------------------------------------------------------------------------

struct StdFontMapEntry {
  char *altName;
  char *properName;
};

// Acrobat 4.0 and earlier substituted Base14-compatible fonts without
// providing Widths and a FontDescriptor, so we munge the names into
// the proper Base14 names.  This table is from implementation note 44
// in the PDF 1.4 spec, with some additions based on empirical
// evidence.
static StdFontMapEntry stdFontMap[] = {
  { "Arial",                        "Helvetica" },
  { "Arial,Bold",                   "Helvetica-Bold" },
  { "Arial,BoldItalic",             "Helvetica-BoldOblique" },
  { "Arial,Italic",                 "Helvetica-Oblique" },
  { "Arial-Bold",                   "Helvetica-Bold" },
  { "Arial-BoldItalic",             "Helvetica-BoldOblique" },
  { "Arial-BoldItalicMT",           "Helvetica-BoldOblique" },
  { "Arial-BoldMT",                 "Helvetica-Bold" },
  { "Arial-Italic",                 "Helvetica-Oblique" },
  { "Arial-ItalicMT",               "Helvetica-Oblique" },
  { "ArialMT",                      "Helvetica" },
  { "Courier,Bold",                 "Courier-Bold" },
  { "Courier,BoldItalic",           "Courier-BoldOblique" },
  { "Courier,Italic",               "Courier-Oblique" },
  { "CourierNew",                   "Courier" },
  { "CourierNew,Bold",              "Courier-Bold" },
  { "CourierNew,BoldItalic",        "Courier-BoldOblique" },
  { "CourierNew,Italic",            "Courier-Oblique" },
  { "CourierNew-Bold",              "Courier-Bold" },
  { "CourierNew-BoldItalic",        "Courier-BoldOblique" },
  { "CourierNew-Italic",            "Courier-Oblique" },
  { "CourierNewPS-BoldItalicMT",    "Courier-BoldOblique" },
  { "CourierNewPS-BoldMT",          "Courier-Bold" },
  { "CourierNewPS-ItalicMT",        "Courier-Oblique" },
  { "CourierNewPSMT",               "Courier" },
  { "Helvetica,Bold",               "Helvetica-Bold" },
  { "Helvetica,BoldItalic",         "Helvetica-BoldOblique" },
  { "Helvetica,Italic",             "Helvetica-Oblique" },
  { "Helvetica-BoldItalic",         "Helvetica-BoldOblique" },
  { "Helvetica-Italic",             "Helvetica-Oblique" },
  { "Symbol,Bold",                  "Symbol" },
  { "Symbol,BoldItalic",            "Symbol" },
  { "Symbol,Italic",                "Symbol" },
  { "TimesNewRoman",                "Times-Roman" },
  { "TimesNewRoman,Bold",           "Times-Bold" },
  { "TimesNewRoman,BoldItalic",     "Times-BoldItalic" },
  { "TimesNewRoman,Italic",         "Times-Italic" },
  { "TimesNewRoman-Bold",           "Times-Bold" },
  { "TimesNewRoman-BoldItalic",     "Times-BoldItalic" },
  { "TimesNewRoman-Italic",         "Times-Italic" },
  { "TimesNewRomanPS",              "Times-Roman" },
  { "TimesNewRomanPS-Bold",         "Times-Bold" },
  { "TimesNewRomanPS-BoldItalic",   "Times-BoldItalic" },
  { "TimesNewRomanPS-BoldItalicMT", "Times-BoldItalic" },
  { "TimesNewRomanPS-BoldMT",       "Times-Bold" },
  { "TimesNewRomanPS-Italic",       "Times-Italic" },
  { "TimesNewRomanPS-ItalicMT",     "Times-Italic" },
  { "TimesNewRomanPSMT",            "Times-Roman" },
  { "TimesNewRomanPSMT,Bold",       "Times-Bold" },
  { "TimesNewRomanPSMT,BoldItalic", "Times-BoldItalic" },
  { "TimesNewRomanPSMT,Italic",     "Times-Italic" }
};

//------------------------------------------------------------------------
// GfxFont
//------------------------------------------------------------------------

GfxFont *GfxFont::makeFont(XRef *xref, char *tagA, Ref idA, Dict *fontDict) {
  GString *nameA;
  GfxFont *font;
  Object obj1;

  // get base font name
  nameA = NULL;
  fontDict->lookup("BaseFont", &obj1);
  if (obj1.isName()) {
    nameA = new GString(obj1.getName());
  }
  obj1.free();

  // get font type
  font = NULL;
  fontDict->lookup("Subtype", &obj1);
  if (obj1.isName("Type1") || obj1.isName("MMType1")) {
    font = new Gfx8BitFont(xref, tagA, idA, nameA, fontType1, fontDict);
  } else if (obj1.isName("Type1C")) {
    font = new Gfx8BitFont(xref, tagA, idA, nameA, fontType1C, fontDict);
  } else if (obj1.isName("Type3")) {
    font = new Gfx8BitFont(xref, tagA, idA, nameA, fontType3, fontDict);
  } else if (obj1.isName("TrueType")) {
    font = new Gfx8BitFont(xref, tagA, idA, nameA, fontTrueType, fontDict);
  } else if (obj1.isName("Type0")) {
    font = new GfxCIDFont(xref, tagA, idA, nameA, fontDict);
  } else {
    error(-1, "Unknown font type: '%s'",
	  obj1.isName() ? obj1.getName() : "???");
    font = new Gfx8BitFont(xref, tagA, idA, nameA, fontUnknownType, fontDict);
  }
  obj1.free();

  return font;
}

GfxFont::GfxFont(char *tagA, Ref idA, GString *nameA) {
  ok = gFalse;
  tag = new GString(tagA);
  id = idA;
  name = nameA;
  origName = nameA;
  embFontName = NULL;
  extFontFile = NULL;
}

GfxFont::~GfxFont() {
  delete tag;
  if (origName && origName != name) {
    delete origName;
  }
  if (name) {
    delete name;
  }
  if (embFontName) {
    delete embFontName;
  }
  if (extFontFile) {
    delete extFontFile;
  }
}

void GfxFont::readFontDescriptor(XRef *xref, Dict *fontDict) {
  Object obj1, obj2, obj3, obj4;
  double t;
  int i;

  // assume Times-Roman by default (for substitution purposes)
  flags = fontSerif;

  embFontID.num = -1;
  embFontID.gen = -1;
  missingWidth = 0;

  if (fontDict->lookup("FontDescriptor", &obj1)->isDict()) {

    // get flags
    if (obj1.dictLookup("Flags", &obj2)->isInt()) {
      flags = obj2.getInt();
    }
    obj2.free();

    // get name
    obj1.dictLookup("FontName", &obj2);
    if (obj2.isName()) {
      embFontName = new GString(obj2.getName());
    }
    obj2.free();

    // look for embedded font file
    if (obj1.dictLookupNF("FontFile", &obj2)->isRef()) {
      embFontID = obj2.getRef();
      if (type != fontType1) {
	error(-1, "Mismatch between font type and embedded font file");
	type = fontType1;
      }
    }
    obj2.free();
    if (embFontID.num == -1 &&
	obj1.dictLookupNF("FontFile2", &obj2)->isRef()) {
      embFontID = obj2.getRef();
      if (type != fontTrueType && type != fontCIDType2) {
	error(-1, "Mismatch between font type and embedded font file");
	type = type == fontCIDType0 ? fontCIDType2 : fontTrueType;
      }
    }
    obj2.free();
    if (embFontID.num == -1 &&
	obj1.dictLookupNF("FontFile3", &obj2)->isRef()) {
      if (obj2.fetch(xref, &obj3)->isStream()) {
	obj3.streamGetDict()->lookup("Subtype", &obj4);
	if (obj4.isName("Type1")) {
	  embFontID = obj2.getRef();
	  if (type != fontType1) {
	    error(-1, "Mismatch between font type and embedded font file");
	    type = fontType1;
	  }
	} else if (obj4.isName("Type1C")) {
	  embFontID = obj2.getRef();
	  if (type != fontType1 && type != fontType1C) {
	    error(-1, "Mismatch between font type and embedded font file");
	  }
	  type = fontType1C;
	} else if (obj4.isName("TrueType")) {
	  embFontID = obj2.getRef();
	  if (type != fontTrueType) {
	    error(-1, "Mismatch between font type and embedded font file");
	    type = fontTrueType;
	  }
	} else if (obj4.isName("CIDFontType0C")) {
	  embFontID = obj2.getRef();
	  if (type != fontCIDType0) {
	    error(-1, "Mismatch between font type and embedded font file");
	  }
	  type = fontCIDType0C;
	} else if (obj4.isName("OpenType")) {
	  embFontID = obj2.getRef();
	  if (type == fontTrueType) {
	    type = fontTrueTypeOT;
	  } else if (type == fontType1) {
	    type = fontType1COT;
	  } else if (type == fontCIDType0) {
	    type = fontCIDType0COT;
	  } else if (type == fontCIDType2) {
	    type = fontCIDType2OT;
	  } else {
	    error(-1, "Mismatch between font type and embedded font file");
	  }
	} else {
	  error(-1, "Unknown embedded font type '%s'",
		obj4.isName() ? obj4.getName() : "???");
	}
	obj4.free();
      }
      obj3.free();
    }
    obj2.free();

    // look for MissingWidth
    obj1.dictLookup("MissingWidth", &obj2);
    if (obj2.isNum()) {
      missingWidth = obj2.getNum();
    }
    obj2.free();

    // get Ascent and Descent
    obj1.dictLookup("Ascent", &obj2);
    if (obj2.isNum()) {
      t = 0.001 * obj2.getNum();
      // some broken font descriptors set ascent and descent to 0
      if (t != 0) {
	ascent = t;
      }
    }
    obj2.free();
    obj1.dictLookup("Descent", &obj2);
    if (obj2.isNum()) {
      t = 0.001 * obj2.getNum();
      // some broken font descriptors set ascent and descent to 0
      if (t != 0) {
	descent = t;
      }
      // some broken font descriptors specify a positive descent
      if (descent > 0) {
	descent = -descent;
      }
    }
    obj2.free();

    // font FontBBox
    if (obj1.dictLookup("FontBBox", &obj2)->isArray()) {
      for (i = 0; i < 4 && i < obj2.arrayGetLength(); ++i) {
	if (obj2.arrayGet(i, &obj3)->isNum()) {
	  fontBBox[i] = 0.001 * obj3.getNum();
	}
	obj3.free();
      }
    }
    obj2.free();

  }
  obj1.free();
}

CharCodeToUnicode *GfxFont::readToUnicodeCMap(Dict *fontDict, int nBits,
					      CharCodeToUnicode *ctu) {
  GString *buf;
  Object obj1;
  int c;

  if (!fontDict->lookup("ToUnicode", &obj1)->isStream()) {
    obj1.free();
    return NULL;
  }
  buf = new GString();
  obj1.streamReset();
  while ((c = obj1.streamGetChar()) != EOF) {
    buf->append(c);
  }
  obj1.streamClose();
  obj1.free();
  if (ctu) {
    ctu->mergeCMap(buf, nBits);
  } else {
    ctu = CharCodeToUnicode::parseCMap(buf, nBits);
  }
  delete buf;
  return ctu;
}

void GfxFont::findExtFontFile() {
  static char *type1Exts[] = { ".pfa", ".pfb", ".ps", "", NULL };
  static char *ttExts[] = { ".ttf", NULL };

  if (name) {
    if (type == fontType1) {
      extFontFile = globalParams->findFontFile(name, type1Exts);
    } else if (type == fontTrueType) {
      extFontFile = globalParams->findFontFile(name, ttExts);
    }
  }
}

char *GfxFont::readExtFontFile(int *len) {
  FILE *f;
  char *buf;

  if (!(f = fopen(extFontFile->getCString(), "rb"))) {
    error(-1, "External font file '%s' vanished", extFontFile->getCString());
    return NULL;
  }
  fseek(f, 0, SEEK_END);
  *len = (int)ftell(f);
  fseek(f, 0, SEEK_SET);
  buf = (char *)gmalloc(*len);
  if ((int)fread(buf, 1, *len, f) != *len) {
    error(-1, "Error reading external font file '%s'",
	  extFontFile->getCString());
  }
  fclose(f);
  return buf;
}

char *GfxFont::readEmbFontFile(XRef *xref, int *len) {
  char *buf;
  Object obj1, obj2;
  Stream *str;
  int c;
  int size, i;

  obj1.initRef(embFontID.num, embFontID.gen);
  obj1.fetch(xref, &obj2);
  if (!obj2.isStream()) {
    error(-1, "Embedded font file is not a stream");
    obj2.free();
    obj1.free();
    embFontID.num = -1;
    return NULL;
  }
  str = obj2.getStream();

  buf = NULL;
  i = size = 0;
  str->reset();
  while ((c = str->getChar()) != EOF) {
    if (i == size) {
      size += 4096;
      buf = (char *)grealloc(buf, size);
    }
    buf[i++] = c;
  }
  *len = i;
  str->close();

  obj2.free();
  obj1.free();

  return buf;
}

//------------------------------------------------------------------------
// Gfx8BitFont
//------------------------------------------------------------------------

Gfx8BitFont::Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
			 GfxFontType typeA, Dict *fontDict):
  GfxFont(tagA, idA, nameA)
{
  GString *name2;
  BuiltinFont *builtinFont;
  char **baseEnc;
  GBool baseEncFromFontFile;
  char *buf;
  int len;
  FoFiType1 *ffT1;
  FoFiType1C *ffT1C;
  int code, code2;
  char *charName;
  GBool missing, hex;
  Unicode toUnicode[256];
  CharCodeToUnicode *utu, *ctu2;
  Unicode uBuf[8];
  double mul;
  int firstChar, lastChar;
  Gushort w;
  Object obj1, obj2, obj3;
  int n, i, a, b, m;

  type = typeA;
  ctu = NULL;

  // do font name substitution for various aliases of the Base 14 font
  // names
  if (name) {
    name2 = name->copy();
    i = 0;
    while (i < name2->getLength()) {
      if (name2->getChar(i) == ' ') {
	name2->del(i);
      } else {
	++i;
      }
    }
    a = 0;
    b = sizeof(stdFontMap) / sizeof(StdFontMapEntry);
    // invariant: stdFontMap[a].altName <= name2 < stdFontMap[b].altName
    while (b - a > 1) {
      m = (a + b) / 2;
      if (name2->cmp(stdFontMap[m].altName) >= 0) {
	a = m;
      } else {
	b = m;
      }
    }
    if (!name2->cmp(stdFontMap[a].altName)) {
      name = new GString(stdFontMap[a].properName);
    }
    delete name2;
  }

  // is it a built-in font?
  builtinFont = NULL;
  if (name) {
    for (i = 0; i < nBuiltinFonts; ++i) {
      if (!name->cmp(builtinFonts[i].name)) {
	builtinFont = &builtinFonts[i];
	break;
      }
    }
  }

  // default ascent/descent values
  if (builtinFont) {
    ascent = 0.001 * builtinFont->ascent;
    descent = 0.001 * builtinFont->descent;
    fontBBox[0] = 0.001 * builtinFont->bbox[0];
    fontBBox[1] = 0.001 * builtinFont->bbox[1];
    fontBBox[2] = 0.001 * builtinFont->bbox[2];
    fontBBox[3] = 0.001 * builtinFont->bbox[3];
  } else {
    ascent = 0.95;
    descent = -0.35;
    fontBBox[0] = fontBBox[1] = fontBBox[2] = fontBBox[3] = 0;
  }

  // get info from font descriptor
  readFontDescriptor(xref, fontDict);

  // for non-embedded fonts, don't trust the ascent/descent/bbox
  // values from the font descriptor
  if (builtinFont && embFontID.num < 0) {
    ascent = 0.001 * builtinFont->ascent;
    descent = 0.001 * builtinFont->descent;
    fontBBox[0] = 0.001 * builtinFont->bbox[0];
    fontBBox[1] = 0.001 * builtinFont->bbox[1];
    fontBBox[2] = 0.001 * builtinFont->bbox[2];
    fontBBox[3] = 0.001 * builtinFont->bbox[3];
  }

  // look for an external font file
  findExtFontFile();

  // get font matrix
  fontMat[0] = fontMat[3] = 1;
  fontMat[1] = fontMat[2] = fontMat[4] = fontMat[5] = 0;
  if (fontDict->lookup("FontMatrix", &obj1)->isArray()) {
    for (i = 0; i < 6 && i < obj1.arrayGetLength(); ++i) {
      if (obj1.arrayGet(i, &obj2)->isNum()) {
	fontMat[i] = obj2.getNum();
      }
      obj2.free();
    }
  }
  obj1.free();

  // get Type 3 bounding box, font definition, and resources
  if (type == fontType3) {
    if (fontDict->lookup("FontBBox", &obj1)->isArray()) {
      for (i = 0; i < 4 && i < obj1.arrayGetLength(); ++i) {
	if (obj1.arrayGet(i, &obj2)->isNum()) {
	  fontBBox[i] = obj2.getNum();
	}
	obj2.free();
      }
    }
    obj1.free();
    if (!fontDict->lookup("CharProcs", &charProcs)->isDict()) {
      error(-1, "Missing or invalid CharProcs dictionary in Type 3 font");
      charProcs.free();
    }
    if (!fontDict->lookup("Resources", &resources)->isDict()) {
      resources.free();
    }
  }

  //----- build the font encoding -----

  // Encodings start with a base encoding, which can come from
  // (in order of priority):
  //   1. FontDict.Encoding or FontDict.Encoding.BaseEncoding
  //        - MacRoman / MacExpert / WinAnsi / Standard
  //   2. embedded or external font file
  //   3. default:
  //        - builtin --> builtin encoding
  //        - TrueType --> WinAnsiEncoding
  //        - others --> StandardEncoding
  // and then add a list of differences (if any) from
  // FontDict.Encoding.Differences.

  // check FontDict for base encoding
  hasEncoding = gFalse;
  usesMacRomanEnc = gFalse;
  baseEnc = NULL;
  baseEncFromFontFile = gFalse;
  fontDict->lookup("Encoding", &obj1);
  if (obj1.isDict()) {
    obj1.dictLookup("BaseEncoding", &obj2);
    if (obj2.isName("MacRomanEncoding")) {
      hasEncoding = gTrue;
      usesMacRomanEnc = gTrue;
      baseEnc = macRomanEncoding;
    } else if (obj2.isName("MacExpertEncoding")) {
      hasEncoding = gTrue;
      baseEnc = macExpertEncoding;
    } else if (obj2.isName("WinAnsiEncoding")) {
      hasEncoding = gTrue;
      baseEnc = winAnsiEncoding;
    }
    obj2.free();
  } else if (obj1.isName("MacRomanEncoding")) {
    hasEncoding = gTrue;
    usesMacRomanEnc = gTrue;
    baseEnc = macRomanEncoding;
  } else if (obj1.isName("MacExpertEncoding")) {
    hasEncoding = gTrue;
    baseEnc = macExpertEncoding;
  } else if (obj1.isName("WinAnsiEncoding")) {
    hasEncoding = gTrue;
    baseEnc = winAnsiEncoding;
  }

  // check embedded or external font file for base encoding
  // (only for Type 1 fonts - trying to get an encoding out of a
  // TrueType font is a losing proposition)
  ffT1 = NULL;
  ffT1C = NULL;
  buf = NULL;
  if (type == fontType1 && (extFontFile || embFontID.num >= 0)) {
    if (extFontFile) {
      ffT1 = FoFiType1::load(extFontFile->getCString());
    } else {
      buf = readEmbFontFile(xref, &len);
      ffT1 = FoFiType1::make(buf, len);
    }
    if (ffT1) {
      if (ffT1->getName()) {
	if (embFontName) {
	  delete embFontName;
	}
	embFontName = new GString(ffT1->getName());
      }
      if (!baseEnc) {
	baseEnc = ffT1->getEncoding();
	baseEncFromFontFile = gTrue;
      }
    }
  } else if (type == fontType1C && (extFontFile || embFontID.num >= 0)) {
    if (extFontFile) {
      ffT1C = FoFiType1C::load(extFontFile->getCString());
    } else {
      buf = readEmbFontFile(xref, &len);
      ffT1C = FoFiType1C::make(buf, len);
    }
    if (ffT1C) {
      if (ffT1C->getName()) {
	if (embFontName) {
	  delete embFontName;
	}
	embFontName = new GString(ffT1C->getName());
      }
      if (!baseEnc) {
	baseEnc = ffT1C->getEncoding();
	baseEncFromFontFile = gTrue;
      }
    }
  }
  if (buf) {
    gfree(buf);
  }

  // get default base encoding
  if (!baseEnc) {
    if (builtinFont && embFontID.num < 0) {
      baseEnc = builtinFont->defaultBaseEnc;
      hasEncoding = gTrue;
    } else if (type == fontTrueType) {
      baseEnc = winAnsiEncoding;
    } else {
      baseEnc = standardEncoding;
    }
  }

  // copy the base encoding
  for (i = 0; i < 256; ++i) {
    enc[i] = baseEnc[i];
    if ((encFree[i] = baseEncFromFontFile) && enc[i]) {
      enc[i] = copyString(baseEnc[i]);
    }
  }

  // some Type 1C font files have empty encodings, which can break the
  // T1C->T1 conversion (since the 'seac' operator depends on having
  // the accents in the encoding), so we fill in any gaps from
  // StandardEncoding
  if (type == fontType1C && (extFontFile || embFontID.num >= 0) &&
      baseEncFromFontFile) {
    for (i = 0; i < 256; ++i) {
      if (!enc[i] && standardEncoding[i]) {
	enc[i] = standardEncoding[i];
	encFree[i] = gFalse;
      }
    }
  }

  // merge differences into encoding
  if (obj1.isDict()) {
    obj1.dictLookup("Differences", &obj2);
    if (obj2.isArray()) {
      hasEncoding = gTrue;
      code = 0;
      for (i = 0; i < obj2.arrayGetLength(); ++i) {
	obj2.arrayGet(i, &obj3);
	if (obj3.isInt()) {
	  code = obj3.getInt();
	} else if (obj3.isName()) {
	  if (code >= 0 && code < 256) {
	    if (encFree[code]) {
	      gfree(enc[code]);
	    }
	    enc[code] = copyString(obj3.getName());
	    encFree[code] = gTrue;
	  }
	  ++code;
	} else {
	  error(-1, "Wrong type in font encoding resource differences (%s)",
		obj3.getTypeName());
	}
	obj3.free();
      }
    }
    obj2.free();
  }
  obj1.free();
  if (ffT1) {
    delete ffT1;
  }
  if (ffT1C) {
    delete ffT1C;
  }

  //----- build the mapping to Unicode -----

  // pass 1: use the name-to-Unicode mapping table
  missing = hex = gFalse;
  for (code = 0; code < 256; ++code) {
    if ((charName = enc[code])) {
      if (!(toUnicode[code] = globalParams->mapNameToUnicode(charName)) &&
	  strcmp(charName, ".notdef")) {
	// if it wasn't in the name-to-Unicode table, check for a
	// name that looks like 'Axx' or 'xx', where 'A' is any letter
	// and 'xx' is two hex digits
	if ((strlen(charName) == 3 &&
	     isalpha(charName[0]) &&
	     isxdigit(charName[1]) && isxdigit(charName[2]) &&
	     ((charName[1] >= 'a' && charName[1] <= 'f') ||
	      (charName[1] >= 'A' && charName[1] <= 'F') ||
	      (charName[2] >= 'a' && charName[2] <= 'f') ||
	      (charName[2] >= 'A' && charName[2] <= 'F'))) ||
	    (strlen(charName) == 2 &&
	     isxdigit(charName[0]) && isxdigit(charName[1]) &&
	     ((charName[0] >= 'a' && charName[0] <= 'f') ||
	      (charName[0] >= 'A' && charName[0] <= 'F') ||
	      (charName[1] >= 'a' && charName[1] <= 'f') ||
	      (charName[1] >= 'A' && charName[1] <= 'F')))) {
	  hex = gTrue;
	}
	missing = gTrue;
      }
    } else {
      toUnicode[code] = 0;
    }
  }

  // pass 2: try to fill in the missing chars, looking for names of
  // the form 'Axx', 'xx', 'Ann', 'ABnn', or 'nn', where 'A' and 'B'
  // are any letters, 'xx' is two hex digits, and 'nn' is 2-4
  // decimal digits
  if (missing && globalParams->getMapNumericCharNames()) {
    for (code = 0; code < 256; ++code) {
      if ((charName = enc[code]) && !toUnicode[code] &&
	  strcmp(charName, ".notdef")) {
	n = strlen(charName);
	code2 = -1;
	if (hex && n == 3 && isalpha(charName[0]) &&
	    isxdigit(charName[1]) && isxdigit(charName[2])) {
	  sscanf(charName+1, "%x", &code2);
	} else if (hex && n == 2 &&
		   isxdigit(charName[0]) && isxdigit(charName[1])) {
	  sscanf(charName, "%x", &code2);
	} else if (!hex && n >= 2 && n <= 4 &&
		   isdigit(charName[0]) && isdigit(charName[1])) {
	  code2 = atoi(charName);
	} else if (n >= 3 && n <= 5 &&
		   isdigit(charName[1]) && isdigit(charName[2])) {
	  code2 = atoi(charName+1);
	} else if (n >= 4 && n <= 6 &&
		   isdigit(charName[2]) && isdigit(charName[3])) {
	  code2 = atoi(charName+2);
	}
	if (code2 >= 0 && code2 <= 0xff) {
	  toUnicode[code] = (Unicode)code2;
	}
      }
    }

  // if the 'mapUnknownCharNames' flag is set, do a simple pass-through
  // mapping for unknown character names
  } else if (missing && globalParams->getMapUnknownCharNames()) {
    for (code = 0; code < 256; ++code) {
      if (!toUnicode[code]) {
	toUnicode[code] = code;
      }
    }
  }

  // construct the char code -> Unicode mapping object
  ctu = CharCodeToUnicode::make8BitToUnicode(toUnicode);

  // merge in a ToUnicode CMap, if there is one -- this overwrites
  // existing entries in ctu, i.e., the ToUnicode CMap takes
  // precedence, but the other encoding info is allowed to fill in any
  // holes
  readToUnicodeCMap(fontDict, 8, ctu);

  // look for a Unicode-to-Unicode mapping
  if (name && (utu = globalParams->getUnicodeToUnicode(name))) {
    for (i = 0; i < 256; ++i) {
      toUnicode[i] = 0;
    }
    ctu2 = CharCodeToUnicode::make8BitToUnicode(toUnicode);
    for (i = 0; i < 256; ++i) {
      n = ctu->mapToUnicode((CharCode)i, uBuf, 8);
      if (n >= 1) {
	n = utu->mapToUnicode((CharCode)uBuf[0], uBuf, 8);
	if (n >= 1) {
	  ctu2->setMapping((CharCode)i, uBuf, n);
	}
      }
    }
    utu->decRefCnt();
    delete ctu;
    ctu = ctu2;
  }

  //----- get the character widths -----

  // initialize all widths
  for (code = 0; code < 256; ++code) {
    widths[code] = missingWidth * 0.001;
  }

  // use widths from font dict, if present
  fontDict->lookup("FirstChar", &obj1);
  firstChar = obj1.isInt() ? obj1.getInt() : 0;
  obj1.free();
  if (firstChar < 0 || firstChar > 255) {
    firstChar = 0;
  }
  fontDict->lookup("LastChar", &obj1);
  lastChar = obj1.isInt() ? obj1.getInt() : 255;
  obj1.free();
  if (lastChar < 0 || lastChar > 255) {
    lastChar = 255;
  }
  mul = (type == fontType3) ? fontMat[0] : 0.001;
  fontDict->lookup("Widths", &obj1);
  if (obj1.isArray()) {
    flags |= fontFixedWidth;
    if (obj1.arrayGetLength() < lastChar - firstChar + 1) {
      lastChar = firstChar + obj1.arrayGetLength() - 1;
    }
    for (code = firstChar; code <= lastChar; ++code) {
      obj1.arrayGet(code - firstChar, &obj2);
      if (obj2.isNum()) {
	widths[code] = obj2.getNum() * mul;
	if (widths[code] != widths[firstChar]) {
	  flags &= ~fontFixedWidth;
	}
      }
      obj2.free();
    }

  // use widths from built-in font
  } else if (builtinFont) {
    // this is a kludge for broken PDF files that encode char 32
    // as .notdef
    if (builtinFont->widths->getWidth("space", &w)) {
      widths[32] = 0.001 * w;
    }
    for (code = 0; code < 256; ++code) {
      if (enc[code] && builtinFont->widths->getWidth(enc[code], &w)) {
	widths[code] = 0.001 * w;
      }
    }

  // couldn't find widths -- use defaults 
  } else {
    // this is technically an error -- the Widths entry is required
    // for all but the Base-14 fonts -- but certain PDF generators
    // apparently don't include widths for Arial and TimesNewRoman
    if (isFixedWidth()) {
      i = 0;
    } else if (isSerif()) {
      i = 8;
    } else {
      i = 4;
    }
    if (isBold()) {
      i += 2;
    }
    if (isItalic()) {
      i += 1;
    }
    builtinFont = builtinFontSubst[i];
    // this is a kludge for broken PDF files that encode char 32
    // as .notdef
    if (builtinFont->widths->getWidth("space", &w)) {
      widths[32] = 0.001 * w;
    }
    for (code = 0; code < 256; ++code) {
      if (enc[code] && builtinFont->widths->getWidth(enc[code], &w)) {
	widths[code] = 0.001 * w;
      }
    }
  }
  obj1.free();

  ok = gTrue;
}

Gfx8BitFont::~Gfx8BitFont() {
  int i;

  for (i = 0; i < 256; ++i) {
    if (encFree[i] && enc[i]) {
      gfree(enc[i]);
    }
  }
  ctu->decRefCnt();
  if (charProcs.isDict()) {
    charProcs.free();
  }
  if (resources.isDict()) {
    resources.free();
  }
}

int Gfx8BitFont::getNextChar(char *s, int len, CharCode *code,
			     Unicode *u, int uSize, int *uLen,
			     double *dx, double *dy, double *ox, double *oy) {
  CharCode c;

  *code = c = (CharCode)(*s & 0xff);
  *uLen = ctu->mapToUnicode(c, u, uSize);
  *dx = widths[c];
  *dy = *ox = *oy = 0;
  return 1;
}

CharCodeToUnicode *Gfx8BitFont::getToUnicode() {
  ctu->incRefCnt();
  return ctu;
}

Gushort *Gfx8BitFont::getCodeToGIDMap(FoFiTrueType *ff) {
  Gushort *map;
  int cmapPlatform, cmapEncoding;
  int unicodeCmap, macRomanCmap, msSymbolCmap, cmap;
  GBool useMacRoman, useUnicode;
  char *charName;
  Unicode u;
  int code, i, n;

  map = (Gushort *)gmallocn(256, sizeof(Gushort));
  for (i = 0; i < 256; ++i) {
    map[i] = 0;
  }

  // To match up with the Adobe-defined behaviour, we choose a cmap
  // like this:
  // 1. If the PDF font has an encoding:
  //    1a. If the PDF font specified MacRomanEncoding and the
  //        TrueType font has a Macintosh Roman cmap, use it, and
  //        reverse map the char names through MacRomanEncoding to
  //        get char codes.
  //    1b. If the TrueType font has a Microsoft Unicode cmap or a
  //        non-Microsoft Unicode cmap, use it, and use the Unicode
  //        indexes, not the char codes.
  //    1c. If the PDF font is symbolic and the TrueType font has a
  //        Microsoft Symbol cmap, use it, and use char codes
  //        directly (possibly with an offset of 0xf000).
  //    1d. If the TrueType font has a Macintosh Roman cmap, use it,
  //        as in case 1a.
  // 2. If the PDF font does not have an encoding or the PDF font is
  //    symbolic:
  //    2a. If the TrueType font has a Macintosh Roman cmap, use it,
  //        and use char codes directly (possibly with an offset of
  //        0xf000).
  //    2b. If the TrueType font has a Microsoft Symbol cmap, use it,
  //        and use char codes directly (possible with an offset of
  //        0xf000).
  // 3. If none of these rules apply, use the first cmap and hope for
  //    the best (this shouldn't happen).
  unicodeCmap = macRomanCmap = msSymbolCmap = -1;
  for (i = 0; i < ff->getNumCmaps(); ++i) {
    cmapPlatform = ff->getCmapPlatform(i);
    cmapEncoding = ff->getCmapEncoding(i);
    if ((cmapPlatform == 3 && cmapEncoding == 1) ||
	cmapPlatform == 0) {
      unicodeCmap = i;
    } else if (cmapPlatform == 1 && cmapEncoding == 0) {
      macRomanCmap = i;
    } else if (cmapPlatform == 3 && cmapEncoding == 0) {
      msSymbolCmap = i;
    }
  }
  cmap = 0;
  useMacRoman = gFalse;
  useUnicode = gFalse;
  if (hasEncoding) {
    if (usesMacRomanEnc && macRomanCmap >= 0) {
      cmap = macRomanCmap;
      useMacRoman = gTrue;
    } else if (unicodeCmap >= 0) {
      cmap = unicodeCmap;
      useUnicode = gTrue;
    } else if ((flags & fontSymbolic) && msSymbolCmap >= 0) {
      cmap = msSymbolCmap;
    } else if ((flags & fontSymbolic) && macRomanCmap >= 0) {
      cmap = macRomanCmap;
    } else if (macRomanCmap >= 0) {
      cmap = macRomanCmap;
      useMacRoman = gTrue;
    }
  } else {
    if (msSymbolCmap >= 0) {
      cmap = msSymbolCmap;
    } else if (macRomanCmap >= 0) {
      cmap = macRomanCmap;
    }
  }

  // reverse map the char names through MacRomanEncoding, then map the
  // char codes through the cmap
  if (useMacRoman) {
    for (i = 0; i < 256; ++i) {
      if ((charName = enc[i])) {
	if ((code = globalParams->getMacRomanCharCode(charName))) {
	  map[i] = ff->mapCodeToGID(cmap, code);
	}
      }
    }

  // map Unicode through the cmap
  } else if (useUnicode) {
    for (i = 0; i < 256; ++i) {
      if (((charName = enc[i]) &&
	   (u = globalParams->mapNameToUnicode(charName))) ||
	  (n = ctu->mapToUnicode((CharCode)i, &u, 1))) {
	map[i] = ff->mapCodeToGID(cmap, u);
      }
    }

  // map the char codes through the cmap, possibly with an offset of
  // 0xf000
  } else {
    for (i = 0; i < 256; ++i) {
      if (!(map[i] = ff->mapCodeToGID(cmap, i))) {
	map[i] = ff->mapCodeToGID(cmap, 0xf000 + i);
      }
    }
  }

  // try the TrueType 'post' table to handle any unmapped characters
  for (i = 0; i < 256; ++i) {
    if (!map[i] && (charName = enc[i])) {
      map[i] = (Gushort)(int)ff->mapNameToGID(charName);
    }
  }

  return map;
}

Dict *Gfx8BitFont::getCharProcs() {
  return charProcs.isDict() ? charProcs.getDict() : (Dict *)NULL;
}

Object *Gfx8BitFont::getCharProc(int code, Object *proc) {
  if (enc[code] && charProcs.isDict()) {
    charProcs.dictLookup(enc[code], proc);
  } else {
    proc->initNull();
  }
  return proc;
}

Dict *Gfx8BitFont::getResources() {
  return resources.isDict() ? resources.getDict() : (Dict *)NULL;
}

//------------------------------------------------------------------------
// GfxCIDFont
//------------------------------------------------------------------------

static int CDECL cmpWidthExcep(const void *w1, const void *w2) {
  return ((GfxFontCIDWidthExcep *)w1)->first -
         ((GfxFontCIDWidthExcep *)w2)->first;
}

static int CDECL cmpWidthExcepV(const void *w1, const void *w2) {
  return ((GfxFontCIDWidthExcepV *)w1)->first -
         ((GfxFontCIDWidthExcepV *)w2)->first;
}

GfxCIDFont::GfxCIDFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
		       Dict *fontDict):
  GfxFont(tagA, idA, nameA)
{
  Dict *desFontDict;
  GString *collection, *cMapName;
  Object desFontDictObj;
  Object obj1, obj2, obj3, obj4, obj5, obj6;
  CharCodeToUnicode *utu;
  CharCode c;
  Unicode uBuf[8];
  int c1, c2;
  int excepsSize, i, j, k, n;

  ascent = 0.95;
  descent = -0.35;
  fontBBox[0] = fontBBox[1] = fontBBox[2] = fontBBox[3] = 0;
  cMap = NULL;
  ctu = NULL;
  widths.defWidth = 1.0;
  widths.defHeight = -1.0;
  widths.defVY = 0.880;
  widths.exceps = NULL;
  widths.nExceps = 0;
  widths.excepsV = NULL;
  widths.nExcepsV = 0;
  cidToGID = NULL;
  cidToGIDLen = 0;

  // get the descendant font
  if (!fontDict->lookup("DescendantFonts", &obj1)->isArray()) {
    error(-1, "Missing DescendantFonts entry in Type 0 font");
    obj1.free();
    goto err1;
  }
  if (!obj1.arrayGet(0, &desFontDictObj)->isDict()) {
    error(-1, "Bad descendant font in Type 0 font");
    goto err3;
  }
  obj1.free();
  desFontDict = desFontDictObj.getDict();

  // font type
  if (!desFontDict->lookup("Subtype", &obj1)) {
    error(-1, "Missing Subtype entry in Type 0 descendant font");
    goto err3;
  }
  if (obj1.isName("CIDFontType0")) {
    type = fontCIDType0;
  } else if (obj1.isName("CIDFontType2")) {
    type = fontCIDType2;
  } else {
    error(-1, "Unknown Type 0 descendant font type '%s'",
	  obj1.isName() ? obj1.getName() : "???");
    goto err3;
  }
  obj1.free();

  // get info from font descriptor
  readFontDescriptor(xref, desFontDict);

  // look for an external font file
  findExtFontFile();

  //----- encoding info -----

  // char collection
  if (!desFontDict->lookup("CIDSystemInfo", &obj1)->isDict()) {
    error(-1, "Missing CIDSystemInfo dictionary in Type 0 descendant font");
    goto err3;
  }
  obj1.dictLookup("Registry", &obj2);
  obj1.dictLookup("Ordering", &obj3);
  if (!obj2.isString() || !obj3.isString()) {
    error(-1, "Invalid CIDSystemInfo dictionary in Type 0 descendant font");
    goto err4;
  }
  collection = obj2.getString()->copy()->append('-')->append(obj3.getString());
  obj3.free();
  obj2.free();
  obj1.free();

  // look for a ToUnicode CMap
  if (!(ctu = readToUnicodeCMap(fontDict, 16, NULL))) {

    // the "Adobe-Identity" and "Adobe-UCS" collections don't have
    // cidToUnicode files
    if (collection->cmp("Adobe-Identity") &&
	collection->cmp("Adobe-UCS")) {

      // look for a user-supplied .cidToUnicode file
      if (!(ctu = globalParams->getCIDToUnicode(collection))) {
	error(-1, "Unknown character collection '%s'",
	      collection->getCString());
	// fall-through, assuming the Identity mapping -- this appears
	// to match Adobe's behavior
      }
    }
  }

  // look for a Unicode-to-Unicode mapping
  if (name && (utu = globalParams->getUnicodeToUnicode(name))) {
    if (ctu) {
      for (c = 0; c < ctu->getLength(); ++c) {
	n = ctu->mapToUnicode(c, uBuf, 8);
	if (n >= 1) {
	  n = utu->mapToUnicode((CharCode)uBuf[0], uBuf, 8);
	  if (n >= 1) {
	    ctu->setMapping(c, uBuf, n);
	  }
	}
      }
      utu->decRefCnt();
    } else {
      ctu = utu;
    }
  }

  // encoding (i.e., CMap)
  //~ need to handle a CMap stream here
  //~ also need to deal with the UseCMap entry in the stream dict
  if (!fontDict->lookup("Encoding", &obj1)->isName()) {
    error(-1, "Missing or invalid Encoding entry in Type 0 font");
    delete collection;
    goto err3;
  }
  cMapName = new GString(obj1.getName());
  obj1.free();
  if (!(cMap = globalParams->getCMap(collection, cMapName))) {
    error(-1, "Unknown CMap '%s' for character collection '%s'",
	  cMapName->getCString(), collection->getCString());
    delete collection;
    delete cMapName;
    goto err2;
  }
  delete collection;
  delete cMapName;

  // CIDToGIDMap (for embedded TrueType fonts)
  if (type == fontCIDType2) {
    desFontDict->lookup("CIDToGIDMap", &obj1);
    if (obj1.isStream()) {
      cidToGIDLen = 0;
      i = 64;
      cidToGID = (Gushort *)gmallocn(i, sizeof(Gushort));
      obj1.streamReset();
      while ((c1 = obj1.streamGetChar()) != EOF &&
	     (c2 = obj1.streamGetChar()) != EOF) {
	if (cidToGIDLen == i) {
	  i *= 2;
	  cidToGID = (Gushort *)greallocn(cidToGID, i, sizeof(Gushort));
	}
	cidToGID[cidToGIDLen++] = (Gushort)((c1 << 8) + c2);
      }
    } else if (!obj1.isName("Identity") && !obj1.isNull()) {
      error(-1, "Invalid CIDToGIDMap entry in CID font");
    }
    obj1.free();
  }

  //----- character metrics -----

  // default char width
  if (desFontDict->lookup("DW", &obj1)->isInt()) {
    widths.defWidth = obj1.getInt() * 0.001;
  }
  obj1.free();

  // char width exceptions
  if (desFontDict->lookup("W", &obj1)->isArray()) {
    excepsSize = 0;
    i = 0;
    while (i + 1 < obj1.arrayGetLength()) {
      obj1.arrayGet(i, &obj2);
      obj1.arrayGet(i + 1, &obj3);
      if (obj2.isInt() && obj3.isInt() && i + 2 < obj1.arrayGetLength()) {
	if (obj1.arrayGet(i + 2, &obj4)->isNum()) {
	  if (widths.nExceps == excepsSize) {
	    excepsSize += 16;
	    widths.exceps = (GfxFontCIDWidthExcep *)
	      greallocn(widths.exceps,
			excepsSize, sizeof(GfxFontCIDWidthExcep));
	  }
	  widths.exceps[widths.nExceps].first = obj2.getInt();
	  widths.exceps[widths.nExceps].last = obj3.getInt();
	  widths.exceps[widths.nExceps].width = obj4.getNum() * 0.001;
	  ++widths.nExceps;
	} else {
	  error(-1, "Bad widths array in Type 0 font");
	}
	obj4.free();
	i += 3;
      } else if (obj2.isInt() && obj3.isArray()) {
	if (widths.nExceps + obj3.arrayGetLength() > excepsSize) {
	  excepsSize = (widths.nExceps + obj3.arrayGetLength() + 15) & ~15;
	  widths.exceps = (GfxFontCIDWidthExcep *)
	    greallocn(widths.exceps,
		      excepsSize, sizeof(GfxFontCIDWidthExcep));
	}
	j = obj2.getInt();
	for (k = 0; k < obj3.arrayGetLength(); ++k) {
	  if (obj3.arrayGet(k, &obj4)->isNum()) {
	    widths.exceps[widths.nExceps].first = j;
	    widths.exceps[widths.nExceps].last = j;
	    widths.exceps[widths.nExceps].width = obj4.getNum() * 0.001;
	    ++j;
	    ++widths.nExceps;
	  } else {
	    error(-1, "Bad widths array in Type 0 font");
	  }
	  obj4.free();
	}
	i += 2;
      } else {
	error(-1, "Bad widths array in Type 0 font");
	++i;
      }
      obj3.free();
      obj2.free();
    }
    qsort(widths.exceps, widths.nExceps, sizeof(GfxFontCIDWidthExcep),
	  &cmpWidthExcep);
  }
  obj1.free();

  // default metrics for vertical font
  if (desFontDict->lookup("DW2", &obj1)->isArray() &&
      obj1.arrayGetLength() == 2) {
    if (obj1.arrayGet(0, &obj2)->isNum()) {
      widths.defVY = obj2.getNum() * 0.001;
    }
    obj2.free();
    if (obj1.arrayGet(1, &obj2)->isNum()) {
      widths.defHeight = obj2.getNum() * 0.001;
    }
    obj2.free();
  }
  obj1.free();

  // char metric exceptions for vertical font
  if (desFontDict->lookup("W2", &obj1)->isArray()) {
    excepsSize = 0;
    i = 0;
    while (i + 1 < obj1.arrayGetLength()) {
      obj1.arrayGet(i, &obj2);
      obj1.arrayGet(i+ 1, &obj3);
      if (obj2.isInt() && obj3.isInt() && i + 4 < obj1.arrayGetLength()) {
	if (obj1.arrayGet(i + 2, &obj4)->isNum() &&
	    obj1.arrayGet(i + 3, &obj5)->isNum() &&
	    obj1.arrayGet(i + 4, &obj6)->isNum()) {
	  if (widths.nExcepsV == excepsSize) {
	    excepsSize += 16;
	    widths.excepsV = (GfxFontCIDWidthExcepV *)
	      greallocn(widths.excepsV,
			excepsSize, sizeof(GfxFontCIDWidthExcepV));
	  }
	  widths.excepsV[widths.nExcepsV].first = obj2.getInt();
	  widths.excepsV[widths.nExcepsV].last = obj3.getInt();
	  widths.excepsV[widths.nExcepsV].height = obj4.getNum() * 0.001;
	  widths.excepsV[widths.nExcepsV].vx = obj5.getNum() * 0.001;
	  widths.excepsV[widths.nExcepsV].vy = obj6.getNum() * 0.001;
	  ++widths.nExcepsV;
	} else {
	  error(-1, "Bad widths (W2) array in Type 0 font");
	}
	obj6.free();
	obj5.free();
	obj4.free();
	i += 5;
      } else if (obj2.isInt() && obj3.isArray()) {
	if (widths.nExcepsV + obj3.arrayGetLength() / 3 > excepsSize) {
	  excepsSize =
	    (widths.nExcepsV + obj3.arrayGetLength() / 3 + 15) & ~15;
	  widths.excepsV = (GfxFontCIDWidthExcepV *)
	    greallocn(widths.excepsV,
		      excepsSize, sizeof(GfxFontCIDWidthExcepV));
	}
	j = obj2.getInt();
	for (k = 0; k < obj3.arrayGetLength(); k += 3) {
	  if (obj3.arrayGet(k, &obj4)->isNum() &&
	      obj3.arrayGet(k+1, &obj5)->isNum() &&
	      obj3.arrayGet(k+2, &obj6)->isNum()) {
	    widths.excepsV[widths.nExceps].first = j;
	    widths.excepsV[widths.nExceps].last = j;
	    widths.excepsV[widths.nExceps].height = obj4.getNum() * 0.001;
	    widths.excepsV[widths.nExceps].vx = obj5.getNum() * 0.001;
	    widths.excepsV[widths.nExceps].vy = obj6.getNum() * 0.001;
	    ++j;
	    ++widths.nExcepsV;
	  } else {
	    error(-1, "Bad widths (W2) array in Type 0 font");
	  }
	  obj6.free();
	  obj5.free();
	  obj4.free();
	}
	i += 2;
      } else {
	error(-1, "Bad widths (W2) array in Type 0 font");
	++i;
      }
      obj3.free();
      obj2.free();
    }
    qsort(widths.excepsV, widths.nExcepsV, sizeof(GfxFontCIDWidthExcepV),
	  &cmpWidthExcepV);
  }
  obj1.free();

  desFontDictObj.free();
  ok = gTrue;
  return;

 err4:
  obj3.free();
  obj2.free();
 err3:
  obj1.free();
 err2:
  desFontDictObj.free();
 err1:;
}

GfxCIDFont::~GfxCIDFont() {
  if (cMap) {
    cMap->decRefCnt();
  }
  if (ctu) {
    ctu->decRefCnt();
  }
  gfree(widths.exceps);
  gfree(widths.excepsV);
  if (cidToGID) {
    gfree(cidToGID);
  }
}

int GfxCIDFont::getNextChar(char *s, int len, CharCode *code,
			    Unicode *u, int uSize, int *uLen,
			    double *dx, double *dy, double *ox, double *oy) {
  CID cid;
  double w, h, vx, vy;
  int n, a, b, m;

  if (!cMap) {
    *code = 0;
    *uLen = 0;
    *dx = *dy = 0;
    return 1;
  }

  *code = (CharCode)(cid = cMap->getCID(s, len, &n));
  if (ctu) {
    *uLen = ctu->mapToUnicode(cid, u, uSize);
  } else {
    *uLen = 0;
  }

  // horizontal
  if (cMap->getWMode() == 0) {
    w = widths.defWidth;
    h = vx = vy = 0;
    if (widths.nExceps > 0 && cid >= widths.exceps[0].first) {
      a = 0;
      b = widths.nExceps;
      // invariant: widths.exceps[a].first <= cid < widths.exceps[b].first
      while (b - a > 1) {
	m = (a + b) / 2;
	if (widths.exceps[m].first <= cid) {
	  a = m;
	} else {
	  b = m;
	}
      }
      if (cid <= widths.exceps[a].last) {
	w = widths.exceps[a].width;
      }
    }

  // vertical
  } else {
    w = 0;
    h = widths.defHeight;
    vx = widths.defWidth / 2;
    vy = widths.defVY;
    if (widths.nExcepsV > 0 && cid >= widths.excepsV[0].first) {
      a = 0;
      b = widths.nExcepsV;
      // invariant: widths.excepsV[a].first <= cid < widths.excepsV[b].first
      while (b - a > 1) {
	m = (a + b) / 2;
	if (widths.excepsV[m].last <= cid) {
	  a = m;
	} else {
	  b = m;
	}
      }
      if (cid <= widths.excepsV[a].last) {
	h = widths.excepsV[a].height;
	vx = widths.excepsV[a].vx;
	vy = widths.excepsV[a].vy;
      }
    }
  }

  *dx = w;
  *dy = h;
  *ox = vx;
  *oy = vy;

  return n;
}

int GfxCIDFont::getWMode() {
  return cMap ? cMap->getWMode() : 0;
}

CharCodeToUnicode *GfxCIDFont::getToUnicode() {
  if (ctu) {
    ctu->incRefCnt();
  }
  return ctu;
}

GString *GfxCIDFont::getCollection() {
  return cMap ? cMap->getCollection() : (GString *)NULL;
}

//------------------------------------------------------------------------
// GfxFontDict
//------------------------------------------------------------------------

GfxFontDict::GfxFontDict(XRef *xref, Ref *fontDictRef, Dict *fontDict) {
  int i;
  Object obj1, obj2;
  Ref r;

  numFonts = fontDict->getLength();
  fonts = (GfxFont **)gmallocn(numFonts, sizeof(GfxFont *));
  for (i = 0; i < numFonts; ++i) {
    fontDict->getValNF(i, &obj1);
    obj1.fetch(xref, &obj2);
    if (obj2.isDict()) {
      if (obj1.isRef()) {
	r = obj1.getRef();
      } else {
	// no indirect reference for this font, so invent a unique one
	// (legal generation numbers are five digits, so any 6-digit
	// number would be safe)
	r.num = i;
	if (fontDictRef) {
	  r.gen = 100000 + fontDictRef->num;
	} else {
	  r.gen = 999999;
	}
      }
      fonts[i] = GfxFont::makeFont(xref, fontDict->getKey(i),
				   r, obj2.getDict());
      if (fonts[i] && !fonts[i]->isOk()) {
	delete fonts[i];
	fonts[i] = NULL;
      }
    } else {
      error(-1, "font resource is not a dictionary");
      fonts[i] = NULL;
    }
    obj1.free();
    obj2.free();
  }
}

GfxFontDict::~GfxFontDict() {
  int i;

  for (i = 0; i < numFonts; ++i) {
    if (fonts[i]) {
      delete fonts[i];
    }
  }
  gfree(fonts);
}

GfxFont *GfxFontDict::lookup(char *tag) {
  int i;

  for (i = 0; i < numFonts; ++i) {
    if (fonts[i] && fonts[i]->matches(tag)) {
      return fonts[i];
    }
  }
  return NULL;
}
//========================================================================
//
// GfxFont.h
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef GFXFONT_H
#define GFXFONT_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "gtypes.h"
#include "GString.h"
#include "Object.h"
#include "CharTypes.h"

class Dict;
class CMap;
class CharCodeToUnicode;
class FoFiTrueType;
struct GfxFontCIDWidths;

//------------------------------------------------------------------------
// GfxFontType
//------------------------------------------------------------------------

enum GfxFontType {
  //----- Gfx8BitFont
  fontUnknownType,
  fontType1,
  fontType1C,
  fontType1COT,
  fontType3,
  fontTrueType,
  fontTrueTypeOT,
  //----- GfxCIDFont
  fontCIDType0,
  fontCIDType0C,
  fontCIDType0COT,
  fontCIDType2,
  fontCIDType2OT
};

//------------------------------------------------------------------------
// GfxFontCIDWidths
//------------------------------------------------------------------------

struct GfxFontCIDWidthExcep {
  CID first;			// this record applies to
  CID last;			//   CIDs <first>..<last>
  double width;			// char width
};

struct GfxFontCIDWidthExcepV {
  CID first;			// this record applies to
  CID last;			//   CIDs <first>..<last>
  double height;		// char height
  double vx, vy;		// origin position
};

struct GfxFontCIDWidths {
  double defWidth;		// default char width
  double defHeight;		// default char height
  double defVY;			// default origin position
  GfxFontCIDWidthExcep *exceps;	// exceptions
  int nExceps;			// number of valid entries in exceps
  GfxFontCIDWidthExcepV *	// exceptions for vertical font
    excepsV;
  int nExcepsV;			// number of valid entries in excepsV
};

//------------------------------------------------------------------------
// GfxFont
//------------------------------------------------------------------------

#define fontFixedWidth (1 << 0)
#define fontSerif      (1 << 1)
#define fontSymbolic   (1 << 2)
#define fontItalic     (1 << 6)
#define fontBold       (1 << 18)

class GfxFont {
public:

  // Build a GfxFont object.
  static GfxFont *makeFont(XRef *xref, char *tagA, Ref idA, Dict *fontDict);

  GfxFont(char *tagA, Ref idA, GString *nameA);

  virtual ~GfxFont();

  GBool isOk() { return ok; }

  // Get font tag.
  GString *getTag() { return tag; }

  // Get font dictionary ID.
  Ref *getID() { return &id; }

  // Does this font match the tag?
  GBool matches(char *tagA) { return !tag->cmp(tagA); }

  // Get base font name.
  GString *getName() { return name; }

  // Get the original font name (ignornig any munging that might have
  // been done to map to a canonical Base-14 font name).
  GString *getOrigName() { return origName; }

  // Get font type.
  GfxFontType getType() { return type; }
  virtual GBool isCIDFont() { return gFalse; }

  // Get embedded font ID, i.e., a ref for the font file stream.
  // Returns false if there is no embedded font.
  GBool getEmbeddedFontID(Ref *embID)
    { *embID = embFontID; return embFontID.num >= 0; }

  // Get the PostScript font name for the embedded font.  Returns
  // NULL if there is no embedded font.
  GString *getEmbeddedFontName() { return embFontName; }

  // Get the name of the external font file.  Returns NULL if there
  // is no external font file.
  GString *getExtFontFile() { return extFontFile; }

  // Get font descriptor flags.
  int getFlags() { return flags; }
  GBool isFixedWidth() { return flags & fontFixedWidth; }
  GBool isSerif() { return flags & fontSerif; }
  GBool isSymbolic() { return flags & fontSymbolic; }
  GBool isItalic() { return flags & fontItalic; }
  GBool isBold() { return flags & fontBold; }

  // Return the font matrix.
  double *getFontMatrix() { return fontMat; }

  // Return the font bounding box.
  double *getFontBBox() { return fontBBox; }

  // Return the ascent and descent values.
  double getAscent() { return ascent; }
  double getDescent() { return descent; }

  // Return the writing mode (0=horizontal, 1=vertical).
  virtual int getWMode() { return 0; }

  // Read an external or embedded font file into a buffer.
  char *readExtFontFile(int *len);
  char *readEmbFontFile(XRef *xref, int *len);

  // Get the next char from a string 
 of <len> bytes, returning the
  // char <code>, its Unicode mapping <u>, its displacement vector
  // (<dx>, <dy>), and its origin offset vector (<ox>, <oy>).  <uSize>
  // is the number of entries available in <u>, and <uLen> is set to
  // the number actually used.  Returns the number of bytes used by
  // the char code.
  virtual int getNextChar(char *s, int len, CharCode *code,
			  Unicode *u, int uSize, int *uLen,
			  double *dx, double *dy, double *ox, double *oy) = 0;

protected:

  void readFontDescriptor(XRef *xref, Dict *fontDict);
  CharCodeToUnicode *readToUnicodeCMap(Dict *fontDict, int nBits,
				       CharCodeToUnicode *ctu);
  void findExtFontFile();

  GString *tag;			// PDF font tag
  Ref id;			// reference (used as unique ID)
  GString *name;		// font name
  GString *origName;		// original font name
  GfxFontType type;		// type of font
  int flags;			// font descriptor flags
  GString *embFontName;		// name of embedded font
  Ref embFontID;		// ref to embedded font file stream
  GString *extFontFile;		// external font file name
  double fontMat[6];		// font matrix (Type 3 only)
  double fontBBox[4];		// font bounding box (Type 3 only)
  double missingWidth;		// "default" width
  double ascent;		// max height above baseline
  double descent;		// max depth below baseline
  GBool ok;
};

//------------------------------------------------------------------------
// Gfx8BitFont
//------------------------------------------------------------------------

class Gfx8BitFont: public GfxFont {
public:

  Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
	      GfxFontType typeA, Dict *fontDict);

  virtual ~Gfx8BitFont();

  virtual int getNextChar(char *s, int len, CharCode *code,
			  Unicode *u, int uSize, int *uLen,
			  double *dx, double *dy, double *ox, double *oy);

  // Return the encoding.
  char **getEncoding() { return enc; }

  // Return the Unicode map.
  CharCodeToUnicode *getToUnicode();

  // Return the character name associated with <code>.
  char *getCharName(int code) { return enc[code]; }

  // Returns true if the PDF font specified an encoding.
  GBool getHasEncoding() { return hasEncoding; }

  // Returns true if the PDF font specified MacRomanEncoding.
  GBool getUsesMacRomanEnc() { return usesMacRomanEnc; }

  // Get width of a character.
  double getWidth(Guchar c) { return widths[c]; }

  // Return a char code-to-GID mapping for the provided font file.
  // (This is only useful for TrueType fonts.)
  Gushort *getCodeToGIDMap(FoFiTrueType *ff);

  // Return the Type 3 CharProc dictionary, or NULL if none.
  Dict *getCharProcs();

  // Return the Type 3 CharProc for the character associated with <code>.
  Object *getCharProc(int code, Object *proc);

  // Return the Type 3 Resources dictionary, or NULL if none.
  Dict *getResources();

private:

  char *enc[256];		// char code --> char name
  char encFree[256];		// boolean for each char name: if set,
				//   the string is malloc'ed
  CharCodeToUnicode *ctu;	// char code --> Unicode
  GBool hasEncoding;
  GBool usesMacRomanEnc;
  double widths[256];		// character widths
  Object charProcs;		// Type 3 CharProcs dictionary
  Object resources;		// Type 3 Resources dictionary
};

//------------------------------------------------------------------------
// GfxCIDFont
//------------------------------------------------------------------------

class GfxCIDFont: public GfxFont {
public:

  GfxCIDFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
	     Dict *fontDict);

  virtual ~GfxCIDFont();

  virtual GBool isCIDFont() { return gTrue; }

  virtual int getNextChar(char *s, int len, CharCode *code,
			  Unicode *u, int uSize, int *uLen,
			  double *dx, double *dy, double *ox, double *oy);

  // Return the writing mode (0=horizontal, 1=vertical).
  virtual int getWMode();

  // Return the Unicode map.
  CharCodeToUnicode *getToUnicode();

  // Get the collection name (<registry>-<ordering>).
  GString *getCollection();

  // Return the CID-to-GID mapping table.  These should only be called
  // if type is fontCIDType2.
  Gushort *getCIDToGID() { return cidToGID; }
  int getCIDToGIDLen() { return cidToGIDLen; }

private:

  CMap *cMap;			// char code --> CID
  CharCodeToUnicode *ctu;	// CID --> Unicode
  GfxFontCIDWidths widths;	// character widths
  Gushort *cidToGID;		// CID --> GID mapping (for embedded
				//   TrueType fonts)
  int cidToGIDLen;
};

//------------------------------------------------------------------------
// GfxFontDict
//------------------------------------------------------------------------

class GfxFontDict {
public:

  // Build the font dictionary, given the PDF font dictionary.
  GfxFontDict(XRef *xref, Ref *fontDictRef, Dict *fontDict);

  // Destructor.
  ~GfxFontDict();

  // Get the specified font.
  GfxFont *lookup(char *tag);

  // Iterative access.
  int getNumFonts() { return numFonts; }
  GfxFont *getFont(int i) { return fonts[i]; }

private:

  GfxFont **fonts;		// list of fonts
  int numFonts;			// number of fonts
};

#endif
//========================================================================
//
// GfxState.cc
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stddef.h>
#include <math.h>
#include <string.h>
#include "gmem.h"
#include "Error.h"
#include "Object.h"
#include "Array.h"
#include "Page.h"
#include "GfxState.h"

//------------------------------------------------------------------------

static inline GfxColorComp clip01(GfxColorComp x) {
  return (x < 0) ? 0 : (x > gfxColorComp1) ? gfxColorComp1 : x;
}

static inline double clip01(double x) {
  return (x < 0) ? 0 : (x > 1) ? 1 : x;
}

//------------------------------------------------------------------------

struct GfxBlendModeInfo {
  char *name;
  GfxBlendMode mode;
};

static GfxBlendModeInfo gfxBlendModeNames[] = {
  { "Normal",     gfxBlendNormal },
  { "Compatible", gfxBlendNormal },
  { "Multiply",   gfxBlendMultiply },
  { "Screen",     gfxBlendScreen },
  { "Overlay",    gfxBlendOverlay },
  { "Darken",     gfxBlendDarken },
  { "Lighten",    gfxBlendLighten },
  { "ColorDodge", gfxBlendColorDodge },
  { "ColorBurn",  gfxBlendColorBurn },
  { "HardLight",  gfxBlendHardLight },
  { "SoftLight",  gfxBlendSoftLight },
  { "Difference", gfxBlendDifference },
  { "Exclusion",  gfxBlendExclusion },
  { "Hue",        gfxBlendHue },
  { "Saturation", gfxBlendSaturation },
  { "Color",      gfxBlendColor },
  { "Luminosity", gfxBlendLuminosity }
};

#define nGfxBlendModeNames \
          ((int)((sizeof(gfxBlendModeNames) / sizeof(GfxBlendModeInfo))))

//------------------------------------------------------------------------

// NB: This must match the GfxColorSpaceMode enum defined in
// GfxState.h
static char *gfxColorSpaceModeNames[] = {
  "DeviceGray",
  "CalGray",
  "DeviceRGB",
  "CalRGB",
  "DeviceCMYK",
  "Lab",
  "ICCBased",
  "Indexed",
  "Separation",
  "DeviceN",
  "Pattern"
};

#define nGfxColorSpaceModes ((sizeof(gfxColorSpaceModeNames) / sizeof(char *)))

//------------------------------------------------------------------------
// GfxColorSpace
//------------------------------------------------------------------------

GfxColorSpace::GfxColorSpace() {
}

GfxColorSpace::~GfxColorSpace() {
}

GfxColorSpace *GfxColorSpace::parse(Object *csObj) {
  GfxColorSpace *cs;
  Object obj1;

  cs = NULL;
  if (csObj->isName()) {
    if (csObj->isName("DeviceGray") || csObj->isName("G")) {
      cs = new GfxDeviceGrayColorSpace();
    } else if (csObj->isName("DeviceRGB") || csObj->isName("RGB")) {
      cs = new GfxDeviceRGBColorSpace();
    } else if (csObj->isName("DeviceCMYK") || csObj->isName("CMYK")) {
      cs = new GfxDeviceCMYKColorSpace();
    } else if (csObj->isName("Pattern")) {
      cs = new GfxPatternColorSpace(NULL);
    } else {
      error(-1, "Bad color space '%s'", csObj->getName());
    }
  } else if (csObj->isArray()) {
    csObj->arrayGet(0, &obj1);
    if (obj1.isName("DeviceGray") || obj1.isName("G")) {
      cs = new GfxDeviceGrayColorSpace();
    } else if (obj1.isName("DeviceRGB") || obj1.isName("RGB")) {
      cs = new GfxDeviceRGBColorSpace();
    } else if (obj1.isName("DeviceCMYK") || obj1.isName("CMYK")) {
      cs = new GfxDeviceCMYKColorSpace();
    } else if (obj1.isName("CalGray")) {
      cs = GfxCalGrayColorSpace::parse(csObj->getArray());
    } else if (obj1.isName("CalRGB")) {
      cs = GfxCalRGBColorSpace::parse(csObj->getArray());
    } else if (obj1.isName("Lab")) {
      cs = GfxLabColorSpace::parse(csObj->getArray());
    } else if (obj1.isName("ICCBased")) {
      cs = GfxICCBasedColorSpace::parse(csObj->getArray());
    } else if (obj1.isName("Indexed") || obj1.isName("I")) {
      cs = GfxIndexedColorSpace::parse(csObj->getArray());
    } else if (obj1.isName("Separation")) {
      cs = GfxSeparationColorSpace::parse(csObj->getArray());
    } else if (obj1.isName("DeviceN")) {
      cs = GfxDeviceNColorSpace::parse(csObj->getArray());
    } else if (obj1.isName("Pattern")) {
      cs = GfxPatternColorSpace::parse(csObj->getArray());
    } else {
      error(-1, "Bad color space");
    }
    obj1.free();
  } else {
    error(-1, "Bad color space - expected name or array");
  }
  return cs;
}

void GfxColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange,
				     int maxImgPixel) {
  int i;

  for (i = 0; i < getNComps(); ++i) {
    decodeLow[i] = 0;
    decodeRange[i] = 1;
  }
}

int GfxColorSpace::getNumColorSpaceModes() {
  return nGfxColorSpaceModes;
}

char *GfxColorSpace::getColorSpaceModeName(int idx) {
  return gfxColorSpaceModeNames[idx];
}

//------------------------------------------------------------------------
// GfxDeviceGrayColorSpace
//------------------------------------------------------------------------

GfxDeviceGrayColorSpace::GfxDeviceGrayColorSpace() {
}

GfxDeviceGrayColorSpace::~GfxDeviceGrayColorSpace() {
}

GfxColorSpace *GfxDeviceGrayColorSpace::copy() {
  return new GfxDeviceGrayColorSpace();
}

void GfxDeviceGrayColorSpace::getGray(GfxColor *color, GfxGray *gray) {
  *gray = clip01(color->c[0]);
}

void GfxDeviceGrayColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
  rgb->r = rgb->g = rgb->b = clip01(color->c[0]);
}

void GfxDeviceGrayColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
  cmyk->c = cmyk->m = cmyk->y = 0;
  cmyk->k = clip01(gfxColorComp1 - color->c[0]);
}

void GfxDeviceGrayColorSpace::getDefaultColor(GfxColor *color) {
  color->c[0] = 0;
}

//------------------------------------------------------------------------
// GfxCalGrayColorSpace
//------------------------------------------------------------------------

GfxCalGrayColorSpace::GfxCalGrayColorSpace() {
  whiteX = whiteY = whiteZ = 1;
  blackX = blackY = blackZ = 0;
  gamma = 1;
}

GfxCalGrayColorSpace::~GfxCalGrayColorSpace() {
}

GfxColorSpace *GfxCalGrayColorSpace::copy() {
  GfxCalGrayColorSpace *cs;

  cs = new GfxCalGrayColorSpace();
  cs->whiteX = whiteX;
  cs->whiteY = whiteY;
  cs->whiteZ = whiteZ;
  cs->blackX = blackX;
  cs->blackY = blackY;
  cs->blackZ = blackZ;
  cs->gamma = gamma;
  return cs;
}

GfxColorSpace *GfxCalGrayColorSpace::parse(Array *arr) {
  GfxCalGrayColorSpace *cs;
  Object obj1, obj2, obj3;

  arr->get(1, &obj1);
  if (!obj1.isDict()) {
    error(-1, "Bad CalGray color space");
    obj1.free();
    return NULL;
  }
  cs = new GfxCalGrayColorSpace();
  if (obj1.dictLookup("WhitePoint", &obj2)->isArray() &&
      obj2.arrayGetLength() == 3) {
    obj2.arrayGet(0, &obj3);
    cs->whiteX = obj3.getNum();
    obj3.free();
    obj2.arrayGet(1, &obj3);
    cs->whiteY = obj3.getNum();
    obj3.free();
    obj2.arrayGet(2, &obj3);
    cs->whiteZ = obj3.getNum();
    obj3.free();
  }
  obj2.free();
  if (obj1.dictLookup("BlackPoint", &obj2)->isArray() &&
      obj2.arrayGetLength() == 3) {
    obj2.arrayGet(0, &obj3);
    cs->blackX = obj3.getNum();
    obj3.free();
    obj2.arrayGet(1, &obj3);
    cs->blackY = obj3.getNum();
    obj3.free();
    obj2.arrayGet(2, &obj3);
    cs->blackZ = obj3.getNum();
    obj3.free();
  }
  obj2.free();
  if (obj1.dictLookup("Gamma", &obj2)->isNum()) {
    cs->gamma = obj2.getNum();
  }
  obj2.free();
  obj1.free();
  return cs;
}

void GfxCalGrayColorSpace::getGray(GfxColor *color, GfxGray *gray) {
  *gray = clip01(color->c[0]);
}

void GfxCalGrayColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
  rgb->r = rgb->g = rgb->b = clip01(color->c[0]);
}

void GfxCalGrayColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
  cmyk->c = cmyk->m = cmyk->y = 0;
  cmyk->k = clip01(gfxColorComp1 - color->c[0]);
}

void GfxCalGrayColorSpace::getDefaultColor(GfxColor *color) {
  color->c[0] = 0;
}

//------------------------------------------------------------------------
// GfxDeviceRGBColorSpace
//------------------------------------------------------------------------

GfxDeviceRGBColorSpace::GfxDeviceRGBColorSpace() {
}

GfxDeviceRGBColorSpace::~GfxDeviceRGBColorSpace() {
}

GfxColorSpace *GfxDeviceRGBColorSpace::copy() {
  return new GfxDeviceRGBColorSpace();
}

void GfxDeviceRGBColorSpace::getGray(GfxColor *color, GfxGray *gray) {
  *gray = clip01((GfxColorComp)(0.3  * color->c[0] +
				0.59 * color->c[1] +
				0.11 * color->c[2] + 0.5));
}

void GfxDeviceRGBColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
  rgb->r = clip01(color->c[0]);
  rgb->g = clip01(color->c[1]);
  rgb->b = clip01(color->c[2]);
}

void GfxDeviceRGBColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
  GfxColorComp c, m, y, k;

  c = clip01(gfxColorComp1 - color->c[0]);
  m = clip01(gfxColorComp1 - color->c[1]);
  y = clip01(gfxColorComp1 - color->c[2]);
  k = c;
  if (m < k) {
    k = m;
  }
  if (y < k) {
    k = y;
  }
  cmyk->c = c - k;
  cmyk->m = m - k;
  cmyk->y = y - k;
  cmyk->k = k;
}

void GfxDeviceRGBColorSpace::getDefaultColor(GfxColor *color) {
  color->c[0] = 0;
  color->c[1] = 0;
  color->c[2] = 0;
}

//------------------------------------------------------------------------
// GfxCalRGBColorSpace
//------------------------------------------------------------------------

GfxCalRGBColorSpace::GfxCalRGBColorSpace() {
  whiteX = whiteY = whiteZ = 1;
  blackX = blackY = blackZ = 0;
  gammaR = gammaG = gammaB = 1;
  mat[0] = 1; mat[1] = 0; mat[2] = 0;
  mat[3] = 0; mat[4] = 1; mat[5] = 0;
  mat[6] = 0; mat[7] = 0; mat[8] = 1;
}

GfxCalRGBColorSpace::~GfxCalRGBColorSpace() {
}

GfxColorSpace *GfxCalRGBColorSpace::copy() {
  GfxCalRGBColorSpace *cs;
  int i;

  cs = new GfxCalRGBColorSpace();
  cs->whiteX = whiteX;
  cs->whiteY = whiteY;
  cs->whiteZ = whiteZ;
  cs->blackX = blackX;
  cs->blackY = blackY;
  cs->blackZ = blackZ;
  cs->gammaR = gammaR;
  cs->gammaG = gammaG;
  cs->gammaB = gammaB;
  for (i = 0; i < 9; ++i) {
    cs->mat[i] = mat[i];
  }
  return cs;
}

GfxColorSpace *GfxCalRGBColorSpace::parse(Array *arr) {
  GfxCalRGBColorSpace *cs;
  Object obj1, obj2, obj3;
  int i;

  arr->get(1, &obj1);
  if (!obj1.isDict()) {
    error(-1, "Bad CalRGB color space");
    obj1.free();
    return NULL;
  }
  cs = new GfxCalRGBColorSpace();
  if (obj1.dictLookup("WhitePoint", &obj2)->isArray() &&
      obj2.arrayGetLength() == 3) {
    obj2.arrayGet(0, &obj3);
    cs->whiteX = obj3.getNum();
    obj3.free();
    obj2.arrayGet(1, &obj3);
    cs->whiteY = obj3.getNum();
    obj3.free();
    obj2.arrayGet(2, &obj3);
    cs->whiteZ = obj3.getNum();
    obj3.free();
  }
  obj2.free();
  if (obj1.dictLookup("BlackPoint", &obj2)->isArray() &&
      obj2.arrayGetLength() == 3) {
    obj2.arrayGet(0, &obj3);
    cs->blackX = obj3.getNum();
    obj3.free();
    obj2.arrayGet(1, &obj3);
    cs->blackY = obj3.getNum();
    obj3.free();
    obj2.arrayGet(2, &obj3);
    cs->blackZ = obj3.getNum();
    obj3.free();
  }
  obj2.free();
  if (obj1.dictLookup("Gamma", &obj2)->isArray() &&
      obj2.arrayGetLength() == 3) {
    obj2.arrayGet(0, &obj3);
    cs->gammaR = obj3.getNum();
    obj3.free();
    obj2.arrayGet(1, &obj3);
    cs->gammaG = obj3.getNum();
    obj3.free();
    obj2.arrayGet(2, &obj3);
    cs->gammaB = obj3.getNum();
    obj3.free();
  }
  obj2.free();
  if (obj1.dictLookup("Matrix", &obj2)->isArray() &&
      obj2.arrayGetLength() == 9) {
    for (i = 0; i < 9; ++i) {
      obj2.arrayGet(i, &obj3);
      cs->mat[i] = obj3.getNum();
      obj3.free();
    }
  }
  obj2.free();
  obj1.free();
  return cs;
}

void GfxCalRGBColorSpace::getGray(GfxColor *color, GfxGray *gray) {
  *gray = clip01((GfxColorComp)(0.299 * color->c[0] +
				0.587 * color->c[1] +
				0.114 * color->c[2] + 0.5));
}

void GfxCalRGBColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
  rgb->r = clip01(color->c[0]);
  rgb->g = clip01(color->c[1]);
  rgb->b = clip01(color->c[2]);
}

void GfxCalRGBColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
  GfxColorComp c, m, y, k;

  c = clip01(gfxColorComp1 - color->c[0]);
  m = clip01(gfxColorComp1 - color->c[1]);
  y = clip01(gfxColorComp1 - color->c[2]);
  k = c;
  if (m < k) {
    k = m;
  }
  if (y < k) {
    k = y;
  }
  cmyk->c = c - k;
  cmyk->m = m - k;
  cmyk->y = y - k;
  cmyk->k = k;
}

void GfxCalRGBColorSpace::getDefaultColor(GfxColor *color) {
  color->c[0] = 0;
  color->c[1] = 0;
  color->c[2] = 0;
}

//------------------------------------------------------------------------
// GfxDeviceCMYKColorSpace
//------------------------------------------------------------------------

GfxDeviceCMYKColorSpace::GfxDeviceCMYKColorSpace() {
}

GfxDeviceCMYKColorSpace::~GfxDeviceCMYKColorSpace() {
}

GfxColorSpace *GfxDeviceCMYKColorSpace::copy() {
  return new GfxDeviceCMYKColorSpace();
}

void GfxDeviceCMYKColorSpace::getGray(GfxColor *color, GfxGray *gray) {
  *gray = clip01((GfxColorComp)(gfxColorComp1 - color->c[3]
				- 0.3  * color->c[0]
				- 0.59 * color->c[1]
				- 0.11 * color->c[2] + 0.5));
}

void GfxDeviceCMYKColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
  double c, m, y, k, c1, m1, y1, k1, r, g, b, x;

  c = colToDbl(color->c[0]);
  m = colToDbl(color->c[1]);
  y = colToDbl(color->c[2]);
  k = colToDbl(color->c[3]);
  c1 = 1 - c;
  m1 = 1 - m;
  y1 = 1 - y;
  k1 = 1 - k;
  // this is a matrix multiplication, unrolled for performance
  //                        C M Y K
  x = c1 * m1 * y1 * k1; // 0 0 0 0
  r = g = b = x;
  x = c1 * m1 * y1 * k;  // 0 0 0 1
  r += 0.1373 * x;
  g += 0.1216 * x;
  b += 0.1255 * x;
  x = c1 * m1 * y  * k1; // 0 0 1 0
  r += x;
  g += 0.9490 * x;
  x = c1 * m1 * y  * k;  // 0 0 1 1
  r += 0.1098 * x;
  g += 0.1020 * x;
  x = c1 * m  * y1 * k1; // 0 1 0 0
  r += 0.9255 * x;
  b += 0.5490 * x;
  x = c1 * m  * y1 * k;  // 0 1 0 1
  r += 0.1412 * x;
  x = c1 * m  * y  * k1; // 0 1 1 0
  r += 0.9294 * x;
  g += 0.1098 * x;
  b += 0.1412 * x;
  x = c1 * m  * y  * k;  // 0 1 1 1
  r += 0.1333 * x;
  x = c  * m1 * y1 * k1; // 1 0 0 0
  g += 0.6784 * x;
  b += 0.9373 * x;
  x = c  * m1 * y1 * k;  // 1 0 0 1
  g += 0.0588 * x;
  b += 0.1412 * x;
  x = c  * m1 * y  * k1; // 1 0 1 0
  g += 0.6510 * x;
  b += 0.3137 * x;
  x = c  * m1 * y  * k;  // 1 0 1 1
  g += 0.0745 * x;
  x = c  * m  * y1 * k1; // 1 1 0 0
  r += 0.1804 * x;
  g += 0.1922 * x;
  b += 0.5725 * x;
  x = c  * m  * y1 * k;  // 1 1 0 1
  b += 0.0078 * x;
  x = c  * m  * y  * k1; // 1 1 1 0
  r += 0.2118 * x;
  g += 0.2119 * x;
  b += 0.2235 * x;
  rgb->r = clip01(dblToCol(r));
  rgb->g = clip01(dblToCol(g));
  rgb->b = clip01(dblToCol(b));
}

void GfxDeviceCMYKColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
  cmyk->c = clip01(color->c[0]);
  cmyk->m = clip01(color->c[1]);
  cmyk->y = clip01(color->c[2]);
  cmyk->k = clip01(color->c[3]);
}

void GfxDeviceCMYKColorSpace::getDefaultColor(GfxColor *color) {
  color->c[0] = 0;
  color->c[1] = 0;
  color->c[2] = 0;
  color->c[3] = gfxColorComp1;
}

//------------------------------------------------------------------------
// GfxLabColorSpace
//------------------------------------------------------------------------

// This is the inverse of MatrixLMN in Example 4.10 from the PostScript
// Language Reference, Third Edition.
static double xyzrgb[3][3] = {
  {  3.240449, -1.537136, -0.498531 },
  { -0.969265,  1.876011,  0.041556 },
  {  0.055643, -0.204026,  1.057229 }
};

GfxLabColorSpace::GfxLabColorSpace() {
  whiteX = whiteY = whiteZ = 1;
  blackX = blackY = blackZ = 0;
  aMin = bMin = -100;
  aMax = bMax = 100;
}

GfxLabColorSpace::~GfxLabColorSpace() {
}

GfxColorSpace *GfxLabColorSpace::copy() {
  GfxLabColorSpace *cs;

  cs = new GfxLabColorSpace();
  cs->whiteX = whiteX;
  cs->whiteY = whiteY;
  cs->whiteZ = whiteZ;
  cs->blackX = blackX;
  cs->blackY = blackY;
  cs->blackZ = blackZ;
  cs->aMin = aMin;
  cs->aMax = aMax;
  cs->bMin = bMin;
  cs->bMax = bMax;
  cs->kr = kr;
  cs->kg = kg;
  cs->kb = kb;
  return cs;
}

GfxColorSpace *GfxLabColorSpace::parse(Array *arr) {
  GfxLabColorSpace *cs;
  Object obj1, obj2, obj3;

  arr->get(1, &obj1);
  if (!obj1.isDict()) {
    error(-1, "Bad Lab color space");
    obj1.free();
    return NULL;
  }
  cs = new GfxLabColorSpace();
  if (obj1.dictLookup("WhitePoint", &obj2)->isArray() &&
      obj2.arrayGetLength() == 3) {
    obj2.arrayGet(0, &obj3);
    cs->whiteX = obj3.getNum();
    obj3.free();
    obj2.arrayGet(1, &obj3);
    cs->whiteY = obj3.getNum();
    obj3.free();
    obj2.arrayGet(2, &obj3);
    cs->whiteZ = obj3.getNum();
    obj3.free();
  }
  obj2.free();
  if (obj1.dictLookup("BlackPoint", &obj2)->isArray() &&
      obj2.arrayGetLength() == 3) {
    obj2.arrayGet(0, &obj3);
    cs->blackX = obj3.getNum();
    obj3.free();
    obj2.arrayGet(1, &obj3);
    cs->blackY = obj3.getNum();
    obj3.free();
    obj2.arrayGet(2, &obj3);
    cs->blackZ = obj3.getNum();
    obj3.free();
  }
  obj2.free();
  if (obj1.dictLookup("Range", &obj2)->isArray() &&
      obj2.arrayGetLength() == 4) {
    obj2.arrayGet(0, &obj3);
    cs->aMin = obj3.getNum();
    obj3.free();
    obj2.arrayGet(1, &obj3);
    cs->aMax = obj3.getNum();
    obj3.free();
    obj2.arrayGet(2, &obj3);
    cs->bMin = obj3.getNum();
    obj3.free();
    obj2.arrayGet(3, &obj3);
    cs->bMax = obj3.getNum();
    obj3.free();
  }
  obj2.free();
  obj1.free();

  cs->kr = 1 / (xyzrgb[0][0] * cs->whiteX +
		xyzrgb[0][1] * cs->whiteY +
		xyzrgb[0][2] * cs->whiteZ);
  cs->kg = 1 / (xyzrgb[1][0] * cs->whiteX +
		xyzrgb[1][1] * cs->whiteY +
		xyzrgb[1][2] * cs->whiteZ);
  cs->kb = 1 / (xyzrgb[2][0] * cs->whiteX +
		xyzrgb[2][1] * cs->whiteY +
		xyzrgb[2][2] * cs->whiteZ);

  return cs;
}

void GfxLabColorSpace::getGray(GfxColor *color, GfxGray *gray) {
  GfxRGB rgb;

  getRGB(color, &rgb);
  *gray = clip01((GfxColorComp)(0.299 * rgb.r +
				0.587 * rgb.g +
				0.114 * rgb.b + 0.5));
}

void GfxLabColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
  double X, Y, Z;
  double t1, t2;
  double r, g, b;

  // convert L*a*b* to CIE 1931 XYZ color space
  t1 = (colToDbl(color->c[0]) + 16) / 116;
  t2 = t1 + colToDbl(color->c[1]) / 500;
  if (t2 >= (6.0 / 29.0)) {
    X = t2 * t2 * t2;
  } else {
    X = (108.0 / 841.0) * (t2 - (4.0 / 29.0));
  }
  X *= whiteX;
  if (t1 >= (6.0 / 29.0)) {
    Y = t1 * t1 * t1;
  } else {
    Y = (108.0 / 841.0) * (t1 - (4.0 / 29.0));
  }
  Y *= whiteY;
  t2 = t1 - colToDbl(color->c[2]) / 200;
  if (t2 >= (6.0 / 29.0)) {
    Z = t2 * t2 * t2;
  } else {
    Z = (108.0 / 841.0) * (t2 - (4.0 / 29.0));
  }
  Z *= whiteZ;

  // convert XYZ to RGB, including gamut mapping and gamma correction
  r = xyzrgb[0][0] * X + xyzrgb[0][1] * Y + xyzrgb[0][2] * Z;
  g = xyzrgb[1][0] * X + xyzrgb[1][1] * Y + xyzrgb[1][2] * Z;
  b = xyzrgb[2][0] * X + xyzrgb[2][1] * Y + xyzrgb[2][2] * Z;
  rgb->r = dblToCol(pow(clip01(r * kr), 0.5));
  rgb->g = dblToCol(pow(clip01(g * kg), 0.5));
  rgb->b = dblToCol(pow(clip01(b * kb), 0.5));
}

void GfxLabColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
  GfxRGB rgb;
  GfxColorComp c, m, y, k;

  getRGB(color, &rgb);
  c = clip01(gfxColorComp1 - rgb.r);
  m = clip01(gfxColorComp1 - rgb.g);
  y = clip01(gfxColorComp1 - rgb.b);
  k = c;
  if (m < k) {
    k = m;
  }
  if (y < k) {
    k = y;
  }
  cmyk->c = c - k;
  cmyk->m = m - k;
  cmyk->y = y - k;
  cmyk->k = k;
}

void GfxLabColorSpace::getDefaultColor(GfxColor *color) {
  color->c[0] = 0;
  if (aMin > 0) {
    color->c[1] = dblToCol(aMin);
  } else if (aMax < 0) {
    color->c[1] = dblToCol(aMax);
  } else {
    color->c[1] = 0;
  }
  if (bMin > 0) {
    color->c[2] = dblToCol(bMin);
  } else if (bMax < 0) {
    color->c[2] = dblToCol(bMax);
  } else {
    color->c[2] = 0;
  }
}

void GfxLabColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange,
					int maxImgPixel) {
  decodeLow[0] = 0;
  decodeRange[0] = 100;
  decodeLow[1] = aMin;
  decodeRange[1] = aMax - aMin;
  decodeLow[2] = bMin;
  decodeRange[2] = bMax - bMin;
}

//------------------------------------------------------------------------
// GfxICCBasedColorSpace
//------------------------------------------------------------------------

GfxICCBasedColorSpace::GfxICCBasedColorSpace(int nCompsA, GfxColorSpace *altA,
					     Ref *iccProfileStreamA) {
  nComps = nCompsA;
  alt = altA;
  iccProfileStream = *iccProfileStreamA;
  rangeMin[0] = rangeMin[1] = rangeMin[2] = rangeMin[3] = 0;
  rangeMax[0] = rangeMax[1] = rangeMax[2] = rangeMax[3] = 1;
}

GfxICCBasedColorSpace::~GfxICCBasedColorSpace() {
  delete alt;
}

GfxColorSpace *GfxICCBasedColorSpace::copy() {
  GfxICCBasedColorSpace *cs;
  int i;

  cs = new GfxICCBasedColorSpace(nComps, alt->copy(), &iccProfileStream);
  for (i = 0; i < 4; ++i) {
    cs->rangeMin[i] = rangeMin[i];
    cs->rangeMax[i] = rangeMax[i];
  }
  return cs;
}

GfxColorSpace *GfxICCBasedColorSpace::parse(Array *arr) {
  GfxICCBasedColorSpace *cs;
  Ref iccProfileStreamA;
  int nCompsA;
  GfxColorSpace *altA;
  Dict *dict;
  Object obj1, obj2, obj3;
  int i;

  arr->getNF(1, &obj1);
  if (obj1.isRef()) {
    iccProfileStreamA = obj1.getRef();
  } else {
    iccProfileStreamA.num = 0;
    iccProfileStreamA.gen = 0;
  }
  obj1.free();
  arr->get(1, &obj1);
  if (!obj1.isStream()) {
    error(-1, "Bad ICCBased color space (stream)");
    obj1.free();
    return NULL;
  }
  dict = obj1.streamGetDict();
  if (!dict->lookup("N", &obj2)->isInt()) {
    error(-1, "Bad ICCBased color space (N)");
    obj2.free();
    obj1.free();
    return NULL;
  }
  nCompsA = obj2.getInt();
  obj2.free();
  if (nCompsA > gfxColorMaxComps) {
    error(-1, "ICCBased color space with too many (%d > %d) components",
	  nCompsA, gfxColorMaxComps);
    nCompsA = gfxColorMaxComps;
  }
  if (dict->lookup("Alternate", &obj2)->isNull() ||
      !(altA = GfxColorSpace::parse(&obj2))) {
    switch (nCompsA) {
    case 1:
      altA = new GfxDeviceGrayColorSpace();
      break;
    case 3:
      altA = new GfxDeviceRGBColorSpace();
      break;
    case 4:
      altA = new GfxDeviceCMYKColorSpace();
      break;
    default:
      error(-1, "Bad ICCBased color space - invalid N");
      obj2.free();
      obj1.free();
      return NULL;
    }
  }
  obj2.free();
  cs = new GfxICCBasedColorSpace(nCompsA, altA, &iccProfileStreamA);
  if (dict->lookup("Range", &obj2)->isArray() &&
      obj2.arrayGetLength() == 2 * nCompsA) {
    for (i = 0; i < nCompsA; ++i) {
      obj2.arrayGet(2*i, &obj3);
      cs->rangeMin[i] = obj3.getNum();
      obj3.free();
      obj2.arrayGet(2*i+1, &obj3);
      cs->rangeMax[i] = obj3.getNum();
      obj3.free();
    }
  }
  obj2.free();
  obj1.free();
  return cs;
}

void GfxICCBasedColorSpace::getGray(GfxColor *color, GfxGray *gray) {
  alt->getGray(color, gray);
}

void GfxICCBasedColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
  alt->getRGB(color, rgb);
}

void GfxICCBasedColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
  alt->getCMYK(color, cmyk);
}

void GfxICCBasedColorSpace::getDefaultColor(GfxColor *color) {
  int i;

  for (i = 0; i < nComps; ++i) {
    if (rangeMin[i] > 0) {
      color->c[i] = dblToCol(rangeMin[i]);
    } else if (rangeMax[i] < 0) {
      color->c[i] = dblToCol(rangeMax[i]);
    } else {
      color->c[i] = 0;
    }
  }
}

void GfxICCBasedColorSpace::getDefaultRanges(double *decodeLow,
					     double *decodeRange,
					     int maxImgPixel) {
  alt->getDefaultRanges(decodeLow, decodeRange, maxImgPixel);

#if 0
  // this is nominally correct, but some PDF files don't set the
  // correct ranges in the ICCBased dict
  int i;

  for (i = 0; i < nComps; ++i) {
    decodeLow[i] = rangeMin[i];
    decodeRange[i] = rangeMax[i] - rangeMin[i];
  }
#endif
}

//------------------------------------------------------------------------
// GfxIndexedColorSpace
//------------------------------------------------------------------------

GfxIndexedColorSpace::GfxIndexedColorSpace(GfxColorSpace *baseA,
					   int indexHighA) {
  base = baseA;
  indexHigh = indexHighA;
  lookup = (Guchar *)gmallocn((indexHigh + 1) * base->getNComps(),
			      sizeof(Guchar));
}

GfxIndexedColorSpace::~GfxIndexedColorSpace() {
  delete base;
  gfree(lookup);
}

GfxColorSpace *GfxIndexedColorSpace::copy() {
  GfxIndexedColorSpace *cs;

  cs = new GfxIndexedColorSpace(base->copy(), indexHigh);
  memcpy(cs->lookup, lookup,
	 (indexHigh + 1) * base->getNComps() * sizeof(Guchar));
  return cs;
}

GfxColorSpace *GfxIndexedColorSpace::parse(Array *arr) {
  GfxIndexedColorSpace *cs;
  GfxColorSpace *baseA;
  int indexHighA;
  Object obj1;
  int x;
  char *s;
  int n, i, j;

  if (arr->getLength() != 4) {
    error(-1, "Bad Indexed color space");
    goto err1;
  }
  arr->get(1, &obj1);
  if (!(baseA = GfxColorSpace::parse(&obj1))) {
    error(-1, "Bad Indexed color space (base color space)");
    goto err2;
  }
  obj1.free();
  if (!arr->get(2, &obj1)->isInt()) {
    error(-1, "Bad Indexed color space (hival)");
    delete baseA;
    goto err2;
  }
  indexHighA = obj1.getInt();
  if (indexHighA < 0 || indexHighA > 255) {
    // the PDF spec requires indexHigh to be in [0,255] -- allowing
    // values larger than 255 creates a security hole: if nComps *
    // indexHigh is greater than 2^31, the loop below may overwrite
    // past the end of the array
    error(-1, "Bad Indexed color space (invalid indexHigh value)");
    delete baseA;
    goto err2;
  }
  obj1.free();
  cs = new GfxIndexedColorSpace(baseA, indexHighA);
  arr->get(3, &obj1);
  n = baseA->getNComps();
  if (obj1.isStream()) {
    obj1.streamReset();
    for (i = 0; i <= indexHighA; ++i) {
      for (j = 0; j < n; ++j) {
	if ((x = obj1.streamGetChar()) == EOF) {
	  error(-1, "Bad Indexed color space (lookup table stream too short)");
	  goto err3;
	}
	cs->lookup[i*n + j] = (Guchar)x;
      }
    }
    obj1.streamClose();
  } else if (obj1.isString()) {
    if (obj1.getString()->getLength() < (indexHighA + 1) * n) {
      error(-1, "Bad Indexed color space (lookup table string too short)");
      goto err3;
    }
    s = obj1.getString()->getCString();
    for (i = 0; i <= indexHighA; ++i) {
      for (j = 0; j < n; ++j) {
	cs->lookup[i*n + j] = (Guchar)*s++;
      }
    }
  } else {
    error(-1, "Bad Indexed color space (lookup table)");
    goto err3;
  }
  obj1.free();
  return cs;

 err3:
  delete cs;
 err2:
  obj1.free();
 err1:
  return NULL;
}

GfxColor *GfxIndexedColorSpace::mapColorToBase(GfxColor *color,
					       GfxColor *baseColor) {
  Guchar *p;
  double low[gfxColorMaxComps], range[gfxColorMaxComps];
  int n, i;

  n = base->getNComps();
  base->getDefaultRanges(low, range, indexHigh);
  p = &lookup[(int)(colToDbl(color->c[0]) + 0.5) * n];
  for (i = 0; i < n; ++i) {
    baseColor->c[i] = dblToCol(low[i] + (p[i] / 255.0) * range[i]);
  }
  return baseColor;
}

void GfxIndexedColorSpace::getGray(GfxColor *color, GfxGray *gray) {
  GfxColor color2;

  base->getGray(mapColorToBase(color, &color2), gray);
}

void GfxIndexedColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
  GfxColor color2;

  base->getRGB(mapColorToBase(color, &color2), rgb);
}

void GfxIndexedColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
  GfxColor color2;

  base->getCMYK(mapColorToBase(color, &color2), cmyk);
}

void GfxIndexedColorSpace::getDefaultColor(GfxColor *color) {
  color->c[0] = 0;
}

void GfxIndexedColorSpace::getDefaultRanges(double *decodeLow,
					    double *decodeRange,
					    int maxImgPixel) {
  decodeLow[0] = 0;
  decodeRange[0] = maxImgPixel;
}

//------------------------------------------------------------------------
// GfxSeparationColorSpace
//------------------------------------------------------------------------

GfxSeparationColorSpace::GfxSeparationColorSpace(GString *nameA,
						 GfxColorSpace *altA,
						 Function *funcA) {
  name = nameA;
  alt = altA;
  func = funcA;
  nonMarking = !name->cmp("None");
}

GfxSeparationColorSpace::~GfxSeparationColorSpace() {
  delete name;
  delete alt;
  delete func;
}

GfxColorSpace *GfxSeparationColorSpace::copy() {
  return new GfxSeparationColorSpace(name->copy(), alt->copy(), func->copy());
}

//~ handle the 'All' and 'None' colorants
GfxColorSpace *GfxSeparationColorSpace::parse(Array *arr) {
  GfxSeparationColorSpace *cs;
  GString *nameA;
  GfxColorSpace *altA;
  Function *funcA;
  Object obj1;

  if (arr->getLength() != 4) {
    error(-1, "Bad Separation color space");
    goto err1;
  }
  if (!arr->get(1, &obj1)->isName()) {
    error(-1, "Bad Separation color space (name)");
    goto err2;
  }
  nameA = new GString(obj1.getName());
  obj1.free();
  arr->get(2, &obj1);
  if (!(altA = GfxColorSpace::parse(&obj1))) {
    error(-1, "Bad Separation color space (alternate color space)");
    goto err3;
  }
  obj1.free();
  arr->get(3, &obj1);
  if (!(funcA = Function::parse(&obj1))) {
    goto err4;
  }
  obj1.free();
  cs = new GfxSeparationColorSpace(nameA, altA, funcA);
  return cs;

 err4:
  delete altA;
 err3:
  delete nameA;
 err2:
  obj1.free();
 err1:
  return NULL;
}

void GfxSeparationColorSpace::getGray(GfxColor *color, GfxGray *gray) {
  double x;
  double c[gfxColorMaxComps];
  GfxColor color2;
  int i;

  x = colToDbl(color->c[0]);
  func->transform(&x, c);
  for (i = 0; i < alt->getNComps(); ++i) {
    color2.c[i] = dblToCol(c[i]);
  }
  alt->getGray(&color2, gray);
}

void GfxSeparationColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
  double x;
  double c[gfxColorMaxComps];
  GfxColor color2;
  int i;

  x = colToDbl(color->c[0]);
  func->transform(&x, c);
  for (i = 0; i < alt->getNComps(); ++i) {
    color2.c[i] = dblToCol(c[i]);
  }
  alt->getRGB(&color2, rgb);
}

void GfxSeparationColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
  double x;
  double c[gfxColorMaxComps];
  GfxColor color2;
  int i;

  x = colToDbl(color->c[0]);
  func->transform(&x, c);
  for (i = 0; i < alt->getNComps(); ++i) {
    color2.c[i] = dblToCol(c[i]);
  }
  alt->getCMYK(&color2, cmyk);
}

void GfxSeparationColorSpace::getDefaultColor(GfxColor *color) {
  color->c[0] = gfxColorComp1;
}

//------------------------------------------------------------------------
// GfxDeviceNColorSpace
//------------------------------------------------------------------------

GfxDeviceNColorSpace::GfxDeviceNColorSpace(int nCompsA,
					   GfxColorSpace *altA,
					   Function *funcA) {
  nComps = nCompsA;
  alt = altA;
  func = funcA;
  nonMarking = gFalse;
}

GfxDeviceNColorSpace::~GfxDeviceNColorSpace() {
  int i;

  for (i = 0; i < nComps; ++i) {
    delete names[i];
  }
  delete alt;
  delete func;
}

GfxColorSpace *GfxDeviceNColorSpace::copy() {
  GfxDeviceNColorSpace *cs;
  int i;

  cs = new GfxDeviceNColorSpace(nComps, alt->copy(), func->copy());
  for (i = 0; i < nComps; ++i) {
    cs->names[i] = names[i]->copy();
  }
  cs->nonMarking = nonMarking;
  return cs;
}

//~ handle the 'None' colorant
GfxColorSpace *GfxDeviceNColorSpace::parse(Array *arr) {
  GfxDeviceNColorSpace *cs;
  int nCompsA;
  GString *namesA[gfxColorMaxComps];
  GfxColorSpace *altA;
  Function *funcA;
  Object obj1, obj2;
  int i;

  if (arr->getLength() != 4 && arr->getLength() != 5) {
    error(-1, "Bad DeviceN color space");
    goto err1;
  }
  if (!arr->get(1, &obj1)->isArray()) {
    error(-1, "Bad DeviceN color space (names)");
    goto err2;
  }
  nCompsA = obj1.arrayGetLength();
  if (nCompsA > gfxColorMaxComps) {
    error(-1, "DeviceN color space with too many (%d > %d) components",
	  nCompsA, gfxColorMaxComps);
    nCompsA = gfxColorMaxComps;
  }
  for (i = 0; i < nCompsA; ++i) {
    if (!obj1.arrayGet(i, &obj2)->isName()) {
      error(-1, "Bad DeviceN color space (names)");
      obj2.free();
      goto err2;
    }
    namesA[i] = new GString(obj2.getName());
    obj2.free();
  }
  obj1.free();
  arr->get(2, &obj1);
  if (!(altA = GfxColorSpace::parse(&obj1))) {
    error(-1, "Bad DeviceN color space (alternate color space)");
    goto err3;
  }
  obj1.free();
  arr->get(3, &obj1);
  if (!(funcA = Function::parse(&obj1))) {
    goto err4;
  }
  obj1.free();
  cs = new GfxDeviceNColorSpace(nCompsA, altA, funcA);
  cs->nonMarking = gTrue;
  for (i = 0; i < nCompsA; ++i) {
    cs->names[i] = namesA[i];
    if (namesA[i]->cmp("None")) {
      cs->nonMarking = gFalse;
    }
  }
  return cs;

 err4:
  delete altA;
 err3:
  for (i = 0; i < nCompsA; ++i) {
    delete namesA[i];
  }
 err2:
  obj1.free();
 err1:
  return NULL;
}

void GfxDeviceNColorSpace::getGray(GfxColor *color, GfxGray *gray) {
  double x[gfxColorMaxComps], c[gfxColorMaxComps];
  GfxColor color2;
  int i;

  for (i = 0; i < nComps; ++i) {
    x[i] = colToDbl(color->c[i]);
  }
  func->transform(x, c);
  for (i = 0; i < alt->getNComps(); ++i) {
    color2.c[i] = dblToCol(c[i]);
  }
  alt->getGray(&color2, gray);
}

void GfxDeviceNColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
  double x[gfxColorMaxComps], c[gfxColorMaxComps];
  GfxColor color2;
  int i;

  for (i = 0; i < nComps; ++i) {
    x[i] = colToDbl(color->c[i]);
  }
  func->transform(x, c);
  for (i = 0; i < alt->getNComps(); ++i) {
    color2.c[i] = dblToCol(c[i]);
  }
  alt->getRGB(&color2, rgb);
}

void GfxDeviceNColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
  double x[gfxColorMaxComps], c[gfxColorMaxComps];
  GfxColor color2;
  int i;

  for (i = 0; i < nComps; ++i) {
    x[i] = colToDbl(color->c[i]);
  }
  func->transform(x, c);
  for (i = 0; i < alt->getNComps(); ++i) {
    color2.c[i] = dblToCol(c[i]);
  }
  alt->getCMYK(&color2, cmyk);
}

void GfxDeviceNColorSpace::getDefaultColor(GfxColor *color) {
  int i;

  for (i = 0; i < nComps; ++i) {
    color->c[i] = gfxColorComp1;
  }
}

//------------------------------------------------------------------------
// GfxPatternColorSpace
//------------------------------------------------------------------------

GfxPatternColorSpace::GfxPatternColorSpace(GfxColorSpace *underA) {
  under = underA;
}

GfxPatternColorSpace::~GfxPatternColorSpace() {
  if (under) {
    delete under;
  }
}

GfxColorSpace *GfxPatternColorSpace::copy() {
  return new GfxPatternColorSpace(under ? under->copy() :
				          (GfxColorSpace *)NULL);
}

GfxColorSpace *GfxPatternColorSpace::parse(Array *arr) {
  GfxPatternColorSpace *cs;
  GfxColorSpace *underA;
  Object obj1;

  if (arr->getLength() != 1 && arr->getLength() != 2) {
    error(-1, "Bad Pattern color space");
    return NULL;
  }
  underA = NULL;
  if (arr->getLength() == 2) {
    arr->get(1, &obj1);
    if (!(underA = GfxColorSpace::parse(&obj1))) {
      error(-1, "Bad Pattern color space (underlying color space)");
      obj1.free();
      return NULL;
    }
    obj1.free();
  }
  cs = new GfxPatternColorSpace(underA);
  return cs;
}

void GfxPatternColorSpace::getGray(GfxColor *color, GfxGray *gray) {
  *gray = 0;
}

void GfxPatternColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
  rgb->r = rgb->g = rgb->b = 0;
}

void GfxPatternColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
  cmyk->c = cmyk->m = cmyk->y = 0;
  cmyk->k = 1;
}

void GfxPatternColorSpace::getDefaultColor(GfxColor *color) {
  // not used
}

//------------------------------------------------------------------------
// Pattern
//------------------------------------------------------------------------

GfxPattern::GfxPattern(int typeA) {
  type = typeA;
}

GfxPattern::~GfxPattern() {
}

GfxPattern *GfxPattern::parse(Object *obj) {
  GfxPattern *pattern;
  Object obj1;

  if (obj->isDict()) {
    obj->dictLookup("PatternType", &obj1);
  } else if (obj->isStream()) {
    obj->streamGetDict()->lookup("PatternType", &obj1);
  } else {
    return NULL;
  }
  pattern = NULL;
  if (obj1.isInt() && obj1.getInt() == 1) {
    pattern = GfxTilingPattern::parse(obj);
  } else if (obj1.isInt() && obj1.getInt() == 2) {
    pattern = GfxShadingPattern::parse(obj);
  }
  obj1.free();
  return pattern;
}

//------------------------------------------------------------------------
// GfxTilingPattern
//------------------------------------------------------------------------

GfxTilingPattern *GfxTilingPattern::parse(Object *patObj) {
  GfxTilingPattern *pat;
  Dict *dict;
  int paintTypeA, tilingTypeA;
  double bboxA[4], matrixA[6];
  double xStepA, yStepA;
  Object resDictA;
  Object obj1, obj2;
  int i;

  if (!patObj->isStream()) {
    return NULL;
  }
  dict = patObj->streamGetDict();

  if (dict->lookup("PaintType", &obj1)->isInt()) {
    paintTypeA = obj1.getInt();
  } else {
    paintTypeA = 1;
    error(-1, "Invalid or missing PaintType in pattern");
  }
  obj1.free();
  if (dict->lookup("TilingType", &obj1)->isInt()) {
    tilingTypeA = obj1.getInt();
  } else {
    tilingTypeA = 1;
    error(-1, "Invalid or missing TilingType in pattern");
  }
  obj1.free();
  bboxA[0] = bboxA[1] = 0;
  bboxA[2] = bboxA[3] = 1;
  if (dict->lookup("BBox", &obj1)->isArray() &&
      obj1.arrayGetLength() == 4) {
    for (i = 0; i < 4; ++i) {
      if (obj1.arrayGet(i, &obj2)->isNum()) {
	bboxA[i] = obj2.getNum();
      }
      obj2.free();
    }
  } else {
    error(-1, "Invalid or missing BBox in pattern");
  }
  obj1.free();
  if (dict->lookup("XStep", &obj1)->isNum()) {
    xStepA = obj1.getNum();
  } else {
    xStepA = 1;
    error(-1, "Invalid or missing XStep in pattern");
  }
  obj1.free();
  if (dict->lookup("YStep", &obj1)->isNum()) {
    yStepA = obj1.getNum();
  } else {
    yStepA = 1;
    error(-1, "Invalid or missing YStep in pattern");
  }
  obj1.free();
  if (!dict->lookup("Resources", &resDictA)->isDict()) {
    resDictA.free();
    resDictA.initNull();
    error(-1, "Invalid or missing Resources in pattern");
  }
  matrixA[0] = 1; matrixA[1] = 0;
  matrixA[2] = 0; matrixA[3] = 1;
  matrixA[4] = 0; matrixA[5] = 0;
  if (dict->lookup("Matrix", &obj1)->isArray() &&
      obj1.arrayGetLength() == 6) {
    for (i = 0; i < 6; ++i) {
      if (obj1.arrayGet(i, &obj2)->isNum()) {
	matrixA[i] = obj2.getNum();
      }
      obj2.free();
    }
  }
  obj1.free();

  pat = new GfxTilingPattern(paintTypeA, tilingTypeA, bboxA, xStepA, yStepA,
			     &resDictA, matrixA, patObj);
  resDictA.free();
  return pat;
}

GfxTilingPattern::GfxTilingPattern(int paintTypeA, int tilingTypeA,
				   double *bboxA, double xStepA, double yStepA,
				   Object *resDictA, double *matrixA,
				   Object *contentStreamA):
  GfxPattern(1)
{
  int i;

  paintType = paintTypeA;
  tilingType = tilingTypeA;
  for (i = 0; i < 4; ++i) {
    bbox[i] = bboxA[i];
  }
  xStep = xStepA;
  yStep = yStepA;
  resDictA->copy(&resDict);
  for (i = 0; i < 6; ++i) {
    matrix[i] = matrixA[i];
  }
  contentStreamA->copy(&contentStream);
}

GfxTilingPattern::~GfxTilingPattern() {
  resDict.free();
  contentStream.free();
}

GfxPattern *GfxTilingPattern::copy() {
  return new GfxTilingPattern(paintType, tilingType, bbox, xStep, yStep,
			      &resDict, matrix, &contentStream);
}

//------------------------------------------------------------------------
// GfxShadingPattern
//------------------------------------------------------------------------

GfxShadingPattern *GfxShadingPattern::parse(Object *patObj) {
  Dict *dict;
  GfxShading *shadingA;
  double matrixA[6];
  Object obj1, obj2;
  int i;

  if (!patObj->isDict()) {
    return NULL;
  }
  dict = patObj->getDict();

  dict->lookup("Shading", &obj1);
  shadingA = GfxShading::parse(&obj1);
  obj1.free();
  if (!shadingA) {
    return NULL;
  }

  matrixA[0] = 1; matrixA[1] = 0;
  matrixA[2] = 0; matrixA[3] = 1;
  matrixA[4] = 0; matrixA[5] = 0;
  if (dict->lookup("Matrix", &obj1)->isArray() &&
      obj1.arrayGetLength() == 6) {
    for (i = 0; i < 6; ++i) {
      if (obj1.arrayGet(i, &obj2)->isNum()) {
	matrixA[i] = obj2.getNum();
      }
      obj2.free();
    }
  }
  obj1.free();

  return new GfxShadingPattern(shadingA, matrixA);
}

GfxShadingPattern::GfxShadingPattern(GfxShading *shadingA, double *matrixA):
  GfxPattern(2)
{
  int i;

  shading = shadingA;
  for (i = 0; i < 6; ++i) {
    matrix[i] = matrixA[i];
  }
}

GfxShadingPattern::~GfxShadingPattern() {
  delete shading;
}

GfxPattern *GfxShadingPattern::copy() {
  return new GfxShadingPattern(shading->copy(), matrix);
}

//------------------------------------------------------------------------
// GfxShading
//------------------------------------------------------------------------

GfxShading::GfxShading(int typeA) {
  type = typeA;
  colorSpace = NULL;
}

GfxShading::GfxShading(GfxShading *shading) {
  int i;

  type = shading->type;
  colorSpace = shading->colorSpace->copy();
  for (i = 0; i < gfxColorMaxComps; ++i) {
    background.c[i] = shading->background.c[i];
  }
  hasBackground = shading->hasBackground;
  xMin = shading->xMin;
  yMin = shading->yMin;
  xMax = shading->xMax;
  yMax = shading->yMax;
  hasBBox = shading->hasBBox;
}

GfxShading::~GfxShading() {
  if (colorSpace) {
    delete colorSpace;
  }
}

GfxShading *GfxShading::parse(Object *obj) {
  GfxShading *shading;
  Dict *dict;
  int typeA;
  Object obj1;

  if (obj->isDict()) {
    dict = obj->getDict();
  } else if (obj->isStream()) {
    dict = obj->streamGetDict();
  } else {
    return NULL;
  }

  if (!dict->lookup("ShadingType", &obj1)->isInt()) {
    error(-1, "Invalid ShadingType in shading dictionary");
    obj1.free();
    return NULL;
  }
  typeA = obj1.getInt();
  obj1.free();

  switch (typeA) {
  case 1:
    shading = GfxFunctionShading::parse(dict);
    break;
  case 2:
    shading = GfxAxialShading::parse(dict);
    break;
  case 3:
    shading = GfxRadialShading::parse(dict);
    break;
  case 4:
    if (obj->isStream()) {
      shading = GfxGouraudTriangleShading::parse(4, dict, obj->getStream());
    } else {
      error(-1, "Invalid Type 4 shading object");
      goto err1;
    }
    break;
  case 5:
    if (obj->isStream()) {
      shading = GfxGouraudTriangleShading::parse(5, dict, obj->getStream());
    } else {
      error(-1, "Invalid Type 5 shading object");
      goto err1;
    }
    break;
  case 6:
    if (obj->isStream()) {
      shading = GfxPatchMeshShading::parse(6, dict, obj->getStream());
    } else {
      error(-1, "Invalid Type 6 shading object");
      goto err1;
    }
    break;
  case 7:
    if (obj->isStream()) {
      shading = GfxPatchMeshShading::parse(7, dict, obj->getStream());
    } else {
      error(-1, "Invalid Type 7 shading object");
      goto err1;
    }
    break;
  default:
    error(-1, "Unimplemented shading type %d", typeA);
    goto err1;
  }

  return shading;

 err1:
  return NULL;
}

GBool GfxShading::init(Dict *dict) {
  Object obj1, obj2;
  int i;

  dict->lookup("ColorSpace", &obj1);
  if (!(colorSpace = GfxColorSpace::parse(&obj1))) {
    error(-1, "Bad color space in shading dictionary");
    obj1.free();
    return gFalse;
  }
  obj1.free();

  for (i = 0; i < gfxColorMaxComps; ++i) {
    background.c[i] = 0;
  }
  hasBackground = gFalse;
  if (dict->lookup("Background", &obj1)->isArray()) {
    if (obj1.arrayGetLength() == colorSpace->getNComps()) {
      hasBackground = gTrue;
      for (i = 0; i < colorSpace->getNComps(); ++i) {
	background.c[i] = dblToCol(obj1.arrayGet(i, &obj2)->getNum());
	obj2.free();
      }
    } else {
      error(-1, "Bad Background in shading dictionary");
    }
  }
  obj1.free();

  xMin = yMin = xMax = yMax = 0;
  hasBBox = gFalse;
  if (dict->lookup("BBox", &obj1)->isArray()) {
    if (obj1.arrayGetLength() == 4) {
      hasBBox = gTrue;
      xMin = obj1.arrayGet(0, &obj2)->getNum();
      obj2.free();
      yMin = obj1.arrayGet(1, &obj2)->getNum();
      obj2.free();
      xMax = obj1.arrayGet(2, &obj2)->getNum();
      obj2.free();
      yMax = obj1.arrayGet(3, &obj2)->getNum();
      obj2.free();
    } else {
      error(-1, "Bad BBox in shading dictionary");
    }
  }
  obj1.free();

  return gTrue;
}

//------------------------------------------------------------------------
// GfxFunctionShading
//------------------------------------------------------------------------

GfxFunctionShading::GfxFunctionShading(double x0A, double y0A,
				       double x1A, double y1A,
				       double *matrixA,
				       Function **funcsA, int nFuncsA):
  GfxShading(1)
{
  int i;

  x0 = x0A;
  y0 = y0A;
  x1 = x1A;
  y1 = y1A;
  for (i = 0; i < 6; ++i) {
    matrix[i] = matrixA[i];
  }
  nFuncs = nFuncsA;
  for (i = 0; i < nFuncs; ++i) {
    funcs[i] = funcsA[i];
  }
}

GfxFunctionShading::GfxFunctionShading(GfxFunctionShading *shading):
  GfxShading(shading)
{
  int i;

  x0 = shading->x0;
  y0 = shading->y0;
  x1 = shading->x1;
  y1 = shading->y1;
  for (i = 0; i < 6; ++i) {
    matrix[i] = shading->matrix[i];
  }
  nFuncs = shading->nFuncs;
  for (i = 0; i < nFuncs; ++i) {
    funcs[i] = shading->funcs[i]->copy();
  }
}

GfxFunctionShading::~GfxFunctionShading() {
  int i;

  for (i = 0; i < nFuncs; ++i) {
    delete funcs[i];
  }
}

GfxFunctionShading *GfxFunctionShading::parse(Dict *dict) {
  GfxFunctionShading *shading;
  double x0A, y0A, x1A, y1A;
  double matrixA[6];
  Function *funcsA[gfxColorMaxComps];
  int nFuncsA;
  Object obj1, obj2;
  int i;

  x0A = y0A = 0;
  x1A = y1A = 1;
  if (dict->lookup("Domain", &obj1)->isArray() &&
      obj1.arrayGetLength() == 4) {
    x0A = obj1.arrayGet(0, &obj2)->getNum();
    obj2.free();
    y0A = obj1.arrayGet(1, &obj2)->getNum();
    obj2.free();
    x1A = obj1.arrayGet(2, &obj2)->getNum();
    obj2.free();
    y1A = obj1.arrayGet(3, &obj2)->getNum();
    obj2.free();
  }
  obj1.free();

  matrixA[0] = 1; matrixA[1] = 0;
  matrixA[2] = 0; matrixA[3] = 1;
  matrixA[4] = 0; matrixA[5] = 0;
  if (dict->lookup("Matrix", &obj1)->isArray() &&
      obj1.arrayGetLength() == 6) {
    matrixA[0] = obj1.arrayGet(0, &obj2)->getNum();
    obj2.free();
    matrixA[1] = obj1.arrayGet(1, &obj2)->getNum();
    obj2.free();
    matrixA[2] = obj1.arrayGet(2, &obj2)->getNum();
    obj2.free();
    matrixA[3] = obj1.arrayGet(3, &obj2)->getNum();
    obj2.free();
    matrixA[4] = obj1.arrayGet(4, &obj2)->getNum();
    obj2.free();
    matrixA[5] = obj1.arrayGet(5, &obj2)->getNum();
    obj2.free();
  }
  obj1.free();

  dict->lookup("Function", &obj1);
  if (obj1.isArray()) {
    nFuncsA = obj1.arrayGetLength();
    if (nFuncsA > gfxColorMaxComps) {
      error(-1, "Invalid Function array in shading dictionary");
      goto err1;
    }
    for (i = 0; i < nFuncsA; ++i) {
      obj1.arrayGet(i, &obj2);
      if (!(funcsA[i] = Function::parse(&obj2))) {
	goto err2;
      }
      obj2.free();
    }
  } else {
    nFuncsA = 1;
    if (!(funcsA[0] = Function::parse(&obj1))) {
      goto err1;
    }
  }
  obj1.free();

  shading = new GfxFunctionShading(x0A, y0A, x1A, y1A, matrixA,
				   funcsA, nFuncsA);
  if (!shading->init(dict)) {
    delete shading;
    return NULL;
  }
  return shading;

 err2:
  obj2.free();
 err1:
  obj1.free();
  return NULL;
}

GfxShading *GfxFunctionShading::copy() {
  return new GfxFunctionShading(this);
}

void GfxFunctionShading::getColor(double x, double y, GfxColor *color) {
  double in[2], out[gfxColorMaxComps];
  int i;

  // NB: there can be one function with n outputs or n functions with
  // one output each (where n = number of color components)
  for (i = 0; i < gfxColorMaxComps; ++i) {
    out[i] = 0;
  }
  in[0] = x;
  in[1] = y;
  for (i = 0; i < nFuncs; ++i) {
    funcs[i]->transform(in, &out[i]);
  }
  for (i = 0; i < gfxColorMaxComps; ++i) {
    color->c[i] = dblToCol(out[i]);
  }
}

//------------------------------------------------------------------------
// GfxAxialShading
//------------------------------------------------------------------------

GfxAxialShading::GfxAxialShading(double x0A, double y0A,
				 double x1A, double y1A,
				 double t0A, double t1A,
				 Function **funcsA, int nFuncsA,
				 GBool extend0A, GBool extend1A):
  GfxShading(2)
{
  int i;

  x0 = x0A;
  y0 = y0A;
  x1 = x1A;
  y1 = y1A;
  t0 = t0A;
  t1 = t1A;
  nFuncs = nFuncsA;
  for (i = 0; i < nFuncs; ++i) {
    funcs[i] = funcsA[i];
  }
  extend0 = extend0A;
  extend1 = extend1A;
}

GfxAxialShading::GfxAxialShading(GfxAxialShading *shading):
  GfxShading(shading)
{
  int i;

  x0 = shading->x0;
  y0 = shading->y0;
  x1 = shading->x1;
  y1 = shading->y1;
  t0 = shading->t0;
  y1 = shading->t1;
  nFuncs = shading->nFuncs;
  for (i = 0; i < nFuncs; ++i) {
    funcs[i] = shading->funcs[i]->copy();
  }
  extend0 = shading->extend0;
  extend1 = shading->extend1;
}

GfxAxialShading::~GfxAxialShading() {
  int i;

  for (i = 0; i < nFuncs; ++i) {
    delete funcs[i];
  }
}

GfxAxialShading *GfxAxialShading::parse(Dict *dict) {
  GfxAxialShading *shading;
  double x0A, y0A, x1A, y1A;
  double t0A, t1A;
  Function *funcsA[gfxColorMaxComps];
  int nFuncsA;
  GBool extend0A, extend1A;
  Object obj1, obj2;
  int i;

  x0A = y0A = x1A = y1A = 0;
  if (dict->lookup("Coords", &obj1)->isArray() &&
      obj1.arrayGetLength() == 4) {
    x0A = obj1.arrayGet(0, &obj2)->getNum();
    obj2.free();
    y0A = obj1.arrayGet(1, &obj2)->getNum();
    obj2.free();
    x1A = obj1.arrayGet(2, &obj2)->getNum();
    obj2.free();
    y1A = obj1.arrayGet(3, &obj2)->getNum();
    obj2.free();
  } else {
    error(-1, "Missing or invalid Coords in shading dictionary");
    goto err1;
  }
  obj1.free();

  t0A = 0;
  t1A = 1;
  if (dict->lookup("Domain", &obj1)->isArray() &&
      obj1.arrayGetLength() == 2) {
    t0A = obj1.arrayGet(0, &obj2)->getNum();
    obj2.free();
    t1A = obj1.arrayGet(1, &obj2)->getNum();
    obj2.free();
  }
  obj1.free();

  dict->lookup("Function", &obj1);
  if (obj1.isArray()) {
    nFuncsA = obj1.arrayGetLength();
    if (nFuncsA > gfxColorMaxComps) {
      error(-1, "Invalid Function array in shading dictionary");
      goto err1;
    }
    for (i = 0; i < nFuncsA; ++i) {
      obj1.arrayGet(i, &obj2);
      if (!(funcsA[i] = Function::parse(&obj2))) {
	obj1.free();
	obj2.free();
	goto err1;
      }
      obj2.free();
    }
  } else {
    nFuncsA = 1;
    if (!(funcsA[0] = Function::parse(&obj1))) {
      obj1.free();
      goto err1;
    }
  }
  obj1.free();

  extend0A = extend1A = gFalse;
  if (dict->lookup("Extend", &obj1)->isArray() &&
      obj1.arrayGetLength() == 2) {
    extend0A = obj1.arrayGet(0, &obj2)->getBool();
    obj2.free();
    extend1A = obj1.arrayGet(1, &obj2)->getBool();
    obj2.free();
  }
  obj1.free();

  shading = new GfxAxialShading(x0A, y0A, x1A, y1A, t0A, t1A,
				funcsA, nFuncsA, extend0A, extend1A);
  if (!shading->init(dict)) {
    delete shading;
    return NULL;
  }
  return shading;

 err1:
  return NULL;
}

GfxShading *GfxAxialShading::copy() {
  return new GfxAxialShading(this);
}

void GfxAxialShading::getColor(double t, GfxColor *color) {
  double out[gfxColorMaxComps];
  int i;

  // NB: there can be one function with n outputs or n functions with
  // one output each (where n = number of color components)
  for (i = 0; i < gfxColorMaxComps; ++i) {
    out[i] = 0;
  }
  for (i = 0; i < nFuncs; ++i) {
    funcs[i]->transform(&t, &out[i]);
  }
  for (i = 0; i < gfxColorMaxComps; ++i) {
    color->c[i] = dblToCol(out[i]);
  }
}

//------------------------------------------------------------------------
// GfxRadialShading
//------------------------------------------------------------------------

GfxRadialShading::GfxRadialShading(double x0A, double y0A, double r0A,
				   double x1A, double y1A, double r1A,
				   double t0A, double t1A,
				   Function **funcsA, int nFuncsA,
				   GBool extend0A, GBool extend1A):
  GfxShading(3)
{
  int i;

  x0 = x0A;
  y0 = y0A;
  r0 = r0A;
  x1 = x1A;
  y1 = y1A;
  r1 = r1A;
  t0 = t0A;
  t1 = t1A;
  nFuncs = nFuncsA;
  for (i = 0; i < nFuncs; ++i) {
    funcs[i] = funcsA[i];
  }
  extend0 = extend0A;
  extend1 = extend1A;
}

GfxRadialShading::GfxRadialShading(GfxRadialShading *shading):
  GfxShading(shading)
{
  int i;

  x0 = shading->x0;
  y0 = shading->y0;
  r0 = shading->r0;
  x1 = shading->x1;
  y1 = shading->y1;
  r1 = shading->r1;
  t0 = shading->t0;
  y1 = shading->t1;
  nFuncs = shading->nFuncs;
  for (i = 0; i < nFuncs; ++i) {
    funcs[i] = shading->funcs[i]->copy();
  }
  extend0 = shading->extend0;
  extend1 = shading->extend1;
}

GfxRadialShading::~GfxRadialShading() {
  int i;

  for (i = 0; i < nFuncs; ++i) {
    delete funcs[i];
  }
}

GfxRadialShading *GfxRadialShading::parse(Dict *dict) {
  GfxRadialShading *shading;
  double x0A, y0A, r0A, x1A, y1A, r1A;
  double t0A, t1A;
  Function *funcsA[gfxColorMaxComps];
  int nFuncsA;
  GBool extend0A, extend1A;
  Object obj1, obj2;
  int i;

  x0A = y0A = r0A = x1A = y1A = r1A = 0;
  if (dict->lookup("Coords", &obj1)->isArray() &&
      obj1.arrayGetLength() == 6) {
    x0A = obj1.arrayGet(0, &obj2)->getNum();
    obj2.free();
    y0A = obj1.arrayGet(1, &obj2)->getNum();
    obj2.free();
    r0A = obj1.arrayGet(2, &obj2)->getNum();
    obj2.free();
    x1A = obj1.arrayGet(3, &obj2)->getNum();
    obj2.free();
    y1A = obj1.arrayGet(4, &obj2)->getNum();
    obj2.free();
    r1A = obj1.arrayGet(5, &obj2)->getNum();
    obj2.free();
  } else {
    error(-1, "Missing or invalid Coords in shading dictionary");
    goto err1;
  }
  obj1.free();

  t0A = 0;
  t1A = 1;
  if (dict->lookup("Domain", &obj1)->isArray() &&
      obj1.arrayGetLength() == 2) {
    t0A = obj1.arrayGet(0, &obj2)->getNum();
    obj2.free();
    t1A = obj1.arrayGet(1, &obj2)->getNum();
    obj2.free();
  }
  obj1.free();

  dict->lookup("Function", &obj1);
  if (obj1.isArray()) {
    nFuncsA = obj1.arrayGetLength();
    if (nFuncsA > gfxColorMaxComps) {
      error(-1, "Invalid Function array in shading dictionary");
      goto err1;
    }
    for (i = 0; i < nFuncsA; ++i) {
      obj1.arrayGet(i, &obj2);
      if (!(funcsA[i] = Function::parse(&obj2))) {
	obj1.free();
	obj2.free();
	goto err1;
      }
      obj2.free();
    }
  } else {
    nFuncsA = 1;
    if (!(funcsA[0] = Function::parse(&obj1))) {
      obj1.free();
      goto err1;
    }
  }
  obj1.free();

  extend0A = extend1A = gFalse;
  if (dict->lookup("Extend", &obj1)->isArray() &&
      obj1.arrayGetLength() == 2) {
    extend0A = obj1.arrayGet(0, &obj2)->getBool();
    obj2.free();
    extend1A = obj1.arrayGet(1, &obj2)->getBool();
    obj2.free();
  }
  obj1.free();

  shading = new GfxRadialShading(x0A, y0A, r0A, x1A, y1A, r1A, t0A, t1A,
				 funcsA, nFuncsA, extend0A, extend1A);
  if (!shading->init(dict)) {
    delete shading;
    return NULL;
  }
  return shading;

 err1:
  return NULL;
}

GfxShading *GfxRadialShading::copy() {
  return new GfxRadialShading(this);
}

void GfxRadialShading::getColor(double t, GfxColor *color) {
  double out[gfxColorMaxComps];
  int i;

  // NB: there can be one function with n outputs or n functions with
  // one output each (where n = number of color components)
  for (i = 0; i < gfxColorMaxComps; ++i) {
    out[i] = 0;
  }
  for (i = 0; i < nFuncs; ++i) {
    funcs[i]->transform(&t, &out[i]);
  }
  for (i = 0; i < gfxColorMaxComps; ++i) {
    color->c[i] = dblToCol(out[i]);
  }
}

//------------------------------------------------------------------------
// GfxShadingBitBuf
//------------------------------------------------------------------------

class GfxShadingBitBuf {
public:

  GfxShadingBitBuf(Stream *strA);
  ~GfxShadingBitBuf();
  GBool getBits(int n, Guint *val);
  void flushBits();

private:

  Stream *str;
  int bitBuf;
  int nBits;
};

GfxShadingBitBuf::GfxShadingBitBuf(Stream *strA) {
  str = strA;
  str->reset();
  bitBuf = 0;
  nBits = 0;
}

GfxShadingBitBuf::~GfxShadingBitBuf() {
  str->close();
}

GBool GfxShadingBitBuf::getBits(int n, Guint *val) {
  int x;

  if (nBits >= n) {
    x = (bitBuf >> (nBits - n)) & ((1 << n) - 1);
    nBits -= n;
  } else {
    x = 0;
    if (nBits > 0) {
      x = bitBuf & ((1 << nBits) - 1);
      n -= nBits;
      nBits = 0;
    }
    while (n > 0) {
      if ((bitBuf = str->getChar()) == EOF) {
	nBits = 0;
	return gFalse;
      }
      if (n >= 8) {
	x = (x << 8) | bitBuf;
	n -= 8;
      } else {
	x = (x << n) | (bitBuf >> (8 - n));
	nBits = 8 - n;
	n = 0;
      }
    }
  }
  *val = x;
  return gTrue;
}

void GfxShadingBitBuf::flushBits() {
  bitBuf = 0;
  nBits = 0;
}

//------------------------------------------------------------------------
// GfxGouraudTriangleShading
//------------------------------------------------------------------------

GfxGouraudTriangleShading::GfxGouraudTriangleShading(
			       int typeA,
			       GfxGouraudVertex *verticesA, int nVerticesA,
			       int (*trianglesA)[3], int nTrianglesA,
			       Function **funcsA, int nFuncsA):
  GfxShading(typeA)
{
  int i;

  vertices = verticesA;
  nVertices = nVerticesA;
  triangles = trianglesA;
  nTriangles = nTrianglesA;
  nFuncs = nFuncsA;
  for (i = 0; i < nFuncs; ++i) {
    funcs[i] = funcsA[i];
  }
}

GfxGouraudTriangleShading::GfxGouraudTriangleShading(
			       GfxGouraudTriangleShading *shading):
  GfxShading(shading)
{
  int i;

  nVertices = shading->nVertices;
  vertices = (GfxGouraudVertex *)gmallocn(nVertices, sizeof(GfxGouraudVertex));
  memcpy(vertices, shading->vertices, nVertices * sizeof(GfxGouraudVertex));
  nTriangles = shading->nTriangles;
  triangles = (int (*)[3])gmallocn(nTriangles * 3, sizeof(int));
  memcpy(triangles, shading->triangles, nTriangles * 3 * sizeof(int));
  nFuncs = shading->nFuncs;
  for (i = 0; i < nFuncs; ++i) {
    funcs[i] = shading->funcs[i]->copy();
  }
}

GfxGouraudTriangleShading::~GfxGouraudTriangleShading() {
  int i;

  gfree(vertices);
  gfree(triangles);
  for (i = 0; i < nFuncs; ++i) {
    delete funcs[i];
  }
}

GfxGouraudTriangleShading *GfxGouraudTriangleShading::parse(int typeA,
							    Dict *dict,
							    Stream *str) {
  GfxGouraudTriangleShading *shading;
  Function *funcsA[gfxColorMaxComps];
  int nFuncsA;
  int coordBits, compBits, flagBits, vertsPerRow, nRows;
  double xMin, xMax, yMin, yMax;
  double cMin[gfxColorMaxComps], cMax[gfxColorMaxComps];
  double xMul, yMul;
  double cMul[gfxColorMaxComps];
  GfxGouraudVertex *verticesA;
  int (*trianglesA)[3];
  int nComps, nVerticesA, nTrianglesA, vertSize, triSize;
  Guint x, y, flag;
  Guint c[gfxColorMaxComps];
  GfxShadingBitBuf *bitBuf;
  Object obj1, obj2;
  int i, j, k, state;

  if (dict->lookup("BitsPerCoordinate", &obj1)->isInt()) {
    coordBits = obj1.getInt();
  } else {
    error(-1, "Missing or invalid BitsPerCoordinate in shading dictionary");
    goto err2;
  }
  obj1.free();
  if (dict->lookup("BitsPerComponent", &obj1)->isInt()) {
    compBits = obj1.getInt();
  } else {
    error(-1, "Missing or invalid BitsPerComponent in shading dictionary");
    goto err2;
  }
  obj1.free();
  flagBits = vertsPerRow = 0; // make gcc happy
  if (typeA == 4) {
    if (dict->lookup("BitsPerFlag", &obj1)->isInt()) {
      flagBits = obj1.getInt();
    } else {
      error(-1, "Missing or invalid BitsPerFlag in shading dictionary");
      goto err2;
    }
    obj1.free();
  } else {
    if (dict->lookup("VerticesPerRow", &obj1)->isInt()) {
      vertsPerRow = obj1.getInt();
    } else {
      error(-1, "Missing or invalid VerticesPerRow in shading dictionary");
      goto err2;
    }
    obj1.free();
  }
  if (dict->lookup("Decode", &obj1)->isArray() &&
      obj1.arrayGetLength() >= 6) {
    xMin = obj1.arrayGet(0, &obj2)->getNum();
    obj2.free();
    xMax = obj1.arrayGet(1, &obj2)->getNum();
    obj2.free();
    xMul = (xMax - xMin) / (pow(2.0, coordBits) - 1);
    yMin = obj1.arrayGet(2, &obj2)->getNum();
    obj2.free();
    yMax = obj1.arrayGet(3, &obj2)->getNum();
    obj2.free();
    yMul = (yMax - yMin) / (pow(2.0, coordBits) - 1);
    for (i = 0; 5 + 2*i < obj1.arrayGetLength() && i < gfxColorMaxComps; ++i) {
      cMin[i] = obj1.arrayGet(4 + 2*i, &obj2)->getNum();
      obj2.free();
      cMax[i] = obj1.arrayGet(5 + 2*i, &obj2)->getNum();
      obj2.free();
      cMul[i] = (cMax[i] - cMin[i]) / (double)((1 << compBits) - 1);
    }
    nComps = i;
  } else {
    error(-1, "Missing or invalid Decode array in shading dictionary");
    goto err2;
  }
  obj1.free();

  if (!dict->lookup("Function", &obj1)->isNull()) {
    if (obj1.isArray()) {
      nFuncsA = obj1.arrayGetLength();
      if (nFuncsA > gfxColorMaxComps) {
	error(-1, "Invalid Function array in shading dictionary");
	goto err1;
      }
      for (i = 0; i < nFuncsA; ++i) {
	obj1.arrayGet(i, &obj2);
	if (!(funcsA[i] = Function::parse(&obj2))) {
	  obj1.free();
	  obj2.free();
	  goto err1;
	}
	obj2.free();
      }
    } else {
      nFuncsA = 1;
      if (!(funcsA[0] = Function::parse(&obj1))) {
	obj1.free();
	goto err1;
      }
    }
  } else {
    nFuncsA = 0;
  }
  obj1.free();

  nVerticesA = nTrianglesA = 0;
  verticesA = NULL;
  trianglesA = NULL;
  vertSize = triSize = 0;
  state = 0;
  flag = 0; // make gcc happy
  bitBuf = new GfxShadingBitBuf(str);
  while (1) {
    if (typeA == 4) {
      if (!bitBuf->getBits(flagBits, &flag)) {
	break;
      }
    }
    if (!bitBuf->getBits(coordBits, &x) ||
	!bitBuf->getBits(coordBits, &y)) {
      break;
    }
    for (i = 0; i < nComps; ++i) {
      if (!bitBuf->getBits(compBits, &c[i])) {
	break;
      }
    }
    if (i < nComps) {
      break;
    }
    if (nVerticesA == vertSize) {
      vertSize = (vertSize == 0) ? 16 : 2 * vertSize;
      verticesA = (GfxGouraudVertex *)
	              greallocn(verticesA, vertSize, sizeof(GfxGouraudVertex));
    }
    verticesA[nVerticesA].x = xMin + xMul * (double)x;
    verticesA[nVerticesA].y = yMin + yMul * (double)y;
    for (i = 0; i < nComps; ++i) {
      verticesA[nVerticesA].color.c[i] =
	  dblToCol(cMin[i] + cMul[i] * (double)c[i]);
    }
    ++nVerticesA;
    bitBuf->flushBits();
    if (typeA == 4) {
      if (state == 0 || state == 1) {
	++state;
      } else if (state == 2 || flag > 0) {
	if (nTrianglesA == triSize) {
	  triSize = (triSize == 0) ? 16 : 2 * triSize;
	  trianglesA = (int (*)[3])
	                   greallocn(trianglesA, triSize * 3, sizeof(int));
	}
	if (state == 2) {
	  trianglesA[nTrianglesA][0] = nVerticesA - 3;
	  trianglesA[nTrianglesA][1] = nVerticesA - 2;
	  trianglesA[nTrianglesA][2] = nVerticesA - 1;
	  ++state;
	} else if (flag == 1) {
	  trianglesA[nTrianglesA][0] = trianglesA[nTrianglesA - 1][1];
	  trianglesA[nTrianglesA][1] = trianglesA[nTrianglesA - 1][2];
	  trianglesA[nTrianglesA][2] = nVerticesA - 1;
	} else { // flag == 2
	  trianglesA[nTrianglesA][0] = trianglesA[nTrianglesA - 1][0];
	  trianglesA[nTrianglesA][1] = trianglesA[nTrianglesA - 1][2];
	  trianglesA[nTrianglesA][2] = nVerticesA - 1;
	}
	++nTrianglesA;
      } else { // state == 3 && flag == 0
	state = 1;
      }
    }
  }
  delete bitBuf;
  if (typeA == 5) {
    nRows = nVerticesA / vertsPerRow;
    nTrianglesA = (nRows - 1) * 2 * (vertsPerRow - 1);
    trianglesA = (int (*)[3])gmallocn(nTrianglesA * 3, sizeof(int));
    k = 0;
    for (i = 0; i < nRows - 1; ++i) {
      for (j = 0; j < vertsPerRow - 1; ++j) {
	trianglesA[k][0] = i * vertsPerRow + j;
	trianglesA[k][1] = i * vertsPerRow + j+1;
	trianglesA[k][2] = (i+1) * vertsPerRow + j;
	++k;
	trianglesA[k][0] = i * vertsPerRow + j+1;
	trianglesA[k][1] = (i+1) * vertsPerRow + j;
	trianglesA[k][2] = (i+1) * vertsPerRow + j+1;
	++k;
      }
    }
  }

  shading = new GfxGouraudTriangleShading(typeA, verticesA, nVerticesA,
					  trianglesA, nTrianglesA,
					  funcsA, nFuncsA);
  if (!shading->init(dict)) {
    delete shading;
    return NULL;
  }
  return shading;

 err2:
  obj1.free();
 err1:
  return NULL;
}

GfxShading *GfxGouraudTriangleShading::copy() {
  return new GfxGouraudTriangleShading(this);
}

void GfxGouraudTriangleShading::getTriangle(
				    int i,
				    double *x0, double *y0, GfxColor *color0,
				    double *x1, double *y1, GfxColor *color1,
				    double *x2, double *y2, GfxColor *color2) {
  double in;
  double out[gfxColorMaxComps];
  int v, j;

  v = triangles[i][0];
  *x0 = vertices[v].x;
  *y0 = vertices[v].y;
  if (nFuncs > 0) {
    in = colToDbl(vertices[v].color.c[0]);
    for (j = 0; j < nFuncs; ++j) {
      funcs[j]->transform(&in, &out[j]);
    }
    for (j = 0; j < gfxColorMaxComps; ++j) {
      color0->c[j] = dblToCol(out[j]);
    }
  } else {
    *color0 = vertices[v].color;
  }
  v = triangles[i][1];
  *x1 = vertices[v].x;
  *y1 = vertices[v].y;
  if (nFuncs > 0) {
    in = colToDbl(vertices[v].color.c[0]);
    for (j = 0; j < nFuncs; ++j) {
      funcs[j]->transform(&in, &out[j]);
    }
    for (j = 0; j < gfxColorMaxComps; ++j) {
      color1->c[j] = dblToCol(out[j]);
    }
  } else {
    *color1 = vertices[v].color;
  }
  v = triangles[i][2];
  *x2 = vertices[v].x;
  *y2 = vertices[v].y;
  if (nFuncs > 0) {
    in = colToDbl(vertices[v].color.c[0]);
    for (j = 0; j < nFuncs; ++j) {
      funcs[j]->transform(&in, &out[j]);
    }
    for (j = 0; j < gfxColorMaxComps; ++j) {
      color2->c[j] = dblToCol(out[j]);
    }
  } else {
    *color2 = vertices[v].color;
  }
}

//------------------------------------------------------------------------
// GfxPatchMeshShading
//------------------------------------------------------------------------

GfxPatchMeshShading::GfxPatchMeshShading(int typeA,
					 GfxPatch *patchesA, int nPatchesA,
					 Function **funcsA, int nFuncsA):
  GfxShading(typeA)
{
  int i;

  patches = patchesA;
  nPatches = nPatchesA;
  nFuncs = nFuncsA;
  for (i = 0; i < nFuncs; ++i) {
    funcs[i] = funcsA[i];
  }
}

GfxPatchMeshShading::GfxPatchMeshShading(GfxPatchMeshShading *shading):
  GfxShading(shading)
{
  int i;

  nPatches = shading->nPatches;
  patches = (GfxPatch *)gmallocn(nPatches, sizeof(GfxPatch));
  memcpy(patches, shading->patches, nPatches * sizeof(GfxPatch));
  nFuncs = shading->nFuncs;
  for (i = 0; i < nFuncs; ++i) {
    funcs[i] = shading->funcs[i]->copy();
  }
}

GfxPatchMeshShading::~GfxPatchMeshShading() {
  int i;

  gfree(patches);
  for (i = 0; i < nFuncs; ++i) {
    delete funcs[i];
  }
}

GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
						Stream *str) {
  GfxPatchMeshShading *shading;
  Function *funcsA[gfxColorMaxComps];
  int nFuncsA;
  int coordBits, compBits, flagBits;
  double xMin, xMax, yMin, yMax;
  double cMin[gfxColorMaxComps], cMax[gfxColorMaxComps];
  double xMul, yMul;
  double cMul[gfxColorMaxComps];
  GfxPatch *patchesA, *p;
  int nComps, nPatchesA, patchesSize, nPts, nColors;
  Guint flag;
  double x[16], y[16];
  Guint xi, yi;
  GfxColorComp c[4][gfxColorMaxComps];
  Guint ci[4];
  GfxShadingBitBuf *bitBuf;
  Object obj1, obj2;
  int i, j;

  if (dict->lookup("BitsPerCoordinate", &obj1)->isInt()) {
    coordBits = obj1.getInt();
  } else {
    error(-1, "Missing or invalid BitsPerCoordinate in shading dictionary");
    goto err2;
  }
  obj1.free();
  if (dict->lookup("BitsPerComponent", &obj1)->isInt()) {
    compBits = obj1.getInt();
  } else {
    error(-1, "Missing or invalid BitsPerComponent in shading dictionary");
    goto err2;
  }
  obj1.free();
  if (dict->lookup("BitsPerFlag", &obj1)->isInt()) {
    flagBits = obj1.getInt();
  } else {
    error(-1, "Missing or invalid BitsPerFlag in shading dictionary");
    goto err2;
  }
  obj1.free();
  if (dict->lookup("Decode", &obj1)->isArray() &&
      obj1.arrayGetLength() >= 6) {
    xMin = obj1.arrayGet(0, &obj2)->getNum();
    obj2.free();
    xMax = obj1.arrayGet(1, &obj2)->getNum();
    obj2.free();
    xMul = (xMax - xMin) / (pow(2.0, coordBits) - 1);
    yMin = obj1.arrayGet(2, &obj2)->getNum();
    obj2.free();
    yMax = obj1.arrayGet(3, &obj2)->getNum();
    obj2.free();
    yMul = (yMax - yMin) / (pow(2.0, coordBits) - 1);
    for (i = 0; 5 + 2*i < obj1.arrayGetLength() && i < gfxColorMaxComps; ++i) {
      cMin[i] = obj1.arrayGet(4 + 2*i, &obj2)->getNum();
      obj2.free();
      cMax[i] = obj1.arrayGet(5 + 2*i, &obj2)->getNum();
      obj2.free();
      cMul[i] = (cMax[i] - cMin[i]) / (double)((1 << compBits) - 1);
    }
    nComps = i;
  } else {
    error(-1, "Missing or invalid Decode array in shading dictionary");
    goto err2;
  }
  obj1.free();

  if (!dict->lookup("Function", &obj1)->isNull()) {
    if (obj1.isArray()) {
      nFuncsA = obj1.arrayGetLength();
      if (nFuncsA > gfxColorMaxComps) {
	error(-1, "Invalid Function array in shading dictionary");
	goto err1;
      }
      for (i = 0; i < nFuncsA; ++i) {
	obj1.arrayGet(i, &obj2);
	if (!(funcsA[i] = Function::parse(&obj2))) {
	  obj1.free();
	  obj2.free();
	  goto err1;
	}
	obj2.free();
      }
    } else {
      nFuncsA = 1;
      if (!(funcsA[0] = Function::parse(&obj1))) {
	obj1.free();
	goto err1;
      }
    }
  } else {
    nFuncsA = 0;
  }
  obj1.free();

  nPatchesA = 0;
  patchesA = NULL;
  patchesSize = 0;
  bitBuf = new GfxShadingBitBuf(str);
  while (1) {
    if (!bitBuf->getBits(flagBits, &flag)) {
      break;
    }
    if (typeA == 6) {
      switch (flag) {
      case 0: nPts = 12; nColors = 4; break;
      case 1:
      case 2:
      case 3:
      default: nPts =  8; nColors = 2; break;
      }
    } else {
      switch (flag) {
      case 0: nPts = 16; nColors = 4; break;
      case 1:
      case 2:
      case 3:
      default: nPts = 12; nColors = 2; break;
      }
    }
    for (i = 0; i < nPts; ++i) {
      if (!bitBuf->getBits(coordBits, &xi) ||
	  !bitBuf->getBits(coordBits, &yi)) {
	break;
      }
      x[i] = xMin + xMul * (double)xi;
      y[i] = yMin + yMul * (double)yi;
    }
    if (i < nPts) {
      break;
    }
    for (i = 0; i < nColors; ++i) {
      for (j = 0; j < nComps; ++j) {
	if (!bitBuf->getBits(compBits, &ci[j])) {
	  break;
	}
	c[i][j] = dblToCol(cMin[j] + cMul[j] * (double)ci[j]);
      }
      if (j < nComps) {
	break;
      }
    }
    if (i < nColors) {
      break;
    }
    if (nPatchesA == patchesSize) {
      patchesSize = (patchesSize == 0) ? 16 : 2 * patchesSize;
      patchesA = (GfxPatch *)greallocn(patchesA,
				       patchesSize, sizeof(GfxPatch));
    }
    p = &patchesA[nPatchesA];
    if (typeA == 6) {
      switch (flag) {
      case 0:
	p->x[0][0] = x[0];
	p->y[0][0] = y[0];
	p->x[0][1] = x[1];
	p->y[0][1] = y[1];
	p->x[0][2] = x[2];
	p->y[0][2] = y[2];
	p->x[0][3] = x[3];
	p->y[0][3] = y[3];
	p->x[1][3] = x[4];
	p->y[1][3] = y[4];
	p->x[2][3] = x[5];
	p->y[2][3] = y[5];
	p->x[3][3] = x[6];
	p->y[3][3] = y[6];
	p->x[3][2] = x[7];
	p->y[3][2] = y[7];
	p->x[3][1] = x[8];
	p->y[3][1] = y[8];
	p->x[3][0] = x[9];
	p->y[3][0] = y[9];
	p->x[2][0] = x[10];
	p->y[2][0] = y[10];
	p->x[1][0] = x[11];
	p->y[1][0] = y[11];
	for (j = 0; j < nComps; ++j) {
	  p->color[0][0].c[j] = c[0][j];
	  p->color[0][1].c[j] = c[1][j];
	  p->color[1][1].c[j] = c[2][j];
	  p->color[1][0].c[j] = c[3][j];
	}
	break;
      case 1:
	p->x[0][0] = patchesA[nPatchesA-1].x[0][3];
	p->y[0][0] = patchesA[nPatchesA-1].y[0][3];
	p->x[0][1] = patchesA[nPatchesA-1].x[1][3];
	p->y[0][1] = patchesA[nPatchesA-1].y[1][3];
	p->x[0][2] = patchesA[nPatchesA-1].x[2][3];
	p->y[0][2] = patchesA[nPatchesA-1].y[2][3];
	p->x[0][3] = patchesA[nPatchesA-1].x[3][3];
	p->y[0][3] = patchesA[nPatchesA-1].y[3][3];
	p->x[1][3] = x[0];
	p->y[1][3] = y[0];
	p->x[2][3] = x[1];
	p->y[2][3] = y[1];
	p->x[3][3] = x[2];
	p->y[3][3] = y[2];
	p->x[3][2] = x[3];
	p->y[3][2] = y[3];
	p->x[3][1] = x[4];
	p->y[3][1] = y[4];
	p->x[3][0] = x[5];
	p->y[3][0] = y[5];
	p->x[2][0] = x[6];
	p->y[2][0] = y[6];
	p->x[1][0] = x[7];
	p->y[1][0] = y[7];
	for (j = 0; j < nComps; ++j) {
	  p->color[0][0].c[j] = patchesA[nPatchesA-1].color[0][1].c[j];
	  p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][1].c[j];
	  p->color[1][1].c[j] = c[0][j];
	  p->color[1][0].c[j] = c[1][j];
	}
	break;
      case 2:
	p->x[0][0] = patchesA[nPatchesA-1].x[3][3];
	p->y[0][0] = patchesA[nPatchesA-1].y[3][3];
	p->x[0][1] = patchesA[nPatchesA-1].x[3][2];
	p->y[0][1] = patchesA[nPatchesA-1].y[3][2];
	p->x[0][2] = patchesA[nPatchesA-1].x[3][1];
	p->y[0][2] = patchesA[nPatchesA-1].y[3][1];
	p->x[0][3] = patchesA[nPatchesA-1].x[3][0];
	p->y[0][3] = patchesA[nPatchesA-1].y[3][0];
	p->x[1][3] = x[0];
	p->y[1][3] = y[0];
	p->x[2][3] = x[1];
	p->y[2][3] = y[1];
	p->x[3][3] = x[2];
	p->y[3][3] = y[2];
	p->x[3][2] = x[3];
	p->y[3][2] = y[3];
	p->x[3][1] = x[4];
	p->y[3][1] = y[4];
	p->x[3][0] = x[5];
	p->y[3][0] = y[5];
	p->x[2][0] = x[6];
	p->y[2][0] = y[6];
	p->x[1][0] = x[7];
	p->y[1][0] = y[7];
	for (j = 0; j < nComps; ++j) {
	  p->color[0][0].c[j] = patchesA[nPatchesA-1].color[1][1].c[j];
	  p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][0].c[j];
	  p->color[1][1].c[j] = c[0][j];
	  p->color[1][0].c[j] = c[1][j];
	}
	break;
      case 3:
	p->x[0][0] = patchesA[nPatchesA-1].x[3][0];
	p->y[0][0] = patchesA[nPatchesA-1].y[3][0];
	p->x[0][1] = patchesA[nPatchesA-1].x[2][0];
	p->y[0][1] = patchesA[nPatchesA-1].y[2][0];
	p->x[0][2] = patchesA[nPatchesA-1].x[1][0];
	p->y[0][2] = patchesA[nPatchesA-1].y[1][0];
	p->x[0][3] = patchesA[nPatchesA-1].x[0][0];
	p->y[0][3] = patchesA[nPatchesA-1].y[0][0];
	p->x[1][3] = x[0];
	p->y[1][3] = y[0];
	p->x[2][3] = x[1];
	p->y[2][3] = y[1];
	p->x[3][3] = x[2];
	p->y[3][3] = y[2];
	p->x[3][2] = x[3];
	p->y[3][2] = y[3];
	p->x[3][1] = x[4];
	p->y[3][1] = y[4];
	p->x[3][0] = x[5];
	p->y[3][0] = y[5];
	p->x[2][0] = x[6];
	p->y[2][0] = y[6];
	p->x[1][0] = x[7];
	p->y[1][0] = y[7];
	for (j = 0; j < nComps; ++j) {
	  p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][0].c[j];
	  p->color[0][1].c[j] = patchesA[nPatchesA-1].color[0][0].c[j];
	  p->color[1][1].c[j] = c[0][j];
	  p->color[1][0].c[j] = c[1][j];
	}
	break;
      }
    } else {
      switch (flag) {
      case 0:
	p->x[0][0] = x[0];
	p->y[0][0] = y[0];
	p->x[0][1] = x[1];
	p->y[0][1] = y[1];
	p->x[0][2] = x[2];
	p->y[0][2] = y[2];
	p->x[0][3] = x[3];
	p->y[0][3] = y[3];
	p->x[1][3] = x[4];
	p->y[1][3] = y[4];
	p->x[2][3] = x[5];
	p->y[2][3] = y[5];
	p->x[3][3] = x[6];
	p->y[3][3] = y[6];
	p->x[3][2] = x[7];
	p->y[3][2] = y[7];
	p->x[3][1] = x[8];
	p->y[3][1] = y[8];
	p->x[3][0] = x[9];
	p->y[3][0] = y[9];
	p->x[2][0] = x[10];
	p->y[2][0] = y[10];
	p->x[1][0] = x[11];
	p->y[1][0] = y[11];
	p->x[1][1] = x[12];
	p->y[1][1] = y[12];
	p->x[1][2] = x[13];
	p->y[1][2] = y[13];
	p->x[2][2] = x[14];
	p->y[2][2] = y[14];
	p->x[2][1] = x[15];
	p->y[2][1] = y[15];
	for (j = 0; j < nComps; ++j) {
	  p->color[0][0].c[j] = c[0][j];
	  p->color[0][1].c[j] = c[1][j];
	  p->color[1][1].c[j] = c[2][j];
	  p->color[1][0].c[j] = c[3][j];
	}
	break;
      case 1:
	p->x[0][0] = patchesA[nPatchesA-1].x[0][3];
	p->y[0][0] = patchesA[nPatchesA-1].y[0][3];
	p->x[0][1] = patchesA[nPatchesA-1].x[1][3];
	p->y[0][1] = patchesA[nPatchesA-1].y[1][3];
	p->x[0][2] = patchesA[nPatchesA-1].x[2][3];
	p->y[0][2] = patchesA[nPatchesA-1].y[2][3];
	p->x[0][3] = patchesA[nPatchesA-1].x[3][3];
	p->y[0][3] = patchesA[nPatchesA-1].y[3][3];
	p->x[1][3] = x[0];
	p->y[1][3] = y[0];
	p->x[2][3] = x[1];
	p->y[2][3] = y[1];
	p->x[3][3] = x[2];
	p->y[3][3] = y[2];
	p->x[3][2] = x[3];
	p->y[3][2] = y[3];
	p->x[3][1] = x[4];
	p->y[3][1] = y[4];
	p->x[3][0] = x[5];
	p->y[3][0] = y[5];
	p->x[2][0] = x[6];
	p->y[2][0] = y[6];
	p->x[1][0] = x[7];
	p->y[1][0] = y[7];
	p->x[1][1] = x[8];
	p->y[1][1] = y[8];
	p->x[1][2] = x[9];
	p->y[1][2] = y[9];
	p->x[2][2] = x[10];
	p->y[2][2] = y[10];
	p->x[2][1] = x[11];
	p->y[2][1] = y[11];
	for (j = 0; j < nComps; ++j) {
	  p->color[0][0].c[j] = patchesA[nPatchesA-1].color[0][1].c[j];
	  p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][1].c[j];
	  p->color[1][1].c[j] = c[0][j];
	  p->color[1][0].c[j] = c[1][j];
	}
	break;
      case 2:
	p->x[0][0] = patchesA[nPatchesA-1].x[3][3];
	p->y[0][0] = patchesA[nPatchesA-1].y[3][3];
	p->x[0][1] = patchesA[nPatchesA-1].x[3][2];
	p->y[0][1] = patchesA[nPatchesA-1].y[3][2];
	p->x[0][2] = patchesA[nPatchesA-1].x[3][1];
	p->y[0][2] = patchesA[nPatchesA-1].y[3][1];
	p->x[0][3] = patchesA[nPatchesA-1].x[3][0];
	p->y[0][3] = patchesA[nPatchesA-1].y[3][0];
	p->x[1][3] = x[0];
	p->y[1][3] = y[0];
	p->x[2][3] = x[1];
	p->y[2][3] = y[1];
	p->x[3][3] = x[2];
	p->y[3][3] = y[2];
	p->x[3][2] = x[3];
	p->y[3][2] = y[3];
	p->x[3][1] = x[4];
	p->y[3][1] = y[4];
	p->x[3][0] = x[5];
	p->y[3][0] = y[5];
	p->x[2][0] = x[6];
	p->y[2][0] = y[6];
	p->x[1][0] = x[7];
	p->y[1][0] = y[7];
	p->x[1][1] = x[8];
	p->y[1][1] = y[8];
	p->x[1][2] = x[9];
	p->y[1][2] = y[9];
	p->x[2][2] = x[10];
	p->y[2][2] = y[10];
	p->x[2][1] = x[11];
	p->y[2][1] = y[11];
	for (j = 0; j < nComps; ++j) {
	  p->color[0][0].c[j] = patchesA[nPatchesA-1].color[1][1].c[j];
	  p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][0].c[j];
	  p->color[1][1].c[j] = c[0][j];
	  p->color[1][0].c[j] = c[1][j];
	}
	break;
      case 3:
	p->x[0][0] = patchesA[nPatchesA-1].x[3][0];
	p->y[0][0] = patchesA[nPatchesA-1].y[3][0];
	p->x[0][1] = patchesA[nPatchesA-1].x[2][0];
	p->y[0][1] = patchesA[nPatchesA-1].y[2][0];
	p->x[0][2] = patchesA[nPatchesA-1].x[1][0];
	p->y[0][2] = patchesA[nPatchesA-1].y[1][0];
	p->x[0][3] = patchesA[nPatchesA-1].x[0][0];
	p->y[0][3] = patchesA[nPatchesA-1].y[0][0];
	p->x[1][3] = x[0];
	p->y[1][3] = y[0];
	p->x[2][3] = x[1];
	p->y[2][3] = y[1];
	p->x[3][3] = x[2];
	p->y[3][3] = y[2];
	p->x[3][2] = x[3];
	p->y[3][2] = y[3];
	p->x[3][1] = x[4];
	p->y[3][1] = y[4];
	p->x[3][0] = x[5];
	p->y[3][0] = y[5];
	p->x[2][0] = x[6];
	p->y[2][0] = y[6];
	p->x[1][0] = x[7];
	p->y[1][0] = y[7];
	p->x[1][1] = x[8];
	p->y[1][1] = y[8];
	p->x[1][2] = x[9];
	p->y[1][2] = y[9];
	p->x[2][2] = x[10];
	p->y[2][2] = y[10];
	p->x[2][1] = x[11];
	p->y[2][1] = y[11];
	for (j = 0; j < nComps; ++j) {
	  p->color[0][0].c[j] = patchesA[nPatchesA-1].color[1][0].c[j];
	  p->color[0][1].c[j] = patchesA[nPatchesA-1].color[0][0].c[j];
	  p->color[1][1].c[j] = c[0][j];
	  p->color[1][0].c[j] = c[1][j];
	}
	break;
      }
    }
    ++nPatchesA;
    bitBuf->flushBits();
  }
  delete bitBuf;

  if (typeA == 6) {
    for (i = 0; i < nPatchesA; ++i) {
      p = &patchesA[i];
      p->x[1][1] = (-4 * p->x[0][0]
		    +6 * (p->x[0][1] + p->x[1][0])
		    -2 * (p->x[0][3] + p->x[3][0])
		    +3 * (p->x[3][1] + p->x[1][3])
		    - p->x[3][3]) / 9;
      p->y[1][1] = (-4 * p->y[0][0]
		    +6 * (p->y[0][1] + p->y[1][0])
		    -2 * (p->y[0][3] + p->y[3][0])
		    +3 * (p->y[3][1] + p->y[1][3])
		    - p->y[3][3]) / 9;
      p->x[1][2] = (-4 * p->x[0][3]
		    +6 * (p->x[0][2] + p->x[1][3])
		    -2 * (p->x[0][0] + p->x[3][3])
		    +3 * (p->x[3][2] + p->x[1][0])
		    - p->x[3][0]) / 9;
      p->y[1][2] = (-4 * p->y[0][3]
		    +6 * (p->y[0][2] + p->y[1][3])
		    -2 * (p->y[0][0] + p->y[3][3])
		    +3 * (p->y[3][2] + p->y[1][0])
		    - p->y[3][0]) / 9;
      p->x[2][1] = (-4 * p->x[3][0]
		    +6 * (p->x[3][1] + p->x[2][0])
		    -2 * (p->x[3][3] + p->x[0][0])
		    +3 * (p->x[0][1] + p->x[2][3])
		    - p->x[0][3]) / 9;
      p->y[2][1] = (-4 * p->y[3][0]
		    +6 * (p->y[3][1] + p->y[2][0])
		    -2 * (p->y[3][3] + p->y[0][0])
		    +3 * (p->y[0][1] + p->y[2][3])
		    - p->y[0][3]) / 9;
      p->x[2][2] = (-4 * p->x[3][3]
		    +6 * (p->x[3][2] + p->x[2][3])
		    -2 * (p->x[3][0] + p->x[0][3])
		    +3 * (p->x[0][2] + p->x[2][0])
		    - p->x[0][0]) / 9;
      p->y[2][2] = (-4 * p->y[3][3]
		    +6 * (p->y[3][2] + p->y[2][3])
		    -2 * (p->y[3][0] + p->y[0][3])
		    +3 * (p->y[0][2] + p->y[2][0])
		    - p->y[0][0]) / 9;
    }
  }

  shading = new GfxPatchMeshShading(typeA, patchesA, nPatchesA,
				    funcsA, nFuncsA);
  if (!shading->init(dict)) {
    delete shading;
    return NULL;
  }
  return shading;

 err2:
  obj1.free();
 err1:
  return NULL;
}

GfxShading *GfxPatchMeshShading::copy() {
  return new GfxPatchMeshShading(this);
}

//------------------------------------------------------------------------
// GfxImageColorMap
//------------------------------------------------------------------------

GfxImageColorMap::GfxImageColorMap(int bitsA, Object *decode,
				   GfxColorSpace *colorSpaceA) {
  GfxIndexedColorSpace *indexedCS;
  GfxSeparationColorSpace *sepCS;
  int maxPixel, indexHigh;
  Guchar *lookup2;
  Function *sepFunc;
  Object obj;
  double x[gfxColorMaxComps];
  double y[gfxColorMaxComps];
  int i, j, k;

  ok = gTrue;

  // bits per component and color space
  bits = bitsA;
  maxPixel = (1 << bits) - 1;
  colorSpace = colorSpaceA;

  // initialize
  for (k = 0; k < gfxColorMaxComps; ++k) {
    lookup[k] = NULL;
  }

  // get decode map
  if (decode->isNull()) {
    nComps = colorSpace->getNComps();
    colorSpace->getDefaultRanges(decodeLow, decodeRange, maxPixel);
  } else if (decode->isArray()) {
    nComps = decode->arrayGetLength() / 2;
    if (nComps != colorSpace->getNComps()) {
      goto err1;
    }
    for (i = 0; i < nComps; ++i) {
      decode->arrayGet(2*i, &obj);
      if (!obj.isNum()) {
	goto err2;
      }
      decodeLow[i] = obj.getNum();
      obj.free();
      decode->arrayGet(2*i+1, &obj);
      if (!obj.isNum()) {
	goto err2;
      }
      decodeRange[i] = obj.getNum() - decodeLow[i];
      obj.free();
    }
  } else {
    goto err1;
  }

  // Construct a lookup table -- this stores pre-computed decoded
  // values for each component, i.e., the result of applying the
  // decode mapping to each possible image pixel component value.
  //
  // Optimization: for Indexed and Separation color spaces (which have
  // only one component), we store color values in the lookup table
  // rather than component values.
  colorSpace2 = NULL;
  nComps2 = 0;
  if (colorSpace->getMode() == csIndexed) {
    // Note that indexHigh may not be the same as maxPixel --
    // Distiller will remove unused palette entries, resulting in
    // indexHigh < maxPixel.
    indexedCS = (GfxIndexedColorSpace *)colorSpace;
    colorSpace2 = indexedCS->getBase();
    indexHigh = indexedCS->getIndexHigh();
    nComps2 = colorSpace2->getNComps();
    lookup2 = indexedCS->getLookup();
    colorSpace2->getDefaultRanges(x, y, indexHigh);
    for (k = 0; k < nComps2; ++k) {
      lookup[k] = (GfxColorComp *)gmallocn(maxPixel + 1,
					   sizeof(GfxColorComp));
      for (i = 0; i <= maxPixel; ++i) {
	j = (int)(decodeLow[0] + (i * decodeRange[0]) / maxPixel + 0.5);
	if (j < 0) {
	  j = 0;
	} else if (j > indexHigh) {
	  j = indexHigh;
	}
	lookup[k][i] =
	    dblToCol(x[k] + (lookup2[j*nComps2 + k] / 255.0) * y[k]);
      }
    }
  } else if (colorSpace->getMode() == csSeparation) {
    sepCS = (GfxSeparationColorSpace *)colorSpace;
    colorSpace2 = sepCS->getAlt();
    nComps2 = colorSpace2->getNComps();
    sepFunc = sepCS->getFunc();
    for (k = 0; k < nComps2; ++k) {
      lookup[k] = (GfxColorComp *)gmallocn(maxPixel + 1,
					   sizeof(GfxColorComp));
      for (i = 0; i <= maxPixel; ++i) {
	x[0] = decodeLow[0] + (i * decodeRange[0]) / maxPixel;
	sepFunc->transform(x, y);
	lookup[k][i] = dblToCol(y[k]);
      }
    }
  } else {
    for (k = 0; k < nComps; ++k) {
      lookup[k] = (GfxColorComp *)gmallocn(maxPixel + 1,
					   sizeof(GfxColorComp));
      for (i = 0; i <= maxPixel; ++i) {
	lookup[k][i] = dblToCol(decodeLow[k] +
				(i * decodeRange[k]) / maxPixel);
      }
    }
  }

  return;

 err2:
  obj.free();
 err1:
  ok = gFalse;
}

GfxImageColorMap::GfxImageColorMap(GfxImageColorMap *colorMap) {
  int n, i, k;

  colorSpace = colorMap->colorSpace->copy();
  bits = colorMap->bits;
  nComps = colorMap->nComps;
  nComps2 = colorMap->nComps2;
  colorSpace2 = NULL;
  for (k = 0; k < gfxColorMaxComps; ++k) {
    lookup[k] = NULL;
  }
  n = 1 << bits;
  if (colorSpace->getMode() == csIndexed) {
    colorSpace2 = ((GfxIndexedColorSpace *)colorSpace)->getBase();
    for (k = 0; k < nComps2; ++k) {
      lookup[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp));
      memcpy(lookup[k], colorMap->lookup[k], n * sizeof(GfxColorComp));
    }
  } else if (colorSpace->getMode() == csSeparation) {
    colorSpace2 = ((GfxSeparationColorSpace *)colorSpace)->getAlt();
    for (k = 0; k < nComps2; ++k) {
      lookup[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp));
      memcpy(lookup[k], colorMap->lookup[k], n * sizeof(GfxColorComp));
    }
  } else {
    for (k = 0; k < nComps; ++k) {
      lookup[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp));
      memcpy(lookup[k], colorMap->lookup[k], n * sizeof(GfxColorComp));
    }
  }
  for (i = 0; i < nComps; ++i) {
    decodeLow[i] = colorMap->decodeLow[i];
    decodeRange[i] = colorMap->decodeRange[i];
  }
  ok = gTrue;
}

GfxImageColorMap::~GfxImageColorMap() {
  int i;

  delete colorSpace;
  for (i = 0; i < gfxColorMaxComps; ++i) {
    gfree(lookup[i]);
  }
}

void GfxImageColorMap::getGray(Guchar *x, GfxGray *gray) {
  GfxColor color;
  int i;

  if (colorSpace2) {
    for (i = 0; i < nComps2; ++i) {
      color.c[i] = lookup[i][x[0]];
    }
    colorSpace2->getGray(&color, gray);
  } else {
    for (i = 0; i < nComps; ++i) {
      color.c[i] = lookup[i][x[i]];
    }
    colorSpace->getGray(&color, gray);
  }
}

void GfxImageColorMap::getRGB(Guchar *x, GfxRGB *rgb) {
  GfxColor color;
  int i;

  if (colorSpace2) {
    for (i = 0; i < nComps2; ++i) {
      color.c[i] = lookup[i][x[0]];
    }
    colorSpace2->getRGB(&color, rgb);
  } else {
    for (i = 0; i < nComps; ++i) {
      color.c[i] = lookup[i][x[i]];
    }
    colorSpace->getRGB(&color, rgb);
  }
}

void GfxImageColorMap::getCMYK(Guchar *x, GfxCMYK *cmyk) {
  GfxColor color;
  int i;

  if (colorSpace2) {
    for (i = 0; i < nComps2; ++i) {
      color.c[i] = lookup[i][x[0]];
    }
    colorSpace2->getCMYK(&color, cmyk);
  } else {
    for (i = 0; i < nComps; ++i) {
      color.c[i] = lookup[i][x[i]];
    }
    colorSpace->getCMYK(&color, cmyk);
  }
}

void GfxImageColorMap::getColor(Guchar *x, GfxColor *color) {
  int maxPixel, i;

  maxPixel = (1 << bits) - 1;
  for (i = 0; i < nComps; ++i) {
    color->c[i] = dblToCol(decodeLow[i] + (x[i] * decodeRange[i]) / maxPixel);
  }
}

//------------------------------------------------------------------------
// GfxSubpath and GfxPath
//------------------------------------------------------------------------

GfxSubpath::GfxSubpath(double x1, double y1) {
  size = 16;
  x = (double *)gmallocn(size, sizeof(double));
  y = (double *)gmallocn(size, sizeof(double));
  curve = (GBool *)gmallocn(size, sizeof(GBool));
  n = 1;
  x[0] = x1;
  y[0] = y1;
  curve[0] = gFalse;
  closed = gFalse;
}

GfxSubpath::~GfxSubpath() {
  gfree(x);
  gfree(y);
  gfree(curve);
}

// Used for copy().
GfxSubpath::GfxSubpath(GfxSubpath *subpath) {
  size = subpath->size;
  n = subpath->n;
  x = (double *)gmallocn(size, sizeof(double));
  y = (double *)gmallocn(size, sizeof(double));
  curve = (GBool *)gmallocn(size, sizeof(GBool));
  memcpy(x, subpath->x, n * sizeof(double));
  memcpy(y, subpath->y, n * sizeof(double));
  memcpy(curve, subpath->curve, n * sizeof(GBool));
  closed = subpath->closed;
}

void GfxSubpath::lineTo(double x1, double y1) {
  if (n >= size) {
    size += 16;
    x = (double *)greallocn(x, size, sizeof(double));
    y = (double *)greallocn(y, size, sizeof(double));
    curve = (GBool *)greallocn(curve, size, sizeof(GBool));
  }
  x[n] = x1;
  y[n] = y1;
  curve[n] = gFalse;
  ++n;
}

void GfxSubpath::curveTo(double x1, double y1, double x2, double y2,
			 double x3, double y3) {
  if (n+3 > size) {
    size += 16;
    x = (double *)greallocn(x, size, sizeof(double));
    y = (double *)greallocn(y, size, sizeof(double));
    curve = (GBool *)greallocn(curve, size, sizeof(GBool));
  }
  x[n] = x1;
  y[n] = y1;
  x[n+1] = x2;
  y[n+1] = y2;
  x[n+2] = x3;
  y[n+2] = y3;
  curve[n] = curve[n+1] = gTrue;
  curve[n+2] = gFalse;
  n += 3;
}

void GfxSubpath::close() {
  if (x[n-1] != x[0] || y[n-1] != y[0]) {
    lineTo(x[0], y[0]);
  }
  closed = gTrue;
}

void GfxSubpath::offset(double dx, double dy) {
  int i;

  for (i = 0; i < n; ++i) {
    x[i] += dx;
    y[i] += dy;
  }
}

GfxPath::GfxPath() {
  justMoved = gFalse;
  size = 16;
  n = 0;
  firstX = firstY = 0;
  subpaths = (GfxSubpath **)gmallocn(size, sizeof(GfxSubpath *));
}

GfxPath::~GfxPath() {
  int i;

  for (i = 0; i < n; ++i)
    delete subpaths[i];
  gfree(subpaths);
}

// Used for copy().
GfxPath::GfxPath(GBool justMoved1, double firstX1, double firstY1,
		 GfxSubpath **subpaths1, int n1, int size1) {
  int i;

  justMoved = justMoved1;
  firstX = firstX1;
  firstY = firstY1;
  size = size1;
  n = n1;
  subpaths = (GfxSubpath **)gmallocn(size, sizeof(GfxSubpath *));
  for (i = 0; i < n; ++i)
    subpaths[i] = subpaths1[i]->copy();
}

void GfxPath::moveTo(double x, double y) {
  justMoved = gTrue;
  firstX = x;
  firstY = y;
}

void GfxPath::lineTo(double x, double y) {
  if (justMoved) {
    if (n >= size) {
      size += 16;
      subpaths = (GfxSubpath **)
	           greallocn(subpaths, size, sizeof(GfxSubpath *));
    }
    subpaths[n] = new GfxSubpath(firstX, firstY);
    ++n;
    justMoved = gFalse;
  }
  subpaths[n-1]->lineTo(x, y);
}

void GfxPath::curveTo(double x1, double y1, double x2, double y2,
	     double x3, double y3) {
  if (justMoved) {
    if (n >= size) {
      size += 16;
      subpaths = (GfxSubpath **)
	           greallocn(subpaths, size, sizeof(GfxSubpath *));
    }
    subpaths[n] = new GfxSubpath(firstX, firstY);
    ++n;
    justMoved = gFalse;
  }
  subpaths[n-1]->curveTo(x1, y1, x2, y2, x3, y3);
}

void GfxPath::close() {
  // this is necessary to handle the pathological case of
  // moveto/closepath/clip, which defines an empty clipping region
  if (justMoved) {
    if (n >= size) {
      size += 16;
      subpaths = (GfxSubpath **)
	           greallocn(subpaths, size, sizeof(GfxSubpath *));
    }
    subpaths[n] = new GfxSubpath(firstX, firstY);
    ++n;
    justMoved = gFalse;
  }
  subpaths[n-1]->close();
}

void GfxPath::append(GfxPath *path) {
  int i;

  if (n + path->n > size) {
    size = n + path->n;
    subpaths = (GfxSubpath **)
                 greallocn(subpaths, size, sizeof(GfxSubpath *));
  }
  for (i = 0; i < path->n; ++i) {
    subpaths[n++] = path->subpaths[i]->copy();
  }
  justMoved = gFalse;
}

void GfxPath::offset(double dx, double dy) {
  int i;

  for (i = 0; i < n; ++i) {
    subpaths[i]->offset(dx, dy);
  }
}

//------------------------------------------------------------------------
// GfxState
//------------------------------------------------------------------------

GfxState::GfxState(double hDPIA, double vDPIA, PDFRectangle *pageBox,
		   int rotateA, GBool upsideDown) {
  double kx, ky;

  hDPI = hDPIA;
  vDPI = vDPIA;
  rotate = rotateA;
  px1 = pageBox->x1;
  py1 = pageBox->y1;
  px2 = pageBox->x2;
  py2 = pageBox->y2;
  kx = hDPI / 72.0;
  ky = vDPI / 72.0;
  if (rotate == 90) {
    ctm[0] = 0;
    ctm[1] = upsideDown ? ky : -ky;
    ctm[2] = kx;
    ctm[3] = 0;
    ctm[4] = -kx * py1;
    ctm[5] = ky * (upsideDown ? -px1 : px2);
    pageWidth = kx * (py2 - py1);
    pageHeight = ky * (px2 - px1);
  } else if (rotate == 180) {
    ctm[0] = -kx;
    ctm[1] = 0;
    ctm[2] = 0;
    ctm[3] = upsideDown ? ky : -ky;
    ctm[4] = kx * px2;
    ctm[5] = ky * (upsideDown ? -py1 : py2);
    pageWidth = kx * (px2 - px1);
    pageHeight = ky * (py2 - py1);
  } else if (rotate == 270) {
    ctm[0] = 0;
    ctm[1] = upsideDown ? -ky : ky;
    ctm[2] = -kx;
    ctm[3] = 0;
    ctm[4] = kx * py2;
    ctm[5] = ky * (upsideDown ? px2 : -px1);
    pageWidth = kx * (py2 - py1);
    pageHeight = ky * (px2 - px1);
  } else {
    ctm[0] = kx;
    ctm[1] = 0;
    ctm[2] = 0;
    ctm[3] = upsideDown ? -ky : ky;
    ctm[4] = -kx * px1;
    ctm[5] = ky * (upsideDown ? py2 : -py1);
    pageWidth = kx * (px2 - px1);
    pageHeight = ky * (py2 - py1);
  }

  fillColorSpace = new GfxDeviceGrayColorSpace();
  strokeColorSpace = new GfxDeviceGrayColorSpace();
  fillColor.c[0] = 0;
  strokeColor.c[0] = 0;
  fillPattern = NULL;
  strokePattern = NULL;
  blendMode = gfxBlendNormal;
  fillOpacity = 1;
  strokeOpacity = 1;
  fillOverprint = gFalse;
  strokeOverprint = gFalse;
  transfer[0] = transfer[1] = transfer[2] = transfer[3] = NULL;

  lineWidth = 1;
  lineDash = NULL;
  lineDashLength = 0;
  lineDashStart = 0;
  flatness = 1;
  lineJoin = 0;
  lineCap = 0;
  miterLimit = 10;
  strokeAdjust = gFalse;

  font = NULL;
  fontSize = 0;
  textMat[0] = 1; textMat[1] = 0;
  textMat[2] = 0; textMat[3] = 1;
  textMat[4] = 0; textMat[5] = 0;
  charSpace = 0;
  wordSpace = 0;
  horizScaling = 1;
  leading = 0;
  rise = 0;
  render = 0;

  path = new GfxPath();
  curX = curY = 0;
  lineX = lineY = 0;

  clipXMin = 0;
  clipYMin = 0;
  clipXMax = pageWidth;
  clipYMax = pageHeight;

  saved = NULL;
}

GfxState::~GfxState() {
  int i;

  if (fillColorSpace) {
    delete fillColorSpace;
  }
  if (strokeColorSpace) {
    delete strokeColorSpace;
  }
  if (fillPattern) {
    delete fillPattern;
  }
  if (strokePattern) {
    delete strokePattern;
  }
  for (i = 0; i < 4; ++i) {
    if (transfer[i]) {
      delete transfer[i];
    }
  }
  gfree(lineDash);
  if (path) {
    // this gets set to NULL by restore()
    delete path;
  }
  if (saved) {
    delete saved;
  }
}

// Used for copy();
GfxState::GfxState(GfxState *state) {
  int i;

  memcpy(this, state, sizeof(GfxState));
  if (fillColorSpace) {
    fillColorSpace = state->fillColorSpace->copy();
  }
  if (strokeColorSpace) {
    strokeColorSpace = state->strokeColorSpace->copy();
  }
  if (fillPattern) {
    fillPattern = state->fillPattern->copy();
  }
  if (strokePattern) {
    strokePattern = state->strokePattern->copy();
  }
  for (i = 0; i < 4; ++i) {
    if (transfer[i]) {
      transfer[i] = state->transfer[i]->copy();
    }
  }
  if (lineDashLength > 0) {
    lineDash = (double *)gmallocn(lineDashLength, sizeof(double));
    memcpy(lineDash, state->lineDash, lineDashLength * sizeof(double));
  }
  saved = NULL;
}

void GfxState::setPath(GfxPath *pathA) {
  delete path;
  path = pathA;
}

void GfxState::getUserClipBBox(double *xMin, double *yMin,
			       double *xMax, double *yMax) {
  double ictm[6];
  double xMin1, yMin1, xMax1, yMax1, det, tx, ty;

  // invert the CTM
  det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
  ictm[0] = ctm[3] * det;
  ictm[1] = -ctm[1] * det;
  ictm[2] = -ctm[2] * det;
  ictm[3] = ctm[0] * det;
  ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
  ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;

  // transform all four corners of the clip bbox; find the min and max
  // x and y values
  xMin1 = xMax1 = clipXMin * ictm[0] + clipYMin * ictm[2] + ictm[4];
  yMin1 = yMax1 = clipXMin * ictm[1] + clipYMin * ictm[3] + ictm[5];
  tx = clipXMin * ictm[0] + clipYMax * ictm[2] + ictm[4];
  ty = clipXMin * ictm[1] + clipYMax * ictm[3] + ictm[5];
  if (tx < xMin1) {
    xMin1 = tx;
  } else if (tx > xMax1) {
    xMax1 = tx;
  }
  if (ty < yMin1) {
    yMin1 = ty;
  } else if (ty > yMax1) {
    yMax1 = ty;
  }
  tx = clipXMax * ictm[0] + clipYMin * ictm[2] + ictm[4];
  ty = clipXMax * ictm[1] + clipYMin * ictm[3] + ictm[5];
  if (tx < xMin1) {
    xMin1 = tx;
  } else if (tx > xMax1) {
    xMax1 = tx;
  }
  if (ty < yMin1) {
    yMin1 = ty;
  } else if (ty > yMax1) {
    yMax1 = ty;
  }
  tx = clipXMax * ictm[0] + clipYMax * ictm[2] + ictm[4];
  ty = clipXMax * ictm[1] + clipYMax * ictm[3] + ictm[5];
  if (tx < xMin1) {
    xMin1 = tx;
  } else if (tx > xMax1) {
    xMax1 = tx;
  }
  if (ty < yMin1) {
    yMin1 = ty;
  } else if (ty > yMax1) {
    yMax1 = ty;
  }

  *xMin = xMin1;
  *yMin = yMin1;
  *xMax = xMax1;
  *yMax = yMax1;
}

double GfxState::transformWidth(double w) {
  double x, y;

  x = ctm[0] + ctm[2];
  y = ctm[1] + ctm[3];
  return w * sqrt(0.5 * (x * x + y * y));
}

double GfxState::getTransformedFontSize() {
  double x1, y1, x2, y2;

  x1 = textMat[2] * fontSize;
  y1 = textMat[3] * fontSize;
  x2 = ctm[0] * x1 + ctm[2] * y1;
  y2 = ctm[1] * x1 + ctm[3] * y1;
  return sqrt(x2 * x2 + y2 * y2);
}

void GfxState::getFontTransMat(double *m11, double *m12,
			       double *m21, double *m22) {
  *m11 = (textMat[0] * ctm[0] + textMat[1] * ctm[2]) * fontSize;
  *m12 = (textMat[0] * ctm[1] + textMat[1] * ctm[3]) * fontSize;
  *m21 = (textMat[2] * ctm[0] + textMat[3] * ctm[2]) * fontSize;
  *m22 = (textMat[2] * ctm[1] + textMat[3] * ctm[3]) * fontSize;
}

void GfxState::setCTM(double a, double b, double c,
		      double d, double e, double f) {
  int i;

  ctm[0] = a;
  ctm[1] = b;
  ctm[2] = c;
  ctm[3] = d;
  ctm[4] = e;
  ctm[5] = f;

  // avoid FP exceptions on badly messed up PDF files
  for (i = 0; i < 6; ++i) {
    if (ctm[i] > 1e10) {
      ctm[i] = 1e10;
    } else if (ctm[i] < -1e10) {
      ctm[i] = -1e10;
    }
  }
}

void GfxState::concatCTM(double a, double b, double c,
			 double d, double e, double f) {
  double a1 = ctm[0];
  double b1 = ctm[1];
  double c1 = ctm[2];
  double d1 = ctm[3];
  int i;

  ctm[0] = a * a1 + b * c1;
  ctm[1] = a * b1 + b * d1;
  ctm[2] = c * a1 + d * c1;
  ctm[3] = c * b1 + d * d1;
  ctm[4] = e * a1 + f * c1 + ctm[4];
  ctm[5] = e * b1 + f * d1 + ctm[5];

  // avoid FP exceptions on badly messed up PDF files
  for (i = 0; i < 6; ++i) {
    if (ctm[i] > 1e10) {
      ctm[i] = 1e10;
    } else if (ctm[i] < -1e10) {
      ctm[i] = -1e10;
    }
  }
}

void GfxState::shiftCTM(double tx, double ty) {
  ctm[4] += tx;
  ctm[5] += ty;
  clipXMin += tx;
  clipYMin += ty;
  clipXMax += tx;
  clipYMax += ty;
}

void GfxState::setFillColorSpace(GfxColorSpace *colorSpace) {
  if (fillColorSpace) {
    delete fillColorSpace;
  }
  fillColorSpace = colorSpace;
}

void GfxState::setStrokeColorSpace(GfxColorSpace *colorSpace) {
  if (strokeColorSpace) {
    delete strokeColorSpace;
  }
  strokeColorSpace = colorSpace;
}

void GfxState::setFillPattern(GfxPattern *pattern) {
  if (fillPattern) {
    delete fillPattern;
  }
  fillPattern = pattern;
}

void GfxState::setStrokePattern(GfxPattern *pattern) {
  if (strokePattern) {
    delete strokePattern;
  }
  strokePattern = pattern;
}

void GfxState::setTransfer(Function **funcs) {
  int i;

  for (i = 0; i < 4; ++i) {
    if (transfer[i]) {
      delete transfer[i];
    }
    transfer[i] = funcs[i];
  }
}

void GfxState::setLineDash(double *dash, int length, double start) {
  if (lineDash)
    gfree(lineDash);
  lineDash = dash;
  lineDashLength = length;
  lineDashStart = start;
}

void GfxState::clearPath() {
  delete path;
  path = new GfxPath();
}

void GfxState::clip() {
  double xMin, yMin, xMax, yMax, x, y;
  GfxSubpath *subpath;
  int i, j;

  xMin = xMax = yMin = yMax = 0; // make gcc happy
  for (i = 0; i < path->getNumSubpaths(); ++i) {
    subpath = path->getSubpath(i);
    for (j = 0; j < subpath->getNumPoints(); ++j) {
      transform(subpath->getX(j), subpath->getY(j), &x, &y);
      if (i == 0 && j == 0) {
	xMin = xMax = x;
	yMin = yMax = y;
      } else {
	if (x < xMin) {
	  xMin = x;
	} else if (x > xMax) {
	  xMax = x;
	}
	if (y < yMin) {
	  yMin = y;
	} else if (y > yMax) {
	  yMax = y;
	}
      }
    }
  }
  if (xMin > clipXMin) {
    clipXMin = xMin;
  }
  if (yMin > clipYMin) {
    clipYMin = yMin;
  }
  if (xMax < clipXMax) {
    clipXMax = xMax;
  }
  if (yMax < clipYMax) {
    clipYMax = yMax;
  }
}

void GfxState::clipToStrokePath() {
  double xMin, yMin, xMax, yMax, x, y, t0, t1;
  GfxSubpath *subpath;
  int i, j;

  xMin = xMax = yMin = yMax = 0; // make gcc happy
  for (i = 0; i < path->getNumSubpaths(); ++i) {
    subpath = path->getSubpath(i);
    for (j = 0; j < subpath->getNumPoints(); ++j) {
      transform(subpath->getX(j), subpath->getY(j), &x, &y);
      if (i == 0 && j == 0) {
	xMin = xMax = x;
	yMin = yMax = y;
      } else {
	if (x < xMin) {
	  xMin = x;
	} else if (x > xMax) {
	  xMax = x;
	}
	if (y < yMin) {
	  yMin = y;
	} else if (y > yMax) {
	  yMax = y;
	}
      }
    }
  }

  // allow for the line width
  //~ miter joins can extend farther than this
  t0 = fabs(ctm[0]);
  t1 = fabs(ctm[2]);
  if (t0 > t1) {
    xMin -= 0.5 * lineWidth * t0;
    xMax += 0.5 * lineWidth * t0;
  } else {
    xMin -= 0.5 * lineWidth * t1;
    xMax += 0.5 * lineWidth * t1;
  }
  t0 = fabs(ctm[0]);
  t1 = fabs(ctm[3]);
  if (t0 > t1) {
    yMin -= 0.5 * lineWidth * t0;
    yMax += 0.5 * lineWidth * t0;
  } else {
    yMin -= 0.5 * lineWidth * t1;
    yMax += 0.5 * lineWidth * t1;
  }

  if (xMin > clipXMin) {
    clipXMin = xMin;
  }
  if (yMin > clipYMin) {
    clipYMin = yMin;
  }
  if (xMax < clipXMax) {
    clipXMax = xMax;
  }
  if (yMax < clipYMax) {
    clipYMax = yMax;
  }
}

void GfxState::textShift(double tx, double ty) {
  double dx, dy;

  textTransformDelta(tx, ty, &dx, &dy);
  curX += dx;
  curY += dy;
}

void GfxState::shift(double dx, double dy) {
  curX += dx;
  curY += dy;
}

GfxState *GfxState::save() {
  GfxState *newState;

  newState = copy();
  newState->saved = this;
  return newState;
}

GfxState *GfxState::restore() {
  GfxState *oldState;

  if (saved) {
    oldState = saved;

    // these attributes aren't saved/restored by the q/Q operators
    oldState->path = path;
    oldState->curX = curX;
    oldState->curY = curY;
    oldState->lineX = lineX;
    oldState->lineY = lineY;

    path = NULL;
    saved = NULL;
    delete this;

  } else {
    oldState = this;
  }

  return oldState;
}

GBool GfxState::parseBlendMode(Object *obj, GfxBlendMode *mode) {
  Object obj2;
  int i, j;

  if (obj->isName()) {
    for (i = 0; i < nGfxBlendModeNames; ++i) {
      if (!strcmp(obj->getName(), gfxBlendModeNames[i].name)) {
	*mode = gfxBlendModeNames[i].mode;
	return gTrue;
      }
    }
    return gFalse;
  } else if (obj->isArray()) {
    for (i = 0; i < obj->arrayGetLength(); ++i) {
      obj->arrayGet(i, &obj2);
      if (!obj2.isName()) {
	obj2.free();
	return gFalse;
      }
      for (j = 0; j < nGfxBlendModeNames; ++j) {
	if (!strcmp(obj2.getName(), gfxBlendModeNames[j].name)) {
	  obj2.free();
	  *mode = gfxBlendModeNames[j].mode;
	  return gTrue;
	}
      }
      obj2.free();
    }
    *mode = gfxBlendNormal;
    return gTrue;
  } else {
    return gFalse;
  }
}
//========================================================================
//
// GfxState.h
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef GFXSTATE_H
#define GFXSTATE_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "gtypes.h"
#include "Object.h"
#include "Function.h"

class Array;
class GfxFont;
class PDFRectangle;
class GfxShading;

//------------------------------------------------------------------------
// GfxBlendMode
//------------------------------------------------------------------------

enum GfxBlendMode {
  gfxBlendNormal,
  gfxBlendMultiply,
  gfxBlendScreen,
  gfxBlendOverlay,
  gfxBlendDarken,
  gfxBlendLighten,
  gfxBlendColorDodge,
  gfxBlendColorBurn,
  gfxBlendHardLight,
  gfxBlendSoftLight,
  gfxBlendDifference,
  gfxBlendExclusion,
  gfxBlendHue,
  gfxBlendSaturation,
  gfxBlendColor,
  gfxBlendLuminosity
};

//------------------------------------------------------------------------
// GfxColorComp
//------------------------------------------------------------------------

// 16.16 fixed point color component
typedef int GfxColorComp;

#define gfxColorComp1 0x10000

static inline GfxColorComp dblToCol(double x) {
  return (GfxColorComp)(x * gfxColorComp1);
}

static inline double colToDbl(GfxColorComp x) {
  return (double)x / (double)gfxColorComp1;
}

static inline GfxColorComp byteToCol(Guchar x) {
  // (x / 255) << 16  =  (0.0000000100000001... * x) << 16
  //                  =  ((x << 8) + (x) + (x >> 8) + ...) << 16
  //                  =  (x << 8) + (x) + (x >> 7)
  //                                      [for rounding]
  return (GfxColorComp)((x << 8) + x + (x >> 7));
}

static inline Guchar colToByte(GfxColorComp x) {
  // 255 * x + 0.5  =  256 * x - x + 0x8000
  return (Guchar)(((x << 8) - x + 0x8000) >> 16);
}

//------------------------------------------------------------------------
// GfxColor
//------------------------------------------------------------------------

#define gfxColorMaxComps funcMaxOutputs

struct GfxColor {
  GfxColorComp c[gfxColorMaxComps];
};

//------------------------------------------------------------------------
// GfxGray
//------------------------------------------------------------------------

typedef GfxColorComp GfxGray;

//------------------------------------------------------------------------
// GfxRGB
//------------------------------------------------------------------------

struct GfxRGB {
  GfxColorComp r, g, b;
};

//------------------------------------------------------------------------
// GfxCMYK
//------------------------------------------------------------------------

struct GfxCMYK {
  GfxColorComp c, m, y, k;
};

//------------------------------------------------------------------------
// GfxColorSpace
//------------------------------------------------------------------------

// NB: The nGfxColorSpaceModes constant and the gfxColorSpaceModeNames
// array defined in GfxState.cc must match this enum.
enum GfxColorSpaceMode {
  csDeviceGray,
  csCalGray,
  csDeviceRGB,
  csCalRGB,
  csDeviceCMYK,
  csLab,
  csICCBased,
  csIndexed,
  csSeparation,
  csDeviceN,
  csPattern
};

class GfxColorSpace {
public:

  GfxColorSpace();
  virtual ~GfxColorSpace();
  virtual GfxColorSpace *copy() = 0;
  virtual GfxColorSpaceMode getMode() = 0;

  // Construct a color space.  Returns NULL if unsuccessful.
  static GfxColorSpace *parse(Object *csObj);

  // Convert to gray, RGB, or CMYK.
  virtual void getGray(GfxColor *color, GfxGray *gray) = 0;
  virtual void getRGB(GfxColor *color, GfxRGB *rgb) = 0;
  virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk) = 0;

  // Return the number of color components.
  virtual int getNComps() = 0;

  // Get this color space's default color.
  virtual void getDefaultColor(GfxColor *color) = 0;

  // Return the default ranges for each component, assuming an image
  // with a max pixel value of <maxImgPixel>.
  virtual void getDefaultRanges(double *decodeLow, double *decodeRange,
				int maxImgPixel);

  // Returns true if painting operations in this color space never
  // mark the page (e.g., the "None" colorant).
  virtual GBool isNonMarking() { return gFalse; }

  // Return the number of color space modes
  static int getNumColorSpaceModes();

  // Return the name of the <idx>th color space mode.
  static char *getColorSpaceModeName(int idx);

private:
};

//------------------------------------------------------------------------
// GfxDeviceGrayColorSpace
//------------------------------------------------------------------------

class GfxDeviceGrayColorSpace: public GfxColorSpace {
public:

  GfxDeviceGrayColorSpace();
  virtual ~GfxDeviceGrayColorSpace();
  virtual GfxColorSpace *copy();
  virtual GfxColorSpaceMode getMode() { return csDeviceGray; }

  virtual void getGray(GfxColor *color, GfxGray *gray);
  virtual void getRGB(GfxColor *color, GfxRGB *rgb);
  virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);

  virtual int getNComps() { return 1; }
  virtual void getDefaultColor(GfxColor *color);

private:
};

//------------------------------------------------------------------------
// GfxCalGrayColorSpace
//------------------------------------------------------------------------

class GfxCalGrayColorSpace: public GfxColorSpace {
public:

  GfxCalGrayColorSpace();
  virtual ~GfxCalGrayColorSpace();
  virtual GfxColorSpace *copy();
  virtual GfxColorSpaceMode getMode() { return csCalGray; }

  // Construct a CalGray color space.  Returns NULL if unsuccessful.
  static GfxColorSpace *parse(Array *arr);

  virtual void getGray(GfxColor *color, GfxGray *gray);
  virtual void getRGB(GfxColor *color, GfxRGB *rgb);
  virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);

  virtual int getNComps() { return 1; }
  virtual void getDefaultColor(GfxColor *color);

  // CalGray-specific access.
  double getWhiteX() { return whiteX; }
  double getWhiteY() { return whiteY; }
  double getWhiteZ() { return whiteZ; }
  double getBlackX() { return blackX; }
  double getBlackY() { return blackY; }
  double getBlackZ() { return blackZ; }
  double getGamma() { return gamma; }

private:

  double whiteX, whiteY, whiteZ;    // white point
  double blackX, blackY, blackZ;    // black point
  double gamma;			    // gamma value
};

//------------------------------------------------------------------------
// GfxDeviceRGBColorSpace
//------------------------------------------------------------------------

class GfxDeviceRGBColorSpace: public GfxColorSpace {
public:

  GfxDeviceRGBColorSpace();
  virtual ~GfxDeviceRGBColorSpace();
  virtual GfxColorSpace *copy();
  virtual GfxColorSpaceMode getMode() { return csDeviceRGB; }

  virtual void getGray(GfxColor *color, GfxGray *gray);
  virtual void getRGB(GfxColor *color, GfxRGB *rgb);
  virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);

  virtual int getNComps() { return 3; }
  virtual void getDefaultColor(GfxColor *color);

private:
};

//------------------------------------------------------------------------
// GfxCalRGBColorSpace
//------------------------------------------------------------------------

class GfxCalRGBColorSpace: public GfxColorSpace {
public:

  GfxCalRGBColorSpace();
  virtual ~GfxCalRGBColorSpace();
  virtual GfxColorSpace *copy();
  virtual GfxColorSpaceMode getMode() { return csCalRGB; }

  // Construct a CalRGB color space.  Returns NULL if unsuccessful.
  static GfxColorSpace *parse(Array *arr);

  virtual void getGray(GfxColor *color, GfxGray *gray);
  virtual void getRGB(GfxColor *color, GfxRGB *rgb);
  virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);

  virtual int getNComps() { return 3; }
  virtual void getDefaultColor(GfxColor *color);

  // CalRGB-specific access.
  double getWhiteX() { return whiteX; }
  double getWhiteY() { return whiteY; }
  double getWhiteZ() { return whiteZ; }
  double getBlackX() { return blackX; }
  double getBlackY() { return blackY; }
  double getBlackZ() { return blackZ; }
  double getGammaR() { return gammaR; }
  double getGammaG() { return gammaG; }
  double getGammaB() { return gammaB; }
  double *getMatrix() { return mat; }

private:

  double whiteX, whiteY, whiteZ;    // white point
  double blackX, blackY, blackZ;    // black point
  double gammaR, gammaG, gammaB;    // gamma values
  double mat[9];		    // ABC -> XYZ transform matrix
};

//------------------------------------------------------------------------
// GfxDeviceCMYKColorSpace
//------------------------------------------------------------------------

class GfxDeviceCMYKColorSpace: public GfxColorSpace {
public:

  GfxDeviceCMYKColorSpace();
  virtual ~GfxDeviceCMYKColorSpace();
  virtual GfxColorSpace *copy();
  virtual GfxColorSpaceMode getMode() { return csDeviceCMYK; }

  virtual void getGray(GfxColor *color, GfxGray *gray);
  virtual void getRGB(GfxColor *color, GfxRGB *rgb);
  virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);

  virtual int getNComps() { return 4; }
  virtual void getDefaultColor(GfxColor *color);

private:
};

//------------------------------------------------------------------------
// GfxLabColorSpace
//------------------------------------------------------------------------

class GfxLabColorSpace: public GfxColorSpace {
public:

  GfxLabColorSpace();
  virtual ~GfxLabColorSpace();
  virtual GfxColorSpace *copy();
  virtual GfxColorSpaceMode getMode() { return csLab; }

  // Construct a Lab color space.  Returns NULL if unsuccessful.
  static GfxColorSpace *parse(Array *arr);

  virtual void getGray(GfxColor *color, GfxGray *gray);
  virtual void getRGB(GfxColor *color, GfxRGB *rgb);
  virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);

  virtual int getNComps() { return 3; }
  virtual void getDefaultColor(GfxColor *color);

  virtual void getDefaultRanges(double *decodeLow, double *decodeRange,
				int maxImgPixel);

  // Lab-specific access.
  double getWhiteX() { return whiteX; }
  double getWhiteY() { return whiteY; }
  double getWhiteZ() { return whiteZ; }
  double getBlackX() { return blackX; }
  double getBlackY() { return blackY; }
  double getBlackZ() { return blackZ; }
  double getAMin() { return aMin; }
  double getAMax() { return aMax; }
  double getBMin() { return bMin; }
  double getBMax() { return bMax; }

private:

  double whiteX, whiteY, whiteZ;    // white point
  double blackX, blackY, blackZ;    // black point
  double aMin, aMax, bMin, bMax;    // range for the a and b components
  double kr, kg, kb;		    // gamut mapping mulitpliers
};

//------------------------------------------------------------------------
// GfxICCBasedColorSpace
//------------------------------------------------------------------------

class GfxICCBasedColorSpace: public GfxColorSpace {
public:

  GfxICCBasedColorSpace(int nCompsA, GfxColorSpace *altA,
			Ref *iccProfileStreamA);
  virtual ~GfxICCBasedColorSpace();
  virtual GfxColorSpace *copy();
  virtual GfxColorSpaceMode getMode() { return csICCBased; }

  // Construct an ICCBased color space.  Returns NULL if unsuccessful.
  static GfxColorSpace *parse(Array *arr);

  virtual void getGray(GfxColor *color, GfxGray *gray);
  virtual void getRGB(GfxColor *color, GfxRGB *rgb);
  virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);

  virtual int getNComps() { return nComps; }
  virtual void getDefaultColor(GfxColor *color);

  virtual void getDefaultRanges(double *decodeLow, double *decodeRange,
				int maxImgPixel);

  // ICCBased-specific access.
  GfxColorSpace *getAlt() { return alt; }

private:

  int nComps;			// number of color components (1, 3, or 4)
  GfxColorSpace *alt;		// alternate color space
  double rangeMin[4];		// min values for each component
  double rangeMax[4];		// max values for each component
  Ref iccProfileStream;		// the ICC profile
};

//------------------------------------------------------------------------
// GfxIndexedColorSpace
//------------------------------------------------------------------------

class GfxIndexedColorSpace: public GfxColorSpace {
public:

  GfxIndexedColorSpace(GfxColorSpace *baseA, int indexHighA);
  virtual ~GfxIndexedColorSpace();
  virtual GfxColorSpace *copy();
  virtual GfxColorSpaceMode getMode() { return csIndexed; }

  // Construct a Lab color space.  Returns NULL if unsuccessful.
  static GfxColorSpace *parse(Array *arr);

  virtual void getGray(GfxColor *color, GfxGray *gray);
  virtual void getRGB(GfxColor *color, GfxRGB *rgb);
  virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);

  virtual int getNComps() { return 1; }
  virtual void getDefaultColor(GfxColor *color);

  virtual void getDefaultRanges(double *decodeLow, double *decodeRange,
				int maxImgPixel);

  // Indexed-specific access.
  GfxColorSpace *getBase() { return base; }
  int getIndexHigh() { return indexHigh; }
  Guchar *getLookup() { return lookup; }
  GfxColor *mapColorToBase(GfxColor *color, GfxColor *baseColor);

private:

  GfxColorSpace *base;		// base color space
  int indexHigh;		// max pixel value
  Guchar *lookup;		// lookup table
};

//------------------------------------------------------------------------
// GfxSeparationColorSpace
//------------------------------------------------------------------------

class GfxSeparationColorSpace: public GfxColorSpace {
public:

  GfxSeparationColorSpace(GString *nameA, GfxColorSpace *altA,
			  Function *funcA);
  virtual ~GfxSeparationColorSpace();
  virtual GfxColorSpace *copy();
  virtual GfxColorSpaceMode getMode() { return csSeparation; }

  // Construct a Separation color space.  Returns NULL if unsuccessful.
  static GfxColorSpace *parse(Array *arr);

  virtual void getGray(GfxColor *color, GfxGray *gray);
  virtual void getRGB(GfxColor *color, GfxRGB *rgb);
  virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);

  virtual int getNComps() { return 1; }
  virtual void getDefaultColor(GfxColor *color);

  virtual GBool isNonMarking() { return nonMarking; }

  // Separation-specific access.
  GString *getName() { return name; }
  GfxColorSpace *getAlt() { return alt; }
  Function *getFunc() { return func; }

private:

  GString *name;		// colorant name
  GfxColorSpace *alt;		// alternate color space
  Function *func;		// tint transform (into alternate color space)
  GBool nonMarking;
};

//------------------------------------------------------------------------
// GfxDeviceNColorSpace
//------------------------------------------------------------------------

class GfxDeviceNColorSpace: public GfxColorSpace {
public:

  GfxDeviceNColorSpace(int nCompsA, GfxColorSpace *alt, Function *func);
  virtual ~GfxDeviceNColorSpace();
  virtual GfxColorSpace *copy();
  virtual GfxColorSpaceMode getMode() { return csDeviceN; }

  // Construct a DeviceN color space.  Returns NULL if unsuccessful.
  static GfxColorSpace *parse(Array *arr);

  virtual void getGray(GfxColor *color, GfxGray *gray);
  virtual void getRGB(GfxColor *color, GfxRGB *rgb);
  virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);

  virtual int getNComps() { return nComps; }
  virtual void getDefaultColor(GfxColor *color);

  virtual GBool isNonMarking() { return nonMarking; }

  // DeviceN-specific access.
  GString *getColorantName(int i) { return names[i]; }
  GfxColorSpace *getAlt() { return alt; }
  Function *getTintTransformFunc() { return func; }

private:

  int nComps;			// number of components
  GString			// colorant names
    *names[gfxColorMaxComps];
  GfxColorSpace *alt;		// alternate color space
  Function *func;		// tint transform (into alternate color space)
  GBool nonMarking;
};

//------------------------------------------------------------------------
// GfxPatternColorSpace
//------------------------------------------------------------------------

class GfxPatternColorSpace: public GfxColorSpace {
public:

  GfxPatternColorSpace(GfxColorSpace *underA);
  virtual ~GfxPatternColorSpace();
  virtual GfxColorSpace *copy();
  virtual GfxColorSpaceMode getMode() { return csPattern; }

  // Construct a Pattern color space.  Returns NULL if unsuccessful.
  static GfxColorSpace *parse(Array *arr);

  virtual void getGray(GfxColor *color, GfxGray *gray);
  virtual void getRGB(GfxColor *color, GfxRGB *rgb);
  virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);

  virtual int getNComps() { return 0; }
  virtual void getDefaultColor(GfxColor *color);

  // Pattern-specific access.
  GfxColorSpace *getUnder() { return under; }

private:

  GfxColorSpace *under;		// underlying color space (for uncolored
				//   patterns)
};

//------------------------------------------------------------------------
// GfxPattern
//------------------------------------------------------------------------

class GfxPattern {
public:

  GfxPattern(int typeA);
  virtual ~GfxPattern();

  static GfxPattern *parse(Object *obj);

  virtual GfxPattern *copy() = 0;

  int getType() { return type; }

private:

  int type;
};

//------------------------------------------------------------------------
// GfxTilingPattern
//------------------------------------------------------------------------

class GfxTilingPattern: public GfxPattern {
public:

  static GfxTilingPattern *parse(Object *patObj);
  virtual ~GfxTilingPattern();

  virtual GfxPattern *copy();

  int getPaintType() { return paintType; }
  int getTilingType() { return tilingType; }
  double *getBBox() { return bbox; }
  double getXStep() { return xStep; }
  double getYStep() { return yStep; }
  Dict *getResDict()
    { return resDict.isDict() ? resDict.getDict() : (Dict *)NULL; }
  double *getMatrix() { return matrix; }
  Object *getContentStream() { return &contentStream; }

private:

  GfxTilingPattern(int paintTypeA, int tilingTypeA,
		   double *bboxA, double xStepA, double yStepA,
		   Object *resDictA, double *matrixA,
		   Object *contentStreamA);

  int paintType;
  int tilingType;
  double bbox[4];
  double xStep, yStep;
  Object resDict;
  double matrix[6];
  Object contentStream;
};

//------------------------------------------------------------------------
// GfxShadingPattern
//------------------------------------------------------------------------

class GfxShadingPattern: public GfxPattern {
public:

  static GfxShadingPattern *parse(Object *patObj);
  virtual ~GfxShadingPattern();

  virtual GfxPattern *copy();

  GfxShading *getShading() { return shading; }
  double *getMatrix() { return matrix; }

private:

  GfxShadingPattern(GfxShading *shadingA, double *matrixA);

  GfxShading *shading;
  double matrix[6];
};

//------------------------------------------------------------------------
// GfxShading
//------------------------------------------------------------------------

class GfxShading {
public:

  GfxShading(int typeA);
  GfxShading(GfxShading *shading);
  virtual ~GfxShading();

  static GfxShading *parse(Object *obj);

  virtual GfxShading *copy() = 0;

  int getType() { return type; }
  GfxColorSpace *getColorSpace() { return colorSpace; }
  GfxColor *getBackground() { return &background; }
  GBool getHasBackground() { return hasBackground; }
  void getBBox(double *xMinA, double *yMinA, double *xMaxA, double *yMaxA)
    { *xMinA = xMin; *yMinA = yMin; *xMaxA = xMax; *yMaxA = yMax; }
  GBool getHasBBox() { return hasBBox; }

protected:

  GBool init(Dict *dict);

  int type;
  GfxColorSpace *colorSpace;
  GfxColor background;
  GBool hasBackground;
  double xMin, yMin, xMax, yMax;
  GBool hasBBox;
};

//------------------------------------------------------------------------
// GfxFunctionShading
//------------------------------------------------------------------------

class GfxFunctionShading: public GfxShading {
public:

  GfxFunctionShading(double x0A, double y0A,
		     double x1A, double y1A,
		     double *matrixA,
		     Function **funcsA, int nFuncsA);
  GfxFunctionShading(GfxFunctionShading *shading);
  virtual ~GfxFunctionShading();

  static GfxFunctionShading *parse(Dict *dict);

  virtual GfxShading *copy();

  void getDomain(double *x0A, double *y0A, double *x1A, double *y1A)
    { *x0A = x0; *y0A = y0; *x1A = x1; *y1A = y1; }
  double *getMatrix() { return matrix; }
  int getNFuncs() { return nFuncs; }
  Function *getFunc(int i) { return funcs[i]; }
  void getColor(double x, double y, GfxColor *color);

private:

  double x0, y0, x1, y1;
  double matrix[6];
  Function *funcs[gfxColorMaxComps];
  int nFuncs;
};

//------------------------------------------------------------------------
// GfxAxialShading
//------------------------------------------------------------------------

class GfxAxialShading: public GfxShading {
public:

  GfxAxialShading(double x0A, double y0A,
		  double x1A, double y1A,
		  double t0A, double t1A,
		  Function **funcsA, int nFuncsA,
		  GBool extend0A, GBool extend1A);
  GfxAxialShading(GfxAxialShading *shading);
  virtual ~GfxAxialShading();

  static GfxAxialShading *parse(Dict *dict);

  virtual GfxShading *copy();

  void getCoords(double *x0A, double *y0A, double *x1A, double *y1A)
    { *x0A = x0; *y0A = y0; *x1A = x1; *y1A = y1; }
  double getDomain0() { return t0; }
  double getDomain1() { return t1; }
  GBool getExtend0() { return extend0; }
  GBool getExtend1() { return extend1; }
  int getNFuncs() { return nFuncs; }
  Function *getFunc(int i) { return funcs[i]; }
  void getColor(double t, GfxColor *color);

private:

  double x0, y0, x1, y1;
  double t0, t1;
  Function *funcs[gfxColorMaxComps];
  int nFuncs;
  GBool extend0, extend1;
};

//------------------------------------------------------------------------
// GfxRadialShading
//------------------------------------------------------------------------

class GfxRadialShading: public GfxShading {
public:

  GfxRadialShading(double x0A, double y0A, double r0A,
		   double x1A, double y1A, double r1A,
		   double t0A, double t1A,
		   Function **funcsA, int nFuncsA,
		   GBool extend0A, GBool extend1A);
  GfxRadialShading(GfxRadialShading *shading);
  virtual ~GfxRadialShading();

  static GfxRadialShading *parse(Dict *dict);

  virtual GfxShading *copy();

  void getCoords(double *x0A, double *y0A, double *r0A,
		 double *x1A, double *y1A, double *r1A)
    { *x0A = x0; *y0A = y0; *r0A = r0; *x1A = x1; *y1A = y1; *r1A = r1; }
  double getDomain0() { return t0; }
  double getDomain1() { return t1; }
  GBool getExtend0() { return extend0; }
  GBool getExtend1() { return extend1; }
  int getNFuncs() { return nFuncs; }
  Function *getFunc(int i) { return funcs[i]; }
  void getColor(double t, GfxColor *color);

private:

  double x0, y0, r0, x1, y1, r1;
  double t0, t1;
  Function *funcs[gfxColorMaxComps];
  int nFuncs;
  GBool extend0, extend1;
};

//------------------------------------------------------------------------
// GfxGouraudTriangleShading
//------------------------------------------------------------------------

struct GfxGouraudVertex {
  double x, y;
  GfxColor color;
};

class GfxGouraudTriangleShading: public GfxShading {
public:

  GfxGouraudTriangleShading(int typeA,
			    GfxGouraudVertex *verticesA, int nVerticesA,
			    int (*trianglesA)[3], int nTrianglesA,
			    Function **funcsA, int nFuncsA);
  GfxGouraudTriangleShading(GfxGouraudTriangleShading *shading);
  virtual ~GfxGouraudTriangleShading();

  static GfxGouraudTriangleShading *parse(int typeA, Dict *dict, Stream *str);

  virtual GfxShading *copy();

  int getNTriangles() { return nTriangles; }
  void getTriangle(int i, double *x0, double *y0, GfxColor *color0,
		   double *x1, double *y1, GfxColor *color1,
		   double *x2, double *y2, GfxColor *color2);

private:

  GfxGouraudVertex *vertices;
  int nVertices;
  int (*triangles)[3];
  int nTriangles;
  Function *funcs[gfxColorMaxComps];
  int nFuncs;
};

//------------------------------------------------------------------------
// GfxPatchMeshShading
//------------------------------------------------------------------------

struct GfxPatch {
  double x[4][4];
  double y[4][4];
  GfxColor color[2][2];
};

class GfxPatchMeshShading: public GfxShading {
public:

  GfxPatchMeshShading(int typeA, GfxPatch *patchesA, int nPatchesA,
		      Function **funcsA, int nFuncsA);
  GfxPatchMeshShading(GfxPatchMeshShading *shading);
  virtual ~GfxPatchMeshShading();

  static GfxPatchMeshShading *parse(int typeA, Dict *dict, Stream *str);

  virtual GfxShading *copy();

  int getNPatches() { return nPatches; }
  GfxPatch *getPatch(int i) { return &patches[i]; }

private:

  GfxPatch *patches;
  int nPatches;
  Function *funcs[gfxColorMaxComps];
  int nFuncs;
};

//------------------------------------------------------------------------
// GfxImageColorMap
//------------------------------------------------------------------------

class GfxImageColorMap {
public:

  // Constructor.
  GfxImageColorMap(int bitsA, Object *decode, GfxColorSpace *colorSpaceA);

  // Destructor.
  ~GfxImageColorMap();

  // Return a copy of this color map.
  GfxImageColorMap *copy() { return new GfxImageColorMap(this); }

  // Is color map valid?
  GBool isOk() { return ok; }

  // Get the color space.
  GfxColorSpace *getColorSpace() { return colorSpace; }

  // Get stream decoding info.
  int getNumPixelComps() { return nComps; }
  int getBits() { return bits; }

  // Get decode table.
  double getDecodeLow(int i) { return decodeLow[i]; }
  double getDecodeHigh(int i) { return decodeLow[i] + decodeRange[i]; }

  // Convert an image pixel to a color.
  void getGray(Guchar *x, GfxGray *gray);
  void getRGB(Guchar *x, GfxRGB *rgb);
  void getCMYK(Guchar *x, GfxCMYK *cmyk);
  void getColor(Guchar *x, GfxColor *color);

private:

  GfxImageColorMap(GfxImageColorMap *colorMap);

  GfxColorSpace *colorSpace;	// the image color space
  int bits;			// bits per component
  int nComps;			// number of components in a pixel
  GfxColorSpace *colorSpace2;	// secondary color space
  int nComps2;			// number of components in colorSpace2
  GfxColorComp *		// lookup table
    lookup[gfxColorMaxComps];
  double			// minimum values for each component
    decodeLow[gfxColorMaxComps];
  double			// max - min value for each component
    decodeRange[gfxColorMaxComps];
  GBool ok;
};

//------------------------------------------------------------------------
// GfxSubpath and GfxPath
//------------------------------------------------------------------------

class GfxSubpath {
public:

  // Constructor.
  GfxSubpath(double x1, double y1);

  // Destructor.
  ~GfxSubpath();

  // Copy.
  GfxSubpath *copy() { return new GfxSubpath(this); }

  // Get points.
  int getNumPoints() { return n; }
  double getX(int i) { return x[i]; }
  double getY(int i) { return y[i]; }
  GBool getCurve(int i) { return curve[i]; }

  // Get last point.
  double getLastX() { return x[n-1]; }
  double getLastY() { return y[n-1]; }

  // Add a line segment.
  void lineTo(double x1, double y1);

  // Add a Bezier curve.
  void curveTo(double x1, double y1, double x2, double y2,
	       double x3, double y3);

  // Close the subpath.
  void close();
  GBool isClosed() { return closed; }

  // Add (<dx>, <dy>) to each point in the subpath.
  void offset(double dx, double dy);

private:

  double *x, *y;		// points
  GBool *curve;			// curve[i] => point i is a control point
				//   for a Bezier curve
  int n;			// number of points
  int size;			// size of x/y arrays
  GBool closed;			// set if path is closed

  GfxSubpath(GfxSubpath *subpath);
};

class GfxPath {
public:

  // Constructor.
  GfxPath();

  // Destructor.
  ~GfxPath();

  // Copy.
  GfxPath *copy()
    { return new GfxPath(justMoved, firstX, firstY, subpaths, n, size); }

  // Is there a current point?
  GBool isCurPt() { return n > 0 || justMoved; }

  // Is the path non-empty, i.e., is there at least one segment?
  GBool isPath() { return n > 0; }

  // Get subpaths.
  int getNumSubpaths() { return n; }
  GfxSubpath *getSubpath(int i) { return subpaths[i]; }

  // Get last point on last subpath.
  double getLastX() { return subpaths[n-1]->getLastX(); }
  double getLastY() { return subpaths[n-1]->getLastY(); }

  // Move the current point.
  void moveTo(double x, double y);

  // Add a segment to the last subpath.
  void lineTo(double x, double y);

  // Add a Bezier curve to the last subpath
  void curveTo(double x1, double y1, double x2, double y2,
	       double x3, double y3);

  // Close the last subpath.
  void close();

  // Append <path> to <this>.
  void append(GfxPath *path);

  // Add (<dx>, <dy>) to each point in the path.
  void offset(double dx, double dy);

private:

  GBool justMoved;		// set if a new subpath was just started
  double firstX, firstY;	// first point in new subpath
  GfxSubpath **subpaths;	// subpaths
  int n;			// number of subpaths
  int size;			// size of subpaths array

  GfxPath(GBool justMoved1, double firstX1, double firstY1,
	  GfxSubpath **subpaths1, int n1, int size1);
};

//------------------------------------------------------------------------
// GfxState
//------------------------------------------------------------------------

class GfxState {
public:

  // Construct a default GfxState, for a device with resolution <hDPI>
  // x <vDPI>, page box <pageBox>, page rotation <rotateA>, and
  // coordinate system specified by <upsideDown>.
  GfxState(double hDPIA, double vDPIA, PDFRectangle *pageBox,
	   int rotateA, GBool upsideDown);

  // Destructor.
  ~GfxState();

  // Copy.
  GfxState *copy() { return new GfxState(this); }

  // Accessors.
  double getHDPI() { return hDPI; }
  double getVDPI() { return vDPI; }
  double *getCTM() { return ctm; }
  double getX1() { return px1; }
  double getY1() { return py1; }
  double getX2() { return px2; }
  double getY2() { return py2; }
  double getPageWidth() { return pageWidth; }
  double getPageHeight() { return pageHeight; }
  int getRotate() { return rotate; }
  GfxColor *getFillColor() { return &fillColor; }
  GfxColor *getStrokeColor() { return &strokeColor; }
  void getFillGray(GfxGray *gray)
    { fillColorSpace->getGray(&fillColor, gray); }
  void getStrokeGray(GfxGray *gray)
    { strokeColorSpace->getGray(&strokeColor, gray); }
  void getFillRGB(GfxRGB *rgb)
    { fillColorSpace->getRGB(&fillColor, rgb); }
  void getStrokeRGB(GfxRGB *rgb)
    { strokeColorSpace->getRGB(&strokeColor, rgb); }
  void getFillCMYK(GfxCMYK *cmyk)
    { fillColorSpace->getCMYK(&fillColor, cmyk); }
  void getStrokeCMYK(GfxCMYK *cmyk)
    { strokeColorSpace->getCMYK(&strokeColor, cmyk); }
  GfxColorSpace *getFillColorSpace() { return fillColorSpace; }
  GfxColorSpace *getStrokeColorSpace() { return strokeColorSpace; }
  GfxPattern *getFillPattern() { return fillPattern; }
  GfxPattern *getStrokePattern() { return strokePattern; }
  GfxBlendMode getBlendMode() { return blendMode; }
  double getFillOpacity() { return fillOpacity; }
  double getStrokeOpacity() { return strokeOpacity; }
  GBool getFillOverprint() { return fillOverprint; }
  GBool getStrokeOverprint() { return strokeOverprint; }
  Function **getTransfer() { return transfer; }
  double getLineWidth() { return lineWidth; }
  void getLineDash(double **dash, int *length, double *start)
    { *dash = lineDash; *length = lineDashLength; *start = lineDashStart; }
  int getFlatness() { return flatness; }
  int getLineJoin() { return lineJoin; }
  int getLineCap() { return lineCap; }
  double getMiterLimit() { return miterLimit; }
  GBool getStrokeAdjust() { return strokeAdjust; }
  GfxFont *getFont() { return font; }
  double getFontSize() { return fontSize; }
  double *getTextMat() { return textMat; }
  double getCharSpace() { return charSpace; }
  double getWordSpace() { return wordSpace; }
  double getHorizScaling() { return horizScaling; }
  double getLeading() { return leading; }
  double getRise() { return rise; }
  int getRender() { return render; }
  GfxPath *getPath() { return path; }
  void setPath(GfxPath *pathA);
  double getCurX() { return curX; }
  double getCurY() { return curY; }
  void getClipBBox(double *xMin, double *yMin, double *xMax, double *yMax)
    { *xMin = clipXMin; *yMin = clipYMin; *xMax = clipXMax; *yMax = clipYMax; }
  void getUserClipBBox(double *xMin, double *yMin, double *xMax, double *yMax);
  double getLineX() { return lineX; }
  double getLineY() { return lineY; }

  // Is there a current point/path?
  GBool isCurPt() { return path->isCurPt(); }
  GBool isPath() { return path->isPath(); }

  // Transforms.
  void transform(double x1, double y1, double *x2, double *y2)
    { *x2 = ctm[0] * x1 + ctm[2] * y1 + ctm[4];
      *y2 = ctm[1] * x1 + ctm[3] * y1 + ctm[5]; }
  void transformDelta(double x1, double y1, double *x2, double *y2)
    { *x2 = ctm[0] * x1 + ctm[2] * y1;
      *y2 = ctm[1] * x1 + ctm[3] * y1; }
  void textTransform(double x1, double y1, double *x2, double *y2)
    { *x2 = textMat[0] * x1 + textMat[2] * y1 + textMat[4];
      *y2 = textMat[1] * x1 + textMat[3] * y1 + textMat[5]; }
  void textTransformDelta(double x1, double y1, double *x2, double *y2)
    { *x2 = textMat[0] * x1 + textMat[2] * y1;
      *y2 = textMat[1] * x1 + textMat[3] * y1; }
  double transformWidth(double w);
  double getTransformedLineWidth()
    { return transformWidth(lineWidth); }
  double getTransformedFontSize();
  void getFontTransMat(double *m11, double *m12, double *m21, double *m22);

  // Change state parameters.
  void setCTM(double a, double b, double c,
	      double d, double e, double f);
  void concatCTM(double a, double b, double c,
		 double d, double e, double f);
  void shiftCTM(double tx, double ty);
  void setFillColorSpace(GfxColorSpace *colorSpace);
  void setStrokeColorSpace(GfxColorSpace *colorSpace);
  void setFillColor(GfxColor *color) { fillColor = *color; }
  void setStrokeColor(GfxColor *color) { strokeColor = *color; }
  void setFillPattern(GfxPattern *pattern);
  void setStrokePattern(GfxPattern *pattern);
  void setBlendMode(GfxBlendMode mode) { blendMode = mode; }
  void setFillOpacity(double opac) { fillOpacity = opac; }
  void setStrokeOpacity(double opac) { strokeOpacity = opac; }
  void setFillOverprint(GBool op) { fillOverprint = op; }
  void setStrokeOverprint(GBool op) { strokeOverprint = op; }
  void setTransfer(Function **funcs);
  void setLineWidth(double width) { lineWidth = width; }
  void setLineDash(double *dash, int length, double start);
  void setFlatness(int flatness1) { flatness = flatness1; }
  void setLineJoin(int lineJoin1) { lineJoin = lineJoin1; }
  void setLineCap(int lineCap1) { lineCap = lineCap1; }
  void setMiterLimit(double limit) { miterLimit = limit; }
  void setStrokeAdjust(GBool sa) { strokeAdjust = sa; }
  void setFont(GfxFont *fontA, double fontSizeA)
    { font = fontA; fontSize = fontSizeA; }
  void setTextMat(double a, double b, double c,
		  double d, double e, double f)
    { textMat[0] = a; textMat[1] = b; textMat[2] = c;
      textMat[3] = d; textMat[4] = e; textMat[5] = f; }
  void setCharSpace(double space)
    { charSpace = space; }
  void setWordSpace(double space)
    { wordSpace = space; }
  void setHorizScaling(double scale)
    { horizScaling = 0.01 * scale; }
  void setLeading(double leadingA)
    { leading = leadingA; }
  void setRise(double riseA)
    { rise = riseA; }
  void setRender(int renderA)
    { render = renderA; }

  // Add to path.
  void moveTo(double x, double y)
    { path->moveTo(curX = x, curY = y); }
  void lineTo(double x, double y)
    { path->lineTo(curX = x, curY = y); }
  void curveTo(double x1, double y1, double x2, double y2,
	       double x3, double y3)
    { path->curveTo(x1, y1, x2, y2, curX = x3, curY = y3); }
  void closePath()
    { path->close(); curX = path->getLastX(); curY = path->getLastY(); }
  void clearPath();

  // Update clip region.
  void clip();
  void clipToStrokePath();

  // Text position.
  void textSetPos(double tx, double ty) { lineX = tx; lineY = ty; }
  void textMoveTo(double tx, double ty)
    { lineX = tx; lineY = ty; textTransform(tx, ty, &curX, &curY); }
  void textShift(double tx, double ty);
  void shift(double dx, double dy);

  // Push/pop GfxState on/off stack.
  GfxState *save();
  GfxState *restore();
  GBool hasSaves() { return saved != NULL; }

  // Misc
  GBool parseBlendMode(Object *obj, GfxBlendMode *mode);

private:

  double hDPI, vDPI;		// resolution
  double ctm[6];		// coord transform matrix
  double px1, py1, px2, py2;	// page corners (user coords)
  double pageWidth, pageHeight;	// page size (pixels)
  int rotate;			// page rotation angle

  GfxColorSpace *fillColorSpace;   // fill color space
  GfxColorSpace *strokeColorSpace; // stroke color space
  GfxColor fillColor;		// fill color
  GfxColor strokeColor;		// stroke color
  GfxPattern *fillPattern;	// fill pattern
  GfxPattern *strokePattern;	// stroke pattern
  GfxBlendMode blendMode;	// transparency blend mode
  double fillOpacity;		// fill opacity
  double strokeOpacity;		// stroke opacity
  GBool fillOverprint;		// fill overprint
  GBool strokeOverprint;	// stroke overprint
  Function *transfer[4];	// transfer function (entries may be: all
				//   NULL = identity; last three NULL =
				//   single function; all four non-NULL =
				//   R,G,B,gray functions)

  double lineWidth;		// line width
  double *lineDash;		// line dash
  int lineDashLength;
  double lineDashStart;
  int flatness;			// curve flatness
  int lineJoin;			// line join style
  int lineCap;			// line cap style
  double miterLimit;		// line miter limit
  GBool strokeAdjust;		// stroke adjustment

  GfxFont *font;		// font
  double fontSize;		// font size
  double textMat[6];		// text matrix
  double charSpace;		// character spacing
  double wordSpace;		// word spacing
  double horizScaling;		// horizontal scaling
  double leading;		// text leading
  double rise;			// text rise
  int render;			// text rendering mode

  GfxPath *path;		// array of path elements
  double curX, curY;		// current point (user coords)
  double lineX, lineY;		// start of current text line (text coords)

  double clipXMin, clipYMin,	// bounding box for clip region
         clipXMax, clipYMax;

  GfxState *saved;		// next GfxState on stack

  GfxState(GfxState *state);
};

#endif
//========================================================================
//
// GlobalParams.cc
//
// Copyright 2001-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <string.h>
#include <stdio.h>
#include <ctype.h>
#ifdef ENABLE_PLUGINS
#  ifndef WIN32
#    include <dlfcn.h>
#  endif
#endif
#ifdef WIN32
#  include <shlobj.h>
#endif
#if HAVE_PAPER_H
#include <paper.h>
#endif
#include "gmem.h"
#include "GString.h"
#include "GList.h"
#include "GHash.h"
#include "gfile.h"
#include "Error.h"
#include "NameToCharCode.h"
#include "CharCodeToUnicode.h"
#include "UnicodeMap.h"
#include "CMap.h"
#include "BuiltinFontTables.h"
#include "FontEncodingTables.h"
#ifdef ENABLE_PLUGINS
#  include "XpdfPluginAPI.h"
#endif
#include "GlobalParams.h"

#ifdef WIN32
#  define strcasecmp stricmp
#endif

#if MULTITHREADED
#  define lockGlobalParams            gLockMutex(&mutex)
#  define lockUnicodeMapCache         gLockMutex(&unicodeMapCacheMutex)
#  define lockCMapCache               gLockMutex(&cMapCacheMutex)
#  define unlockGlobalParams          gUnlockMutex(&mutex)
#  define unlockUnicodeMapCache       gUnlockMutex(&unicodeMapCacheMutex)
#  define unlockCMapCache             gUnlockMutex(&cMapCacheMutex)
#else
#  define lockGlobalParams
#  define lockUnicodeMapCache
#  define lockCMapCache
#  define unlockGlobalParams
#  define unlockUnicodeMapCache
#  define unlockCMapCache
#endif

#include "NameToUnicodeTable.h"
#include "UnicodeMapTables.h"
#include "UTF8.h"

#ifdef ENABLE_PLUGINS
#  ifdef WIN32
extern XpdfPluginVecTable xpdfPluginVecTable;
#  endif
#endif

//------------------------------------------------------------------------

#define cidToUnicodeCacheSize     4
#define unicodeToUnicodeCacheSize 4

//------------------------------------------------------------------------

static struct {
  char *name;
  char *t1FileName;
  char *ttFileName;
} displayFontTab[] = {
  {"Courier",               "n022003l.pfb", "cour.ttf"},
  {"Courier-Bold",          "n022004l.pfb", "courbd.ttf"},
  {"Courier-BoldOblique",   "n022024l.pfb", "courbi.ttf"},
  {"Courier-Oblique",       "n022023l.pfb", "couri.ttf"},
  {"Helvetica",             "n019003l.pfb", "arial.ttf"},
  {"Helvetica-Bold",        "n019004l.pfb", "arialbd.ttf"},
  {"Helvetica-BoldOblique", "n019024l.pfb", "arialbi.ttf"},
  {"Helvetica-Oblique",     "n019023l.pfb", "ariali.ttf"},
  {"Symbol",                "s050000l.pfb", NULL},
  {"Times-Bold",            "n021004l.pfb", "timesbd.ttf"},
  {"Times-BoldItalic",      "n021024l.pfb", "timesbi.ttf"},
  {"Times-Italic",          "n021023l.pfb", "timesi.ttf"},
  {"Times-Roman",           "n021003l.pfb", "times.ttf"},
  {"ZapfDingbats",          "d050000l.pfb", NULL},
  {NULL}
};

#ifdef WIN32
static char *displayFontDirs[] = {
  "c:/windows/fonts",
  "c:/winnt/fonts",
  NULL
};
#else
static char *displayFontDirs[] = {
  "/usr/share/ghostscript/fonts",
  "/usr/local/share/ghostscript/fonts",
  "/usr/share/fonts/default/Type1",
  "/usr/share/fonts/default/ghostscript",
  "/usr/share/fonts/type1/gsfonts",
  NULL
};
#endif

//------------------------------------------------------------------------

GlobalParams *globalParams = NULL;

//------------------------------------------------------------------------
// DisplayFontParam
//------------------------------------------------------------------------

DisplayFontParam::DisplayFontParam(GString *nameA,
				   DisplayFontParamKind kindA) {
  name = nameA;
  kind = kindA;
  switch (kind) {
  case displayFontT1:
    t1.fileName = NULL;
    break;
  case displayFontTT:
    tt.fileName = NULL;
    break;
  }
}

DisplayFontParam::~DisplayFontParam() {
  delete name;
  switch (kind) {
  case displayFontT1:
    if (t1.fileName) {
      delete t1.fileName;
    }
    break;
  case displayFontTT:
    if (tt.fileName) {
      delete tt.fileName;
    }
    break;
  }
}

#ifdef WIN32

//------------------------------------------------------------------------
// WinFontInfo
//------------------------------------------------------------------------

class WinFontInfo: public DisplayFontParam {
public:

  GBool bold, italic;

  static WinFontInfo *make(GString *nameA, GBool boldA, GBool italicA,
			   HKEY regKey, char *winFontDir);
  WinFontInfo(GString *nameA, GBool boldA, GBool italicA,
	      GString *fileNameA);
  virtual ~WinFontInfo();
  GBool equals(WinFontInfo *fi);
};

WinFontInfo *WinFontInfo::make(GString *nameA, GBool boldA, GBool italicA,
			       HKEY regKey, char *winFontDir) {
  GString *regName;
  GString *fileNameA;
  char buf[MAX_PATH];
  DWORD n;
  char c;
  int i;

  //----- find the font file
  fileNameA = NULL;
  regName = nameA->copy();
  if (boldA) {
    regName->append(" Bold");
  }
  if (italicA) {
    regName->append(" Italic");
  }
  regName->append(" (TrueType)");
  n = sizeof(buf);
  if (RegQueryValueEx(regKey, regName->getCString(), NULL, NULL,
		      (LPBYTE)buf, &n) == ERROR_SUCCESS) {
    fileNameA = new GString(winFontDir);
    fileNameA->append('\\')->append(buf);
  }
  delete regName;
  if (!fileNameA) {
    delete nameA;
    return NULL;
  }

  //----- normalize the font name
  i = 0;
  while (i < nameA->getLength()) {
    c = nameA->getChar(i);
    if (c == ' ' || c == ',' || c == '-') {
      nameA->del(i);
    } else {
      ++i;
    }
  }

  return new WinFontInfo(nameA, boldA, italicA, fileNameA);
}

WinFontInfo::WinFontInfo(GString *nameA, GBool boldA, GBool italicA,
			 GString *fileNameA):
  DisplayFontParam(nameA, displayFontTT)
{
  bold = boldA;
  italic = italicA;
  tt.fileName = fileNameA;
}

WinFontInfo::~WinFontInfo() {
}

GBool WinFontInfo::equals(WinFontInfo *fi) {
  return !name->cmp(fi->name) && bold == fi->bold && italic == fi->italic;
}

//------------------------------------------------------------------------
// WinFontList
//------------------------------------------------------------------------

class WinFontList {
public:

  WinFontList(char *winFontDirA);
  ~WinFontList();
  WinFontInfo *find(GString *font);

private:

  void add(WinFontInfo *fi);
  static int CALLBACK enumFunc1(CONST LOGFONT *font,
				CONST TEXTMETRIC *metrics,
				DWORD type, LPARAM data);
  static int CALLBACK enumFunc2(CONST LOGFONT *font,
				CONST TEXTMETRIC *metrics,
				DWORD type, LPARAM data);

  GList *fonts;			// [WinFontInfo]
  HDC dc;			// (only used during enumeration)
  HKEY regKey;			// (only used during enumeration)
  char *winFontDir;		// (only used during enumeration)
};

WinFontList::WinFontList(char *winFontDirA) {
  OSVERSIONINFO version;
  char *path;

  fonts = new GList();
  dc = GetDC(NULL);
  winFontDir = winFontDirA;
  version.dwOSVersionInfoSize = sizeof(version);
  GetVersionEx(&version);
  if (version.dwPlatformId == VER_PLATFORM_WIN32_NT) {
    path = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts\\";
  } else {
    path = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Fonts\\";
  }
  if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, path, 0,
		   KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS,
		   &regKey) == ERROR_SUCCESS) {
    EnumFonts(dc, NULL, &WinFontList::enumFunc1, (LPARAM)this);
    RegCloseKey(regKey);
  }
  ReleaseDC(NULL, dc);
}

WinFontList::~WinFontList() {
  deleteGList(fonts, WinFontInfo);
}

void WinFontList::add(WinFontInfo *fi) {
  int i;

  for (i = 0; i < fonts->getLength(); ++i) {
    if (((WinFontInfo *)fonts->get(i))->equals(fi)) {
      delete fi;
      return;
    }
  }
  fonts->append(fi);
}

WinFontInfo *WinFontList::find(GString *font) {
  GString *name;
  GBool bold, italic;
  WinFontInfo *fi;
  char c;
  int n, i;

  name = font->copy();

  // remove space, comma, dash chars
  i = 0;
  while (i < name->getLength()) {
    c = name->getChar(i);
    if (c == ' ' || c == ',' || c == '-') {
      name->del(i);
    } else {
      ++i;
    }
  }
  n = name->getLength();

  // remove trailing "MT" (Foo-MT, Foo-BoldMT, etc.)
  if (!strcmp(name->getCString() + n - 2, "MT")) {
    name->del(n - 2, 2);
    n -= 2;
  }

  // look for "Italic"
  if (!strcmp(name->getCString() + n - 6, "Italic")) {
    name->del(n - 6, 6);
    italic = gTrue;
    n -= 6;
  } else {
    italic = gFalse;
  }

  // look for "Bold"
  if (!strcmp(name->getCString() + n - 4, "Bold")) {
    name->del(n - 4, 4);
    bold = gTrue;
    n -= 4;
  } else {
    bold = gFalse;
  }

  // remove trailing "MT" (FooMT-Bold, etc.)
  if (!strcmp(name->getCString() + n - 2, "MT")) {
    name->del(n - 2, 2);
    n -= 2;
  }

  // remove trailing "PS"
  if (!strcmp(name->getCString() + n - 2, "PS")) {
    name->del(n - 2, 2);
    n -= 2;
  }

  // search for the font
  fi = NULL;
  for (i = 0; i < fonts->getLength(); ++i) {
    fi = (WinFontInfo *)fonts->get(i);
    if (!fi->name->cmp(name) && fi->bold == bold && fi->italic == italic) {
      break;
    }
    fi = NULL;
  }

  delete name;
  return fi;
}

int CALLBACK WinFontList::enumFunc1(CONST LOGFONT *font,
				    CONST TEXTMETRIC *metrics,
				    DWORD type, LPARAM data) {
  WinFontList *fl = (WinFontList *)data;

  EnumFonts(fl->dc, font->lfFaceName, &WinFontList::enumFunc2, (LPARAM)fl);
  return 1;
}

int CALLBACK WinFontList::enumFunc2(CONST LOGFONT *font,
				    CONST TEXTMETRIC *metrics,
				    DWORD type, LPARAM data) {
  WinFontList *fl = (WinFontList *)data;
  WinFontInfo *fi;

  if (type & TRUETYPE_FONTTYPE) {
    if ((fi = WinFontInfo::make(new GString(font->lfFaceName),
				font->lfWeight >= 600,
				font->lfItalic ? gTrue : gFalse,
				fl->regKey, fl->winFontDir))) {
      fl->add(fi);
    }
  }
  return 1;
}

#endif // WIN32

//------------------------------------------------------------------------
// PSFontParam
//------------------------------------------------------------------------

PSFontParam::PSFontParam(GString *pdfFontNameA, int wModeA,
			 GString *psFontNameA, GString *encodingA) {
  pdfFontName = pdfFontNameA;
  wMode = wModeA;
  psFontName = psFontNameA;
  encoding = encodingA;
}

PSFontParam::~PSFontParam() {
  delete pdfFontName;
  delete psFontName;
  if (encoding) {
    delete encoding;
  }
}

//------------------------------------------------------------------------
// KeyBinding
//------------------------------------------------------------------------

KeyBinding::KeyBinding(int codeA, int modsA, int contextA, char *cmd0) {
  code = codeA;
  mods = modsA;
  context = contextA;
  cmds = new GList();
  cmds->append(new GString(cmd0));
}

KeyBinding::KeyBinding(int codeA, int modsA, int contextA,
		       char *cmd0, char *cmd1) {
  code = codeA;
  mods = modsA;
  context = contextA;
  cmds = new GList();
  cmds->append(new GString(cmd0));
  cmds->append(new GString(cmd1));
}

KeyBinding::KeyBinding(int codeA, int modsA, int contextA, GList *cmdsA) {
  code = codeA;
  mods = modsA;
  context = contextA;
  cmds = cmdsA;
}

KeyBinding::~KeyBinding() {
  deleteGList(cmds, GString);
}

#ifdef ENABLE_PLUGINS
//------------------------------------------------------------------------
// Plugin
//------------------------------------------------------------------------

class Plugin {
public:

  static Plugin *load(char *type, char *name);
  ~Plugin();

private:

#ifdef WIN32
  Plugin(HMODULE libA);
  HMODULE lib;
#else
  Plugin(void *dlA);
  void *dl;
#endif
};

Plugin *Plugin::load(char *type, char *name) {
  GString *path;
  Plugin *plugin;
  XpdfPluginVecTable *vt;
  XpdfBool (*xpdfInitPlugin)(void);
#ifdef WIN32
  HMODULE libA;
#else
  void *dlA;
#endif

  path = globalParams->getBaseDir();
  appendToPath(path, "plugins");
  appendToPath(path, type);
  appendToPath(path, name);

#ifdef WIN32
  path->append(".dll");
  if (!(libA = LoadLibrary(path->getCString()))) {
    error(-1, "Failed to load plugin '%s'",
	  path->getCString());
    goto err1;
  }
  if (!(vt = (XpdfPluginVecTable *)
	         GetProcAddress(libA, "xpdfPluginVecTable"))) {
    error(-1, "Failed to find xpdfPluginVecTable in plugin '%s'",
	  path->getCString());
    goto err2;
  }
#else
  //~ need to deal with other extensions here
  path->append(".so");
  if (!(dlA = dlopen(path->getCString(), RTLD_NOW))) {
    error(-1, "Failed to load plugin '%s': %s",
	  path->getCString(), dlerror());
    goto err1;
  }
  if (!(vt = (XpdfPluginVecTable *)dlsym(dlA, "xpdfPluginVecTable"))) {
    error(-1, "Failed to find xpdfPluginVecTable in plugin '%s'",
	  path->getCString());
    goto err2;
  }
#endif

  if (vt->version != xpdfPluginVecTable.version) {
    error(-1, "Plugin '%s' is wrong version", path->getCString());
    goto err2;
  }
  memcpy(vt, &xpdfPluginVecTable, sizeof(xpdfPluginVecTable));

#ifdef WIN32
  if (!(xpdfInitPlugin = (XpdfBool (*)(void))
	                     GetProcAddress(libA, "xpdfInitPlugin"))) {
    error(-1, "Failed to find xpdfInitPlugin in plugin '%s'",
	  path->getCString());
    goto err2;
  }
#else
  if (!(xpdfInitPlugin = (XpdfBool (*)(void))dlsym(dlA, "xpdfInitPlugin"))) {
    error(-1, "Failed to find xpdfInitPlugin in plugin '%s'",
	  path->getCString());
    goto err2;
  }
#endif

  if (!(*xpdfInitPlugin)()) {
    error(-1, "Initialization of plugin '%s' failed",
	  path->getCString());
    goto err2;
  }

#ifdef WIN32
  plugin = new Plugin(libA);
#else
  plugin = new Plugin(dlA);
#endif

  delete path;
  return plugin;

 err2:
#ifdef WIN32
  FreeLibrary(libA);
#else
  dlclose(dlA);
#endif
 err1:
  delete path;
  return NULL;
}

#ifdef WIN32
Plugin::Plugin(HMODULE libA) {
  lib = libA;
}
#else
Plugin::Plugin(void *dlA) {
  dl = dlA;
}
#endif

Plugin::~Plugin() {
  void (*xpdfFreePlugin)(void);

#ifdef WIN32
  if ((xpdfFreePlugin = (void (*)(void))
                            GetProcAddress(lib, "xpdfFreePlugin"))) {
    (*xpdfFreePlugin)();
  }
  FreeLibrary(lib);
#else
  if ((xpdfFreePlugin = (void (*)(void))dlsym(dl, "xpdfFreePlugin"))) {
    (*xpdfFreePlugin)();
  }
  dlclose(dl);
#endif
}

#endif // ENABLE_PLUGINS

//------------------------------------------------------------------------
// parsing
//------------------------------------------------------------------------

GlobalParams::GlobalParams(char *cfgFileName) {
  UnicodeMap *map;
  GString *fileName;
  FILE *f;
  int i;

#if MULTITHREADED
  gInitMutex(&mutex);
  gInitMutex(&unicodeMapCacheMutex);
  gInitMutex(&cMapCacheMutex);
#endif

  initBuiltinFontTables();

  // scan the encoding in reverse because we want the lowest-numbered
  // index for each char name ('space' is encoded twice)
  macRomanReverseMap = new NameToCharCode();
  for (i = 255; i >= 0; --i) {
    if (macRomanEncoding[i]) {
      macRomanReverseMap->add(macRomanEncoding[i], (CharCode)i);
    }
  }

#ifdef WIN32
  // baseDir will be set by a call to setBaseDir
  baseDir = new GString();
#else
  baseDir = appendToPath(getHomeDir(), ".xpdf");
#endif
  nameToUnicode = new NameToCharCode();
  cidToUnicodes = new GHash(gTrue);
  unicodeToUnicodes = new GHash(gTrue);
  residentUnicodeMaps = new GHash();
  unicodeMaps = new GHash(gTrue);
  cMapDirs = new GHash(gTrue);
  toUnicodeDirs = new GList();
  displayFonts = new GHash();
  displayCIDFonts = new GHash();
  displayNamedCIDFonts = new GHash();
#if HAVE_PAPER_H
  char *paperName;
  const struct paper *paperType;
  paperinit();
  if ((paperName = systempapername())) {
    paperType = paperinfo(paperName);
    psPaperWidth = (int)paperpswidth(paperType);
    psPaperHeight = (int)paperpsheight(paperType);
  } else {
    error(-1, "No paper information available - using defaults");
    psPaperWidth = defPaperWidth;
    psPaperHeight = defPaperHeight;
  }
  paperdone();
#else
  psPaperWidth = defPaperWidth;
  psPaperHeight = defPaperHeight;
#endif
  psImageableLLX = psImageableLLY = 0;
  psImageableURX = psPaperWidth;
  psImageableURY = psPaperHeight;
  psCrop = gTrue;
  psExpandSmaller = gFalse;
  psShrinkLarger = gTrue;
  psCenter = gTrue;
  psDuplex = gFalse;
  psLevel = psLevel2;
  psFile = NULL;
  psFonts = new GHash();
  psNamedFonts16 = new GList();
  psFonts16 = new GList();
  psEmbedType1 = gTrue;
  psEmbedTrueType = gTrue;
  psEmbedCIDPostScript = gTrue;
  psEmbedCIDTrueType = gTrue;
  psPreload = gFalse;
  psOPI = gFalse;
  psASCIIHex = gFalse;
  textEncoding = new GString("Latin1");
#if defined(WIN32)
  textEOL = eolDOS;
#elif defined(MACOS)
  textEOL = eolMac;
#else
  textEOL = eolUnix;
#endif
  textPageBreaks = gTrue;
  textKeepTinyChars = gFalse;
  fontDirs = new GList();
  initialZoom = new GString("125");
  continuousView = gFalse;
  enableT1lib = gTrue;
  enableFreeType = gTrue;
  antialias = gTrue;
  vectorAntialias = gTrue;
  strokeAdjust = gTrue;
  screenType = screenUnset;
  screenSize = -1;
  screenDotRadius = -1;
  screenGamma = 1.0;
  screenBlackThreshold = 0.0;
  screenWhiteThreshold = 1.0;
  urlCommand = NULL;
  movieCommand = NULL;
  mapNumericCharNames = gTrue;
  mapUnknownCharNames = gFalse;
  createDefaultKeyBindings();
  printCommands = gFalse;
  errQuiet = gFalse;

  cidToUnicodeCache = new CharCodeToUnicodeCache(cidToUnicodeCacheSize);
  unicodeToUnicodeCache =
      new CharCodeToUnicodeCache(unicodeToUnicodeCacheSize);
  unicodeMapCache = new UnicodeMapCache();
  cMapCache = new CMapCache();

#ifdef WIN32
  winFontList = NULL;
#endif

#ifdef ENABLE_PLUGINS
  plugins = new GList();
  securityHandlers = new GList();
#endif

  // set up the initial nameToUnicode table
  for (i = 0; nameToUnicodeTab[i].name; ++i) {
    nameToUnicode->add(nameToUnicodeTab[i].name, nameToUnicodeTab[i].u);
  }

  // set up the residentUnicodeMaps table
  map = new UnicodeMap("Latin1", gFalse,
		       latin1UnicodeMapRanges, latin1UnicodeMapLen);
  residentUnicodeMaps->add(map->getEncodingName(), map);
  map = new UnicodeMap("ASCII7", gFalse,
		       ascii7UnicodeMapRanges, ascii7UnicodeMapLen);
  residentUnicodeMaps->add(map->getEncodingName(), map);
  map = new UnicodeMap("Symbol", gFalse,
		       symbolUnicodeMapRanges, symbolUnicodeMapLen);
  residentUnicodeMaps->add(map->getEncodingName(), map);
  map = new UnicodeMap("ZapfDingbats", gFalse, zapfDingbatsUnicodeMapRanges,
		       zapfDingbatsUnicodeMapLen);
  residentUnicodeMaps->add(map->getEncodingName(), map);
  map = new UnicodeMap("UTF-8", gTrue, &mapUTF8);
  residentUnicodeMaps->add(map->getEncodingName(), map);
  map = new UnicodeMap("UCS-2", gTrue, &mapUCS2);
  residentUnicodeMaps->add(map->getEncodingName(), map);

  // look for a user config file, then a system-wide config file
  f = NULL;
  fileName = NULL;
  if (cfgFileName && cfgFileName[0]) {
    fileName = new GString(cfgFileName);
    if (!(f = fopen(fileName->getCString(), "r"))) {
      delete fileName;
    }
  }
  if (!f) {
    fileName = appendToPath(getHomeDir(), xpdfUserConfigFile);
    if (!(f = fopen(fileName->getCString(), "r"))) {
      delete fileName;
    }
  }
  if (!f) {
#if defined(WIN32) && !defined(__CYGWIN32__)
    char buf[512];
    i = GetModuleFileName(NULL, buf, sizeof(buf));
    if (i <= 0 || i >= sizeof(buf)) {
      // error or path too long for buffer - just use the current dir
      buf[0] = '\0';
    }
    fileName = grabPath(buf);
    appendToPath(fileName, xpdfSysConfigFile);
#else
    fileName = new GString(xpdfSysConfigFile);
#endif
    if (!(f = fopen(fileName->getCString(), "r"))) {
      delete fileName;
    }
  }
  if (f) {
    parseFile(fileName, f);
    delete fileName;
    fclose(f);
  }
}

void GlobalParams::createDefaultKeyBindings() {
  keyBindings = new GList();

  //----- mouse buttons
  keyBindings->append(new KeyBinding(xpdfKeyCodeMousePress1, xpdfKeyModNone,
				     xpdfKeyContextAny, "startSelection"));
  keyBindings->append(new KeyBinding(xpdfKeyCodeMouseRelease1, xpdfKeyModNone,
				     xpdfKeyContextAny, "endSelection",
				     "followLinkNoSel"));
  keyBindings->append(new KeyBinding(xpdfKeyCodeMousePress2, xpdfKeyModNone,
				     xpdfKeyContextAny, "startPan"));
  keyBindings->append(new KeyBinding(xpdfKeyCodeMouseRelease2, xpdfKeyModNone,
				     xpdfKeyContextAny, "endPan"));
  keyBindings->append(new KeyBinding(xpdfKeyCodeMousePress3, xpdfKeyModNone,
				     xpdfKeyContextAny, "postPopupMenu"));
  keyBindings->append(new KeyBinding(xpdfKeyCodeMousePress4, xpdfKeyModNone,
				     xpdfKeyContextAny,
				     "scrollUpPrevPage(16)"));
  keyBindings->append(new KeyBinding(xpdfKeyCodeMousePress5, xpdfKeyModNone,
				     xpdfKeyContextAny,
				     "scrollDownNextPage(16)"));
  keyBindings->append(new KeyBinding(xpdfKeyCodeMousePress6, xpdfKeyModNone,
				     xpdfKeyContextAny, "scrollLeft(16)"));
  keyBindings->append(new KeyBinding(xpdfKeyCodeMousePress7, xpdfKeyModNone,
				     xpdfKeyContextAny, "scrollRight(16)"));

  //----- keys
  keyBindings->append(new KeyBinding(xpdfKeyCodeHome, xpdfKeyModCtrl,
				     xpdfKeyContextAny, "gotoPage(1)"));
  keyBindings->append(new KeyBinding(xpdfKeyCodeHome, xpdfKeyModNone,
				     xpdfKeyContextAny, "scrollToTopLeft"));
  keyBindings->append(new KeyBinding(xpdfKeyCodeEnd, xpdfKeyModCtrl,
				     xpdfKeyContextAny, "gotoLastPage"));
  keyBindings->append(new KeyBinding(xpdfKeyCodeEnd, xpdfKeyModNone,
				     xpdfKeyContextAny,
				     "scrollToBottomRight"));
  keyBindings->append(new KeyBinding(xpdfKeyCodePgUp, xpdfKeyModNone,
				     xpdfKeyContextAny, "pageUp"));
  keyBindings->append(new KeyBinding(xpdfKeyCodeBackspace, xpdfKeyModNone,
				     xpdfKeyContextAny, "pageUp"));
  keyBindings->append(new KeyBinding(xpdfKeyCodeDelete, xpdfKeyModNone,
				     xpdfKeyContextAny, "pageUp"));
  keyBindings->append(new KeyBinding(xpdfKeyCodePgDn, xpdfKeyModNone,
				     xpdfKeyContextAny, "pageDown"));
  keyBindings->append(new KeyBinding(' ', xpdfKeyModNone,
				     xpdfKeyContextAny, "pageDown"));
  keyBindings->append(new KeyBinding(xpdfKeyCodeLeft, xpdfKeyModNone,
				     xpdfKeyContextAny, "scrollLeft(16)"));
  keyBindings->append(new KeyBinding(xpdfKeyCodeRight, xpdfKeyModNone,
				     xpdfKeyContextAny, "scrollRight(16)"));
  keyBindings->append(new KeyBinding(xpdfKeyCodeUp, xpdfKeyModNone,
				     xpdfKeyContextAny, "scrollUp(16)"));
  keyBindings->append(new KeyBinding(xpdfKeyCodeDown, xpdfKeyModNone,
				     xpdfKeyContextAny, "scrollDown(16)"));
  keyBindings->append(new KeyBinding('o', xpdfKeyModNone,
				     xpdfKeyContextAny, "open"));
  keyBindings->append(new KeyBinding('O', xpdfKeyModNone,
				     xpdfKeyContextAny, "open"));
  keyBindings->append(new KeyBinding('r', xpdfKeyModNone,
				     xpdfKeyContextAny, "reload"));
  keyBindings->append(new KeyBinding('R', xpdfKeyModNone,
				     xpdfKeyContextAny, "reload"));
  keyBindings->append(new KeyBinding('f', xpdfKeyModNone,
				     xpdfKeyContextAny, "find"));
  keyBindings->append(new KeyBinding('F', xpdfKeyModNone,
				     xpdfKeyContextAny, "find"));
  keyBindings->append(new KeyBinding('f', xpdfKeyModCtrl,
				     xpdfKeyContextAny, "find"));
  keyBindings->append(new KeyBinding('g', xpdfKeyModCtrl,
				     xpdfKeyContextAny, "findNext"));
  keyBindings->append(new KeyBinding('p', xpdfKeyModCtrl,
				     xpdfKeyContextAny, "print"));
  keyBindings->append(new KeyBinding('n', xpdfKeyModNone,
				     xpdfKeyContextScrLockOff, "nextPage"));
  keyBindings->append(new KeyBinding('N', xpdfKeyModNone,
				     xpdfKeyContextScrLockOff, "nextPage"));
  keyBindings->append(new KeyBinding('n', xpdfKeyModNone,
				     xpdfKeyContextScrLockOn,
				     "nextPageNoScroll"));
  keyBindings->append(new KeyBinding('N', xpdfKeyModNone,
				     xpdfKeyContextScrLockOn,
				     "nextPageNoScroll"));
  keyBindings->append(new KeyBinding('p', xpdfKeyModNone,
				     xpdfKeyContextScrLockOff, "prevPage"));
  keyBindings->append(new KeyBinding('P', xpdfKeyModNone,
				     xpdfKeyContextScrLockOff, "prevPage"));
  keyBindings->append(new KeyBinding('p', xpdfKeyModNone,
				     xpdfKeyContextScrLockOn,
				     "prevPageNoScroll"));
  keyBindings->append(new KeyBinding('P', xpdfKeyModNone,
				     xpdfKeyContextScrLockOn,
				     "prevPageNoScroll"));
  keyBindings->append(new KeyBinding('v', xpdfKeyModNone,
				     xpdfKeyContextAny, "goForward"));
  keyBindings->append(new KeyBinding('b', xpdfKeyModNone,
				     xpdfKeyContextAny, "goBackward"));
  keyBindings->append(new KeyBinding('g', xpdfKeyModNone,
				     xpdfKeyContextAny, "focusToPageNum"));
  keyBindings->append(new KeyBinding('0', xpdfKeyModNone,
				     xpdfKeyContextAny, "zoomPercent(125)"));
  keyBindings->append(new KeyBinding('+', xpdfKeyModNone,
				     xpdfKeyContextAny, "zoomIn"));
  keyBindings->append(new KeyBinding('-', xpdfKeyModNone,
				     xpdfKeyContextAny, "zoomOut"));
  keyBindings->append(new KeyBinding('z', xpdfKeyModNone,
				     xpdfKeyContextAny, "zoomFitPage"));
  keyBindings->append(new KeyBinding('w', xpdfKeyModNone,
				     xpdfKeyContextAny, "zoomFitWidth"));
  keyBindings->append(new KeyBinding('f', xpdfKeyModAlt,
				     xpdfKeyContextAny,
				     "toggleFullScreenMode"));
  keyBindings->append(new KeyBinding('l', xpdfKeyModCtrl,
				     xpdfKeyContextAny, "redraw"));
  keyBindings->append(new KeyBinding('w', xpdfKeyModCtrl,
				     xpdfKeyContextAny, "closeWindow"));
  keyBindings->append(new KeyBinding('?', xpdfKeyModNone,
				     xpdfKeyContextAny, "about"));
  keyBindings->append(new KeyBinding('q', xpdfKeyModNone,
				     xpdfKeyContextAny, "quit"));
  keyBindings->append(new KeyBinding('Q', xpdfKeyModNone,
				     xpdfKeyContextAny, "quit"));
}

void GlobalParams::parseFile(GString *fileName, FILE *f) {
  int line;
  char buf[512];

  line = 1;
  while (getLine(buf, sizeof(buf) - 1, f)) {
    parseLine(buf, fileName, line);
    ++line;
  }
}

void GlobalParams::parseLine(char *buf, GString *fileName, int line) {
  GList *tokens;
  GString *cmd, *incFile;
  char *p1, *p2;
  FILE *f2;

  // break the line into tokens
  tokens = new GList();
  p1 = buf;
  while (*p1) {
    for (; *p1 && isspace(*p1); ++p1) ;
    if (!*p1) {
      break;
    }
    if (*p1 == '"' || *p1 == '\'') {
      for (p2 = p1 + 1; *p2 && *p2 != *p1; ++p2) ;
      ++p1;
    } else {
      for (p2 = p1 + 1; *p2 && !isspace(*p2); ++p2) ;
    }
    tokens->append(new GString(p1, p2 - p1));
    p1 = *p2 ? p2 + 1 : p2;
  }

  // parse the line
  if (tokens->getLength() > 0 &&
      ((GString *)tokens->get(0))->getChar(0) != '#') {
    cmd = (GString *)tokens->get(0);
    if (!cmd->cmp("include")) {
      if (tokens->getLength() == 2) {
	incFile = (GString *)tokens->get(1);
	if ((f2 = fopen(incFile->getCString(), "r"))) {
	  parseFile(incFile, f2);
	  fclose(f2);
	} else {
	  error(-1, "Couldn't find included config file: '%s' (%s:%d)",
		incFile->getCString(), fileName->getCString(), line);
	}
      } else {
	error(-1, "Bad 'include' config file command (%s:%d)",
	      fileName->getCString(), line);
      }
    } else if (!cmd->cmp("nameToUnicode")) {
      parseNameToUnicode(tokens, fileName, line);
    } else if (!cmd->cmp("cidToUnicode")) {
      parseCIDToUnicode(tokens, fileName, line);
    } else if (!cmd->cmp("unicodeToUnicode")) {
      parseUnicodeToUnicode(tokens, fileName, line);
    } else if (!cmd->cmp("unicodeMap")) {
      parseUnicodeMap(tokens, fileName, line);
    } else if (!cmd->cmp("cMapDir")) {
      parseCMapDir(tokens, fileName, line);
    } else if (!cmd->cmp("toUnicodeDir")) {
      parseToUnicodeDir(tokens, fileName, line);
    } else if (!cmd->cmp("displayFontT1")) {
      parseDisplayFont(tokens, displayFonts, displayFontT1, fileName, line);
    } else if (!cmd->cmp("displayFontTT")) {
      parseDisplayFont(tokens, displayFonts, displayFontTT, fileName, line);
    } else if (!cmd->cmp("displayNamedCIDFontT1")) {
      parseDisplayFont(tokens, displayNamedCIDFonts,
		       displayFontT1, fileName, line);
    } else if (!cmd->cmp("displayCIDFontT1")) {
      parseDisplayFont(tokens, displayCIDFonts,
		       displayFontT1, fileName, line);
    } else if (!cmd->cmp("displayNamedCIDFontTT")) {
      parseDisplayFont(tokens, displayNamedCIDFonts,
		       displayFontTT, fileName, line);
    } else if (!cmd->cmp("displayCIDFontTT")) {
      parseDisplayFont(tokens, displayCIDFonts,
		       displayFontTT, fileName, line);
    } else if (!cmd->cmp("psFile")) {
      parsePSFile(tokens, fileName, line);
    } else if (!cmd->cmp("psFont")) {
      parsePSFont(tokens, fileName, line);
    } else if (!cmd->cmp("psNamedFont16")) {
      parsePSFont16("psNamedFont16", psNamedFonts16,
		    tokens, fileName, line);
    } else if (!cmd->cmp("psFont16")) {
      parsePSFont16("psFont16", psFonts16, tokens, fileName, line);
    } else if (!cmd->cmp("psPaperSize")) {
      parsePSPaperSize(tokens, fileName, line);
    } else if (!cmd->cmp("psImageableArea")) {
      parsePSImageableArea(tokens, fileName, line);
    } else if (!cmd->cmp("psCrop")) {
      parseYesNo("psCrop", &psCrop, tokens, fileName, line);
    } else if (!cmd->cmp("psExpandSmaller")) {
      parseYesNo("psExpandSmaller", &psExpandSmaller,
		 tokens, fileName, line);
    } else if (!cmd->cmp("psShrinkLarger")) {
      parseYesNo("psShrinkLarger", &psShrinkLarger, tokens, fileName, line);
    } else if (!cmd->cmp("psCenter")) {
      parseYesNo("psCenter", &psCenter, tokens, fileName, line);
    } else if (!cmd->cmp("psDuplex")) {
      parseYesNo("psDuplex", &psDuplex, tokens, fileName, line);
    } else if (!cmd->cmp("psLevel")) {
      parsePSLevel(tokens, fileName, line);
    } else if (!cmd->cmp("psEmbedType1Fonts")) {
      parseYesNo("psEmbedType1", &psEmbedType1, tokens, fileName, line);
    } else if (!cmd->cmp("psEmbedTrueTypeFonts")) {
      parseYesNo("psEmbedTrueType", &psEmbedTrueType,
		 tokens, fileName, line);
    } else if (!cmd->cmp("psEmbedCIDPostScriptFonts")) {
      parseYesNo("psEmbedCIDPostScript", &psEmbedCIDPostScript,
		 tokens, fileName, line);
    } else if (!cmd->cmp("psEmbedCIDTrueTypeFonts")) {
      parseYesNo("psEmbedCIDTrueType", &psEmbedCIDTrueType,
		 tokens, fileName, line);
    } else if (!cmd->cmp("psPreload")) {
      parseYesNo("psPreload", &psPreload, tokens, fileName, line);
    } else if (!cmd->cmp("psOPI")) {
      parseYesNo("psOPI", &psOPI, tokens, fileName, line);
    } else if (!cmd->cmp("psASCIIHex")) {
      parseYesNo("psASCIIHex", &psASCIIHex, tokens, fileName, line);
    } else if (!cmd->cmp("textEncoding")) {
      parseTextEncoding(tokens, fileName, line);
    } else if (!cmd->cmp("textEOL")) {
      parseTextEOL(tokens, fileName, line);
    } else if (!cmd->cmp("textPageBreaks")) {
      parseYesNo("textPageBreaks", &textPageBreaks,
		 tokens, fileName, line);
    } else if (!cmd->cmp("textKeepTinyChars")) {
      parseYesNo("textKeepTinyChars", &textKeepTinyChars,
		 tokens, fileName, line);
    } else if (!cmd->cmp("fontDir")) {
      parseFontDir(tokens, fileName, line);
    } else if (!cmd->cmp("initialZoom")) {
      parseInitialZoom(tokens, fileName, line);
    } else if (!cmd->cmp("continuousView")) {
      parseYesNo("continuousView", &continuousView, tokens, fileName, line);
    } else if (!cmd->cmp("enableT1lib")) {
      parseYesNo("enableT1lib", &enableT1lib, tokens, fileName, line);
    } else if (!cmd->cmp("enableFreeType")) {
      parseYesNo("enableFreeType", &enableFreeType, tokens, fileName, line);
    } else if (!cmd->cmp("antialias")) {
      parseYesNo("antialias", &antialias, tokens, fileName, line);
    } else if (!cmd->cmp("vectorAntialias")) {
      parseYesNo("vectorAntialias", &vectorAntialias,
		 tokens, fileName, line);
    } else if (!cmd->cmp("strokeAdjust")) {
      parseYesNo("strokeAdjust", &strokeAdjust, tokens, fileName, line);
    } else if (!cmd->cmp("screenType")) {
      parseScreenType(tokens, fileName, line);
    } else if (!cmd->cmp("screenSize")) {
      parseInteger("screenSize", &screenSize, tokens, fileName, line);
    } else if (!cmd->cmp("screenDotRadius")) {
      parseInteger("screenDotRadius", &screenDotRadius,
		   tokens, fileName, line);
    } else if (!cmd->cmp("screenGamma")) {
      parseFloat("screenGamma", &screenGamma,
		 tokens, fileName, line);
    } else if (!cmd->cmp("screenBlackThreshold")) {
      parseFloat("screenBlackThreshold", &screenBlackThreshold,
		 tokens, fileName, line);
    } else if (!cmd->cmp("screenWhiteThreshold")) {
      parseFloat("screenWhiteThreshold", &screenWhiteThreshold,
		 tokens, fileName, line);
    } else if (!cmd->cmp("urlCommand")) {
      parseCommand("urlCommand", &urlCommand, tokens, fileName, line);
    } else if (!cmd->cmp("movieCommand")) {
      parseCommand("movieCommand", &movieCommand, tokens, fileName, line);
    } else if (!cmd->cmp("mapNumericCharNames")) {
      parseYesNo("mapNumericCharNames", &mapNumericCharNames,
		 tokens, fileName, line);
    } else if (!cmd->cmp("mapUnknownCharNames")) {
      parseYesNo("mapUnknownCharNames", &mapUnknownCharNames,
		 tokens, fileName, line);
    } else if (!cmd->cmp("bind")) {
      parseBind(tokens, fileName, line);
    } else if (!cmd->cmp("unbind")) {
      parseUnbind(tokens, fileName, line);
    } else if (!cmd->cmp("printCommands")) {
      parseYesNo("printCommands", &printCommands, tokens, fileName, line);
    } else if (!cmd->cmp("errQuiet")) {
      parseYesNo("errQuiet", &errQuiet, tokens, fileName, line);
    } else {
      error(-1, "Unknown config file command '%s' (%s:%d)",
	    cmd->getCString(), fileName->getCString(), line);
      if (!cmd->cmp("displayFontX") ||
	  !cmd->cmp("displayNamedCIDFontX") ||
	  !cmd->cmp("displayCIDFontX")) {
	error(-1, "-- Xpdf no longer supports X fonts");
      } else if (!cmd->cmp("t1libControl") || !cmd->cmp("freetypeControl")) {
	error(-1, "-- The t1libControl and freetypeControl options have been replaced");
	error(-1, "   by the enableT1lib, enableFreeType, and antialias options");
      } else if (!cmd->cmp("fontpath") || !cmd->cmp("fontmap")) {
	error(-1, "-- the config file format has changed since Xpdf 0.9x");
      }
    }
  }

  deleteGList(tokens, GString);
}

void GlobalParams::parseNameToUnicode(GList *tokens, GString *fileName,
					 int line) {
  GString *name;
  char *tok1, *tok2;
  FILE *f;
  char buf[256];
  int line2;
  Unicode u;

  if (tokens->getLength() != 2) {
    error(-1, "Bad 'nameToUnicode' config file command (%s:%d)",
	  fileName->getCString(), line);
    return;
  }
  name = (GString *)tokens->get(1);
  if (!(f = fopen(name->getCString(), "r"))) {
    error(-1, "Couldn't open 'nameToUnicode' file '%s'",
	  name->getCString());
    return;
  }
  line2 = 1;
  while (getLine(buf, sizeof(buf), f)) {
    tok1 = strtok(buf, " \t\r\n");
    tok2 = strtok(NULL, " \t\r\n");
    if (tok1 && tok2) {
      sscanf(tok1, "%x", &u);
      nameToUnicode->add(tok2, u);
    } else {
      error(-1, "Bad line in 'nameToUnicode' file (%s:%d)", name, line2);
    }
    ++line2;
  }
  fclose(f);
}

void GlobalParams::parseCIDToUnicode(GList *tokens, GString *fileName,
				     int line) {
  GString *collection, *name, *old;

  if (tokens->getLength() != 3) {
    error(-1, "Bad 'cidToUnicode' config file command (%s:%d)",
	  fileName->getCString(), line);
    return;
  }
  collection = (GString *)tokens->get(1);
  name = (GString *)tokens->get(2);
  if ((old = (GString *)cidToUnicodes->remove(collection))) {
    delete old;
  }
  cidToUnicodes->add(collection->copy(), name->copy());
}

void GlobalParams::parseUnicodeToUnicode(GList *tokens, GString *fileName,
					 int line) {
  GString *font, *file, *old;

  if (tokens->getLength() != 3) {
    error(-1, "Bad 'unicodeToUnicode' config file command (%s:%d)",
	  fileName->getCString(), line);
    return;
  }
  font = (GString *)tokens->get(1);
  file = (GString *)tokens->get(2);
  if ((old = (GString *)unicodeToUnicodes->remove(font))) {
    delete old;
  }
  unicodeToUnicodes->add(font->copy(), file->copy());
}

void GlobalParams::parseUnicodeMap(GList *tokens, GString *fileName,
				   int line) {
  GString *encodingName, *name, *old;

  if (tokens->getLength() != 3) {
    error(-1, "Bad 'unicodeMap' config file command (%s:%d)",
	  fileName->getCString(), line);
    return;
  }
  encodingName = (GString *)tokens->get(1);
  name = (GString *)tokens->get(2);
  if ((old = (GString *)unicodeMaps->remove(encodingName))) {
    delete old;
  }
  unicodeMaps->add(encodingName->copy(), name->copy());
}

void GlobalParams::parseCMapDir(GList *tokens, GString *fileName, int line) {
  GString *collection, *dir;
  GList *list;

  if (tokens->getLength() != 3) {
    error(-1, "Bad 'cMapDir' config file command (%s:%d)",
	  fileName->getCString(), line);
    return;
  }
  collection = (GString *)tokens->get(1);
  dir = (GString *)tokens->get(2);
  if (!(list = (GList *)cMapDirs->lookup(collection))) {
    list = new GList();
    cMapDirs->add(collection->copy(), list);
  }
  list->append(dir->copy());
}

void GlobalParams::parseToUnicodeDir(GList *tokens, GString *fileName,
				     int line) {
  if (tokens->getLength() != 2) {
    error(-1, "Bad 'toUnicodeDir' config file command (%s:%d)",
	  fileName->getCString(), line);
    return;
  }
  toUnicodeDirs->append(((GString *)tokens->get(1))->copy());
}

void GlobalParams::parseDisplayFont(GList *tokens, GHash *fontHash,
				    DisplayFontParamKind kind,
				    GString *fileName, int line) {
  DisplayFontParam *param, *old;

  if (tokens->getLength() < 2) {
    goto err1;
  }
  param = new DisplayFontParam(((GString *)tokens->get(1))->copy(), kind);
  
  switch (kind) {
  case displayFontT1:
    if (tokens->getLength() != 3) {
      goto err2;
    }
    param->t1.fileName = ((GString *)tokens->get(2))->copy();
    break;
  case displayFontTT:
    if (tokens->getLength() != 3) {
      goto err2;
    }
    param->tt.fileName = ((GString *)tokens->get(2))->copy();
    break;
  }

  if ((old = (DisplayFontParam *)fontHash->remove(param->name))) {
    delete old;
  }
  fontHash->add(param->name, param);
  return;

 err2:
  delete param;
 err1:
  error(-1, "Bad 'display*Font*' config file command (%s:%d)",
	fileName->getCString(), line);
}

void GlobalParams::parsePSPaperSize(GList *tokens, GString *fileName,
				    int line) {
  GString *tok;

  if (tokens->getLength() == 2) {
    tok = (GString *)tokens->get(1);
    if (!setPSPaperSize(tok->getCString())) {
      error(-1, "Bad 'psPaperSize' config file command (%s:%d)",
	    fileName->getCString(), line);
    }
  } else if (tokens->getLength() == 3) {
    tok = (GString *)tokens->get(1);
    psPaperWidth = atoi(tok->getCString());
    tok = (GString *)tokens->get(2);
    psPaperHeight = atoi(tok->getCString());
    psImageableLLX = psImageableLLY = 0;
    psImageableURX = psPaperWidth;
    psImageableURY = psPaperHeight;
  } else {
    error(-1, "Bad 'psPaperSize' config file command (%s:%d)",
	  fileName->getCString(), line);
  }
}

void GlobalParams::parsePSImageableArea(GList *tokens, GString *fileName,
					int line) {
  if (tokens->getLength() != 5) {
    error(-1, "Bad 'psImageableArea' config file command (%s:%d)",
	  fileName->getCString(), line);
    return;
  }
  psImageableLLX = atoi(((GString *)tokens->get(1))->getCString());
  psImageableLLY = atoi(((GString *)tokens->get(2))->getCString());
  psImageableURX = atoi(((GString *)tokens->get(3))->getCString());
  psImageableURY = atoi(((GString *)tokens->get(4))->getCString());
}

void GlobalParams::parsePSLevel(GList *tokens, GString *fileName, int line) {
  GString *tok;

  if (tokens->getLength() != 2) {
    error(-1, "Bad 'psLevel' config file command (%s:%d)",
	  fileName->getCString(), line);
    return;
  }
  tok = (GString *)tokens->get(1);
  if (!tok->cmp("level1")) {
    psLevel = psLevel1;
  } else if (!tok->cmp("level1sep")) {
    psLevel = psLevel1Sep;
  } else if (!tok->cmp("level2")) {
    psLevel = psLevel2;
  } else if (!tok->cmp("level2sep")) {
    psLevel = psLevel2Sep;
  } else if (!tok->cmp("level3")) {
    psLevel = psLevel3;
  } else if (!tok->cmp("level3Sep")) {
    psLevel = psLevel3Sep;
  } else {
    error(-1, "Bad 'psLevel' config file command (%s:%d)",
	  fileName->getCString(), line);
  }
}

void GlobalParams::parsePSFile(GList *tokens, GString *fileName, int line) {
  if (tokens->getLength() != 2) {
    error(-1, "Bad 'psFile' config file command (%s:%d)",
	  fileName->getCString(), line);
    return;
  }
  if (psFile) {
    delete psFile;
  }
  psFile = ((GString *)tokens->get(1))->copy();
}

void GlobalParams::parsePSFont(GList *tokens, GString *fileName, int line) {
  PSFontParam *param;

  if (tokens->getLength() != 3) {
    error(-1, "Bad 'psFont' config file command (%s:%d)",
	  fileName->getCString(), line);
    return;
  }
  param = new PSFontParam(((GString *)tokens->get(1))->copy(), 0,
			  ((GString *)tokens->get(2))->copy(), NULL);
  psFonts->add(param->pdfFontName, param);
}

void GlobalParams::parsePSFont16(char *cmdName, GList *fontList,
				 GList *tokens, GString *fileName, int line) {
  PSFontParam *param;
  int wMode;
  GString *tok;

  if (tokens->getLength() != 5) {
    error(-1, "Bad '%s' config file command (%s:%d)",
	  cmdName, fileName->getCString(), line);
    return;
  }
  tok = (GString *)tokens->get(2);
  if (!tok->cmp("H")) {
    wMode = 0;
  } else if (!tok->cmp("V")) {
    wMode = 1;
  } else {
    error(-1, "Bad '%s' config file command (%s:%d)",
	  cmdName, fileName->getCString(), line);
    return;
  }
  param = new PSFontParam(((GString *)tokens->get(1))->copy(),
			  wMode,
			  ((GString *)tokens->get(3))->copy(),
			  ((GString *)tokens->get(4))->copy());
  fontList->append(param);
}

void GlobalParams::parseTextEncoding(GList *tokens, GString *fileName,
				     int line) {
  if (tokens->getLength() != 2) {
    error(-1, "Bad 'textEncoding' config file command (%s:%d)",
	  fileName->getCString(), line);
    return;
  }
  delete textEncoding;
  textEncoding = ((GString *)tokens->get(1))->copy();
}

void GlobalParams::parseTextEOL(GList *tokens, GString *fileName, int line) {
  GString *tok;

  if (tokens->getLength() != 2) {
    error(-1, "Bad 'textEOL' config file command (%s:%d)",
	  fileName->getCString(), line);
    return;
  }
  tok = (GString *)tokens->get(1);
  if (!tok->cmp("unix")) {
    textEOL = eolUnix;
  } else if (!tok->cmp("dos")) {
    textEOL = eolDOS;
  } else if (!tok->cmp("mac")) {
    textEOL = eolMac;
  } else {
    error(-1, "Bad 'textEOL' config file command (%s:%d)",
	  fileName->getCString(), line);
  }
}

void GlobalParams::parseFontDir(GList *tokens, GString *fileName, int line) {
  if (tokens->getLength() != 2) {
    error(-1, "Bad 'fontDir' config file command (%s:%d)",
	  fileName->getCString(), line);
    return;
  }
  fontDirs->append(((GString *)tokens->get(1))->copy());
}

void GlobalParams::parseInitialZoom(GList *tokens,
				    GString *fileName, int line) {
  if (tokens->getLength() != 2) {
    error(-1, "Bad 'initialZoom' config file command (%s:%d)",
	  fileName->getCString(), line);
    return;
  }
  delete initialZoom;
  initialZoom = ((GString *)tokens->get(1))->copy();
}

void GlobalParams::parseScreenType(GList *tokens, GString *fileName,
				   int line) {
  GString *tok;

  if (tokens->getLength() != 2) {
    error(-1, "Bad 'screenType' config file command (%s:%d)",
	  fileName->getCString(), line);
    return;
  }
  tok = (GString *)tokens->get(1);
  if (!tok->cmp("dispersed")) {
    screenType = screenDispersed;
  } else if (!tok->cmp("clustered")) {
    screenType = screenClustered;
  } else if (!tok->cmp("stochasticClustered")) {
    screenType = screenStochasticClustered;
  } else {
    error(-1, "Bad 'screenType' config file command (%s:%d)",
	  fileName->getCString(), line);
  }
}

void GlobalParams::parseBind(GList *tokens, GString *fileName, int line) {
  KeyBinding *binding;
  GList *cmds;
  int code, mods, context, i;

  if (tokens->getLength() < 4) {
    error(-1, "Bad 'bind' config file command (%s:%d)",
	  fileName->getCString(), line);
    return;
  }
  if (!parseKey((GString *)tokens->get(1), (GString *)tokens->get(2),
		&code, &mods, &context,
		"bind", tokens, fileName, line)) {
    return;
  }
  for (i = 0; i < keyBindings->getLength(); ++i) {
    binding = (KeyBinding *)keyBindings->get(i);
    if (binding->code == code &&
	binding->mods == mods &&
	binding->context == context) {
      delete (KeyBinding *)keyBindings->del(i);
      break;
    }
  }
  cmds = new GList();
  for (i = 3; i < tokens->getLength(); ++i) {
    cmds->append(((GString *)tokens->get(i))->copy());
  }
  keyBindings->append(new KeyBinding(code, mods, context, cmds));
}

void GlobalParams::parseUnbind(GList *tokens, GString *fileName, int line) {
  KeyBinding *binding;
  int code, mods, context, i;

  if (tokens->getLength() != 3) {
    error(-1, "Bad 'unbind' config file command (%s:%d)",
	  fileName->getCString(), line);
    return;
  }
  if (!parseKey((GString *)tokens->get(1), (GString *)tokens->get(2),
		&code, &mods, &context,
		"unbind", tokens, fileName, line)) {
    return;
  }
  for (i = 0; i < keyBindings->getLength(); ++i) {
    binding = (KeyBinding *)keyBindings->get(i);
    if (binding->code == code &&
	binding->mods == mods &&
	binding->context == context) {
      delete (KeyBinding *)keyBindings->del(i);
      break;
    }
  }
}

GBool GlobalParams::parseKey(GString *modKeyStr, GString *contextStr,
			     int *code, int *mods, int *context,
			     char *cmdName,
			     GList *tokens, GString *fileName, int line) {
  char *p0;

  *mods = xpdfKeyModNone;
  p0 = modKeyStr->getCString();
  while (1) {
    if (!strncmp(p0, "shift-", 6)) {
      *mods |= xpdfKeyModShift;
      p0 += 6;
    } else if (!strncmp(p0, "ctrl-", 5)) {
      *mods |= xpdfKeyModCtrl;
      p0 += 5;
    } else if (!strncmp(p0, "alt-", 4)) {
      *mods |= xpdfKeyModAlt;
      p0 += 4;
    } else {
      break;
    }
  }

  if (!strcmp(p0, "space")) {
    *code = ' ';
  } else if (!strcmp(p0, "tab")) {
    *code = xpdfKeyCodeTab;
  } else if (!strcmp(p0, "return")) {
    *code = xpdfKeyCodeReturn;
  } else if (!strcmp(p0, "enter")) {
    *code = xpdfKeyCodeEnter;
  } else if (!strcmp(p0, "backspace")) {
    *code = xpdfKeyCodeBackspace;
  } else if (!strcmp(p0, "insert")) {
    *code = xpdfKeyCodeInsert;
  } else if (!strcmp(p0, "delete")) {
    *code = xpdfKeyCodeDelete;
  } else if (!strcmp(p0, "home")) {
    *code = xpdfKeyCodeHome;
  } else if (!strcmp(p0, "end")) {
    *code = xpdfKeyCodeEnd;
  } else if (!strcmp(p0, "pgup")) {
    *code = xpdfKeyCodePgUp;
  } else if (!strcmp(p0, "pgdn")) {
    *code = xpdfKeyCodePgDn;
  } else if (!strcmp(p0, "left")) {
    *code = xpdfKeyCodeLeft;
  } else if (!strcmp(p0, "right")) {
    *code = xpdfKeyCodeRight;
  } else if (!strcmp(p0, "up")) {
    *code = xpdfKeyCodeUp;
  } else if (!strcmp(p0, "down")) {
    *code = xpdfKeyCodeDown;
  } else if (p0[0] == 'f' && p0[1] >= '1' && p0[1] <= '9' && !p0[2]) {
    *code = xpdfKeyCodeF1 + (p0[1] - '1');
  } else if (p0[0] == 'f' &&
	     ((p0[1] >= '1' && p0[1] <= '2' && p0[2] >= '0' && p0[2] <= '9') ||
	      (p0[1] == '3' && p0[2] >= '0' && p0[2] <= '5')) &&
	     !p0[3]) {
    *code = xpdfKeyCodeF1 + 10 * (p0[1] - '0') + (p0[2] - '0') - 1;
  } else if (!strncmp(p0, "mousePress", 10) &&
	     p0[10] >= '1' && p0[10] <= '7' && !p0[11]) {
    *code = xpdfKeyCodeMousePress1 + (p0[10] - '1');
  } else if (!strncmp(p0, "mouseRelease", 12) &&
	     p0[12] >= '1' && p0[12] <= '7' && !p0[13]) {
    *code = xpdfKeyCodeMouseRelease1 + (p0[12] - '1');
  } else if (*p0 >= 0x20 && *p0 <= 0x7e && !p0[1]) {
    *code = (int)*p0;
  } else {
    error(-1, "Bad key/modifier in '%s' config file command (%s:%d)",
	  cmdName, fileName->getCString(), line);
    return gFalse;
  }

  p0 = contextStr->getCString();
  if (!strcmp(p0, "any")) {
    *context = xpdfKeyContextAny;
  } else {
    *context = xpdfKeyContextAny;
    while (1) {
      if (!strncmp(p0, "fullScreen", 10)) {
	*context |= xpdfKeyContextFullScreen;
	p0 += 10;
      } else if (!strncmp(p0, "window", 6)) {
	*context |= xpdfKeyContextWindow;
	p0 += 6;
      } else if (!strncmp(p0, "continuous", 10)) {
	*context |= xpdfKeyContextContinuous;
	p0 += 10;
      } else if (!strncmp(p0, "singlePage", 10)) {
	*context |= xpdfKeyContextSinglePage;
	p0 += 10;
      } else if (!strncmp(p0, "overLink", 8)) {
	*context |= xpdfKeyContextOverLink;
	p0 += 8;
      } else if (!strncmp(p0, "offLink", 7)) {
	*context |= xpdfKeyContextOffLink;
	p0 += 7;
      } else if (!strncmp(p0, "outline", 7)) {
	*context |= xpdfKeyContextOutline;
	p0 += 7;
      } else if (!strncmp(p0, "mainWin", 7)) {
	*context |= xpdfKeyContextMainWin;
	p0 += 7;
      } else if (!strncmp(p0, "scrLockOn", 9)) {
	*context |= xpdfKeyContextScrLockOn;
	p0 += 9;
      } else if (!strncmp(p0, "scrLockOff", 10)) {
	*context |= xpdfKeyContextScrLockOff;
	p0 += 10;
      } else {
	error(-1, "Bad context in '%s' config file command (%s:%d)",
	      cmdName, fileName->getCString(), line);
	return gFalse;
      }
      if (!*p0) {
	break;
      }
      if (*p0 != ',') {
	error(-1, "Bad context in '%s' config file command (%s:%d)",
	      cmdName, fileName->getCString(), line);
	return gFalse;
      }
      ++p0;
    }
  }

  return gTrue;
}

void GlobalParams::parseCommand(char *cmdName, GString **val,
				GList *tokens, GString *fileName, int line) {
  if (tokens->getLength() != 2) {
    error(-1, "Bad '%s' config file command (%s:%d)",
	  cmdName, fileName->getCString(), line);
    return;
  }
  if (*val) {
    delete *val;
  }
  *val = ((GString *)tokens->get(1))->copy();
}

void GlobalParams::parseYesNo(char *cmdName, GBool *flag,
			      GList *tokens, GString *fileName, int line) {
  GString *tok;

  if (tokens->getLength() != 2) {
    error(-1, "Bad '%s' config file command (%s:%d)",
	  cmdName, fileName->getCString(), line);
    return;
  }
  tok = (GString *)tokens->get(1);
  if (!parseYesNo2(tok->getCString(), flag)) {
    error(-1, "Bad '%s' config file command (%s:%d)",
	  cmdName, fileName->getCString(), line);
  }
}

GBool GlobalParams::parseYesNo2(char *token, GBool *flag) {
  if (!strcmp(token, "yes")) {
    *flag = gTrue;
  } else if (!strcmp(token, "no")) {
    *flag = gFalse;
  } else {
    return gFalse;
  }
  return gTrue;
}

void GlobalParams::parseInteger(char *cmdName, int *val,
				GList *tokens, GString *fileName, int line) {
  GString *tok;
  int i;

  if (tokens->getLength() != 2) {
    error(-1, "Bad '%s' config file command (%s:%d)",
	  cmdName, fileName->getCString(), line);
    return;
  }
  tok = (GString *)tokens->get(1);
  if (tok->getLength() == 0) {
    error(-1, "Bad '%s' config file command (%s:%d)",
	  cmdName, fileName->getCString(), line);
    return;
  }
  if (tok->getChar(0) == '-') {
    i = 1;
  } else {
    i = 0;
  }
  for (; i < tok->getLength(); ++i) {
    if (tok->getChar(i) < '0' || tok->getChar(i) > '9') {
      error(-1, "Bad '%s' config file command (%s:%d)",
	    cmdName, fileName->getCString(), line);
      return;
    }
  }
  *val = atoi(tok->getCString());
}

void GlobalParams::parseFloat(char *cmdName, double *val,
			      GList *tokens, GString *fileName, int line) {
  GString *tok;
  int i;

  if (tokens->getLength() != 2) {
    error(-1, "Bad '%s' config file command (%s:%d)",
	  cmdName, fileName->getCString(), line);
    return;
  }
  tok = (GString *)tokens->get(1);
  if (tok->getLength() == 0) {
    error(-1, "Bad '%s' config file command (%s:%d)",
	  cmdName, fileName->getCString(), line);
    return;
  }
  if (tok->getChar(0) == '-') {
    i = 1;
  } else {
    i = 0;
  }
  for (; i < tok->getLength(); ++i) {
    if (!((tok->getChar(i) >= '0' && tok->getChar(i) <= '9') ||
	  tok->getChar(i) == '.')) {
      error(-1, "Bad '%s' config file command (%s:%d)",
	    cmdName, fileName->getCString(), line);
      return;
    }
  }
  *val = atof(tok->getCString());
}

GlobalParams::~GlobalParams() {
  GHashIter *iter;
  GString *key;
  GList *list;

  freeBuiltinFontTables();

  delete macRomanReverseMap;

  delete baseDir;
  delete nameToUnicode;
  deleteGHash(cidToUnicodes, GString);
  deleteGHash(unicodeToUnicodes, GString);
  deleteGHash(residentUnicodeMaps, UnicodeMap);
  deleteGHash(unicodeMaps, GString);
  deleteGList(toUnicodeDirs, GString);
  deleteGHash(displayFonts, DisplayFontParam);
  deleteGHash(displayCIDFonts, DisplayFontParam);
  deleteGHash(displayNamedCIDFonts, DisplayFontParam);
#ifdef WIN32
  if (winFontList) {
    delete winFontList;
  }
#endif
  if (psFile) {
    delete psFile;
  }
  deleteGHash(psFonts, PSFontParam);
  deleteGList(psNamedFonts16, PSFontParam);
  deleteGList(psFonts16, PSFontParam);
  delete textEncoding;
  deleteGList(fontDirs, GString);
  delete initialZoom;
  if (urlCommand) {
    delete urlCommand;
  }
  if (movieCommand) {
    delete movieCommand;
  }
  deleteGList(keyBindings, KeyBinding);

  cMapDirs->startIter(&iter);
  while (cMapDirs->getNext(&iter, &key, (void **)&list)) {
    deleteGList(list, GString);
  }
  delete cMapDirs;

  delete cidToUnicodeCache;
  delete unicodeToUnicodeCache;
  delete unicodeMapCache;
  delete cMapCache;

#ifdef ENABLE_PLUGINS
  delete securityHandlers;
  deleteGList(plugins, Plugin);
#endif

#if MULTITHREADED
  gDestroyMutex(&mutex);
  gDestroyMutex(&unicodeMapCacheMutex);
  gDestroyMutex(&cMapCacheMutex);
#endif
}

//------------------------------------------------------------------------

void GlobalParams::setBaseDir(char *dir) {
  delete baseDir;
  baseDir = new GString(dir);
}

void GlobalParams::setupBaseFonts(char *dir) {
  GString *fontName;
  GString *fileName;
#ifdef WIN32
  HMODULE shell32Lib;
  BOOL (__stdcall *SHGetSpecialFolderPathFunc)(HWND hwndOwner,
					       LPTSTR lpszPath,
					       int nFolder,
					       BOOL fCreate);
  char winFontDir[MAX_PATH];
#endif
  FILE *f;
  DisplayFontParamKind kind;
  DisplayFontParam *dfp;
  int i, j;

#ifdef WIN32
  // SHGetSpecialFolderPath isn't available in older versions of
  // shell32.dll (Win95 and WinNT4), so do a dynamic load
  winFontDir[0] = '\0';
  if ((shell32Lib = LoadLibrary("shell32.dll"))) {
    if ((SHGetSpecialFolderPathFunc = 
	 (BOOL (__stdcall *)(HWND hwndOwner, LPTSTR lpszPath,
			     int nFolder, BOOL fCreate))
	 GetProcAddress(shell32Lib, "SHGetSpecialFolderPathA"))) {
      if (!(*SHGetSpecialFolderPathFunc)(NULL, winFontDir,
					 CSIDL_FONTS, FALSE)) {
	winFontDir[0] = '\0';
      }
    }
  }
#endif
  for (i = 0; displayFontTab[i].name; ++i) {
    fontName = new GString(displayFontTab[i].name);
    if (getDisplayFont(fontName)) {
      delete fontName;
      continue;
    }
    fileName = NULL;
    kind = displayFontT1; // make gcc happy
    if (dir) {
      fileName = appendToPath(new GString(dir), displayFontTab[i].t1FileName);
      kind = displayFontT1;
      if ((f = fopen(fileName->getCString(), "rb"))) {
	fclose(f);
      } else {
	delete fileName;
	fileName = NULL;
      }
    }
#ifdef WIN32
    if (!fileName && winFontDir[0] && displayFontTab[i].ttFileName) {
      fileName = appendToPath(new GString(winFontDir),
			      displayFontTab[i].ttFileName);
      kind = displayFontTT;
      if ((f = fopen(fileName->getCString(), "rb"))) {
	fclose(f);
      } else {
	delete fileName;
	fileName = NULL;
      }
    }
    // SHGetSpecialFolderPath(CSIDL_FONTS) doesn't work on Win 2k Server
    // or Win2003 Server, or with older versions of shell32.dll, so check
    // the "standard" directories
    if (displayFontTab[i].ttFileName) {
      for (j = 0; !fileName && displayFontDirs[j]; ++j) {
	fileName = appendToPath(new GString(displayFontDirs[j]),
				displayFontTab[i].ttFileName);
	kind = displayFontTT;
	if ((f = fopen(fileName->getCString(), "rb"))) {
	  fclose(f);
	} else {
	  delete fileName;
	  fileName = NULL;
	}
      }
    }
#else
    for (j = 0; !fileName && displayFontDirs[j]; ++j) {
      fileName = appendToPath(new GString(displayFontDirs[j]),
			      displayFontTab[i].t1FileName);
      kind = displayFontT1;
      if ((f = fopen(fileName->getCString(), "rb"))) {
	fclose(f);
      } else {
	delete fileName;
	fileName = NULL;
      }
    }
#endif
    if (!fileName) {
      error(-1, "No display font for '%s'", displayFontTab[i].name);
      delete fontName;
      continue;
    }
    dfp = new DisplayFontParam(fontName, kind);
    dfp->t1.fileName = fileName;
    globalParams->addDisplayFont(dfp);
  }

#ifdef WIN32
  if (winFontDir[0]) {
    winFontList = new WinFontList(winFontDir);
  }
#endif
}

//------------------------------------------------------------------------
// accessors
//------------------------------------------------------------------------

CharCode GlobalParams::getMacRomanCharCode(char *charName) {
  // no need to lock - macRomanReverseMap is constant
  return macRomanReverseMap->lookup(charName);
}

GString *GlobalParams::getBaseDir() {
  GString *s;

  lockGlobalParams;
  s = baseDir->copy();
  unlockGlobalParams;
  return s;
}

Unicode GlobalParams::mapNameToUnicode(char *charName) {
  // no need to lock - nameToUnicode is constant
  return nameToUnicode->lookup(charName);
}

UnicodeMap *GlobalParams::getResidentUnicodeMap(GString *encodingName) {
  UnicodeMap *map;

  lockGlobalParams;
  map = (UnicodeMap *)residentUnicodeMaps->lookup(encodingName);
  unlockGlobalParams;
  if (map) {
    map->incRefCnt();
  }
  return map;
}

FILE *GlobalParams::getUnicodeMapFile(GString *encodingName) {
  GString *fileName;
  FILE *f;

  lockGlobalParams;
  if ((fileName = (GString *)unicodeMaps->lookup(encodingName))) {
    f = fopen(fileName->getCString(), "r");
  } else {
    f = NULL;
  }
  unlockGlobalParams;
  return f;
}

FILE *GlobalParams::findCMapFile(GString *collection, GString *cMapName) {
  GList *list;
  GString *dir;
  GString *fileName;
  FILE *f;
  int i;

  lockGlobalParams;
  if (!(list = (GList *)cMapDirs->lookup(collection))) {
    unlockGlobalParams;
    return NULL;
  }
  for (i = 0; i < list->getLength(); ++i) {
    dir = (GString *)list->get(i);
    fileName = appendToPath(dir->copy(), cMapName->getCString());
    f = fopen(fileName->getCString(), "r");
    delete fileName;
    if (f) {
      unlockGlobalParams;
      return f;
    }
  }
  unlockGlobalParams;
  return NULL;
}

FILE *GlobalParams::findToUnicodeFile(GString *name) {
  GString *dir, *fileName;
  FILE *f;
  int i;

  lockGlobalParams;
  for (i = 0; i < toUnicodeDirs->getLength(); ++i) {
    dir = (GString *)toUnicodeDirs->get(i);
    fileName = appendToPath(dir->copy(), name->getCString());
    f = fopen(fileName->getCString(), "r");
    delete fileName;
    if (f) {
      unlockGlobalParams;
      return f;
    }
  }
  unlockGlobalParams;
  return NULL;
}

DisplayFontParam *GlobalParams::getDisplayFont(GString *fontName) {
  DisplayFontParam *dfp;

  lockGlobalParams;
  dfp = (DisplayFontParam *)displayFonts->lookup(fontName);
#ifdef WIN32
  if (!dfp && winFontList) {
    dfp = winFontList->find(fontName);
  }
#endif
  unlockGlobalParams;
  return dfp;
}

DisplayFontParam *GlobalParams::getDisplayCIDFont(GString *fontName,
						  GString *collection) {
  DisplayFontParam *dfp;

  lockGlobalParams;
  if (!fontName ||
      !(dfp = (DisplayFontParam *)displayNamedCIDFonts->lookup(fontName))) {
    dfp = (DisplayFontParam *)displayCIDFonts->lookup(collection);
  }
  unlockGlobalParams;
  return dfp;
}

GString *GlobalParams::getPSFile() {
  GString *s;

  lockGlobalParams;
  s = psFile ? psFile->copy() : (GString *)NULL;
  unlockGlobalParams;
  return s;
}

int GlobalParams::getPSPaperWidth() {
  int w;

  lockGlobalParams;
  w = psPaperWidth;
  unlockGlobalParams;
  return w;
}

int GlobalParams::getPSPaperHeight() {
  int h;

  lockGlobalParams;
  h = psPaperHeight;
  unlockGlobalParams;
  return h;
}

void GlobalParams::getPSImageableArea(int *llx, int *lly, int *urx, int *ury) {
  lockGlobalParams;
  *llx = psImageableLLX;
  *lly = psImageableLLY;
  *urx = psImageableURX;
  *ury = psImageableURY;
  unlockGlobalParams;
}

GBool GlobalParams::getPSCrop() {
  GBool f;

  lockGlobalParams;
  f = psCrop;
  unlockGlobalParams;
  return f;
}

GBool GlobalParams::getPSExpandSmaller() {
  GBool f;

  lockGlobalParams;
  f = psExpandSmaller;
  unlockGlobalParams;
  return f;
}

GBool GlobalParams::getPSShrinkLarger() {
  GBool f;

  lockGlobalParams;
  f = psShrinkLarger;
  unlockGlobalParams;
  return f;
}

GBool GlobalParams::getPSCenter() {
  GBool f;

  lockGlobalParams;
  f = psCenter;
  unlockGlobalParams;
  return f;
}

GBool GlobalParams::getPSDuplex() {
  GBool d;

  lockGlobalParams;
  d = psDuplex;
  unlockGlobalParams;
  return d;
}

GBool GlobalParams::getPSNoText() {
  GBool d;

  lockGlobalParams;
  d = psNoText;
  unlockGlobalParams;
  return d;
}

PSLevel GlobalParams::getPSLevel() {
  PSLevel level;

  lockGlobalParams;
  level = psLevel;
  unlockGlobalParams;
  return level;
}

PSFontParam *GlobalParams::getPSFont(GString *fontName) {
  PSFontParam *p;

  lockGlobalParams;
  p = (PSFontParam *)psFonts->lookup(fontName);
  unlockGlobalParams;
  return p;
}

PSFontParam *GlobalParams::getPSFont16(GString *fontName,
				       GString *collection, int wMode) {
  PSFontParam *p;
  int i;

  lockGlobalParams;
  p = NULL;
  if (fontName) {
    for (i = 0; i < psNamedFonts16->getLength(); ++i) {
      p = (PSFontParam *)psNamedFonts16->get(i);
      if (!p->pdfFontName->cmp(fontName) &&
	  p->wMode == wMode) {
	break;
      }
      p = NULL;
    }
  }
  if (!p && collection) {
    for (i = 0; i < psFonts16->getLength(); ++i) {
      p = (PSFontParam *)psFonts16->get(i);
      if (!p->pdfFontName->cmp(collection) &&
	  p->wMode == wMode) {
	break;
      }
      p = NULL;
    }
  }
  unlockGlobalParams;
  return p;
}

GBool GlobalParams::getPSEmbedType1() {
  GBool e;

  lockGlobalParams;
  e = psEmbedType1;
  unlockGlobalParams;
  return e;
}

GBool GlobalParams::getPSEmbedTrueType() {
  GBool e;

  lockGlobalParams;
  e = psEmbedTrueType;
  unlockGlobalParams;
  return e;
}

GBool GlobalParams::getPSEmbedCIDPostScript() {
  GBool e;

  lockGlobalParams;
  e = psEmbedCIDPostScript;
  unlockGlobalParams;
  return e;
}

GBool GlobalParams::getPSEmbedCIDTrueType() {
  GBool e;

  lockGlobalParams;
  e = psEmbedCIDTrueType;
  unlockGlobalParams;
  return e;
}

GBool GlobalParams::getPSPreload() {
  GBool preload;

  lockGlobalParams;
  preload = psPreload;
  unlockGlobalParams;
  return preload;
}

GBool GlobalParams::getPSOPI() {
  GBool opi;

  lockGlobalParams;
  opi = psOPI;
  unlockGlobalParams;
  return opi;
}

GBool GlobalParams::getPSASCIIHex() {
  GBool ah;

  lockGlobalParams;
  ah = psASCIIHex;
  unlockGlobalParams;
  return ah;
}

GString *GlobalParams::getTextEncodingName() {
  GString *s;

  lockGlobalParams;
  s = textEncoding->copy();
  unlockGlobalParams;
  return s;
}

EndOfLineKind GlobalParams::getTextEOL() {
  EndOfLineKind eol;

  lockGlobalParams;
  eol = textEOL;
  unlockGlobalParams;
  return eol;
}

GBool GlobalParams::getTextPageBreaks() {
  GBool pageBreaks;

  lockGlobalParams;
  pageBreaks = textPageBreaks;
  unlockGlobalParams;
  return pageBreaks;
}

GBool GlobalParams::getTextKeepTinyChars() {
  GBool tiny;

  lockGlobalParams;
  tiny = textKeepTinyChars;
  unlockGlobalParams;
  return tiny;
}

GString *GlobalParams::findFontFile(GString *fontName, char **exts) {
  GString *dir, *fileName;
  char **ext;
  FILE *f;
  int i;

  lockGlobalParams;
  for (i = 0; i < fontDirs->getLength(); ++i) {
    dir = (GString *)fontDirs->get(i);
    for (ext = exts; *ext; ++ext) {
      fileName = appendToPath(dir->copy(), fontName->getCString());
      fileName->append(*ext);
      if ((f = fopen(fileName->getCString(), "rb"))) {
	fclose(f);
	unlockGlobalParams;
	return fileName;
      }
      delete fileName;
    }
  }
  unlockGlobalParams;
  return NULL;
}

GString *GlobalParams::getInitialZoom() {
  GString *s;

  lockGlobalParams;
  s = initialZoom->copy();
  unlockGlobalParams;
  return s;
}

GBool GlobalParams::getContinuousView() {
  GBool f;

  lockGlobalParams;
  f = continuousView;
  unlockGlobalParams;
  return f;
}

GBool GlobalParams::getEnableT1lib() {
  GBool f;

  lockGlobalParams;
  f = enableT1lib;
  unlockGlobalParams;
  return f;
}

GBool GlobalParams::getEnableFreeType() {
  GBool f;

  lockGlobalParams;
  f = enableFreeType;
  unlockGlobalParams;
  return f;
}


GBool GlobalParams::getAntialias() {
  GBool f;

  lockGlobalParams;
  f = antialias;
  unlockGlobalParams;
  return f;
}

GBool GlobalParams::getVectorAntialias() {
  GBool f;

  lockGlobalParams;
  f = vectorAntialias;
  unlockGlobalParams;
  return f;
}

GBool GlobalParams::getStrokeAdjust() {
  GBool f;

  lockGlobalParams;
  f = strokeAdjust;
  unlockGlobalParams;
  return f;
}

ScreenType GlobalParams::getScreenType() {
  ScreenType t;

  lockGlobalParams;
  t = screenType;
  unlockGlobalParams;
  return t;
}

int GlobalParams::getScreenSize() {
  int size;

  lockGlobalParams;
  size = screenSize;
  unlockGlobalParams;
  return size;
}

int GlobalParams::getScreenDotRadius() {
  int r;

  lockGlobalParams;
  r = screenDotRadius;
  unlockGlobalParams;
  return r;
}

double GlobalParams::getScreenGamma() {
  double gamma;

  lockGlobalParams;
  gamma = screenGamma;
  unlockGlobalParams;
  return gamma;
}

double GlobalParams::getScreenBlackThreshold() {
  double thresh;

  lockGlobalParams;
  thresh = screenBlackThreshold;
  unlockGlobalParams;
  return thresh;
}

double GlobalParams::getScreenWhiteThreshold() {
  double thresh;

  lockGlobalParams;
  thresh = screenWhiteThreshold;
  unlockGlobalParams;
  return thresh;
}

GBool GlobalParams::getMapNumericCharNames() {
  GBool map;

  lockGlobalParams;
  map = mapNumericCharNames;
  unlockGlobalParams;
  return map;
}

GBool GlobalParams::getMapUnknownCharNames() {
  GBool map;

  lockGlobalParams;
  map = mapUnknownCharNames;
  unlockGlobalParams;
  return map;
}

GList *GlobalParams::getKeyBinding(int code, int mods, int context) {
  KeyBinding *binding;
  GList *cmds;
  int modMask;
  int i, j;

  lockGlobalParams;
  cmds = NULL;
  // for ASCII chars, ignore the shift modifier
  modMask = code <= 0xff ? ~xpdfKeyModShift : ~0;
  for (i = 0; i < keyBindings->getLength(); ++i) {
    binding = (KeyBinding *)keyBindings->get(i);
    if (binding->code == code &&
	(binding->mods & modMask) == (mods & modMask) &&
	(~binding->context | context) == ~0) {
      cmds = new GList();
      for (j = 0; j < binding->cmds->getLength(); ++j) {
	cmds->append(((GString *)binding->cmds->get(j))->copy());
      }
      break;
    }
  }
  unlockGlobalParams;
  return cmds;
}

GBool GlobalParams::getPrintCommands() {
  GBool p;

  lockGlobalParams;
  p = printCommands;
  unlockGlobalParams;
  return p;
}

GBool GlobalParams::getErrQuiet() {
  // no locking -- this function may get called from inside a locked
  // section
  return errQuiet;
}

CharCodeToUnicode *GlobalParams::getCIDToUnicode(GString *collection) {
  GString *fileName;
  CharCodeToUnicode *ctu;

  lockGlobalParams;
  if (!(ctu = cidToUnicodeCache->getCharCodeToUnicode(collection))) {
    if ((fileName = (GString *)cidToUnicodes->lookup(collection)) &&
	(ctu = CharCodeToUnicode::parseCIDToUnicode(fileName, collection))) {
      cidToUnicodeCache->add(ctu);
    }
  }
  unlockGlobalParams;
  return ctu;
}

CharCodeToUnicode *GlobalParams::getUnicodeToUnicode(GString *fontName) {
  CharCodeToUnicode *ctu;
  GHashIter *iter;
  GString *fontPattern, *fileName;

  lockGlobalParams;
  fileName = NULL;
  unicodeToUnicodes->startIter(&iter);
  while (unicodeToUnicodes->getNext(&iter, &fontPattern, (void **)&fileName)) {
    if (strstr(fontName->getCString(), fontPattern->getCString())) {
      unicodeToUnicodes->killIter(&iter);
      break;
    }
    fileName = NULL;
  }
  if (fileName) {
    if (!(ctu = unicodeToUnicodeCache->getCharCodeToUnicode(fileName))) {
      if ((ctu = CharCodeToUnicode::parseUnicodeToUnicode(fileName))) {
	unicodeToUnicodeCache->add(ctu);
      }
    }
  } else {
    ctu = NULL;
  }
  unlockGlobalParams;
  return ctu;
}

UnicodeMap *GlobalParams::getUnicodeMap(GString *encodingName) {
  return getUnicodeMap2(encodingName);
}

UnicodeMap *GlobalParams::getUnicodeMap2(GString *encodingName) {
  UnicodeMap *map;

  if (!(map = getResidentUnicodeMap(encodingName))) {
    lockUnicodeMapCache;
    map = unicodeMapCache->getUnicodeMap(encodingName);
    unlockUnicodeMapCache;
  }
  return map;
}

CMap *GlobalParams::getCMap(GString *collection, GString *cMapName) {
  CMap *cMap;

  lockCMapCache;
  cMap = cMapCache->getCMap(collection, cMapName);
  unlockCMapCache;
  return cMap;
}

UnicodeMap *GlobalParams::getTextEncoding() {
  return getUnicodeMap2(textEncoding);
}

//------------------------------------------------------------------------
// functions to set parameters
//------------------------------------------------------------------------

void GlobalParams::addDisplayFont(DisplayFontParam *param) {
  DisplayFontParam *old;

  lockGlobalParams;
  if ((old = (DisplayFontParam *)displayFonts->remove(param->name))) {
    delete old;
  }
  displayFonts->add(param->name, param);
  unlockGlobalParams;
}

void GlobalParams::setPSFile(char *file) {
  lockGlobalParams;
  if (psFile) {
    delete psFile;
  }
  psFile = new GString(file);
  unlockGlobalParams;
}

GBool GlobalParams::setPSPaperSize(char *size) {
  lockGlobalParams;
  if (!strcmp(size, "match")) {
    psPaperWidth = psPaperHeight = -1;
  } else if (!strcmp(size, "letter")) {
    psPaperWidth = 612;
    psPaperHeight = 792;
  } else if (!strcmp(size, "legal")) {
    psPaperWidth = 612;
    psPaperHeight = 1008;
  } else if (!strcmp(size, "A4")) {
    psPaperWidth = 595;
    psPaperHeight = 842;
  } else if (!strcmp(size, "A3")) {
    psPaperWidth = 842;
    psPaperHeight = 1190;
  } else {
    unlockGlobalParams;
    return gFalse;
  }
  psImageableLLX = psImageableLLY = 0;
  psImageableURX = psPaperWidth;
  psImageableURY = psPaperHeight;
  unlockGlobalParams;
  return gTrue;
}

void GlobalParams::setPSPaperWidth(int width) {
  lockGlobalParams;
  psPaperWidth = width;
  psImageableLLX = 0;
  psImageableURX = psPaperWidth;
  unlockGlobalParams;
}

void GlobalParams::setPSPaperHeight(int height) {
  lockGlobalParams;
  psPaperHeight = height;
  psImageableLLY = 0;
  psImageableURY = psPaperHeight;
  unlockGlobalParams;
}

void GlobalParams::setPSImageableArea(int llx, int lly, int urx, int ury) {
  lockGlobalParams;
  psImageableLLX = llx;
  psImageableLLY = lly;
  psImageableURX = urx;
  psImageableURY = ury;
  unlockGlobalParams;
}

void GlobalParams::setPSCrop(GBool crop) {
  lockGlobalParams;
  psCrop = crop;
  unlockGlobalParams;
}

void GlobalParams::setPSExpandSmaller(GBool expand) {
  lockGlobalParams;
  psExpandSmaller = expand;
  unlockGlobalParams;
}

void GlobalParams::setPSShrinkLarger(GBool shrink) {
  lockGlobalParams;
  psShrinkLarger = shrink;
  unlockGlobalParams;
}

void GlobalParams::setPSCenter(GBool center) {
  lockGlobalParams;
  psCenter = center;
  unlockGlobalParams;
}

void GlobalParams::setPSDuplex(GBool duplex) {
  lockGlobalParams;
  psDuplex = duplex;
  unlockGlobalParams;
}

void GlobalParams::setPSNoText(GBool notext) 
{
  lockGlobalParams;
  psNoText = notext;
  unlockGlobalParams;
}

void GlobalParams::setPSLevel(PSLevel level) {
  lockGlobalParams;
  psLevel = level;
  unlockGlobalParams;
}

void GlobalParams::setPSEmbedType1(GBool embed) {
  lockGlobalParams;
  psEmbedType1 = embed;
  unlockGlobalParams;
}

void GlobalParams::setPSEmbedTrueType(GBool embed) {
  lockGlobalParams;
  psEmbedTrueType = embed;
  unlockGlobalParams;
}

void GlobalParams::setPSEmbedCIDPostScript(GBool embed) {
  lockGlobalParams;
  psEmbedCIDPostScript = embed;
  unlockGlobalParams;
}

void GlobalParams::setPSEmbedCIDTrueType(GBool embed) {
  lockGlobalParams;
  psEmbedCIDTrueType = embed;
  unlockGlobalParams;
}

void GlobalParams::setPSPreload(GBool preload) {
  lockGlobalParams;
  psPreload = preload;
  unlockGlobalParams;
}

void GlobalParams::setPSOPI(GBool opi) {
  lockGlobalParams;
  psOPI = opi;
  unlockGlobalParams;
}

void GlobalParams::setPSASCIIHex(GBool hex) {
  lockGlobalParams;
  psASCIIHex = hex;
  unlockGlobalParams;
}

void GlobalParams::setTextEncoding(char *encodingName) {
  lockGlobalParams;
  delete textEncoding;
  textEncoding = new GString(encodingName);
  unlockGlobalParams;
}

GBool GlobalParams::setTextEOL(char *s) {
  lockGlobalParams;
  if (!strcmp(s, "unix")) {
    textEOL = eolUnix;
  } else if (!strcmp(s, "dos")) {
    textEOL = eolDOS;
  } else if (!strcmp(s, "mac")) {
    textEOL = eolMac;
  } else {
    unlockGlobalParams;
    return gFalse;
  }
  unlockGlobalParams;
  return gTrue;
}

void GlobalParams::setTextPageBreaks(GBool pageBreaks) {
  lockGlobalParams;
  textPageBreaks = pageBreaks;
  unlockGlobalParams;
}

void GlobalParams::setTextKeepTinyChars(GBool keep) {
  lockGlobalParams;
  textKeepTinyChars = keep;
  unlockGlobalParams;
}

void GlobalParams::setInitialZoom(char *s) {
  lockGlobalParams;
  delete initialZoom;
  initialZoom = new GString(s);
  unlockGlobalParams;
}

void GlobalParams::setContinuousView(GBool cont) {
  lockGlobalParams;
  continuousView = cont;
  unlockGlobalParams;
}

GBool GlobalParams::setEnableT1lib(char *s) {
  GBool ok;

  lockGlobalParams;
  ok = parseYesNo2(s, &enableT1lib);
  unlockGlobalParams;
  return ok;
}

GBool GlobalParams::setEnableFreeType(char *s) {
  GBool ok;

  lockGlobalParams;
  ok = parseYesNo2(s, &enableFreeType);
  unlockGlobalParams;
  return ok;
}


GBool GlobalParams::setAntialias(char *s) {
  GBool ok;

  lockGlobalParams;
  ok = parseYesNo2(s, &antialias);
  unlockGlobalParams;
  return ok;
}

GBool GlobalParams::setVectorAntialias(char *s) {
  GBool ok;

  lockGlobalParams;
  ok = parseYesNo2(s, &vectorAntialias);
  unlockGlobalParams;
  return ok;
}

void GlobalParams::setScreenType(ScreenType t) {
  lockGlobalParams;
  screenType = t;
  unlockGlobalParams;
}

void GlobalParams::setScreenSize(int size) {
  lockGlobalParams;
  screenSize = size;
  unlockGlobalParams;
}

void GlobalParams::setScreenDotRadius(int r) {
  lockGlobalParams;
  screenDotRadius = r;
  unlockGlobalParams;
}

void GlobalParams::setScreenGamma(double gamma) {
  lockGlobalParams;
  screenGamma = gamma;
  unlockGlobalParams;
}

void GlobalParams::setScreenBlackThreshold(double thresh) {
  lockGlobalParams;
  screenBlackThreshold = thresh;
  unlockGlobalParams;
}

void GlobalParams::setScreenWhiteThreshold(double thresh) {
  lockGlobalParams;
  screenWhiteThreshold = thresh;
  unlockGlobalParams;
}

void GlobalParams::setMapNumericCharNames(GBool map) {
  lockGlobalParams;
  mapNumericCharNames = map;
  unlockGlobalParams;
}

void GlobalParams::setMapUnknownCharNames(GBool map) {
  lockGlobalParams;
  mapUnknownCharNames = map;
  unlockGlobalParams;
}

void GlobalParams::setPrintCommands(GBool printCommandsA) {
  lockGlobalParams;
  printCommands = printCommandsA;
  unlockGlobalParams;
}

void GlobalParams::setErrQuiet(GBool errQuietA) {
  lockGlobalParams;
  errQuiet = errQuietA;
  unlockGlobalParams;
}

void GlobalParams::addSecurityHandler(XpdfSecurityHandler *handler) {
#ifdef ENABLE_PLUGINS
  lockGlobalParams;
  securityHandlers->append(handler);
  unlockGlobalParams;
#endif
}

XpdfSecurityHandler *GlobalParams::getSecurityHandler(char *name) {
#ifdef ENABLE_PLUGINS
  XpdfSecurityHandler *hdlr;
  int i;

  lockGlobalParams;
  for (i = 0; i < securityHandlers->getLength(); ++i) {
    hdlr = (XpdfSecurityHandler *)securityHandlers->get(i);
    if (!strcasecmp(hdlr->name, name)) {
      unlockGlobalParams;
      return hdlr;
    }
  }
  unlockGlobalParams;

  if (!loadPlugin("security", name)) {
    return NULL;
  }

  lockGlobalParams;
  for (i = 0; i < securityHandlers->getLength(); ++i) {
    hdlr = (XpdfSecurityHandler *)securityHandlers->get(i);
    if (!strcmp(hdlr->name, name)) {
      unlockGlobalParams;
      return hdlr;
    }
  }
  unlockGlobalParams;
#endif

  return NULL;
}

#ifdef ENABLE_PLUGINS
//------------------------------------------------------------------------
// plugins
//------------------------------------------------------------------------

GBool GlobalParams::loadPlugin(char *type, char *name) {
  Plugin *plugin;

  if (!(plugin = Plugin::load(type, name))) {
    return gFalse;
  }
  lockGlobalParams;
  plugins->append(plugin);
  unlockGlobalParams;
  return gTrue;
}

#endif // ENABLE_PLUGINS
//========================================================================
//
// GlobalParams.cc
//
// Copyright 2001-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <string.h>
#include <stdio.h>
#include <ctype.h>
#ifdef ENABLE_PLUGINS
#  ifndef WIN32
#    include <dlfcn.h>
#  endif
#endif
#ifdef WIN32
#  include <shlobj.h>
#endif
#if HAVE_PAPER_H
#include <paper.h>
#endif
#include "gmem.h"
#include "GString.h"
#include "GList.h"
#include "GHash.h"
#include "gfile.h"
#include "Error.h"
#include "NameToCharCode.h"
#include "CharCodeToUnicode.h"
#include "UnicodeMap.h"
#include "CMap.h"
#include "BuiltinFontTables.h"
#include "FontEncodingTables.h"
#ifdef ENABLE_PLUGINS
#  include "XpdfPluginAPI.h"
#endif
#include "GlobalParams.h"

#ifdef WIN32
#  define strcasecmp stricmp
#endif

#if MULTITHREADED
#  define lockGlobalParams            gLockMutex(&mutex)
#  define lockUnicodeMapCache         gLockMutex(&unicodeMapCacheMutex)
#  define lockCMapCache               gLockMutex(&cMapCacheMutex)
#  define unlockGlobalParams          gUnlockMutex(&mutex)
#  define unlockUnicodeMapCache       gUnlockMutex(&unicodeMapCacheMutex)
#  define unlockCMapCache             gUnlockMutex(&cMapCacheMutex)
#else
#  define lockGlobalParams
#  define lockUnicodeMapCache
#  define lockCMapCache
#  define unlockGlobalParams
#  define unlockUnicodeMapCache
#  define unlockCMapCache
#endif

#include "NameToUnicodeTable.h"
#include "UnicodeMapTables.h"
#include "UTF8.h"

#ifdef ENABLE_PLUGINS
#  ifdef WIN32
extern XpdfPluginVecTable xpdfPluginVecTable;
#  endif
#endif

//------------------------------------------------------------------------

#define cidToUnicodeCacheSize     4
#define unicodeToUnicodeCacheSize 4

//------------------------------------------------------------------------

static struct {
  char *name;
  char *t1FileName;
  char *ttFileName;
} displayFontTab[] = {
  {"Courier",               "n022003l.pfb", "cour.ttf"},
  {"Courier-Bold",          "n022004l.pfb", "courbd.ttf"},
  {"Courier-BoldOblique",   "n022024l.pfb", "courbi.ttf"},
  {"Courier-Oblique",       "n022023l.pfb", "couri.ttf"},
  {"Helvetica",             "n019003l.pfb", "arial.ttf"},
  {"Helvetica-Bold",        "n019004l.pfb", "arialbd.ttf"},
  {"Helvetica-BoldOblique", "n019024l.pfb", "arialbi.ttf"},
  {"Helvetica-Oblique",     "n019023l.pfb", "ariali.ttf"},
  {"Symbol",                "s050000l.pfb", NULL},
  {"Times-Bold",            "n021004l.pfb", "timesbd.ttf"},
  {"Times-BoldItalic",      "n021024l.pfb", "timesbi.ttf"},
  {"Times-Italic",          "n021023l.pfb", "timesi.ttf"},
  {"Times-Roman",           "n021003l.pfb", "times.ttf"},
  {"ZapfDingbats",          "d050000l.pfb", NULL},
  {NULL}
};

#ifdef WIN32
static char *displayFontDirs[] = {
  "c:/windows/fonts",
  "c:/winnt/fonts",
  NULL
};
#else
static char *displayFontDirs[] = {
  "/usr/share/ghostscript/fonts",
  "/usr/local/share/ghostscript/fonts",
  "/usr/share/fonts/default/Type1",
  "/usr/share/fonts/default/ghostscript",
  "/usr/share/fonts/type1/gsfonts",
  NULL
};
#endif

//------------------------------------------------------------------------

GlobalParams *globalParams = NULL;

//------------------------------------------------------------------------
// DisplayFontParam
//------------------------------------------------------------------------

DisplayFontParam::DisplayFontParam(GString *nameA,
				   DisplayFontParamKind kindA) {
  name = nameA;
  kind = kindA;
  switch (kind) {
  case displayFontT1:
    t1.fileName = NULL;
    break;
  case displayFontTT:
    tt.fileName = NULL;
    break;
  }
}

DisplayFontParam::~DisplayFontParam() {
  delete name;
  switch (kind) {
  case displayFontT1:
    if (t1.fileName) {
      delete t1.fileName;
    }
    break;
  case displayFontTT:
    if (tt.fileName) {
      delete tt.fileName;
    }
    break;
  }
}

#ifdef WIN32

//------------------------------------------------------------------------
// WinFontInfo
//------------------------------------------------------------------------

class WinFontInfo: public DisplayFontParam {
public:

  GBool bold, italic;

  static WinFontInfo *make(GString *nameA, GBool boldA, GBool italicA,
			   HKEY regKey, char *winFontDir);
  WinFontInfo(GString *nameA, GBool boldA, GBool italicA,
	      GString *fileNameA);
  virtual ~WinFontInfo();
  GBool equals(WinFontInfo *fi);
};

WinFontInfo *WinFontInfo::make(GString *nameA, GBool boldA, GBool italicA,
			       HKEY regKey, char *winFontDir) {
  GString *regName;
  GString *fileNameA;
  char buf[MAX_PATH];
  DWORD n;
  char c;
  int i;

  //----- find the font file
  fileNameA = NULL;
  regName = nameA->copy();
  if (boldA) {
    regName->append(" Bold");
  }
  if (italicA) {
    regName->append(" Italic");
  }
  regName->append(" (TrueType)");
  n = sizeof(buf);
  if (RegQueryValueEx(regKey, regName->getCString(), NULL, NULL,
		      (LPBYTE)buf, &n) == ERROR_SUCCESS) {
    fileNameA = new GString(winFontDir);
    fileNameA->append('\\')->append(buf);
  }
  delete regName;
  if (!fileNameA) {
    delete nameA;
    return NULL;
  }

  //----- normalize the font name
  i = 0;
  while (i < nameA->getLength()) {
    c = nameA->getChar(i);
    if (c == ' ' || c == ',' || c == '-') {
      nameA->del(i);
    } else {
      ++i;
    }
  }

  return new WinFontInfo(nameA, boldA, italicA, fileNameA);
}

WinFontInfo::WinFontInfo(GString *nameA, GBool boldA, GBool italicA,
			 GString *fileNameA):
  DisplayFontParam(nameA, displayFontTT)
{
  bold = boldA;
  italic = italicA;
  tt.fileName = fileNameA;
}

WinFontInfo::~WinFontInfo() {
}

GBool WinFontInfo::equals(WinFontInfo *fi) {
  return !name->cmp(fi->name) && bold == fi->bold && italic == fi->italic;
}

//------------------------------------------------------------------------
// WinFontList
//------------------------------------------------------------------------

class WinFontList {
public:

  WinFontList(char *winFontDirA);
  ~WinFontList();
  WinFontInfo *find(GString *font);

private:

  void add(WinFontInfo *fi);
  static int CALLBACK enumFunc1(CONST LOGFONT *font,
				CONST TEXTMETRIC *metrics,
				DWORD type, LPARAM data);
  static int CALLBACK enumFunc2(CONST LOGFONT *font,
				CONST TEXTMETRIC *metrics,
				DWORD type, LPARAM data);

  GList *fonts;			// [WinFontInfo]
  HDC dc;			// (only used during enumeration)
  HKEY regKey;			// (only used during enumeration)
  char *winFontDir;		// (only used during enumeration)
};

WinFontList::WinFontList(char *winFontDirA) {
  OSVERSIONINFO version;
  char *path;

  fonts = new GList();
  dc = GetDC(NULL);
  winFontDir = winFontDirA;
  version.dwOSVersionInfoSize = sizeof(version);
  GetVersionEx(&version);
  if (version.dwPlatformId == VER_PLATFORM_WIN32_NT) {
    path = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts\\";
  } else {
    path = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Fonts\\";
  }
  if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, path, 0,
		   KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS,
		   &regKey) == ERROR_SUCCESS) {
    EnumFonts(dc, NULL, &WinFontList::enumFunc1, (LPARAM)this);
    RegCloseKey(regKey);
  }
  ReleaseDC(NULL, dc);
}

WinFontList::~WinFontList() {
  deleteGList(fonts, WinFontInfo);
}

void WinFontList::add(WinFontInfo *fi) {
  int i;

  for (i = 0; i < fonts->getLength(); ++i) {
    if (((WinFontInfo *)fonts->get(i))->equals(fi)) {
      delete fi;
      return;
    }
  }
  fonts->append(fi);
}

WinFontInfo *WinFontList::find(GString *font) {
  GString *name;
  GBool bold, italic;
  WinFontInfo *fi;
  char c;
  int n, i;

  name = font->copy();

  // remove space, comma, dash chars
  i = 0;
  while (i < name->getLength()) {
    c = name->getChar(i);
    if (c == ' ' || c == ',' || c == '-') {
      name->del(i);
    } else {
      ++i;
    }
  }
  n = name->getLength();

  // remove trailing "MT" (Foo-MT, Foo-BoldMT, etc.)
  if (!strcmp(name->getCString() + n - 2, "MT")) {
    name->del(n - 2, 2);
    n -= 2;
  }

  // look for "Italic"
  if (!strcmp(name->getCString() + n - 6, "Italic")) {
    name->del(n - 6, 6);
    italic = gTrue;
    n -= 6;
  } else {
    italic = gFalse;
  }

  // look for "Bold"
  if (!strcmp(name->getCString() + n - 4, "Bold")) {
    name->del(n - 4, 4);
    bold = gTrue;
    n -= 4;
  } else {
    bold = gFalse;
  }

  // remove trailing "MT" (FooMT-Bold, etc.)
  if (!strcmp(name->getCString() + n - 2, "MT")) {
    name->del(n - 2, 2);
    n -= 2;
  }

  // remove trailing "PS"
  if (!strcmp(name->getCString() + n - 2, "PS")) {
    name->del(n - 2, 2);
    n -= 2;
  }

  // search for the font
  fi = NULL;
  for (i = 0; i < fonts->getLength(); ++i) {
    fi = (WinFontInfo *)fonts->get(i);
    if (!fi->name->cmp(name) && fi->bold == bold && fi->italic == italic) {
      break;
    }
    fi = NULL;
  }

  delete name;
  return fi;
}

int CALLBACK WinFontList::enumFunc1(CONST LOGFONT *font,
				    CONST TEXTMETRIC *metrics,
				    DWORD type, LPARAM data) {
  WinFontList *fl = (WinFontList *)data;

  EnumFonts(fl->dc, font->lfFaceName, &WinFontList::enumFunc2, (LPARAM)fl);
  return 1;
}

int CALLBACK WinFontList::enumFunc2(CONST LOGFONT *font,
				    CONST TEXTMETRIC *metrics,
				    DWORD type, LPARAM data) {
  WinFontList *fl = (WinFontList *)data;
  WinFontInfo *fi;

  if (type & TRUETYPE_FONTTYPE) {
    if ((fi = WinFontInfo::make(new GString(font->lfFaceName),
				font->lfWeight >= 600,
				font->lfItalic ? gTrue : gFalse,
				fl->regKey, fl->winFontDir))) {
      fl->add(fi);
    }
  }
  return 1;
}

#endif // WIN32

//------------------------------------------------------------------------
// PSFontParam
//------------------------------------------------------------------------

PSFontParam::PSFontParam(GString *pdfFontNameA, int wModeA,
			 GString *psFontNameA, GString *encodingA) {
  pdfFontName = pdfFontNameA;
  wMode = wModeA;
  psFontName = psFontNameA;
  encoding = encodingA;
}

PSFontParam::~PSFontParam() {
  delete pdfFontName;
  delete psFontName;
  if (encoding) {
    delete encoding;
  }
}

//------------------------------------------------------------------------
// KeyBinding
//------------------------------------------------------------------------

KeyBinding::KeyBinding(int codeA, int modsA, int contextA, char *cmd0) {
  code = codeA;
  mods = modsA;
  context = contextA;
  cmds = new GList();
  cmds->append(new GString(cmd0));
}

KeyBinding::KeyBinding(int codeA, int modsA, int contextA,
		       char *cmd0, char *cmd1) {
  code = codeA;
  mods = modsA;
  context = contextA;
  cmds = new GList();
  cmds->append(new GString(cmd0));
  cmds->append(new GString(cmd1));
}

KeyBinding::KeyBinding(int codeA, int modsA, int contextA, GList *cmdsA) {
  code = codeA;
  mods = modsA;
  context = contextA;
  cmds = cmdsA;
}

KeyBinding::~KeyBinding() {
  deleteGList(cmds, GString);
}

#ifdef ENABLE_PLUGINS
//------------------------------------------------------------------------
// Plugin
//------------------------------------------------------------------------

class Plugin {
public:

  static Plugin *load(char *type, char *name);
  ~Plugin();

private:

#ifdef WIN32
  Plugin(HMODULE libA);
  HMODULE lib;
#else
  Plugin(void *dlA);
  void *dl;
#endif
};

Plugin *Plugin::load(char *type, char *name) {
  GString *path;
  Plugin *plugin;
  XpdfPluginVecTable *vt;
  XpdfBool (*xpdfInitPlugin)(void);
#ifdef WIN32
  HMODULE libA;
#else
  void *dlA;
#endif

  path = globalParams->getBaseDir();
  appendToPath(path, "plugins");
  appendToPath(path, type);
  appendToPath(path, name);

#ifdef WIN32
  path->append(".dll");
  if (!(libA = LoadLibrary(path->getCString()))) {
    error(-1, "Failed to load plugin '%s'",
	  path->getCString());
    goto err1;
  }
  if (!(vt = (XpdfPluginVecTable *)
	         GetProcAddress(libA, "xpdfPluginVecTable"))) {
    error(-1, "Failed to find xpdfPluginVecTable in plugin '%s'",
	  path->getCString());
    goto err2;
  }
#else
  //~ need to deal with other extensions here
  path->append(".so");
  if (!(dlA = dlopen(path->getCString(), RTLD_NOW))) {
    error(-1, "Failed to load plugin '%s': %s",
	  path->getCString(), dlerror());
    goto err1;
  }
  if (!(vt = (XpdfPluginVecTable *)dlsym(dlA, "xpdfPluginVecTable"))) {
    error(-1, "Failed to find xpdfPluginVecTable in plugin '%s'",
	  path->getCString());
    goto err2;
  }
#endif

  if (vt->version != xpdfPluginVecTable.version) {
    error(-1, "Plugin '%s' is wrong version", path->getCString());
    goto err2;
  }
  memcpy(vt, &xpdfPluginVecTable, sizeof(xpdfPluginVecTable));

#ifdef WIN32
  if (!(xpdfInitPlugin = (XpdfBool (*)(void))
	                     GetProcAddress(libA, "xpdfInitPlugin"))) {
    error(-1, "Failed to find xpdfInitPlugin in plugin '%s'",
	  path->getCString());
    goto err2;
  }
#else
  if (!(xpdfInitPlugin = (XpdfBool (*)(void))dlsym(dlA, "xpdfInitPlugin"))) {
    error(-1, "Failed to find xpdfInitPlugin in plugin '%s'",
	  path->getCString());
    goto err2;
  }
#endif

  if (!(*xpdfInitPlugin)()) {
    error(-1, "Initialization of plugin '%s' failed",
	  path->getCString());
    goto err2;
  }

#ifdef WIN32
  plugin = new Plugin(libA);
#else
  plugin = new Plugin(dlA);
#endif

  delete path;
  return plugin;

 err2:
#ifdef WIN32
  FreeLibrary(libA);
#else
  dlclose(dlA);
#endif
 err1:
  delete path;
  return NULL;
}

#ifdef WIN32
Plugin::Plugin(HMODULE libA) {
  lib = libA;
}
#else
Plugin::Plugin(void *dlA) {
  dl = dlA;
}
#endif

Plugin::~Plugin() {
  void (*xpdfFreePlugin)(void);

#ifdef WIN32
  if ((xpdfFreePlugin = (void (*)(void))
                            GetProcAddress(lib, "xpdfFreePlugin"))) {
    (*xpdfFreePlugin)();
  }
  FreeLibrary(lib);
#else
  if ((xpdfFreePlugin = (void (*)(void))dlsym(dl, "xpdfFreePlugin"))) {
    (*xpdfFreePlugin)();
  }
  dlclose(dl);
#endif
}

#endif // ENABLE_PLUGINS

//------------------------------------------------------------------------
// parsing
//------------------------------------------------------------------------

GlobalParams::GlobalParams(char *cfgFileName) {
  UnicodeMap *map;
  GString *fileName;
  FILE *f;
  int i;

#if MULTITHREADED
  gInitMutex(&mutex);
  gInitMutex(&unicodeMapCacheMutex);
  gInitMutex(&cMapCacheMutex);
#endif

  initBuiltinFontTables();

  // scan the encoding in reverse because we want the lowest-numbered
  // index for each char name ('space' is encoded twice)
  macRomanReverseMap = new NameToCharCode();
  for (i = 255; i >= 0; --i) {
    if (macRomanEncoding[i]) {
      macRomanReverseMap->add(macRomanEncoding[i], (CharCode)i);
    }
  }

#ifdef WIN32
  // baseDir will be set by a call to setBaseDir
  baseDir = new GString();
#else
  baseDir = appendToPath(getHomeDir(), ".xpdf");
#endif
  nameToUnicode = new NameToCharCode();
  cidToUnicodes = new GHash(gTrue);
  unicodeToUnicodes = new GHash(gTrue);
  residentUnicodeMaps = new GHash();
  unicodeMaps = new GHash(gTrue);
  cMapDirs = new GHash(gTrue);
  toUnicodeDirs = new GList();
  displayFonts = new GHash();
  displayCIDFonts = new GHash();
  displayNamedCIDFonts = new GHash();
#if HAVE_PAPER_H
  char *paperName;
  const struct paper *paperType;
  paperinit();
  if ((paperName = systempapername())) {
    paperType = paperinfo(paperName);
    psPaperWidth = (int)paperpswidth(paperType);
    psPaperHeight = (int)paperpsheight(paperType);
  } else {
    error(-1, "No paper information available - using defaults");
    psPaperWidth = defPaperWidth;
    psPaperHeight = defPaperHeight;
  }
  paperdone();
#else
  psPaperWidth = defPaperWidth;
  psPaperHeight = defPaperHeight;
#endif
  psImageableLLX = psImageableLLY = 0;
  psImageableURX = psPaperWidth;
  psImageableURY = psPaperHeight;
  psCrop = gTrue;
  psExpandSmaller = gFalse;
  psShrinkLarger = gTrue;
  psCenter = gTrue;
  psDuplex = gFalse;
  psLevel = psLevel2;
  psFile = NULL;
  psFonts = new GHash();
  psNamedFonts16 = new GList();
  psFonts16 = new GList();
  psEmbedType1 = gTrue;
  psEmbedTrueType = gTrue;
  psEmbedCIDPostScript = gTrue;
  psEmbedCIDTrueType = gTrue;
  psPreload = gFalse;
  psOPI = gFalse;
  psASCIIHex = gFalse;
  textEncoding = new GString("Latin1");
#if defined(WIN32)
  textEOL = eolDOS;
#elif defined(MACOS)
  textEOL = eolMac;
#else
  textEOL = eolUnix;
#endif
  textPageBreaks = gTrue;
  textKeepTinyChars = gFalse;
  fontDirs = new GList();
  initialZoom = new GString("125");
  continuousView = gFalse;
  enableT1lib = gTrue;
  enableFreeType = gTrue;
  antialias = gTrue;
  vectorAntialias = gTrue;
  strokeAdjust = gTrue;
  screenType = screenUnset;
  screenSize = -1;
  screenDotRadius = -1;
  screenGamma = 1.0;
  screenBlackThreshold = 0.0;
  screenWhiteThreshold = 1.0;
  urlCommand = NULL;
  movieCommand = NULL;
  mapNumericCharNames = gTrue;
  mapUnknownCharNames = gFalse;
  createDefaultKeyBindings();
  printCommands = gFalse;
  errQuiet = gFalse;

  cidToUnicodeCache = new CharCodeToUnicodeCache(cidToUnicodeCacheSize);
  unicodeToUnicodeCache =
      new CharCodeToUnicodeCache(unicodeToUnicodeCacheSize);
  unicodeMapCache = new UnicodeMapCache();
  cMapCache = new CMapCache();

#ifdef WIN32
  winFontList = NULL;
#endif

#ifdef ENABLE_PLUGINS
  plugins = new GList();
  securityHandlers = new GList();
#endif

  // set up the initial nameToUnicode table
  for (i = 0; nameToUnicodeTab[i].name; ++i) {
    nameToUnicode->add(nameToUnicodeTab[i].name, nameToUnicodeTab[i].u);
  }

  // set up the residentUnicodeMaps table
  map = new UnicodeMap("Latin1", gFalse,
		       latin1UnicodeMapRanges, latin1UnicodeMapLen);
  residentUnicodeMaps->add(map->getEncodingName(), map);
  map = new UnicodeMap("ASCII7", gFalse,
		       ascii7UnicodeMapRanges, ascii7UnicodeMapLen);
  residentUnicodeMaps->add(map->getEncodingName(), map);
  map = new UnicodeMap("Symbol", gFalse,
		       symbolUnicodeMapRanges, symbolUnicodeMapLen);
  residentUnicodeMaps->add(map->getEncodingName(), map);
  map = new UnicodeMap("ZapfDingbats", gFalse, zapfDingbatsUnicodeMapRanges,
		       zapfDingbatsUnicodeMapLen);
  residentUnicodeMaps->add(map->getEncodingName(), map);
  map = new UnicodeMap("UTF-8", gTrue, &mapUTF8);
  residentUnicodeMaps->add(map->getEncodingName(), map);
  map = new UnicodeMap("UCS-2", gTrue, &mapUCS2);
  residentUnicodeMaps->add(map->getEncodingName(), map);

  // look for a user config file, then a system-wide config file
  f = NULL;
  fileName = NULL;
  if (cfgFileName && cfgFileName[0]) {
    fileName = new GString(cfgFileName);
    if (!(f = fopen(fileName->getCString(), "r"))) {
      delete fileName;
    }
  }
  if (!f) {
    fileName = appendToPath(getHomeDir(), xpdfUserConfigFile);
    if (!(f = fopen(fileName->getCString(), "r"))) {
      delete fileName;
    }
  }
  if (!f) {
#if defined(WIN32) && !defined(__CYGWIN32__)
    char buf[512];
    i = GetModuleFileName(NULL, buf, sizeof(buf));
    if (i <= 0 || i >= sizeof(buf)) {
      // error or path too long for buffer - just use the current dir
      buf[0] = '\0';
    }
    fileName = grabPath(buf);
    appendToPath(fileName, xpdfSysConfigFile);
#else
    fileName = new GString(xpdfSysConfigFile);
#endif
    if (!(f = fopen(fileName->getCString(), "r"))) {
      delete fileName;
    }
  }
  if (f) {
    parseFile(fileName, f);
    delete fileName;
    fclose(f);
  }
}

void GlobalParams::createDefaultKeyBindings() {
  keyBindings = new GList();

  //----- mouse buttons
  keyBindings->append(new KeyBinding(xpdfKeyCodeMousePress1, xpdfKeyModNone,
				     xpdfKeyContextAny, "startSelection"));
  keyBindings->append(new KeyBinding(xpdfKeyCodeMouseRelease1, xpdfKeyModNone,
				     xpdfKeyContextAny, "endSelection",
				     "followLinkNoSel"));
  keyBindings->append(new KeyBinding(xpdfKeyCodeMousePress2, xpdfKeyModNone,
				     xpdfKeyContextAny, "startPan"));
  keyBindings->append(new KeyBinding(xpdfKeyCodeMouseRelease2, xpdfKeyModNone,
				     xpdfKeyContextAny, "endPan"));
  keyBindings->append(new KeyBinding(xpdfKeyCodeMousePress3, xpdfKeyModNone,
				     xpdfKeyContextAny, "postPopupMenu"));
  keyBindings->append(new KeyBinding(xpdfKeyCodeMousePress4, xpdfKeyModNone,
				     xpdfKeyContextAny,
				     "scrollUpPrevPage(16)"));
  keyBindings->append(new KeyBinding(xpdfKeyCodeMousePress5, xpdfKeyModNone,
				     xpdfKeyContextAny,
				     "scrollDownNextPage(16)"));
  keyBindings->append(new KeyBinding(xpdfKeyCodeMousePress6, xpdfKeyModNone,
				     xpdfKeyContextAny, "scrollLeft(16)"));
  keyBindings->append(new KeyBinding(xpdfKeyCodeMousePress7, xpdfKeyModNone,
				     xpdfKeyContextAny, "scrollRight(16)"));

  //----- keys
  keyBindings->append(new KeyBinding(xpdfKeyCodeHome, xpdfKeyModCtrl,
				     xpdfKeyContextAny, "gotoPage(1)"));
  keyBindings->append(new KeyBinding(xpdfKeyCodeHome, xpdfKeyModNone,
				     xpdfKeyContextAny, "scrollToTopLeft"));
  keyBindings->append(new KeyBinding(xpdfKeyCodeEnd, xpdfKeyModCtrl,
				     xpdfKeyContextAny, "gotoLastPage"));
  keyBindings->append(new KeyBinding(xpdfKeyCodeEnd, xpdfKeyModNone,
				     xpdfKeyContextAny,
				     "scrollToBottomRight"));
  keyBindings->append(new KeyBinding(xpdfKeyCodePgUp, xpdfKeyModNone,
				     xpdfKeyContextAny, "pageUp"));
  keyBindings->append(new KeyBinding(xpdfKeyCodeBackspace, xpdfKeyModNone,
				     xpdfKeyContextAny, "pageUp"));
  keyBindings->append(new KeyBinding(xpdfKeyCodeDelete, xpdfKeyModNone,
				     xpdfKeyContextAny, "pageUp"));
  keyBindings->append(new KeyBinding(xpdfKeyCodePgDn, xpdfKeyModNone,
				     xpdfKeyContextAny, "pageDown"));
  keyBindings->append(new KeyBinding(' ', xpdfKeyModNone,
				     xpdfKeyContextAny, "pageDown"));
  keyBindings->append(new KeyBinding(xpdfKeyCodeLeft, xpdfKeyModNone,
				     xpdfKeyContextAny, "scrollLeft(16)"));
  keyBindings->append(new KeyBinding(xpdfKeyCodeRight, xpdfKeyModNone,
				     xpdfKeyContextAny, "scrollRight(16)"));
  keyBindings->append(new KeyBinding(xpdfKeyCodeUp, xpdfKeyModNone,
				     xpdfKeyContextAny, "scrollUp(16)"));
  keyBindings->append(new KeyBinding(xpdfKeyCodeDown, xpdfKeyModNone,
				     xpdfKeyContextAny, "scrollDown(16)"));
  keyBindings->append(new KeyBinding('o', xpdfKeyModNone,
				     xpdfKeyContextAny, "open"));
  keyBindings->append(new KeyBinding('O', xpdfKeyModNone,
				     xpdfKeyContextAny, "open"));
  keyBindings->append(new KeyBinding('r', xpdfKeyModNone,
				     xpdfKeyContextAny, "reload"));
  keyBindings->append(new KeyBinding('R', xpdfKeyModNone,
				     xpdfKeyContextAny, "reload"));
  keyBindings->append(new KeyBinding('f', xpdfKeyModNone,
				     xpdfKeyContextAny, "find"));
  keyBindings->append(new KeyBinding('F', xpdfKeyModNone,
				     xpdfKeyContextAny, "find"));
  keyBindings->append(new KeyBinding('f', xpdfKeyModCtrl,
				     xpdfKeyContextAny, "find"));
  keyBindings->append(new KeyBinding('g', xpdfKeyModCtrl,
				     xpdfKeyContextAny, "findNext"));
  keyBindings->append(new KeyBinding('p', xpdfKeyModCtrl,
				     xpdfKeyContextAny, "print"));
  keyBindings->append(new KeyBinding('n', xpdfKeyModNone,
				     xpdfKeyContextScrLockOff, "nextPage"));
  keyBindings->append(new KeyBinding('N', xpdfKeyModNone,
				     xpdfKeyContextScrLockOff, "nextPage"));
  keyBindings->append(new KeyBinding('n', xpdfKeyModNone,
				     xpdfKeyContextScrLockOn,
				     "nextPageNoScroll"));
  keyBindings->append(new KeyBinding('N', xpdfKeyModNone,
				     xpdfKeyContextScrLockOn,
				     "nextPageNoScroll"));
  keyBindings->append(new KeyBinding('p', xpdfKeyModNone,
				     xpdfKeyContextScrLockOff, "prevPage"));
  keyBindings->append(new KeyBinding('P', xpdfKeyModNone,
				     xpdfKeyContextScrLockOff, "prevPage"));
  keyBindings->append(new KeyBinding('p', xpdfKeyModNone,
				     xpdfKeyContextScrLockOn,
				     "prevPageNoScroll"));
  keyBindings->append(new KeyBinding('P', xpdfKeyModNone,
				     xpdfKeyContextScrLockOn,
				     "prevPageNoScroll"));
  keyBindings->append(new KeyBinding('v', xpdfKeyModNone,
				     xpdfKeyContextAny, "goForward"));
  keyBindings->append(new KeyBinding('b', xpdfKeyModNone,
				     xpdfKeyContextAny, "goBackward"));
  keyBindings->append(new KeyBinding('g', xpdfKeyModNone,
				     xpdfKeyContextAny, "focusToPageNum"));
  keyBindings->append(new KeyBinding('0', xpdfKeyModNone,
				     xpdfKeyContextAny, "zoomPercent(125)"));
  keyBindings->append(new KeyBinding('+', xpdfKeyModNone,
				     xpdfKeyContextAny, "zoomIn"));
  keyBindings->append(new KeyBinding('-', xpdfKeyModNone,
				     xpdfKeyContextAny, "zoomOut"));
  keyBindings->append(new KeyBinding('z', xpdfKeyModNone,
				     xpdfKeyContextAny, "zoomFitPage"));
  keyBindings->append(new KeyBinding('w', xpdfKeyModNone,
				     xpdfKeyContextAny, "zoomFitWidth"));
  keyBindings->append(new KeyBinding('f', xpdfKeyModAlt,
				     xpdfKeyContextAny,
				     "toggleFullScreenMode"));
  keyBindings->append(new KeyBinding('l', xpdfKeyModCtrl,
				     xpdfKeyContextAny, "redraw"));
  keyBindings->append(new KeyBinding('w', xpdfKeyModCtrl,
				     xpdfKeyContextAny, "closeWindow"));
  keyBindings->append(new KeyBinding('?', xpdfKeyModNone,
				     xpdfKeyContextAny, "about"));
  keyBindings->append(new KeyBinding('q', xpdfKeyModNone,
				     xpdfKeyContextAny, "quit"));
  keyBindings->append(new KeyBinding('Q', xpdfKeyModNone,
				     xpdfKeyContextAny, "quit"));
}

void GlobalParams::parseFile(GString *fileName, FILE *f) {
  int line;
  char buf[512];

  line = 1;
  while (getLine(buf, sizeof(buf) - 1, f)) {
    parseLine(buf, fileName, line);
    ++line;
  }
}

void GlobalParams::parseLine(char *buf, GString *fileName, int line) {
  GList *tokens;
  GString *cmd, *incFile;
  char *p1, *p2;
  FILE *f2;

  // break the line into tokens
  tokens = new GList();
  p1 = buf;
  while (*p1) {
    for (; *p1 && isspace(*p1); ++p1) ;
    if (!*p1) {
      break;
    }
    if (*p1 == '"' || *p1 == '\'') {
      for (p2 = p1 + 1; *p2 && *p2 != *p1; ++p2) ;
      ++p1;
    } else {
      for (p2 = p1 + 1; *p2 && !isspace(*p2); ++p2) ;
    }
    tokens->append(new GString(p1, p2 - p1));
    p1 = *p2 ? p2 + 1 : p2;
  }

  // parse the line
  if (tokens->getLength() > 0 &&
      ((GString *)tokens->get(0))->getChar(0) != '#') {
    cmd = (GString *)tokens->get(0);
    if (!cmd->cmp("include")) {
      if (tokens->getLength() == 2) {
	incFile = (GString *)tokens->get(1);
	if ((f2 = fopen(incFile->getCString(), "r"))) {
	  parseFile(incFile, f2);
	  fclose(f2);
	} else {
	  error(-1, "Couldn't find included config file: '%s' (%s:%d)",
		incFile->getCString(), fileName->getCString(), line);
	}
      } else {
	error(-1, "Bad 'include' config file command (%s:%d)",
	      fileName->getCString(), line);
      }
    } else if (!cmd->cmp("nameToUnicode")) {
      parseNameToUnicode(tokens, fileName, line);
    } else if (!cmd->cmp("cidToUnicode")) {
      parseCIDToUnicode(tokens, fileName, line);
    } else if (!cmd->cmp("unicodeToUnicode")) {
      parseUnicodeToUnicode(tokens, fileName, line);
    } else if (!cmd->cmp("unicodeMap")) {
      parseUnicodeMap(tokens, fileName, line);
    } else if (!cmd->cmp("cMapDir")) {
      parseCMapDir(tokens, fileName, line);
    } else if (!cmd->cmp("toUnicodeDir")) {
      parseToUnicodeDir(tokens, fileName, line);
    } else if (!cmd->cmp("displayFontT1")) {
      parseDisplayFont(tokens, displayFonts, displayFontT1, fileName, line);
    } else if (!cmd->cmp("displayFontTT")) {
      parseDisplayFont(tokens, displayFonts, displayFontTT, fileName, line);
    } else if (!cmd->cmp("displayNamedCIDFontT1")) {
      parseDisplayFont(tokens, displayNamedCIDFonts,
		       displayFontT1, fileName, line);
    } else if (!cmd->cmp("displayCIDFontT1")) {
      parseDisplayFont(tokens, displayCIDFonts,
		       displayFontT1, fileName, line);
    } else if (!cmd->cmp("displayNamedCIDFontTT")) {
      parseDisplayFont(tokens, displayNamedCIDFonts,
		       displayFontTT, fileName, line);
    } else if (!cmd->cmp("displayCIDFontTT")) {
      parseDisplayFont(tokens, displayCIDFonts,
		       displayFontTT, fileName, line);
    } else if (!cmd->cmp("psFile")) {
      parsePSFile(tokens, fileName, line);
    } else if (!cmd->cmp("psFont")) {
      parsePSFont(tokens, fileName, line);
    } else if (!cmd->cmp("psNamedFont16")) {
      parsePSFont16("psNamedFont16", psNamedFonts16,
		    tokens, fileName, line);
    } else if (!cmd->cmp("psFont16")) {
      parsePSFont16("psFont16", psFonts16, tokens, fileName, line);
    } else if (!cmd->cmp("psPaperSize")) {
      parsePSPaperSize(tokens, fileName, line);
    } else if (!cmd->cmp("psImageableArea")) {
      parsePSImageableArea(tokens, fileName, line);
    } else if (!cmd->cmp("psCrop")) {
      parseYesNo("psCrop", &psCrop, tokens, fileName, line);
    } else if (!cmd->cmp("psExpandSmaller")) {
      parseYesNo("psExpandSmaller", &psExpandSmaller,
		 tokens, fileName, line);
    } else if (!cmd->cmp("psShrinkLarger")) {
      parseYesNo("psShrinkLarger", &psShrinkLarger, tokens, fileName, line);
    } else if (!cmd->cmp("psCenter")) {
      parseYesNo("psCenter", &psCenter, tokens, fileName, line);
    } else if (!cmd->cmp("psDuplex")) {
      parseYesNo("psDuplex", &psDuplex, tokens, fileName, line);
    } else if (!cmd->cmp("psLevel")) {
      parsePSLevel(tokens, fileName, line);
    } else if (!cmd->cmp("psEmbedType1Fonts")) {
      parseYesNo("psEmbedType1", &psEmbedType1, tokens, fileName, line);
    } else if (!cmd->cmp("psEmbedTrueTypeFonts")) {
      parseYesNo("psEmbedTrueType", &psEmbedTrueType,
		 tokens, fileName, line);
    } else if (!cmd->cmp("psEmbedCIDPostScriptFonts")) {
      parseYesNo("psEmbedCIDPostScript", &psEmbedCIDPostScript,
		 tokens, fileName, line);
    } else if (!cmd->cmp("psEmbedCIDTrueTypeFonts")) {
      parseYesNo("psEmbedCIDTrueType", &psEmbedCIDTrueType,
		 tokens, fileName, line);
    } else if (!cmd->cmp("psPreload")) {
      parseYesNo("psPreload", &psPreload, tokens, fileName, line);
    } else if (!cmd->cmp("psOPI")) {
      parseYesNo("psOPI", &psOPI, tokens, fileName, line);
    } else if (!cmd->cmp("psASCIIHex")) {
      parseYesNo("psASCIIHex", &psASCIIHex, tokens, fileName, line);
    } else if (!cmd->cmp("textEncoding")) {
      parseTextEncoding(tokens, fileName, line);
    } else if (!cmd->cmp("textEOL")) {
      parseTextEOL(tokens, fileName, line);
    } else if (!cmd->cmp("textPageBreaks")) {
      parseYesNo("textPageBreaks", &textPageBreaks,
		 tokens, fileName, line);
    } else if (!cmd->cmp("textKeepTinyChars")) {
      parseYesNo("textKeepTinyChars", &textKeepTinyChars,
		 tokens, fileName, line);
    } else if (!cmd->cmp("fontDir")) {
      parseFontDir(tokens, fileName, line);
    } else if (!cmd->cmp("initialZoom")) {
      parseInitialZoom(tokens, fileName, line);
    } else if (!cmd->cmp("continuousView")) {
      parseYesNo("continuousView", &continuousView, tokens, fileName, line);
    } else if (!cmd->cmp("enableT1lib")) {
      parseYesNo("enableT1lib", &enableT1lib, tokens, fileName, line);
    } else if (!cmd->cmp("enableFreeType")) {
      parseYesNo("enableFreeType", &enableFreeType, tokens, fileName, line);
    } else if (!cmd->cmp("antialias")) {
      parseYesNo("antialias", &antialias, tokens, fileName, line);
    } else if (!cmd->cmp("vectorAntialias")) {
      parseYesNo("vectorAntialias", &vectorAntialias,
		 tokens, fileName, line);
    } else if (!cmd->cmp("strokeAdjust")) {
      parseYesNo("strokeAdjust", &strokeAdjust, tokens, fileName, line);
    } else if (!cmd->cmp("screenType")) {
      parseScreenType(tokens, fileName, line);
    } else if (!cmd->cmp("screenSize")) {
      parseInteger("screenSize", &screenSize, tokens, fileName, line);
    } else if (!cmd->cmp("screenDotRadius")) {
      parseInteger("screenDotRadius", &screenDotRadius,
		   tokens, fileName, line);
    } else if (!cmd->cmp("screenGamma")) {
      parseFloat("screenGamma", &screenGamma,
		 tokens, fileName, line);
    } else if (!cmd->cmp("screenBlackThreshold")) {
      parseFloat("screenBlackThreshold", &screenBlackThreshold,
		 tokens, fileName, line);
    } else if (!cmd->cmp("screenWhiteThreshold")) {
      parseFloat("screenWhiteThreshold", &screenWhiteThreshold,
		 tokens, fileName, line);
    } else if (!cmd->cmp("urlCommand")) {
      parseCommand("urlCommand", &urlCommand, tokens, fileName, line);
    } else if (!cmd->cmp("movieCommand")) {
      parseCommand("movieCommand", &movieCommand, tokens, fileName, line);
    } else if (!cmd->cmp("mapNumericCharNames")) {
      parseYesNo("mapNumericCharNames", &mapNumericCharNames,
		 tokens, fileName, line);
    } else if (!cmd->cmp("mapUnknownCharNames")) {
      parseYesNo("mapUnknownCharNames", &mapUnknownCharNames,
		 tokens, fileName, line);
    } else if (!cmd->cmp("bind")) {
      parseBind(tokens, fileName, line);
    } else if (!cmd->cmp("unbind")) {
      parseUnbind(tokens, fileName, line);
    } else if (!cmd->cmp("printCommands")) {
      parseYesNo("printCommands", &printCommands, tokens, fileName, line);
    } else if (!cmd->cmp("errQuiet")) {
      parseYesNo("errQuiet", &errQuiet, tokens, fileName, line);
    } else {
      error(-1, "Unknown config file command '%s' (%s:%d)",
	    cmd->getCString(), fileName->getCString(), line);
      if (!cmd->cmp("displayFontX") ||
	  !cmd->cmp("displayNamedCIDFontX") ||
	  !cmd->cmp("displayCIDFontX")) {
	error(-1, "-- Xpdf no longer supports X fonts");
      } else if (!cmd->cmp("t1libControl") || !cmd->cmp("freetypeControl")) {
	error(-1, "-- The t1libControl and freetypeControl options have been replaced");
	error(-1, "   by the enableT1lib, enableFreeType, and antialias options");
      } else if (!cmd->cmp("fontpath") || !cmd->cmp("fontmap")) {
	error(-1, "-- the config file format has changed since Xpdf 0.9x");
      }
    }
  }

  deleteGList(tokens, GString);
}

void GlobalParams::parseNameToUnicode(GList *tokens, GString *fileName,
					 int line) {
  GString *name;
  char *tok1, *tok2;
  FILE *f;
  char buf[256];
  int line2;
  Unicode u;

  if (tokens->getLength() != 2) {
    error(-1, "Bad 'nameToUnicode' config file command (%s:%d)",
	  fileName->getCString(), line);
    return;
  }
  name = (GString *)tokens->get(1);
  if (!(f = fopen(name->getCString(), "r"))) {
    error(-1, "Couldn't open 'nameToUnicode' file '%s'",
	  name->getCString());
    return;
  }
  line2 = 1;
  while (getLine(buf, sizeof(buf), f)) {
    tok1 = strtok(buf, " \t\r\n");
    tok2 = strtok(NULL, " \t\r\n");
    if (tok1 && tok2) {
      sscanf(tok1, "%x", &u);
      nameToUnicode->add(tok2, u);
    } else {
      error(-1, "Bad line in 'nameToUnicode' file (%s:%d)", name, line2);
    }
    ++line2;
  }
  fclose(f);
}

void GlobalParams::parseCIDToUnicode(GList *tokens, GString *fileName,
				     int line) {
  GString *collection, *name, *old;

  if (tokens->getLength() != 3) {
    error(-1, "Bad 'cidToUnicode' config file command (%s:%d)",
	  fileName->getCString(), line);
    return;
  }
  collection = (GString *)tokens->get(1);
  name = (GString *)tokens->get(2);
  if ((old = (GString *)cidToUnicodes->remove(collection))) {
    delete old;
  }
  cidToUnicodes->add(collection->copy(), name->copy());
}

void GlobalParams::parseUnicodeToUnicode(GList *tokens, GString *fileName,
					 int line) {
  GString *font, *file, *old;

  if (tokens->getLength() != 3) {
    error(-1, "Bad 'unicodeToUnicode' config file command (%s:%d)",
	  fileName->getCString(), line);
    return;
  }
  font = (GString *)tokens->get(1);
  file = (GString *)tokens->get(2);
  if ((old = (GString *)unicodeToUnicodes->remove(font))) {
    delete old;
  }
  unicodeToUnicodes->add(font->copy(), file->copy());
}

void GlobalParams::parseUnicodeMap(GList *tokens, GString *fileName,
				   int line) {
  GString *encodingName, *name, *old;

  if (tokens->getLength() != 3) {
    error(-1, "Bad 'unicodeMap' config file command (%s:%d)",
	  fileName->getCString(), line);
    return;
  }
  encodingName = (GString *)tokens->get(1);
  name = (GString *)tokens->get(2);
  if ((old = (GString *)unicodeMaps->remove(encodingName))) {
    delete old;
  }
  unicodeMaps->add(encodingName->copy(), name->copy());
}

void GlobalParams::parseCMapDir(GList *tokens, GString *fileName, int line) {
  GString *collection, *dir;
  GList *list;

  if (tokens->getLength() != 3) {
    error(-1, "Bad 'cMapDir' config file command (%s:%d)",
	  fileName->getCString(), line);
    return;
  }
  collection = (GString *)tokens->get(1);
  dir = (GString *)tokens->get(2);
  if (!(list = (GList *)cMapDirs->lookup(collection))) {
    list = new GList();
    cMapDirs->add(collection->copy(), list);
  }
  list->append(dir->copy());
}

void GlobalParams::parseToUnicodeDir(GList *tokens, GString *fileName,
				     int line) {
  if (tokens->getLength() != 2) {
    error(-1, "Bad 'toUnicodeDir' config file command (%s:%d)",
	  fileName->getCString(), line);
    return;
  }
  toUnicodeDirs->append(((GString *)tokens->get(1))->copy());
}

void GlobalParams::parseDisplayFont(GList *tokens, GHash *fontHash,
				    DisplayFontParamKind kind,
				    GString *fileName, int line) {
  DisplayFontParam *param, *old;

  if (tokens->getLength() < 2) {
    goto err1;
  }
  param = new DisplayFontParam(((GString *)tokens->get(1))->copy(), kind);
  
  switch (kind) {
  case displayFontT1:
    if (tokens->getLength() != 3) {
      goto err2;
    }
    param->t1.fileName = ((GString *)tokens->get(2))->copy();
    break;
  case displayFontTT:
    if (tokens->getLength() != 3) {
      goto err2;
    }
    param->tt.fileName = ((GString *)tokens->get(2))->copy();
    break;
  }

  if ((old = (DisplayFontParam *)fontHash->remove(param->name))) {
    delete old;
  }
  fontHash->add(param->name, param);
  return;

 err2:
  delete param;
 err1:
  error(-1, "Bad 'display*Font*' config file command (%s:%d)",
	fileName->getCString(), line);
}

void GlobalParams::parsePSPaperSize(GList *tokens, GString *fileName,
				    int line) {
  GString *tok;

  if (tokens->getLength() == 2) {
    tok = (GString *)tokens->get(1);
    if (!setPSPaperSize(tok->getCString())) {
      error(-1, "Bad 'psPaperSize' config file command (%s:%d)",
	    fileName->getCString(), line);
    }
  } else if (tokens->getLength() == 3) {
    tok = (GString *)tokens->get(1);
    psPaperWidth = atoi(tok->getCString());
    tok = (GString *)tokens->get(2);
    psPaperHeight = atoi(tok->getCString());
    psImageableLLX = psImageableLLY = 0;
    psImageableURX = psPaperWidth;
    psImageableURY = psPaperHeight;
  } else {
    error(-1, "Bad 'psPaperSize' config file command (%s:%d)",
	  fileName->getCString(), line);
  }
}

void GlobalParams::parsePSImageableArea(GList *tokens, GString *fileName,
					int line) {
  if (tokens->getLength() != 5) {
    error(-1, "Bad 'psImageableArea' config file command (%s:%d)",
	  fileName->getCString(), line);
    return;
  }
  psImageableLLX = atoi(((GString *)tokens->get(1))->getCString());
  psImageableLLY = atoi(((GString *)tokens->get(2))->getCString());
  psImageableURX = atoi(((GString *)tokens->get(3))->getCString());
  psImageableURY = atoi(((GString *)tokens->get(4))->getCString());
}

void GlobalParams::parsePSLevel(GList *tokens, GString *fileName, int line) {
  GString *tok;

  if (tokens->getLength() != 2) {
    error(-1, "Bad 'psLevel' config file command (%s:%d)",
	  fileName->getCString(), line);
    return;
  }
  tok = (GString *)tokens->get(1);
  if (!tok->cmp("level1")) {
    psLevel = psLevel1;
  } else if (!tok->cmp("level1sep")) {
    psLevel = psLevel1Sep;
  } else if (!tok->cmp("level2")) {
    psLevel = psLevel2;
  } else if (!tok->cmp("level2sep")) {
    psLevel = psLevel2Sep;
  } else if (!tok->cmp("level3")) {
    psLevel = psLevel3;
  } else if (!tok->cmp("level3Sep")) {
    psLevel = psLevel3Sep;
  } else {
    error(-1, "Bad 'psLevel' config file command (%s:%d)",
	  fileName->getCString(), line);
  }
}

void GlobalParams::parsePSFile(GList *tokens, GString *fileName, int line) {
  if (tokens->getLength() != 2) {
    error(-1, "Bad 'psFile' config file command (%s:%d)",
	  fileName->getCString(), line);
    return;
  }
  if (psFile) {
    delete psFile;
  }
  psFile = ((GString *)tokens->get(1))->copy();
}

void GlobalParams::parsePSFont(GList *tokens, GString *fileName, int line) {
  PSFontParam *param;

  if (tokens->getLength() != 3) {
    error(-1, "Bad 'psFont' config file command (%s:%d)",
	  fileName->getCString(), line);
    return;
  }
  param = new PSFontParam(((GString *)tokens->get(1))->copy(), 0,
			  ((GString *)tokens->get(2))->copy(), NULL);
  psFonts->add(param->pdfFontName, param);
}

void GlobalParams::parsePSFont16(char *cmdName, GList *fontList,
				 GList *tokens, GString *fileName, int line) {
  PSFontParam *param;
  int wMode;
  GString *tok;

  if (tokens->getLength() != 5) {
    error(-1, "Bad '%s' config file command (%s:%d)",
	  cmdName, fileName->getCString(), line);
    return;
  }
  tok = (GString *)tokens->get(2);
  if (!tok->cmp("H")) {
    wMode = 0;
  } else if (!tok->cmp("V")) {
    wMode = 1;
  } else {
    error(-1, "Bad '%s' config file command (%s:%d)",
	  cmdName, fileName->getCString(), line);
    return;
  }
  param = new PSFontParam(((GString *)tokens->get(1))->copy(),
			  wMode,
			  ((GString *)tokens->get(3))->copy(),
			  ((GString *)tokens->get(4))->copy());
  fontList->append(param);
}

void GlobalParams::parseTextEncoding(GList *tokens, GString *fileName,
				     int line) {
  if (tokens->getLength() != 2) {
    error(-1, "Bad 'textEncoding' config file command (%s:%d)",
	  fileName->getCString(), line);
    return;
  }
  delete textEncoding;
  textEncoding = ((GString *)tokens->get(1))->copy();
}

void GlobalParams::parseTextEOL(GList *tokens, GString *fileName, int line) {
  GString *tok;

  if (tokens->getLength() != 2) {
    error(-1, "Bad 'textEOL' config file command (%s:%d)",
	  fileName->getCString(), line);
    return;
  }
  tok = (GString *)tokens->get(1);
  if (!tok->cmp("unix")) {
    textEOL = eolUnix;
  } else if (!tok->cmp("dos")) {
    textEOL = eolDOS;
  } else if (!tok->cmp("mac")) {
    textEOL = eolMac;
  } else {
    error(-1, "Bad 'textEOL' config file command (%s:%d)",
	  fileName->getCString(), line);
  }
}

void GlobalParams::parseFontDir(GList *tokens, GString *fileName, int line) {
  if (tokens->getLength() != 2) {
    error(-1, "Bad 'fontDir' config file command (%s:%d)",
	  fileName->getCString(), line);
    return;
  }
  fontDirs->append(((GString *)tokens->get(1))->copy());
}

void GlobalParams::parseInitialZoom(GList *tokens,
				    GString *fileName, int line) {
  if (tokens->getLength() != 2) {
    error(-1, "Bad 'initialZoom' config file command (%s:%d)",
	  fileName->getCString(), line);
    return;
  }
  delete initialZoom;
  initialZoom = ((GString *)tokens->get(1))->copy();
}

void GlobalParams::parseScreenType(GList *tokens, GString *fileName,
				   int line) {
  GString *tok;

  if (tokens->getLength() != 2) {
    error(-1, "Bad 'screenType' config file command (%s:%d)",
	  fileName->getCString(), line);
    return;
  }
  tok = (GString *)tokens->get(1);
  if (!tok->cmp("dispersed")) {
    screenType = screenDispersed;
  } else if (!tok->cmp("clustered")) {
    screenType = screenClustered;
  } else if (!tok->cmp("stochasticClustered")) {
    screenType = screenStochasticClustered;
  } else {
    error(-1, "Bad 'screenType' config file command (%s:%d)",
	  fileName->getCString(), line);
  }
}

void GlobalParams::parseBind(GList *tokens, GString *fileName, int line) {
  KeyBinding *binding;
  GList *cmds;
  int code, mods, context, i;

  if (tokens->getLength() < 4) {
    error(-1, "Bad 'bind' config file command (%s:%d)",
	  fileName->getCString(), line);
    return;
  }
  if (!parseKey((GString *)tokens->get(1), (GString *)tokens->get(2),
		&code, &mods, &context,
		"bind", tokens, fileName, line)) {
    return;
  }
  for (i = 0; i < keyBindings->getLength(); ++i) {
    binding = (KeyBinding *)keyBindings->get(i);
    if (binding->code == code &&
	binding->mods == mods &&
	binding->context == context) {
      delete (KeyBinding *)keyBindings->del(i);
      break;
    }
  }
  cmds = new GList();
  for (i = 3; i < tokens->getLength(); ++i) {
    cmds->append(((GString *)tokens->get(i))->copy());
  }
  keyBindings->append(new KeyBinding(code, mods, context, cmds));
}

void GlobalParams::parseUnbind(GList *tokens, GString *fileName, int line) {
  KeyBinding *binding;
  int code, mods, context, i;

  if (tokens->getLength() != 3) {
    error(-1, "Bad 'unbind' config file command (%s:%d)",
	  fileName->getCString(), line);
    return;
  }
  if (!parseKey((GString *)tokens->get(1), (GString *)tokens->get(2),
		&code, &mods, &context,
		"unbind", tokens, fileName, line)) {
    return;
  }
  for (i = 0; i < keyBindings->getLength(); ++i) {
    binding = (KeyBinding *)keyBindings->get(i);
    if (binding->code == code &&
	binding->mods == mods &&
	binding->context == context) {
      delete (KeyBinding *)keyBindings->del(i);
      break;
    }
  }
}

GBool GlobalParams::parseKey(GString *modKeyStr, GString *contextStr,
			     int *code, int *mods, int *context,
			     char *cmdName,
			     GList *tokens, GString *fileName, int line) {
  char *p0;

  *mods = xpdfKeyModNone;
  p0 = modKeyStr->getCString();
  while (1) {
    if (!strncmp(p0, "shift-", 6)) {
      *mods |= xpdfKeyModShift;
      p0 += 6;
    } else if (!strncmp(p0, "ctrl-", 5)) {
      *mods |= xpdfKeyModCtrl;
      p0 += 5;
    } else if (!strncmp(p0, "alt-", 4)) {
      *mods |= xpdfKeyModAlt;
      p0 += 4;
    } else {
      break;
    }
  }

  if (!strcmp(p0, "space")) {
    *code = ' ';
  } else if (!strcmp(p0, "tab")) {
    *code = xpdfKeyCodeTab;
  } else if (!strcmp(p0, "return")) {
    *code = xpdfKeyCodeReturn;
  } else if (!strcmp(p0, "enter")) {
    *code = xpdfKeyCodeEnter;
  } else if (!strcmp(p0, "backspace")) {
    *code = xpdfKeyCodeBackspace;
  } else if (!strcmp(p0, "insert")) {
    *code = xpdfKeyCodeInsert;
  } else if (!strcmp(p0, "delete")) {
    *code = xpdfKeyCodeDelete;
  } else if (!strcmp(p0, "home")) {
    *code = xpdfKeyCodeHome;
  } else if (!strcmp(p0, "end")) {
    *code = xpdfKeyCodeEnd;
  } else if (!strcmp(p0, "pgup")) {
    *code = xpdfKeyCodePgUp;
  } else if (!strcmp(p0, "pgdn")) {
    *code = xpdfKeyCodePgDn;
  } else if (!strcmp(p0, "left")) {
    *code = xpdfKeyCodeLeft;
  } else if (!strcmp(p0, "right")) {
    *code = xpdfKeyCodeRight;
  } else if (!strcmp(p0, "up")) {
    *code = xpdfKeyCodeUp;
  } else if (!strcmp(p0, "down")) {
    *code = xpdfKeyCodeDown;
  } else if (p0[0] == 'f' && p0[1] >= '1' && p0[1] <= '9' && !p0[2]) {
    *code = xpdfKeyCodeF1 + (p0[1] - '1');
  } else if (p0[0] == 'f' &&
	     ((p0[1] >= '1' && p0[1] <= '2' && p0[2] >= '0' && p0[2] <= '9') ||
	      (p0[1] == '3' && p0[2] >= '0' && p0[2] <= '5')) &&
	     !p0[3]) {
    *code = xpdfKeyCodeF1 + 10 * (p0[1] - '0') + (p0[2] - '0') - 1;
  } else if (!strncmp(p0, "mousePress", 10) &&
	     p0[10] >= '1' && p0[10] <= '7' && !p0[11]) {
    *code = xpdfKeyCodeMousePress1 + (p0[10] - '1');
  } else if (!strncmp(p0, "mouseRelease", 12) &&
	     p0[12] >= '1' && p0[12] <= '7' && !p0[13]) {
    *code = xpdfKeyCodeMouseRelease1 + (p0[12] - '1');
  } else if (*p0 >= 0x20 && *p0 <= 0x7e && !p0[1]) {
    *code = (int)*p0;
  } else {
    error(-1, "Bad key/modifier in '%s' config file command (%s:%d)",
	  cmdName, fileName->getCString(), line);
    return gFalse;
  }

  p0 = contextStr->getCString();
  if (!strcmp(p0, "any")) {
    *context = xpdfKeyContextAny;
  } else {
    *context = xpdfKeyContextAny;
    while (1) {
      if (!strncmp(p0, "fullScreen", 10)) {
	*context |= xpdfKeyContextFullScreen;
	p0 += 10;
      } else if (!strncmp(p0, "window", 6)) {
	*context |= xpdfKeyContextWindow;
	p0 += 6;
      } else if (!strncmp(p0, "continuous", 10)) {
	*context |= xpdfKeyContextContinuous;
	p0 += 10;
      } else if (!strncmp(p0, "singlePage", 10)) {
	*context |= xpdfKeyContextSinglePage;
	p0 += 10;
      } else if (!strncmp(p0, "overLink", 8)) {
	*context |= xpdfKeyContextOverLink;
	p0 += 8;
      } else if (!strncmp(p0, "offLink", 7)) {
	*context |= xpdfKeyContextOffLink;
	p0 += 7;
      } else if (!strncmp(p0, "outline", 7)) {
	*context |= xpdfKeyContextOutline;
	p0 += 7;
      } else if (!strncmp(p0, "mainWin", 7)) {
	*context |= xpdfKeyContextMainWin;
	p0 += 7;
      } else if (!strncmp(p0, "scrLockOn", 9)) {
	*context |= xpdfKeyContextScrLockOn;
	p0 += 9;
      } else if (!strncmp(p0, "scrLockOff", 10)) {
	*context |= xpdfKeyContextScrLockOff;
	p0 += 10;
      } else {
	error(-1, "Bad context in '%s' config file command (%s:%d)",
	      cmdName, fileName->getCString(), line);
	return gFalse;
      }
      if (!*p0) {
	break;
      }
      if (*p0 != ',') {
	error(-1, "Bad context in '%s' config file command (%s:%d)",
	      cmdName, fileName->getCString(), line);
	return gFalse;
      }
      ++p0;
    }
  }

  return gTrue;
}

void GlobalParams::parseCommand(char *cmdName, GString **val,
				GList *tokens, GString *fileName, int line) {
  if (tokens->getLength() != 2) {
    error(-1, "Bad '%s' config file command (%s:%d)",
	  cmdName, fileName->getCString(), line);
    return;
  }
  if (*val) {
    delete *val;
  }
  *val = ((GString *)tokens->get(1))->copy();
}

void GlobalParams::parseYesNo(char *cmdName, GBool *flag,
			      GList *tokens, GString *fileName, int line) {
  GString *tok;

  if (tokens->getLength() != 2) {
    error(-1, "Bad '%s' config file command (%s:%d)",
	  cmdName, fileName->getCString(), line);
    return;
  }
  tok = (GString *)tokens->get(1);
  if (!parseYesNo2(tok->getCString(), flag)) {
    error(-1, "Bad '%s' config file command (%s:%d)",
	  cmdName, fileName->getCString(), line);
  }
}

GBool GlobalParams::parseYesNo2(char *token, GBool *flag) {
  if (!strcmp(token, "yes")) {
    *flag = gTrue;
  } else if (!strcmp(token, "no")) {
    *flag = gFalse;
  } else {
    return gFalse;
  }
  return gTrue;
}

void GlobalParams::parseInteger(char *cmdName, int *val,
				GList *tokens, GString *fileName, int line) {
  GString *tok;
  int i;

  if (tokens->getLength() != 2) {
    error(-1, "Bad '%s' config file command (%s:%d)",
	  cmdName, fileName->getCString(), line);
    return;
  }
  tok = (GString *)tokens->get(1);
  if (tok->getLength() == 0) {
    error(-1, "Bad '%s' config file command (%s:%d)",
	  cmdName, fileName->getCString(), line);
    return;
  }
  if (tok->getChar(0) == '-') {
    i = 1;
  } else {
    i = 0;
  }
  for (; i < tok->getLength(); ++i) {
    if (tok->getChar(i) < '0' || tok->getChar(i) > '9') {
      error(-1, "Bad '%s' config file command (%s:%d)",
	    cmdName, fileName->getCString(), line);
      return;
    }
  }
  *val = atoi(tok->getCString());
}

void GlobalParams::parseFloat(char *cmdName, double *val,
			      GList *tokens, GString *fileName, int line) {
  GString *tok;
  int i;

  if (tokens->getLength() != 2) {
    error(-1, "Bad '%s' config file command (%s:%d)",
	  cmdName, fileName->getCString(), line);
    return;
  }
  tok = (GString *)tokens->get(1);
  if (tok->getLength() == 0) {
    error(-1, "Bad '%s' config file command (%s:%d)",
	  cmdName, fileName->getCString(), line);
    return;
  }
  if (tok->getChar(0) == '-') {
    i = 1;
  } else {
    i = 0;
  }
  for (; i < tok->getLength(); ++i) {
    if (!((tok->getChar(i) >= '0' && tok->getChar(i) <= '9') ||
	  tok->getChar(i) == '.')) {
      error(-1, "Bad '%s' config file command (%s:%d)",
	    cmdName, fileName->getCString(), line);
      return;
    }
  }
  *val = atof(tok->getCString());
}

GlobalParams::~GlobalParams() {
  GHashIter *iter;
  GString *key;
  GList *list;

  freeBuiltinFontTables();

  delete macRomanReverseMap;

  delete baseDir;
  delete nameToUnicode;
  deleteGHash(cidToUnicodes, GString);
  deleteGHash(unicodeToUnicodes, GString);
  deleteGHash(residentUnicodeMaps, UnicodeMap);
  deleteGHash(unicodeMaps, GString);
  deleteGList(toUnicodeDirs, GString);
  deleteGHash(displayFonts, DisplayFontParam);
  deleteGHash(displayCIDFonts, DisplayFontParam);
  deleteGHash(displayNamedCIDFonts, DisplayFontParam);
#ifdef WIN32
  if (winFontList) {
    delete winFontList;
  }
#endif
  if (psFile) {
    delete psFile;
  }
  deleteGHash(psFonts, PSFontParam);
  deleteGList(psNamedFonts16, PSFontParam);
  deleteGList(psFonts16, PSFontParam);
  delete textEncoding;
  deleteGList(fontDirs, GString);
  delete initialZoom;
  if (urlCommand) {
    delete urlCommand;
  }
  if (movieCommand) {
    delete movieCommand;
  }
  deleteGList(keyBindings, KeyBinding);

  cMapDirs->startIter(&iter);
  while (cMapDirs->getNext(&iter, &key, (void **)&list)) {
    deleteGList(list, GString);
  }
  delete cMapDirs;

  delete cidToUnicodeCache;
  delete unicodeToUnicodeCache;
  delete unicodeMapCache;
  delete cMapCache;

#ifdef ENABLE_PLUGINS
  delete securityHandlers;
  deleteGList(plugins, Plugin);
#endif

#if MULTITHREADED
  gDestroyMutex(&mutex);
  gDestroyMutex(&unicodeMapCacheMutex);
  gDestroyMutex(&cMapCacheMutex);
#endif
}

//------------------------------------------------------------------------

void GlobalParams::setBaseDir(char *dir) {
  delete baseDir;
  baseDir = new GString(dir);
}

void GlobalParams::setupBaseFonts(char *dir) {
  GString *fontName;
  GString *fileName;
#ifdef WIN32
  HMODULE shell32Lib;
  BOOL (__stdcall *SHGetSpecialFolderPathFunc)(HWND hwndOwner,
					       LPTSTR lpszPath,
					       int nFolder,
					       BOOL fCreate);
  char winFontDir[MAX_PATH];
#endif
  FILE *f;
  DisplayFontParamKind kind;
  DisplayFontParam *dfp;
  int i, j;

#ifdef WIN32
  // SHGetSpecialFolderPath isn't available in older versions of
  // shell32.dll (Win95 and WinNT4), so do a dynamic load
  winFontDir[0] = '\0';
  if ((shell32Lib = LoadLibrary("shell32.dll"))) {
    if ((SHGetSpecialFolderPathFunc = 
	 (BOOL (__stdcall *)(HWND hwndOwner, LPTSTR lpszPath,
			     int nFolder, BOOL fCreate))
	 GetProcAddress(shell32Lib, "SHGetSpecialFolderPathA"))) {
      if (!(*SHGetSpecialFolderPathFunc)(NULL, winFontDir,
					 CSIDL_FONTS, FALSE)) {
	winFontDir[0] = '\0';
      }
    }
  }
#endif
  for (i = 0; displayFontTab[i].name; ++i) {
    fontName = new GString(displayFontTab[i].name);
    if (getDisplayFont(fontName)) {
      delete fontName;
      continue;
    }
    fileName = NULL;
    kind = displayFontT1; // make gcc happy
    if (dir) {
      fileName = appendToPath(new GString(dir), displayFontTab[i].t1FileName);
      kind = displayFontT1;
      if ((f = fopen(fileName->getCString(), "rb"))) {
	fclose(f);
      } else {
	delete fileName;
	fileName = NULL;
      }
    }
#ifdef WIN32
    if (!fileName && winFontDir[0] && displayFontTab[i].ttFileName) {
      fileName = appendToPath(new GString(winFontDir),
			      displayFontTab[i].ttFileName);
      kind = displayFontTT;
      if ((f = fopen(fileName->getCString(), "rb"))) {
	fclose(f);
      } else {
	delete fileName;
	fileName = NULL;
      }
    }
    // SHGetSpecialFolderPath(CSIDL_FONTS) doesn't work on Win 2k Server
    // or Win2003 Server, or with older versions of shell32.dll, so check
    // the "standard" directories
    if (displayFontTab[i].ttFileName) {
      for (j = 0; !fileName && displayFontDirs[j]; ++j) {
	fileName = appendToPath(new GString(displayFontDirs[j]),
				displayFontTab[i].ttFileName);
	kind = displayFontTT;
	if ((f = fopen(fileName->getCString(), "rb"))) {
	  fclose(f);
	} else {
	  delete fileName;
	  fileName = NULL;
	}
      }
    }
#else
    for (j = 0; !fileName && displayFontDirs[j]; ++j) {
      fileName = appendToPath(new GString(displayFontDirs[j]),
			      displayFontTab[i].t1FileName);
      kind = displayFontT1;
      if ((f = fopen(fileName->getCString(), "rb"))) {
	fclose(f);
      } else {
	delete fileName;
	fileName = NULL;
      }
    }
#endif
    if (!fileName) {
      error(-1, "No display font for '%s'", displayFontTab[i].name);
      delete fontName;
      continue;
    }
    dfp = new DisplayFontParam(fontName, kind);
    dfp->t1.fileName = fileName;
    globalParams->addDisplayFont(dfp);
  }

#ifdef WIN32
  if (winFontDir[0]) {
    winFontList = new WinFontList(winFontDir);
  }
#endif
}

//------------------------------------------------------------------------
// accessors
//------------------------------------------------------------------------

CharCode GlobalParams::getMacRomanCharCode(char *charName) {
  // no need to lock - macRomanReverseMap is constant
  return macRomanReverseMap->lookup(charName);
}

GString *GlobalParams::getBaseDir() {
  GString *s;

  lockGlobalParams;
  s = baseDir->copy();
  unlockGlobalParams;
  return s;
}

Unicode GlobalParams::mapNameToUnicode(char *charName) {
  // no need to lock - nameToUnicode is constant
  return nameToUnicode->lookup(charName);
}

UnicodeMap *GlobalParams::getResidentUnicodeMap(GString *encodingName) {
  UnicodeMap *map;

  lockGlobalParams;
  map = (UnicodeMap *)residentUnicodeMaps->lookup(encodingName);
  unlockGlobalParams;
  if (map) {
    map->incRefCnt();
  }
  return map;
}

FILE *GlobalParams::getUnicodeMapFile(GString *encodingName) {
  GString *fileName;
  FILE *f;

  lockGlobalParams;
  if ((fileName = (GString *)unicodeMaps->lookup(encodingName))) {
    f = fopen(fileName->getCString(), "r");
  } else {
    f = NULL;
  }
  unlockGlobalParams;
  return f;
}

FILE *GlobalParams::findCMapFile(GString *collection, GString *cMapName) {
  GList *list;
  GString *dir;
  GString *fileName;
  FILE *f;
  int i;

  lockGlobalParams;
  if (!(list = (GList *)cMapDirs->lookup(collection))) {
    unlockGlobalParams;
    return NULL;
  }
  for (i = 0; i < list->getLength(); ++i) {
    dir = (GString *)list->get(i);
    fileName = appendToPath(dir->copy(), cMapName->getCString());
    f = fopen(fileName->getCString(), "r");
    delete fileName;
    if (f) {
      unlockGlobalParams;
      return f;
    }
  }
  unlockGlobalParams;
  return NULL;
}

FILE *GlobalParams::findToUnicodeFile(GString *name) {
  GString *dir, *fileName;
  FILE *f;
  int i;

  lockGlobalParams;
  for (i = 0; i < toUnicodeDirs->getLength(); ++i) {
    dir = (GString *)toUnicodeDirs->get(i);
    fileName = appendToPath(dir->copy(), name->getCString());
    f = fopen(fileName->getCString(), "r");
    delete fileName;
    if (f) {
      unlockGlobalParams;
      return f;
    }
  }
  unlockGlobalParams;
  return NULL;
}

DisplayFontParam *GlobalParams::getDisplayFont(GString *fontName) {
  DisplayFontParam *dfp;

  lockGlobalParams;
  dfp = (DisplayFontParam *)displayFonts->lookup(fontName);
#ifdef WIN32
  if (!dfp && winFontList) {
    dfp = winFontList->find(fontName);
  }
#endif
  unlockGlobalParams;
  return dfp;
}

DisplayFontParam *GlobalParams::getDisplayCIDFont(GString *fontName,
						  GString *collection) {
  DisplayFontParam *dfp;

  lockGlobalParams;
  if (!fontName ||
      !(dfp = (DisplayFontParam *)displayNamedCIDFonts->lookup(fontName))) {
    dfp = (DisplayFontParam *)displayCIDFonts->lookup(collection);
  }
  unlockGlobalParams;
  return dfp;
}

GString *GlobalParams::getPSFile() {
  GString *s;

  lockGlobalParams;
  s = psFile ? psFile->copy() : (GString *)NULL;
  unlockGlobalParams;
  return s;
}

int GlobalParams::getPSPaperWidth() {
  int w;

  lockGlobalParams;
  w = psPaperWidth;
  unlockGlobalParams;
  return w;
}

int GlobalParams::getPSPaperHeight() {
  int h;

  lockGlobalParams;
  h = psPaperHeight;
  unlockGlobalParams;
  return h;
}

void GlobalParams::getPSImageableArea(int *llx, int *lly, int *urx, int *ury) {
  lockGlobalParams;
  *llx = psImageableLLX;
  *lly = psImageableLLY;
  *urx = psImageableURX;
  *ury = psImageableURY;
  unlockGlobalParams;
}

GBool GlobalParams::getPSCrop() {
  GBool f;

  lockGlobalParams;
  f = psCrop;
  unlockGlobalParams;
  return f;
}

GBool GlobalParams::getPSExpandSmaller() {
  GBool f;

  lockGlobalParams;
  f = psExpandSmaller;
  unlockGlobalParams;
  return f;
}

GBool GlobalParams::getPSShrinkLarger() {
  GBool f;

  lockGlobalParams;
  f = psShrinkLarger;
  unlockGlobalParams;
  return f;
}

GBool GlobalParams::getPSCenter() {
  GBool f;

  lockGlobalParams;
  f = psCenter;
  unlockGlobalParams;
  return f;
}

GBool GlobalParams::getPSDuplex() {
  GBool d;

  lockGlobalParams;
  d = psDuplex;
  unlockGlobalParams;
  return d;
}

PSLevel GlobalParams::getPSLevel() {
  PSLevel level;

  lockGlobalParams;
  level = psLevel;
  unlockGlobalParams;
  return level;
}

PSFontParam *GlobalParams::getPSFont(GString *fontName) {
  PSFontParam *p;

  lockGlobalParams;
  p = (PSFontParam *)psFonts->lookup(fontName);
  unlockGlobalParams;
  return p;
}

PSFontParam *GlobalParams::getPSFont16(GString *fontName,
				       GString *collection, int wMode) {
  PSFontParam *p;
  int i;

  lockGlobalParams;
  p = NULL;
  if (fontName) {
    for (i = 0; i < psNamedFonts16->getLength(); ++i) {
      p = (PSFontParam *)psNamedFonts16->get(i);
      if (!p->pdfFontName->cmp(fontName) &&
	  p->wMode == wMode) {
	break;
      }
      p = NULL;
    }
  }
  if (!p && collection) {
    for (i = 0; i < psFonts16->getLength(); ++i) {
      p = (PSFontParam *)psFonts16->get(i);
      if (!p->pdfFontName->cmp(collection) &&
	  p->wMode == wMode) {
	break;
      }
      p = NULL;
    }
  }
  unlockGlobalParams;
  return p;
}

GBool GlobalParams::getPSEmbedType1() {
  GBool e;

  lockGlobalParams;
  e = psEmbedType1;
  unlockGlobalParams;
  return e;
}

GBool GlobalParams::getPSEmbedTrueType() {
  GBool e;

  lockGlobalParams;
  e = psEmbedTrueType;
  unlockGlobalParams;
  return e;
}

GBool GlobalParams::getPSEmbedCIDPostScript() {
  GBool e;

  lockGlobalParams;
  e = psEmbedCIDPostScript;
  unlockGlobalParams;
  return e;
}

GBool GlobalParams::getPSEmbedCIDTrueType() {
  GBool e;

  lockGlobalParams;
  e = psEmbedCIDTrueType;
  unlockGlobalParams;
  return e;
}

GBool GlobalParams::getPSPreload() {
  GBool preload;

  lockGlobalParams;
  preload = psPreload;
  unlockGlobalParams;
  return preload;
}

GBool GlobalParams::getPSOPI() {
  GBool opi;

  lockGlobalParams;
  opi = psOPI;
  unlockGlobalParams;
  return opi;
}

GBool GlobalParams::getPSASCIIHex() {
  GBool ah;

  lockGlobalParams;
  ah = psASCIIHex;
  unlockGlobalParams;
  return ah;
}

GString *GlobalParams::getTextEncodingName() {
  GString *s;

  lockGlobalParams;
  s = textEncoding->copy();
  unlockGlobalParams;
  return s;
}

EndOfLineKind GlobalParams::getTextEOL() {
  EndOfLineKind eol;

  lockGlobalParams;
  eol = textEOL;
  unlockGlobalParams;
  return eol;
}

GBool GlobalParams::getTextPageBreaks() {
  GBool pageBreaks;

  lockGlobalParams;
  pageBreaks = textPageBreaks;
  unlockGlobalParams;
  return pageBreaks;
}

GBool GlobalParams::getTextKeepTinyChars() {
  GBool tiny;

  lockGlobalParams;
  tiny = textKeepTinyChars;
  unlockGlobalParams;
  return tiny;
}

GString *GlobalParams::findFontFile(GString *fontName, char **exts) {
  GString *dir, *fileName;
  char **ext;
  FILE *f;
  int i;

  lockGlobalParams;
  for (i = 0; i < fontDirs->getLength(); ++i) {
    dir = (GString *)fontDirs->get(i);
    for (ext = exts; *ext; ++ext) {
      fileName = appendToPath(dir->copy(), fontName->getCString());
      fileName->append(*ext);
      if ((f = fopen(fileName->getCString(), "rb"))) {
	fclose(f);
	unlockGlobalParams;
	return fileName;
      }
      delete fileName;
    }
  }
  unlockGlobalParams;
  return NULL;
}

GString *GlobalParams::getInitialZoom() {
  GString *s;

  lockGlobalParams;
  s = initialZoom->copy();
  unlockGlobalParams;
  return s;
}

GBool GlobalParams::getContinuousView() {
  GBool f;

  lockGlobalParams;
  f = continuousView;
  unlockGlobalParams;
  return f;
}

GBool GlobalParams::getEnableT1lib() {
  GBool f;

  lockGlobalParams;
  f = enableT1lib;
  unlockGlobalParams;
  return f;
}

GBool GlobalParams::getEnableFreeType() {
  GBool f;

  lockGlobalParams;
  f = enableFreeType;
  unlockGlobalParams;
  return f;
}


GBool GlobalParams::getAntialias() {
  GBool f;

  lockGlobalParams;
  f = antialias;
  unlockGlobalParams;
  return f;
}

GBool GlobalParams::getVectorAntialias() {
  GBool f;

  lockGlobalParams;
  f = vectorAntialias;
  unlockGlobalParams;
  return f;
}

GBool GlobalParams::getStrokeAdjust() {
  GBool f;

  lockGlobalParams;
  f = strokeAdjust;
  unlockGlobalParams;
  return f;
}

ScreenType GlobalParams::getScreenType() {
  ScreenType t;

  lockGlobalParams;
  t = screenType;
  unlockGlobalParams;
  return t;
}

int GlobalParams::getScreenSize() {
  int size;

  lockGlobalParams;
  size = screenSize;
  unlockGlobalParams;
  return size;
}

int GlobalParams::getScreenDotRadius() {
  int r;

  lockGlobalParams;
  r = screenDotRadius;
  unlockGlobalParams;
  return r;
}

double GlobalParams::getScreenGamma() {
  double gamma;

  lockGlobalParams;
  gamma = screenGamma;
  unlockGlobalParams;
  return gamma;
}

double GlobalParams::getScreenBlackThreshold() {
  double thresh;

  lockGlobalParams;
  thresh = screenBlackThreshold;
  unlockGlobalParams;
  return thresh;
}

double GlobalParams::getScreenWhiteThreshold() {
  double thresh;

  lockGlobalParams;
  thresh = screenWhiteThreshold;
  unlockGlobalParams;
  return thresh;
}

GBool GlobalParams::getMapNumericCharNames() {
  GBool map;

  lockGlobalParams;
  map = mapNumericCharNames;
  unlockGlobalParams;
  return map;
}

GBool GlobalParams::getMapUnknownCharNames() {
  GBool map;

  lockGlobalParams;
  map = mapUnknownCharNames;
  unlockGlobalParams;
  return map;
}

GList *GlobalParams::getKeyBinding(int code, int mods, int context) {
  KeyBinding *binding;
  GList *cmds;
  int modMask;
  int i, j;

  lockGlobalParams;
  cmds = NULL;
  // for ASCII chars, ignore the shift modifier
  modMask = code <= 0xff ? ~xpdfKeyModShift : ~0;
  for (i = 0; i < keyBindings->getLength(); ++i) {
    binding = (KeyBinding *)keyBindings->get(i);
    if (binding->code == code &&
	(binding->mods & modMask) == (mods & modMask) &&
	(~binding->context | context) == ~0) {
      cmds = new GList();
      for (j = 0; j < binding->cmds->getLength(); ++j) {
	cmds->append(((GString *)binding->cmds->get(j))->copy());
      }
      break;
    }
  }
  unlockGlobalParams;
  return cmds;
}

GBool GlobalParams::getPrintCommands() {
  GBool p;

  lockGlobalParams;
  p = printCommands;
  unlockGlobalParams;
  return p;
}

GBool GlobalParams::getErrQuiet() {
  // no locking -- this function may get called from inside a locked
  // section
  return errQuiet;
}

CharCodeToUnicode *GlobalParams::getCIDToUnicode(GString *collection) {
  GString *fileName;
  CharCodeToUnicode *ctu;

  lockGlobalParams;
  if (!(ctu = cidToUnicodeCache->getCharCodeToUnicode(collection))) {
    if ((fileName = (GString *)cidToUnicodes->lookup(collection)) &&
	(ctu = CharCodeToUnicode::parseCIDToUnicode(fileName, collection))) {
      cidToUnicodeCache->add(ctu);
    }
  }
  unlockGlobalParams;
  return ctu;
}

CharCodeToUnicode *GlobalParams::getUnicodeToUnicode(GString *fontName) {
  CharCodeToUnicode *ctu;
  GHashIter *iter;
  GString *fontPattern, *fileName;

  lockGlobalParams;
  fileName = NULL;
  unicodeToUnicodes->startIter(&iter);
  while (unicodeToUnicodes->getNext(&iter, &fontPattern, (void **)&fileName)) {
    if (strstr(fontName->getCString(), fontPattern->getCString())) {
      unicodeToUnicodes->killIter(&iter);
      break;
    }
    fileName = NULL;
  }
  if (fileName) {
    if (!(ctu = unicodeToUnicodeCache->getCharCodeToUnicode(fileName))) {
      if ((ctu = CharCodeToUnicode::parseUnicodeToUnicode(fileName))) {
	unicodeToUnicodeCache->add(ctu);
      }
    }
  } else {
    ctu = NULL;
  }
  unlockGlobalParams;
  return ctu;
}

UnicodeMap *GlobalParams::getUnicodeMap(GString *encodingName) {
  return getUnicodeMap2(encodingName);
}

UnicodeMap *GlobalParams::getUnicodeMap2(GString *encodingName) {
  UnicodeMap *map;

  if (!(map = getResidentUnicodeMap(encodingName))) {
    lockUnicodeMapCache;
    map = unicodeMapCache->getUnicodeMap(encodingName);
    unlockUnicodeMapCache;
  }
  return map;
}

CMap *GlobalParams::getCMap(GString *collection, GString *cMapName) {
  CMap *cMap;

  lockCMapCache;
  cMap = cMapCache->getCMap(collection, cMapName);
  unlockCMapCache;
  return cMap;
}

UnicodeMap *GlobalParams::getTextEncoding() {
  return getUnicodeMap2(textEncoding);
}

//------------------------------------------------------------------------
// functions to set parameters
//------------------------------------------------------------------------

void GlobalParams::addDisplayFont(DisplayFontParam *param) {
  DisplayFontParam *old;

  lockGlobalParams;
  if ((old = (DisplayFontParam *)displayFonts->remove(param->name))) {
    delete old;
  }
  displayFonts->add(param->name, param);
  unlockGlobalParams;
}

void GlobalParams::setPSFile(char *file) {
  lockGlobalParams;
  if (psFile) {
    delete psFile;
  }
  psFile = new GString(file);
  unlockGlobalParams;
}

GBool GlobalParams::setPSPaperSize(char *size) {
  lockGlobalParams;
  if (!strcmp(size, "match")) {
    psPaperWidth = psPaperHeight = -1;
  } else if (!strcmp(size, "letter")) {
    psPaperWidth = 612;
    psPaperHeight = 792;
  } else if (!strcmp(size, "legal")) {
    psPaperWidth = 612;
    psPaperHeight = 1008;
  } else if (!strcmp(size, "A4")) {
    psPaperWidth = 595;
    psPaperHeight = 842;
  } else if (!strcmp(size, "A3")) {
    psPaperWidth = 842;
    psPaperHeight = 1190;
  } else {
    unlockGlobalParams;
    return gFalse;
  }
  psImageableLLX = psImageableLLY = 0;
  psImageableURX = psPaperWidth;
  psImageableURY = psPaperHeight;
  unlockGlobalParams;
  return gTrue;
}

void GlobalParams::setPSPaperWidth(int width) {
  lockGlobalParams;
  psPaperWidth = width;
  psImageableLLX = 0;
  psImageableURX = psPaperWidth;
  unlockGlobalParams;
}

void GlobalParams::setPSPaperHeight(int height) {
  lockGlobalParams;
  psPaperHeight = height;
  psImageableLLY = 0;
  psImageableURY = psPaperHeight;
  unlockGlobalParams;
}

void GlobalParams::setPSImageableArea(int llx, int lly, int urx, int ury) {
  lockGlobalParams;
  psImageableLLX = llx;
  psImageableLLY = lly;
  psImageableURX = urx;
  psImageableURY = ury;
  unlockGlobalParams;
}

void GlobalParams::setPSCrop(GBool crop) {
  lockGlobalParams;
  psCrop = crop;
  unlockGlobalParams;
}

void GlobalParams::setPSExpandSmaller(GBool expand) {
  lockGlobalParams;
  psExpandSmaller = expand;
  unlockGlobalParams;
}

void GlobalParams::setPSShrinkLarger(GBool shrink) {
  lockGlobalParams;
  psShrinkLarger = shrink;
  unlockGlobalParams;
}

void GlobalParams::setPSCenter(GBool center) {
  lockGlobalParams;
  psCenter = center;
  unlockGlobalParams;
}

void GlobalParams::setPSDuplex(GBool duplex) {
  lockGlobalParams;
  psDuplex = duplex;
  unlockGlobalParams;
}

void GlobalParams::setPSLevel(PSLevel level) {
  lockGlobalParams;
  psLevel = level;
  unlockGlobalParams;
}

void GlobalParams::setPSEmbedType1(GBool embed) {
  lockGlobalParams;
  psEmbedType1 = embed;
  unlockGlobalParams;
}

void GlobalParams::setPSEmbedTrueType(GBool embed) {
  lockGlobalParams;
  psEmbedTrueType = embed;
  unlockGlobalParams;
}

void GlobalParams::setPSEmbedCIDPostScript(GBool embed) {
  lockGlobalParams;
  psEmbedCIDPostScript = embed;
  unlockGlobalParams;
}

void GlobalParams::setPSEmbedCIDTrueType(GBool embed) {
  lockGlobalParams;
  psEmbedCIDTrueType = embed;
  unlockGlobalParams;
}

void GlobalParams::setPSPreload(GBool preload) {
  lockGlobalParams;
  psPreload = preload;
  unlockGlobalParams;
}

void GlobalParams::setPSOPI(GBool opi) {
  lockGlobalParams;
  psOPI = opi;
  unlockGlobalParams;
}

void GlobalParams::setPSASCIIHex(GBool hex) {
  lockGlobalParams;
  psASCIIHex = hex;
  unlockGlobalParams;
}

void GlobalParams::setTextEncoding(char *encodingName) {
  lockGlobalParams;
  delete textEncoding;
  textEncoding = new GString(encodingName);
  unlockGlobalParams;
}

GBool GlobalParams::setTextEOL(char *s) {
  lockGlobalParams;
  if (!strcmp(s, "unix")) {
    textEOL = eolUnix;
  } else if (!strcmp(s, "dos")) {
    textEOL = eolDOS;
  } else if (!strcmp(s, "mac")) {
    textEOL = eolMac;
  } else {
    unlockGlobalParams;
    return gFalse;
  }
  unlockGlobalParams;
  return gTrue;
}

void GlobalParams::setTextPageBreaks(GBool pageBreaks) {
  lockGlobalParams;
  textPageBreaks = pageBreaks;
  unlockGlobalParams;
}

void GlobalParams::setTextKeepTinyChars(GBool keep) {
  lockGlobalParams;
  textKeepTinyChars = keep;
  unlockGlobalParams;
}

void GlobalParams::setInitialZoom(char *s) {
  lockGlobalParams;
  delete initialZoom;
  initialZoom = new GString(s);
  unlockGlobalParams;
}

void GlobalParams::setContinuousView(GBool cont) {
  lockGlobalParams;
  continuousView = cont;
  unlockGlobalParams;
}

GBool GlobalParams::setEnableT1lib(char *s) {
  GBool ok;

  lockGlobalParams;
  ok = parseYesNo2(s, &enableT1lib);
  unlockGlobalParams;
  return ok;
}

GBool GlobalParams::setEnableFreeType(char *s) {
  GBool ok;

  lockGlobalParams;
  ok = parseYesNo2(s, &enableFreeType);
  unlockGlobalParams;
  return ok;
}


GBool GlobalParams::setAntialias(char *s) {
  GBool ok;

  lockGlobalParams;
  ok = parseYesNo2(s, &antialias);
  unlockGlobalParams;
  return ok;
}

GBool GlobalParams::setVectorAntialias(char *s) {
  GBool ok;

  lockGlobalParams;
  ok = parseYesNo2(s, &vectorAntialias);
  unlockGlobalParams;
  return ok;
}

void GlobalParams::setScreenType(ScreenType t) {
  lockGlobalParams;
  screenType = t;
  unlockGlobalParams;
}

void GlobalParams::setScreenSize(int size) {
  lockGlobalParams;
  screenSize = size;
  unlockGlobalParams;
}

void GlobalParams::setScreenDotRadius(int r) {
  lockGlobalParams;
  screenDotRadius = r;
  unlockGlobalParams;
}

void GlobalParams::setScreenGamma(double gamma) {
  lockGlobalParams;
  screenGamma = gamma;
  unlockGlobalParams;
}

void GlobalParams::setScreenBlackThreshold(double thresh) {
  lockGlobalParams;
  screenBlackThreshold = thresh;
  unlockGlobalParams;
}

void GlobalParams::setScreenWhiteThreshold(double thresh) {
  lockGlobalParams;
  screenWhiteThreshold = thresh;
  unlockGlobalParams;
}

void GlobalParams::setMapNumericCharNames(GBool map) {
  lockGlobalParams;
  mapNumericCharNames = map;
  unlockGlobalParams;
}

void GlobalParams::setMapUnknownCharNames(GBool map) {
  lockGlobalParams;
  mapUnknownCharNames = map;
  unlockGlobalParams;
}

void GlobalParams::setPrintCommands(GBool printCommandsA) {
  lockGlobalParams;
  printCommands = printCommandsA;
  unlockGlobalParams;
}

void GlobalParams::setErrQuiet(GBool errQuietA) {
  lockGlobalParams;
  errQuiet = errQuietA;
  unlockGlobalParams;
}

void GlobalParams::addSecurityHandler(XpdfSecurityHandler *handler) {
#ifdef ENABLE_PLUGINS
  lockGlobalParams;
  securityHandlers->append(handler);
  unlockGlobalParams;
#endif
}

XpdfSecurityHandler *GlobalParams::getSecurityHandler(char *name) {
#ifdef ENABLE_PLUGINS
  XpdfSecurityHandler *hdlr;
  int i;

  lockGlobalParams;
  for (i = 0; i < securityHandlers->getLength(); ++i) {
    hdlr = (XpdfSecurityHandler *)securityHandlers->get(i);
    if (!strcasecmp(hdlr->name, name)) {
      unlockGlobalParams;
      return hdlr;
    }
  }
  unlockGlobalParams;

  if (!loadPlugin("security", name)) {
    return NULL;
  }

  lockGlobalParams;
  for (i = 0; i < securityHandlers->getLength(); ++i) {
    hdlr = (XpdfSecurityHandler *)securityHandlers->get(i);
    if (!strcmp(hdlr->name, name)) {
      unlockGlobalParams;
      return hdlr;
    }
  }
  unlockGlobalParams;
#endif

  return NULL;
}

#ifdef ENABLE_PLUGINS
//------------------------------------------------------------------------
// plugins
//------------------------------------------------------------------------

GBool GlobalParams::loadPlugin(char *type, char *name) {
  Plugin *plugin;

  if (!(plugin = Plugin::load(type, name))) {
    return gFalse;
  }
  lockGlobalParams;
  plugins->append(plugin);
  unlockGlobalParams;
  return gTrue;
}

#endif // ENABLE_PLUGINS
//========================================================================
//
// GlobalParams.h
//
// Copyright 2001-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef GLOBALPARAMS_H
#define GLOBALPARAMS_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include <stdio.h>
#include "gtypes.h"
#include "CharTypes.h"

#if MULTITHREADED
#include "GMutex.h"
#endif

class GString;
class GList;
class GHash;
class NameToCharCode;
class CharCodeToUnicode;
class CharCodeToUnicodeCache;
class UnicodeMap;
class UnicodeMapCache;
class CMap;
class CMapCache;
struct XpdfSecurityHandler;
class GlobalParams;
#ifdef WIN32
class WinFontList;
#endif

//------------------------------------------------------------------------

// The global parameters object.
extern GlobalParams *globalParams;

//------------------------------------------------------------------------

enum DisplayFontParamKind {
  displayFontT1,
  displayFontTT
};

struct DisplayFontParamT1 {
  GString *fileName;
};

struct DisplayFontParamTT {
  GString *fileName;
};

class DisplayFontParam {
public:

  GString *name;		// font name for 8-bit fonts and named
				//   CID fonts; collection name for
				//   generic CID fonts
  DisplayFontParamKind kind;
  union {
    DisplayFontParamT1 t1;
    DisplayFontParamTT tt;
  };

  DisplayFontParam(GString *nameA, DisplayFontParamKind kindA);
  virtual ~DisplayFontParam();
};

//------------------------------------------------------------------------

class PSFontParam {
public:

  GString *pdfFontName;		// PDF font name for 8-bit fonts and
				//   named 16-bit fonts; char collection
				//   name for generic 16-bit fonts
  int wMode;			// writing mode (0=horiz, 1=vert) for
				//   16-bit fonts
  GString *psFontName;		// PostScript font name
  GString *encoding;		// encoding, for 16-bit fonts only

  PSFontParam(GString *pdfFontNameA, int wModeA,
	      GString *psFontNameA, GString *encodingA);
  ~PSFontParam();
};

//------------------------------------------------------------------------

enum PSLevel {
  psLevel1,
  psLevel1Sep,
  psLevel2,
  psLevel2Sep,
  psLevel3,
  psLevel3Sep
};

//------------------------------------------------------------------------

enum EndOfLineKind {
  eolUnix,			// LF
  eolDOS,			// CR+LF
  eolMac			// CR
};

//------------------------------------------------------------------------

enum ScreenType {
  screenUnset,
  screenDispersed,
  screenClustered,
  screenStochasticClustered
};

//------------------------------------------------------------------------

class KeyBinding {
public:

  int code;			// 0x20 .. 0xfe = ASCII,
				//   >=0x10000 = special keys, mouse buttons,
				//   etc. (xpdfKeyCode* symbols)
  int mods;			// modifiers (xpdfKeyMod* symbols, or-ed
				//   together)
  int context;			// context (xpdfKeyContext* symbols, or-ed
				//   together)
  GList *cmds;			// list of commands [GString]

  KeyBinding(int codeA, int modsA, int contextA, char *cmd0);
  KeyBinding(int codeA, int modsA, int contextA, char *cmd0, char *cmd1);
  KeyBinding(int codeA, int modsA, int contextA, GList *cmdsA);
  ~KeyBinding();
};

#define xpdfKeyCodeTab            0x1000
#define xpdfKeyCodeReturn         0x1001
#define xpdfKeyCodeEnter          0x1002
#define xpdfKeyCodeBackspace      0x1003
#define xpdfKeyCodeInsert         0x1004
#define xpdfKeyCodeDelete         0x1005
#define xpdfKeyCodeHome           0x1006
#define xpdfKeyCodeEnd            0x1007
#define xpdfKeyCodePgUp           0x1008
#define xpdfKeyCodePgDn           0x1009
#define xpdfKeyCodeLeft           0x100a
#define xpdfKeyCodeRight          0x100b
#define xpdfKeyCodeUp             0x100c
#define xpdfKeyCodeDown           0x100d
#define xpdfKeyCodeF1             0x1100
#define xpdfKeyCodeF35            0x1122
#define xpdfKeyCodeMousePress1    0x2001
#define xpdfKeyCodeMousePress2    0x2002
#define xpdfKeyCodeMousePress3    0x2003
#define xpdfKeyCodeMousePress4    0x2004
#define xpdfKeyCodeMousePress5    0x2005
#define xpdfKeyCodeMousePress6    0x2006
#define xpdfKeyCodeMousePress7    0x2007
#define xpdfKeyCodeMouseRelease1  0x2101
#define xpdfKeyCodeMouseRelease2  0x2102
#define xpdfKeyCodeMouseRelease3  0x2103
#define xpdfKeyCodeMouseRelease4  0x2104
#define xpdfKeyCodeMouseRelease5  0x2105
#define xpdfKeyCodeMouseRelease6  0x2106
#define xpdfKeyCodeMouseRelease7  0x2107
#define xpdfKeyModNone            0
#define xpdfKeyModShift           (1 << 0)
#define xpdfKeyModCtrl            (1 << 1)
#define xpdfKeyModAlt             (1 << 2)
#define xpdfKeyContextAny         0
#define xpdfKeyContextFullScreen  (1 << 0)
#define xpdfKeyContextWindow      (2 << 0)
#define xpdfKeyContextContinuous  (1 << 2)
#define xpdfKeyContextSinglePage  (2 << 2)
#define xpdfKeyContextOverLink    (1 << 4)
#define xpdfKeyContextOffLink     (2 << 4)
#define xpdfKeyContextOutline     (1 << 6)
#define xpdfKeyContextMainWin     (2 << 6)
#define xpdfKeyContextScrLockOn   (1 << 8)
#define xpdfKeyContextScrLockOff  (2 << 8)

//------------------------------------------------------------------------

class GlobalParams {
public:

  // Initialize the global parameters by attempting to read a config
  // file.
  GlobalParams(char *cfgFileName);

  ~GlobalParams();

  void setBaseDir(char *dir);
  void setupBaseFonts(char *dir);

  void parseLine(char *buf, GString *fileName, int line);

  //----- accessors

  CharCode getMacRomanCharCode(char *charName);

  GString *getBaseDir();
  Unicode mapNameToUnicode(char *charName);
  UnicodeMap *getResidentUnicodeMap(GString *encodingName);
  FILE *getUnicodeMapFile(GString *encodingName);
  FILE *findCMapFile(GString *collection, GString *cMapName);
  FILE *findToUnicodeFile(GString *name);
  DisplayFontParam *getDisplayFont(GString *fontName);
  DisplayFontParam *getDisplayCIDFont(GString *fontName, GString *collection);
  GString *getPSFile();
  int getPSPaperWidth();
  int getPSPaperHeight();
  void getPSImageableArea(int *llx, int *lly, int *urx, int *ury);
  GBool getPSDuplex();
  GBool getPSCrop();
  GBool getPSExpandSmaller();
  GBool getPSShrinkLarger();
  GBool getPSCenter();
  PSLevel getPSLevel();
  PSFontParam *getPSFont(GString *fontName);
  PSFontParam *getPSFont16(GString *fontName, GString *collection, int wMode);
  GBool getPSEmbedType1();
  GBool getPSEmbedTrueType();
  GBool getPSEmbedCIDPostScript();
  GBool getPSEmbedCIDTrueType();
  GBool getPSNoText();
  GBool getPSPreload();
  GBool getPSOPI();
  GBool getPSASCIIHex();
  GString *getTextEncodingName();
  EndOfLineKind getTextEOL();
  GBool getTextPageBreaks();
  GBool getTextKeepTinyChars();
  GString *findFontFile(GString *fontName, char **exts);
  GString *getInitialZoom();
  GBool getContinuousView();
  GBool getEnableT1lib();
  GBool getEnableFreeType();
  GBool getAntialias();
  GBool getVectorAntialias();
  GBool getStrokeAdjust();
  ScreenType getScreenType();
  int getScreenSize();
  int getScreenDotRadius();
  double getScreenGamma();
  double getScreenBlackThreshold();
  double getScreenWhiteThreshold();
  GString *getURLCommand() { return urlCommand; }
  GString *getMovieCommand() { return movieCommand; }
  GBool getMapNumericCharNames();
  GBool getMapUnknownCharNames();
  GList *getKeyBinding(int code, int mods, int context);
  GBool getPrintCommands();
  GBool getErrQuiet();

  CharCodeToUnicode *getCIDToUnicode(GString *collection);
  CharCodeToUnicode *getUnicodeToUnicode(GString *fontName);
  UnicodeMap *getUnicodeMap(GString *encodingName);
  CMap *getCMap(GString *collection, GString *cMapName);
  UnicodeMap *getTextEncoding();

  //----- functions to set parameters

  void addDisplayFont(DisplayFontParam *param);
  void setPSFile(char *file);
  void setPSNoText(GBool notext);
  GBool setPSPaperSize(char *size);
  void setPSPaperWidth(int width);
  void setPSPaperHeight(int height);
  void setPSImageableArea(int llx, int lly, int urx, int ury);
  void setPSDuplex(GBool duplex);
  void setPSCrop(GBool crop);
  void setPSExpandSmaller(GBool expand);
  void setPSShrinkLarger(GBool shrink);
  void setPSCenter(GBool center);
  void setPSLevel(PSLevel level);
  void setPSEmbedType1(GBool embed);
  void setPSEmbedTrueType(GBool embed);
  void setPSEmbedCIDPostScript(GBool embed);
  void setPSEmbedCIDTrueType(GBool embed);
  void setPSPreload(GBool preload);
  void setPSOPI(GBool opi);
  void setPSASCIIHex(GBool hex);
  void setTextEncoding(char *encodingName);
  GBool setTextEOL(char *s);
  void setTextPageBreaks(GBool pageBreaks);
  void setTextKeepTinyChars(GBool keep);
  void setInitialZoom(char *s);
  void setContinuousView(GBool cont);
  GBool setEnableT1lib(char *s);
  GBool setEnableFreeType(char *s);
  GBool setAntialias(char *s);
  GBool setVectorAntialias(char *s);
  void setScreenType(ScreenType t);
  void setScreenSize(int size);
  void setScreenDotRadius(int r);
  void setScreenGamma(double gamma);
  void setScreenBlackThreshold(double thresh);
  void setScreenWhiteThreshold(double thresh);
  void setMapNumericCharNames(GBool map);
  void setMapUnknownCharNames(GBool map);
  void setPrintCommands(GBool printCommandsA);
  void setErrQuiet(GBool errQuietA);

  //----- security handlers

  void addSecurityHandler(XpdfSecurityHandler *handler);
  XpdfSecurityHandler *getSecurityHandler(char *name);

private:

  void createDefaultKeyBindings();
  void parseFile(GString *fileName, FILE *f);
  void parseNameToUnicode(GList *tokens, GString *fileName, int line);
  void parseCIDToUnicode(GList *tokens, GString *fileName, int line);
  void parseUnicodeToUnicode(GList *tokens, GString *fileName, int line);
  void parseUnicodeMap(GList *tokens, GString *fileName, int line);
  void parseCMapDir(GList *tokens, GString *fileName, int line);
  void parseToUnicodeDir(GList *tokens, GString *fileName, int line);
  void parseDisplayFont(GList *tokens, GHash *fontHash,
			DisplayFontParamKind kind,
			GString *fileName, int line);
  void parsePSFile(GList *tokens, GString *fileName, int line);
  void parsePSPaperSize(GList *tokens, GString *fileName, int line);
  void parsePSImageableArea(GList *tokens, GString *fileName, int line);
  void parsePSLevel(GList *tokens, GString *fileName, int line);
  void parsePSFont(GList *tokens, GString *fileName, int line);
  void parsePSFont16(char *cmdName, GList *fontList,
		     GList *tokens, GString *fileName, int line);
  void parseTextEncoding(GList *tokens, GString *fileName, int line);
  void parseTextEOL(GList *tokens, GString *fileName, int line);
  void parseFontDir(GList *tokens, GString *fileName, int line);
  void parseInitialZoom(GList *tokens, GString *fileName, int line);
  void parseScreenType(GList *tokens, GString *fileName, int line);
  void parseBind(GList *tokens, GString *fileName, int line);
  void parseUnbind(GList *tokens, GString *fileName, int line);
  GBool parseKey(GString *modKeyStr, GString *contextStr,
		 int *code, int *mods, int *context,
		 char *cmdName,
		 GList *tokens, GString *fileName, int line);
  void parseCommand(char *cmdName, GString **val,
		    GList *tokens, GString *fileName, int line);
  void parseYesNo(char *cmdName, GBool *flag,
		  GList *tokens, GString *fileName, int line);
  GBool parseYesNo2(char *token, GBool *flag);
  void parseInteger(char *cmdName, int *val,
		    GList *tokens, GString *fileName, int line);
  void parseFloat(char *cmdName, double *val,
		  GList *tokens, GString *fileName, int line);
  UnicodeMap *getUnicodeMap2(GString *encodingName);
#ifdef ENABLE_PLUGINS
  GBool loadPlugin(char *type, char *name);
#endif

  //----- static tables

  NameToCharCode *		// mapping from char name to
    macRomanReverseMap;		//   MacRomanEncoding index

  //----- user-modifiable settings

  GString *baseDir;		// base directory - for plugins, etc.
  NameToCharCode *		// mapping from char name to Unicode
    nameToUnicode;
  GHash *cidToUnicodes;		// files for mappings from char collections
				//   to Unicode, indexed by collection name
				//   [GString]
  GHash *unicodeToUnicodes;	// files for Unicode-to-Unicode mappings,
				//   indexed by font name pattern [GString]
  GHash *residentUnicodeMaps;	// mappings from Unicode to char codes,
				//   indexed by encoding name [UnicodeMap]
  GHash *unicodeMaps;		// files for mappings from Unicode to char
				//   codes, indexed by encoding name [GString]
  GHash *cMapDirs;		// list of CMap dirs, indexed by collection
				//   name [GList[GString]]
  GList *toUnicodeDirs;		// list of ToUnicode CMap dirs [GString]
  GHash *displayFonts;		// display font info, indexed by font name
				//   [DisplayFontParam]
#ifdef WIN32
  WinFontList *winFontList;	// system TrueType fonts
#endif
  GHash *displayCIDFonts;	// display CID font info, indexed by
				//   collection [DisplayFontParam]
  GHash *displayNamedCIDFonts;	// display CID font info, indexed by
				//   font name [DisplayFontParam]
  GString *psFile;		// PostScript file or command (for xpdf)
  int psPaperWidth;		// paper size, in PostScript points, for
  int psPaperHeight;		//   PostScript output
  int psImageableLLX,		// imageable area, in PostScript points,
      psImageableLLY,		//   for PostScript output
      psImageableURX,
      psImageableURY;
  GBool psCrop;			// crop PS output to CropBox
  GBool psExpandSmaller;	// expand smaller pages to fill paper
  GBool psShrinkLarger;		// shrink larger pages to fit paper
  GBool psCenter;		// center pages on the paper
  GBool psDuplex;		// enable duplexing in PostScript?
  GBool psNoText;		// don't output text in PostScript
  PSLevel psLevel;		// PostScript level to generate
  GHash *psFonts;		// PostScript font info, indexed by PDF
				//   font name [PSFontParam]
  GList *psNamedFonts16;	// named 16-bit fonts [PSFontParam]
  GList *psFonts16;		// generic 16-bit fonts [PSFontParam]
  GBool psEmbedType1;		// embed Type 1 fonts?
  GBool psEmbedTrueType;	// embed TrueType fonts?
  GBool psEmbedCIDPostScript;	// embed CID PostScript fonts?
  GBool psEmbedCIDTrueType;	// embed CID TrueType fonts?
  GBool psPreload;		// preload PostScript images and forms into
				//   memory
  GBool psOPI;			// generate PostScript OPI comments?
  GBool psASCIIHex;		// use ASCIIHex instead of ASCII85?
  GString *textEncoding;	// encoding (unicodeMap) to use for text
				//   output
  EndOfLineKind textEOL;	// type of EOL marker to use for text
				//   output
  GBool textPageBreaks;		// insert end-of-page markers?
  GBool textKeepTinyChars;	// keep all characters in text output
  GList *fontDirs;		// list of font dirs [GString]
  GString *initialZoom;		// initial zoom level
  GBool continuousView;		// continuous view mode
  GBool enableT1lib;		// t1lib enable flag
  GBool enableFreeType;		// FreeType enable flag
  GBool antialias;		// font anti-aliasing enable flag
  GBool vectorAntialias;	// vector anti-aliasing enable flag
  GBool strokeAdjust;		// stroke adjustment enable flag
  ScreenType screenType;	// halftone screen type
  int screenSize;		// screen matrix size
  int screenDotRadius;		// screen dot radius
  double screenGamma;		// screen gamma correction
  double screenBlackThreshold;	// screen black clamping threshold
  double screenWhiteThreshold;	// screen white clamping threshold
  GString *urlCommand;		// command executed for URL links
  GString *movieCommand;	// command executed for movie annotations
  GBool mapNumericCharNames;	// map numeric char names (from font subsets)?
  GBool mapUnknownCharNames;	// map unknown char names?
  GList *keyBindings;		// key & mouse button bindings [KeyBinding]
  GBool printCommands;		// print the drawing commands
  GBool errQuiet;		// suppress error messages?

  CharCodeToUnicodeCache *cidToUnicodeCache;
  CharCodeToUnicodeCache *unicodeToUnicodeCache;
  UnicodeMapCache *unicodeMapCache;
  CMapCache *cMapCache;

#ifdef ENABLE_PLUGINS
  GList *plugins;		// list of plugins [Plugin]
  GList *securityHandlers;	// list of loaded security handlers
				//   [XpdfSecurityHandler]
#endif

#if MULTITHREADED
  GMutex mutex;
  GMutex unicodeMapCacheMutex;
  GMutex cMapCacheMutex;
#endif
};

#endif
//========================================================================
//
// GlobalParams.h
//
// Copyright 2001-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef GLOBALPARAMS_H
#define GLOBALPARAMS_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include <stdio.h>
#include "gtypes.h"
#include "CharTypes.h"

#if MULTITHREADED
#include "GMutex.h"
#endif

class GString;
class GList;
class GHash;
class NameToCharCode;
class CharCodeToUnicode;
class CharCodeToUnicodeCache;
class UnicodeMap;
class UnicodeMapCache;
class CMap;
class CMapCache;
struct XpdfSecurityHandler;
class GlobalParams;
#ifdef WIN32
class WinFontList;
#endif

//------------------------------------------------------------------------

// The global parameters object.
extern GlobalParams *globalParams;

//------------------------------------------------------------------------

enum DisplayFontParamKind {
  displayFontT1,
  displayFontTT
};

struct DisplayFontParamT1 {
  GString *fileName;
};

struct DisplayFontParamTT {
  GString *fileName;
};

class DisplayFontParam {
public:

  GString *name;		// font name for 8-bit fonts and named
				//   CID fonts; collection name for
				//   generic CID fonts
  DisplayFontParamKind kind;
  union {
    DisplayFontParamT1 t1;
    DisplayFontParamTT tt;
  };

  DisplayFontParam(GString *nameA, DisplayFontParamKind kindA);
  virtual ~DisplayFontParam();
};

//------------------------------------------------------------------------

class PSFontParam {
public:

  GString *pdfFontName;		// PDF font name for 8-bit fonts and
				//   named 16-bit fonts; char collection
				//   name for generic 16-bit fonts
  int wMode;			// writing mode (0=horiz, 1=vert) for
				//   16-bit fonts
  GString *psFontName;		// PostScript font name
  GString *encoding;		// encoding, for 16-bit fonts only

  PSFontParam(GString *pdfFontNameA, int wModeA,
	      GString *psFontNameA, GString *encodingA);
  ~PSFontParam();
};

//------------------------------------------------------------------------

enum PSLevel {
  psLevel1,
  psLevel1Sep,
  psLevel2,
  psLevel2Sep,
  psLevel3,
  psLevel3Sep
};

//------------------------------------------------------------------------

enum EndOfLineKind {
  eolUnix,			// LF
  eolDOS,			// CR+LF
  eolMac			// CR
};

//------------------------------------------------------------------------

enum ScreenType {
  screenUnset,
  screenDispersed,
  screenClustered,
  screenStochasticClustered
};

//------------------------------------------------------------------------

class KeyBinding {
public:

  int code;			// 0x20 .. 0xfe = ASCII,
				//   >=0x10000 = special keys, mouse buttons,
				//   etc. (xpdfKeyCode* symbols)
  int mods;			// modifiers (xpdfKeyMod* symbols, or-ed
				//   together)
  int context;			// context (xpdfKeyContext* symbols, or-ed
				//   together)
  GList *cmds;			// list of commands [GString]

  KeyBinding(int codeA, int modsA, int contextA, char *cmd0);
  KeyBinding(int codeA, int modsA, int contextA, char *cmd0, char *cmd1);
  KeyBinding(int codeA, int modsA, int contextA, GList *cmdsA);
  ~KeyBinding();
};

#define xpdfKeyCodeTab            0x1000
#define xpdfKeyCodeReturn         0x1001
#define xpdfKeyCodeEnter          0x1002
#define xpdfKeyCodeBackspace      0x1003
#define xpdfKeyCodeInsert         0x1004
#define xpdfKeyCodeDelete         0x1005
#define xpdfKeyCodeHome           0x1006
#define xpdfKeyCodeEnd            0x1007
#define xpdfKeyCodePgUp           0x1008
#define xpdfKeyCodePgDn           0x1009
#define xpdfKeyCodeLeft           0x100a
#define xpdfKeyCodeRight          0x100b
#define xpdfKeyCodeUp             0x100c
#define xpdfKeyCodeDown           0x100d
#define xpdfKeyCodeF1             0x1100
#define xpdfKeyCodeF35            0x1122
#define xpdfKeyCodeMousePress1    0x2001
#define xpdfKeyCodeMousePress2    0x2002
#define xpdfKeyCodeMousePress3    0x2003
#define xpdfKeyCodeMousePress4    0x2004
#define xpdfKeyCodeMousePress5    0x2005
#define xpdfKeyCodeMousePress6    0x2006
#define xpdfKeyCodeMousePress7    0x2007
#define xpdfKeyCodeMouseRelease1  0x2101
#define xpdfKeyCodeMouseRelease2  0x2102
#define xpdfKeyCodeMouseRelease3  0x2103
#define xpdfKeyCodeMouseRelease4  0x2104
#define xpdfKeyCodeMouseRelease5  0x2105
#define xpdfKeyCodeMouseRelease6  0x2106
#define xpdfKeyCodeMouseRelease7  0x2107
#define xpdfKeyModNone            0
#define xpdfKeyModShift           (1 << 0)
#define xpdfKeyModCtrl            (1 << 1)
#define xpdfKeyModAlt             (1 << 2)
#define xpdfKeyContextAny         0
#define xpdfKeyContextFullScreen  (1 << 0)
#define xpdfKeyContextWindow      (2 << 0)
#define xpdfKeyContextContinuous  (1 << 2)
#define xpdfKeyContextSinglePage  (2 << 2)
#define xpdfKeyContextOverLink    (1 << 4)
#define xpdfKeyContextOffLink     (2 << 4)
#define xpdfKeyContextOutline     (1 << 6)
#define xpdfKeyContextMainWin     (2 << 6)
#define xpdfKeyContextScrLockOn   (1 << 8)
#define xpdfKeyContextScrLockOff  (2 << 8)

//------------------------------------------------------------------------

class GlobalParams {
public:

  // Initialize the global parameters by attempting to read a config
  // file.
  GlobalParams(char *cfgFileName);

  ~GlobalParams();

  void setBaseDir(char *dir);
  void setupBaseFonts(char *dir);

  void parseLine(char *buf, GString *fileName, int line);

  //----- accessors

  CharCode getMacRomanCharCode(char *charName);

  GString *getBaseDir();
  Unicode mapNameToUnicode(char *charName);
  UnicodeMap *getResidentUnicodeMap(GString *encodingName);
  FILE *getUnicodeMapFile(GString *encodingName);
  FILE *findCMapFile(GString *collection, GString *cMapName);
  FILE *findToUnicodeFile(GString *name);
  DisplayFontParam *getDisplayFont(GString *fontName);
  DisplayFontParam *getDisplayCIDFont(GString *fontName, GString *collection);
  GString *getPSFile();
  int getPSPaperWidth();
  int getPSPaperHeight();
  void getPSImageableArea(int *llx, int *lly, int *urx, int *ury);
  GBool getPSDuplex();
  GBool getPSCrop();
  GBool getPSExpandSmaller();
  GBool getPSShrinkLarger();
  GBool getPSCenter();
  PSLevel getPSLevel();
  PSFontParam *getPSFont(GString *fontName);
  PSFontParam *getPSFont16(GString *fontName, GString *collection, int wMode);
  GBool getPSEmbedType1();
  GBool getPSEmbedTrueType();
  GBool getPSEmbedCIDPostScript();
  GBool getPSEmbedCIDTrueType();
  GBool getPSPreload();
  GBool getPSOPI();
  GBool getPSASCIIHex();
  GString *getTextEncodingName();
  EndOfLineKind getTextEOL();
  GBool getTextPageBreaks();
  GBool getTextKeepTinyChars();
  GString *findFontFile(GString *fontName, char **exts);
  GString *getInitialZoom();
  GBool getContinuousView();
  GBool getEnableT1lib();
  GBool getEnableFreeType();
  GBool getAntialias();
  GBool getVectorAntialias();
  GBool getStrokeAdjust();
  ScreenType getScreenType();
  int getScreenSize();
  int getScreenDotRadius();
  double getScreenGamma();
  double getScreenBlackThreshold();
  double getScreenWhiteThreshold();
  GString *getURLCommand() { return urlCommand; }
  GString *getMovieCommand() { return movieCommand; }
  GBool getMapNumericCharNames();
  GBool getMapUnknownCharNames();
  GList *getKeyBinding(int code, int mods, int context);
  GBool getPrintCommands();
  GBool getErrQuiet();

  CharCodeToUnicode *getCIDToUnicode(GString *collection);
  CharCodeToUnicode *getUnicodeToUnicode(GString *fontName);
  UnicodeMap *getUnicodeMap(GString *encodingName);
  CMap *getCMap(GString *collection, GString *cMapName);
  UnicodeMap *getTextEncoding();

  //----- functions to set parameters

  void addDisplayFont(DisplayFontParam *param);
  void setPSFile(char *file);
  GBool setPSPaperSize(char *size);
  void setPSPaperWidth(int width);
  void setPSPaperHeight(int height);
  void setPSImageableArea(int llx, int lly, int urx, int ury);
  void setPSDuplex(GBool duplex);
  void setPSCrop(GBool crop);
  void setPSExpandSmaller(GBool expand);
  void setPSShrinkLarger(GBool shrink);
  void setPSCenter(GBool center);
  void setPSLevel(PSLevel level);
  void setPSEmbedType1(GBool embed);
  void setPSEmbedTrueType(GBool embed);
  void setPSEmbedCIDPostScript(GBool embed);
  void setPSEmbedCIDTrueType(GBool embed);
  void setPSPreload(GBool preload);
  void setPSOPI(GBool opi);
  void setPSASCIIHex(GBool hex);
  void setTextEncoding(char *encodingName);
  GBool setTextEOL(char *s);
  void setTextPageBreaks(GBool pageBreaks);
  void setTextKeepTinyChars(GBool keep);
  void setInitialZoom(char *s);
  void setContinuousView(GBool cont);
  GBool setEnableT1lib(char *s);
  GBool setEnableFreeType(char *s);
  GBool setAntialias(char *s);
  GBool setVectorAntialias(char *s);
  void setScreenType(ScreenType t);
  void setScreenSize(int size);
  void setScreenDotRadius(int r);
  void setScreenGamma(double gamma);
  void setScreenBlackThreshold(double thresh);
  void setScreenWhiteThreshold(double thresh);
  void setMapNumericCharNames(GBool map);
  void setMapUnknownCharNames(GBool map);
  void setPrintCommands(GBool printCommandsA);
  void setErrQuiet(GBool errQuietA);

  //----- security handlers

  void addSecurityHandler(XpdfSecurityHandler *handler);
  XpdfSecurityHandler *getSecurityHandler(char *name);

private:

  void createDefaultKeyBindings();
  void parseFile(GString *fileName, FILE *f);
  void parseNameToUnicode(GList *tokens, GString *fileName, int line);
  void parseCIDToUnicode(GList *tokens, GString *fileName, int line);
  void parseUnicodeToUnicode(GList *tokens, GString *fileName, int line);
  void parseUnicodeMap(GList *tokens, GString *fileName, int line);
  void parseCMapDir(GList *tokens, GString *fileName, int line);
  void parseToUnicodeDir(GList *tokens, GString *fileName, int line);
  void parseDisplayFont(GList *tokens, GHash *fontHash,
			DisplayFontParamKind kind,
			GString *fileName, int line);
  void parsePSFile(GList *tokens, GString *fileName, int line);
  void parsePSPaperSize(GList *tokens, GString *fileName, int line);
  void parsePSImageableArea(GList *tokens, GString *fileName, int line);
  void parsePSLevel(GList *tokens, GString *fileName, int line);
  void parsePSFont(GList *tokens, GString *fileName, int line);
  void parsePSFont16(char *cmdName, GList *fontList,
		     GList *tokens, GString *fileName, int line);
  void parseTextEncoding(GList *tokens, GString *fileName, int line);
  void parseTextEOL(GList *tokens, GString *fileName, int line);
  void parseFontDir(GList *tokens, GString *fileName, int line);
  void parseInitialZoom(GList *tokens, GString *fileName, int line);
  void parseScreenType(GList *tokens, GString *fileName, int line);
  void parseBind(GList *tokens, GString *fileName, int line);
  void parseUnbind(GList *tokens, GString *fileName, int line);
  GBool parseKey(GString *modKeyStr, GString *contextStr,
		 int *code, int *mods, int *context,
		 char *cmdName,
		 GList *tokens, GString *fileName, int line);
  void parseCommand(char *cmdName, GString **val,
		    GList *tokens, GString *fileName, int line);
  void parseYesNo(char *cmdName, GBool *flag,
		  GList *tokens, GString *fileName, int line);
  GBool parseYesNo2(char *token, GBool *flag);
  void parseInteger(char *cmdName, int *val,
		    GList *tokens, GString *fileName, int line);
  void parseFloat(char *cmdName, double *val,
		  GList *tokens, GString *fileName, int line);
  UnicodeMap *getUnicodeMap2(GString *encodingName);
#ifdef ENABLE_PLUGINS
  GBool loadPlugin(char *type, char *name);
#endif

  //----- static tables

  NameToCharCode *		// mapping from char name to
    macRomanReverseMap;		//   MacRomanEncoding index

  //----- user-modifiable settings

  GString *baseDir;		// base directory - for plugins, etc.
  NameToCharCode *		// mapping from char name to Unicode
    nameToUnicode;
  GHash *cidToUnicodes;		// files for mappings from char collections
				//   to Unicode, indexed by collection name
				//   [GString]
  GHash *unicodeToUnicodes;	// files for Unicode-to-Unicode mappings,
				//   indexed by font name pattern [GString]
  GHash *residentUnicodeMaps;	// mappings from Unicode to char codes,
				//   indexed by encoding name [UnicodeMap]
  GHash *unicodeMaps;		// files for mappings from Unicode to char
				//   codes, indexed by encoding name [GString]
  GHash *cMapDirs;		// list of CMap dirs, indexed by collection
				//   name [GList[GString]]
  GList *toUnicodeDirs;		// list of ToUnicode CMap dirs [GString]
  GHash *displayFonts;		// display font info, indexed by font name
				//   [DisplayFontParam]
#ifdef WIN32
  WinFontList *winFontList;	// system TrueType fonts
#endif
  GHash *displayCIDFonts;	// display CID font info, indexed by
				//   collection [DisplayFontParam]
  GHash *displayNamedCIDFonts;	// display CID font info, indexed by
				//   font name [DisplayFontParam]
  GString *psFile;		// PostScript file or command (for xpdf)
  int psPaperWidth;		// paper size, in PostScript points, for
  int psPaperHeight;		//   PostScript output
  int psImageableLLX,		// imageable area, in PostScript points,
      psImageableLLY,		//   for PostScript output
      psImageableURX,
      psImageableURY;
  GBool psCrop;			// crop PS output to CropBox
  GBool psExpandSmaller;	// expand smaller pages to fill paper
  GBool psShrinkLarger;		// shrink larger pages to fit paper
  GBool psCenter;		// center pages on the paper
  GBool psDuplex;		// enable duplexing in PostScript?
  PSLevel psLevel;		// PostScript level to generate
  GHash *psFonts;		// PostScript font info, indexed by PDF
				//   font name [PSFontParam]
  GList *psNamedFonts16;	// named 16-bit fonts [PSFontParam]
  GList *psFonts16;		// generic 16-bit fonts [PSFontParam]
  GBool psEmbedType1;		// embed Type 1 fonts?
  GBool psEmbedTrueType;	// embed TrueType fonts?
  GBool psEmbedCIDPostScript;	// embed CID PostScript fonts?
  GBool psEmbedCIDTrueType;	// embed CID TrueType fonts?
  GBool psPreload;		// preload PostScript images and forms into
				//   memory
  GBool psOPI;			// generate PostScript OPI comments?
  GBool psASCIIHex;		// use ASCIIHex instead of ASCII85?
  GString *textEncoding;	// encoding (unicodeMap) to use for text
				//   output
  EndOfLineKind textEOL;	// type of EOL marker to use for text
				//   output
  GBool textPageBreaks;		// insert end-of-page markers?
  GBool textKeepTinyChars;	// keep all characters in text output
  GList *fontDirs;		// list of font dirs [GString]
  GString *initialZoom;		// initial zoom level
  GBool continuousView;		// continuous view mode
  GBool enableT1lib;		// t1lib enable flag
  GBool enableFreeType;		// FreeType enable flag
  GBool antialias;		// font anti-aliasing enable flag
  GBool vectorAntialias;	// vector anti-aliasing enable flag
  GBool strokeAdjust;		// stroke adjustment enable flag
  ScreenType screenType;	// halftone screen type
  int screenSize;		// screen matrix size
  int screenDotRadius;		// screen dot radius
  double screenGamma;		// screen gamma correction
  double screenBlackThreshold;	// screen black clamping threshold
  double screenWhiteThreshold;	// screen white clamping threshold
  GString *urlCommand;		// command executed for URL links
  GString *movieCommand;	// command executed for movie annotations
  GBool mapNumericCharNames;	// map numeric char names (from font subsets)?
  GBool mapUnknownCharNames;	// map unknown char names?
  GList *keyBindings;		// key & mouse button bindings [KeyBinding]
  GBool printCommands;		// print the drawing commands
  GBool errQuiet;		// suppress error messages?

  CharCodeToUnicodeCache *cidToUnicodeCache;
  CharCodeToUnicodeCache *unicodeToUnicodeCache;
  UnicodeMapCache *unicodeMapCache;
  CMapCache *cMapCache;

#ifdef ENABLE_PLUGINS
  GList *plugins;		// list of plugins [Plugin]
  GList *securityHandlers;	// list of loaded security handlers
				//   [XpdfSecurityHandler]
#endif

#if MULTITHREADED
  GMutex mutex;
  GMutex unicodeMapCacheMutex;
  GMutex cMapCacheMutex;
#endif
};

#endif
//========================================================================
//
// GooLikely.h
//
// This file is licensed under the GPLv2 or later
//
// Copyright (C) 2008 Kees Cook <kees@outflux.net>
//
//========================================================================

#ifndef GOOLIKELY_H
#define GOOLIKELY_H

#if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__)
# define likely(x)      __builtin_expect((x), 1)
# define unlikely(x)    __builtin_expect((x), 0)
#else
# define likely(x)      (x)
# define unlikely(x)    (x)
#endif

#endif
//========================================================================
//
// ImageOutputDev.cc
//
// Copyright 1998-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <ctype.h>
#include "gmem.h"
#include "config.h"
#include "Error.h"
#include "GfxState.h"
#include "Object.h"
#include "Stream.h"
#include "ImageOutputDev.h"

ImageOutputDev::ImageOutputDev(char *fileRootA, GBool dumpJPEGA) {
  fileRoot = copyString(fileRootA);
  fileName = (char *)gmalloc(strlen(fileRoot) + 20);
  dumpJPEG = dumpJPEGA;
  imgNum = 0;
  ok = gTrue;
}

ImageOutputDev::~ImageOutputDev() {
  gfree(fileName);
  gfree(fileRoot);
}

void ImageOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
				   int width, int height, GBool invert,
				   GBool inlineImg) {
  FILE *f;
  int c;
  int size, i;

  // dump JPEG file
  if (dumpJPEG && str->getKind() == strDCT && !inlineImg) {

    // open the image file
    sprintf(fileName, "%s-%03d.jpg", fileRoot, imgNum);
    ++imgNum;
    if (!(f = fopen(fileName, "wb"))) {
      error(-1, "Couldn't open image file '%s'", fileName);
      return;
    }

    // initialize stream
    str = ((DCTStream *)str)->getRawStream();
    str->reset();

    // copy the stream
    while ((c = str->getChar()) != EOF)
      fputc(c, f);

    str->close();
    fclose(f);

  // dump PBM file
  } else {

    // open the image file and write the PBM header
    sprintf(fileName, "%s-%03d.pbm", fileRoot, imgNum);
    ++imgNum;
    if (!(f = fopen(fileName, "wb"))) {
      error(-1, "Couldn't open image file '%s'", fileName);
      return;
    }
    fprintf(f, "P4\n");
    fprintf(f, "%d %d\n", width, height);

    // initialize stream
    str->reset();

    // copy the stream
    size = height * ((width + 7) / 8);
    for (i = 0; i < size; ++i) {
      fputc(str->getChar(), f);
    }

    str->close();
    fclose(f);
  }
}

void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
			       int width, int height,
			       GfxImageColorMap *colorMap,
			       int *maskColors, GBool inlineImg) {
  FILE *f;
  ImageStream *imgStr;
  Guchar *p;
  GfxRGB rgb;
  int x, y;
  int c;
  int size, i;

  // dump JPEG file
  if (dumpJPEG && str->getKind() == strDCT &&
      (colorMap->getNumPixelComps() == 1 ||
       colorMap->getNumPixelComps() == 3) &&
      !inlineImg) {

    // open the image file
    sprintf(fileName, "%s-%03d.jpg", fileRoot, imgNum);
    ++imgNum;
    if (!(f = fopen(fileName, "wb"))) {
      error(-1, "Couldn't open image file '%s'", fileName);
      return;
    }

    // initialize stream
    str = ((DCTStream *)str)->getRawStream();
    str->reset();

    // copy the stream
    while ((c = str->getChar()) != EOF)
      fputc(c, f);

    str->close();
    fclose(f);

  // dump PBM file
  } else if (colorMap->getNumPixelComps() == 1 &&
	     colorMap->getBits() == 1) {

    // open the image file and write the PBM header
    sprintf(fileName, "%s-%03d.pbm", fileRoot, imgNum);
    ++imgNum;
    if (!(f = fopen(fileName, "wb"))) {
      error(-1, "Couldn't open image file '%s'", fileName);
      return;
    }
    fprintf(f, "P4\n");
    fprintf(f, "%d %d\n", width, height);

    // initialize stream
    str->reset();

    // copy the stream
    size = height * ((width + 7) / 8);
    for (i = 0; i < size; ++i) {
      fputc(str->getChar() ^ 0xff, f);
    }

    str->close();
    fclose(f);

  // dump PPM file
  } else {

    // open the image file and write the PPM header
    sprintf(fileName, "%s-%03d.ppm", fileRoot, imgNum);
    ++imgNum;
    if (!(f = fopen(fileName, "wb"))) {
      error(-1, "Couldn't open image file '%s'", fileName);
      return;
    }
    fprintf(f, "P6\n");
    fprintf(f, "%d %d\n", width, height);
    fprintf(f, "255\n");

    // initialize stream
    imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
			     colorMap->getBits());
    imgStr->reset();

    // for each line...
    for (y = 0; y < height; ++y) {

      // write the line
      p = imgStr->getLine();
      for (x = 0; x < width; ++x) {
	colorMap->getRGB(p, &rgb);
	fputc(colToByte(rgb.r), f);
	fputc(colToByte(rgb.g), f);
	fputc(colToByte(rgb.b), f);
	p += colorMap->getNumPixelComps();
      }
    }
    delete imgStr;

    fclose(f);
  }
}
//========================================================================
//
// ImageOutputDev.h
//
// Copyright 1998-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef IMAGEOUTPUTDEV_H
#define IMAGEOUTPUTDEV_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include <stdio.h>
#include "gtypes.h"
#include "OutputDev.h"

class GfxState;

//------------------------------------------------------------------------
// ImageOutputDev
//------------------------------------------------------------------------

class ImageOutputDev: public OutputDev {
public:

  // Create an OutputDev which will write images to files named
  // <fileRoot>-NNN.<type>.  Normally, all images are written as PBM
  // (.pbm) or PPM (.ppm) files.  If <dumpJPEG> is set, JPEG images are
  // written as JPEG (.jpg) files.
  ImageOutputDev(char *fileRootA, GBool dumpJPEGA);

  // Destructor.
  virtual ~ImageOutputDev();

  // Check if file was successfully created.
  virtual GBool isOk() { return ok; }

  // Does this device use beginType3Char/endType3Char?  Otherwise,
  // text in Type 3 fonts will be drawn with drawChar/drawString.
  virtual GBool interpretType3Chars() { return gFalse; }

  // Does this device need non-text content?
  virtual GBool needNonText() { return gTrue; }

  //---- get info about output device

  // Does this device use upside-down coordinates?
  // (Upside-down means (0,0) is the top left corner of the page.)
  virtual GBool upsideDown() { return gTrue; }

  // Does this device use drawChar() or drawString()?
  virtual GBool useDrawChar() { return gFalse; }

  //----- image drawing
  virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
			     int width, int height, GBool invert,
			     GBool inlineImg);
  virtual void drawImage(GfxState *state, Object *ref, Stream *str,
			 int width, int height, GfxImageColorMap *colorMap,
			 int *maskColors, GBool inlineImg);

private:

  char *fileRoot;		// root of output file names
  char *fileName;		// buffer for output file names
  GBool dumpJPEG;		// set to dump native JPEG files
  int imgNum;			// current image number
  GBool ok;			// set up ok?
};

#endif
//========================================================================
//
// JArithmeticDecoder.cc
//
// Copyright 2002-2004 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include "Object.h"
#include "Stream.h"
#include "JArithmeticDecoder.h"

//------------------------------------------------------------------------
// JArithmeticDecoderStates
//------------------------------------------------------------------------

JArithmeticDecoderStats::JArithmeticDecoderStats(int contextSizeA) {
  contextSize = contextSizeA;
  cxTab = (Guchar *)gmallocn(contextSize, sizeof(Guchar));
  reset();
}

JArithmeticDecoderStats::~JArithmeticDecoderStats() {
  gfree(cxTab);
}

JArithmeticDecoderStats *JArithmeticDecoderStats::copy() {
  JArithmeticDecoderStats *stats;

  stats = new JArithmeticDecoderStats(contextSize);
  memcpy(stats->cxTab, cxTab, contextSize);
  return stats;
}

void JArithmeticDecoderStats::reset() {
  memset(cxTab, 0, contextSize);
}

void JArithmeticDecoderStats::copyFrom(JArithmeticDecoderStats *stats) {
  memcpy(cxTab, stats->cxTab, contextSize);
}

void JArithmeticDecoderStats::setEntry(Guint cx, int i, int mps) {
  cxTab[cx] = (i << 1) + mps;
}

//------------------------------------------------------------------------
// JArithmeticDecoder
//------------------------------------------------------------------------

Guint JArithmeticDecoder::qeTab[47] = {
  0x56010000, 0x34010000, 0x18010000, 0x0AC10000,
  0x05210000, 0x02210000, 0x56010000, 0x54010000,
  0x48010000, 0x38010000, 0x30010000, 0x24010000,
  0x1C010000, 0x16010000, 0x56010000, 0x54010000,
  0x51010000, 0x48010000, 0x38010000, 0x34010000,
  0x30010000, 0x28010000, 0x24010000, 0x22010000,
  0x1C010000, 0x18010000, 0x16010000, 0x14010000,
  0x12010000, 0x11010000, 0x0AC10000, 0x09C10000,
  0x08A10000, 0x05210000, 0x04410000, 0x02A10000,
  0x02210000, 0x01410000, 0x01110000, 0x00850000,
  0x00490000, 0x00250000, 0x00150000, 0x00090000,
  0x00050000, 0x00010000, 0x56010000
};

int JArithmeticDecoder::nmpsTab[47] = {
   1,  2,  3,  4,  5, 38,  7,  8,  9, 10, 11, 12, 13, 29, 15, 16,
  17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
  33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 45, 46
};

int JArithmeticDecoder::nlpsTab[47] = {
   1,  6,  9, 12, 29, 33,  6, 14, 14, 14, 17, 18, 20, 21, 14, 14,
  15, 16, 17, 18, 19, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
  30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 46
};

int JArithmeticDecoder::switchTab[47] = {
  1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

JArithmeticDecoder::JArithmeticDecoder() {
  str = NULL;
  dataLen = 0;
  limitStream = gFalse;
}

inline Guint JArithmeticDecoder::readByte() {
  if (limitStream) {
    --dataLen;
    if (dataLen < 0) {
      return 0xff;
    }
  }
  return (Guint)str->getChar() & 0xff;
}

JArithmeticDecoder::~JArithmeticDecoder() {
  cleanup();
}

void JArithmeticDecoder::start() {
  buf0 = readByte();
  buf1 = readByte();

  // INITDEC
  c = (buf0 ^ 0xff) << 16;
  byteIn();
  c <<= 7;
  ct -= 7;
  a = 0x80000000;
}

void JArithmeticDecoder::restart(int dataLenA) {
  int oldDataLen;

  oldDataLen = dataLen;
  dataLen = dataLenA;
  if (oldDataLen == -1) {
    buf1 = readByte();
  } else if (oldDataLen <= -2) {
    buf0 = readByte();
    buf1 = readByte();
  }
}

void JArithmeticDecoder::cleanup() {
  if (limitStream) {
    while (dataLen > 0) {
      buf0 = buf1;
      buf1 = readByte();
    }
  }
}

int JArithmeticDecoder::decodeBit(Guint context,
				  JArithmeticDecoderStats *stats) {
  int bit;
  Guint qe;
  int iCX, mpsCX;

  iCX = stats->cxTab[context] >> 1;
  mpsCX = stats->cxTab[context] & 1;
  qe = qeTab[iCX];
  a -= qe;
  if (c < a) {
    if (a & 0x80000000) {
      bit = mpsCX;
    } else {
      // MPS_EXCHANGE
      if (a < qe) {
	bit = 1 - mpsCX;
	if (switchTab[iCX]) {
	  stats->cxTab[context] = (nlpsTab[iCX] << 1) | (1 - mpsCX);
	} else {
	  stats->cxTab[context] = (nlpsTab[iCX] << 1) | mpsCX;
	}
      } else {
	bit = mpsCX;
	stats->cxTab[context] = (nmpsTab[iCX] << 1) | mpsCX;
      }
      // RENORMD
      do {
	if (ct == 0) {
	  byteIn();
	}
	a <<= 1;
	c <<= 1;
	--ct;
      } while (!(a & 0x80000000));
    }
  } else {
    c -= a;
    // LPS_EXCHANGE
    if (a < qe) {
      bit = mpsCX;
      stats->cxTab[context] = (nmpsTab[iCX] << 1) | mpsCX;
    } else {
      bit = 1 - mpsCX;
      if (switchTab[iCX]) {
	stats->cxTab[context] = (nlpsTab[iCX] << 1) | (1 - mpsCX);
      } else {
	stats->cxTab[context] = (nlpsTab[iCX] << 1) | mpsCX;
      }
    }
    a = qe;
    // RENORMD
    do {
      if (ct == 0) {
	byteIn();
      }
      a <<= 1;
      c <<= 1;
      --ct;
    } while (!(a & 0x80000000));
  }
  return bit;
}

int JArithmeticDecoder::decodeByte(Guint context,
				   JArithmeticDecoderStats *stats) {
  int byte;
  int i;

  byte = 0;
  for (i = 0; i < 8; ++i) {
    byte = (byte << 1) | decodeBit(context, stats);
  }
  return byte;
}

GBool JArithmeticDecoder::decodeInt(int *x, JArithmeticDecoderStats *stats) {
  int s;
  Guint v;
  int i;

  prev = 1;
  s = decodeIntBit(stats);
  if (decodeIntBit(stats)) {
    if (decodeIntBit(stats)) {
      if (decodeIntBit(stats)) {
	if (decodeIntBit(stats)) {
	  if (decodeIntBit(stats)) {
	    v = 0;
	    for (i = 0; i < 32; ++i) {
	      v = (v << 1) | decodeIntBit(stats);
	    }
	    v += 4436;
	  } else {
	    v = 0;
	    for (i = 0; i < 12; ++i) {
	      v = (v << 1) | decodeIntBit(stats);
	    }
	    v += 340;
	  }
	} else {
	  v = 0;
	  for (i = 0; i < 8; ++i) {
	    v = (v << 1) | decodeIntBit(stats);
	  }
	  v += 84;
	}
      } else {
	v = 0;
	for (i = 0; i < 6; ++i) {
	  v = (v << 1) | decodeIntBit(stats);
	}
	v += 20;
      }
    } else {
      v = decodeIntBit(stats);
      v = (v << 1) | decodeIntBit(stats);
      v = (v << 1) | decodeIntBit(stats);
      v = (v << 1) | decodeIntBit(stats);
      v += 4;
    }
  } else {
    v = decodeIntBit(stats);
    v = (v << 1) | decodeIntBit(stats);
  }

  if (s) {
    if (v == 0) {
      return gFalse;
    }
    *x = -(int)v;
  } else {
    *x = (int)v;
  }
  return gTrue;
}

int JArithmeticDecoder::decodeIntBit(JArithmeticDecoderStats *stats) {
  int bit;

  bit = decodeBit(prev, stats);
  if (prev < 0x100) {
    prev = (prev << 1) | bit;
  } else {
    prev = (((prev << 1) | bit) & 0x1ff) | 0x100;
  }
  return bit;
}

Guint JArithmeticDecoder::decodeIAID(Guint codeLen,
				     JArithmeticDecoderStats *stats) {
  Guint i;
  int bit;

  prev = 1;
  for (i = 0; i < codeLen; ++i) {
    bit = decodeBit(prev, stats);
    prev = (prev << 1) | bit;
  }
  return prev - (1 << codeLen);
}

void JArithmeticDecoder::byteIn() {
  if (buf0 == 0xff) {
    if (buf1 > 0x8f) {
      ct = 8;
    } else {
      buf0 = buf1;
      buf1 = readByte();
      c = c + 0xfe00 - (buf0 << 9);
      ct = 7;
    }
  } else {
    buf0 = buf1;
    buf1 = readByte();
    c = c + 0xff00 - (buf0 << 8);
    ct = 8;
  }
}
//========================================================================
//
// JArithmeticDecoder.h
//
// Arithmetic decoder used by the JBIG2 and JPEG2000 decoders.
//
// Copyright 2002-2004 Glyph & Cog, LLC
//
//========================================================================

#ifndef JARITHMETICDECODER_H
#define JARITHMETICDECODER_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "gtypes.h"

class Stream;

//------------------------------------------------------------------------
// JArithmeticDecoderStats
//------------------------------------------------------------------------

class JArithmeticDecoderStats {
public:

  JArithmeticDecoderStats(int contextSizeA);
  ~JArithmeticDecoderStats();
  JArithmeticDecoderStats *copy();
  void reset();
  int getContextSize() { return contextSize; }
  void copyFrom(JArithmeticDecoderStats *stats);
  void setEntry(Guint cx, int i, int mps);

private:

  Guchar *cxTab;		// cxTab[cx] = (i[cx] << 1) + mps[cx]
  int contextSize;

  friend class JArithmeticDecoder;
};

//------------------------------------------------------------------------
// JArithmeticDecoder
//------------------------------------------------------------------------

class JArithmeticDecoder {
public:

  JArithmeticDecoder();
  ~JArithmeticDecoder();

  void setStream(Stream *strA)
    { str = strA; dataLen = 0; limitStream = gFalse; }
  void setStream(Stream *strA, int dataLenA)
    { str = strA; dataLen = dataLenA; limitStream = gTrue; }

  // Start decoding on a new stream.  This fills the byte buffers and
  // runs INITDEC.
  void start();

  // Restart decoding on an interrupted stream.  This refills the
  // buffers if needed, but does not run INITDEC.  (This is used in
  // JPEG 2000 streams when codeblock data is split across multiple
  // packets/layers.)
  void restart(int dataLenA);

  // Read any leftover data in the stream.
  void cleanup();

  // Decode one bit.
  int decodeBit(Guint context, JArithmeticDecoderStats *stats);

  // Decode eight bits.
  int decodeByte(Guint context, JArithmeticDecoderStats *stats);

  // Returns false for OOB, otherwise sets *<x> and returns true.
  GBool decodeInt(int *x, JArithmeticDecoderStats *stats);

  Guint decodeIAID(Guint codeLen,
		   JArithmeticDecoderStats *stats);

private:

  Guint readByte();
  int decodeIntBit(JArithmeticDecoderStats *stats);
  void byteIn();

  static Guint qeTab[47];
  static int nmpsTab[47];
  static int nlpsTab[47];
  static int switchTab[47];

  Guint buf0, buf1;
  Guint c, a;
  int ct;

  Guint prev;			// for the integer decoder

  Stream *str;
  int dataLen;
  GBool limitStream;
};

#endif
//========================================================================
//
// JBIG2Stream.cc
//
// Copyright 2002-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stdlib.h>
#include <limits.h>
#include "GList.h"
#include "Error.h"
#include "JArithmeticDecoder.h"
#include "JBIG2Stream.h"

//~ share these tables
#include "Stream-CCITT.h"

//------------------------------------------------------------------------

static int contextSize[4] = { 16, 13, 10, 10 };
static int refContextSize[2] = { 13, 10 };

//------------------------------------------------------------------------
// JBIG2HuffmanTable
//------------------------------------------------------------------------

#define jbig2HuffmanLOW 0xfffffffd
#define jbig2HuffmanOOB 0xfffffffe
#define jbig2HuffmanEOT 0xffffffff

struct JBIG2HuffmanTable {
  int val;
  Guint prefixLen;
  Guint rangeLen;		// can also be LOW, OOB, or EOT
  Guint prefix;
};

JBIG2HuffmanTable huffTableA[] = {
  {     0, 1,  4,              0x000 },
  {    16, 2,  8,              0x002 },
  {   272, 3, 16,              0x006 },
  { 65808, 3, 32,              0x007 },
  {     0, 0, jbig2HuffmanEOT, 0     }
};

JBIG2HuffmanTable huffTableB[] = {
  {     0, 1,  0,              0x000 },
  {     1, 2,  0,              0x002 },
  {     2, 3,  0,              0x006 },
  {     3, 4,  3,              0x00e },
  {    11, 5,  6,              0x01e },
  {    75, 6, 32,              0x03e },
  {     0, 6, jbig2HuffmanOOB, 0x03f },
  {     0, 0, jbig2HuffmanEOT, 0     }
};

JBIG2HuffmanTable huffTableC[] = {
  {     0, 1,  0,              0x000 },
  {     1, 2,  0,              0x002 },
  {     2, 3,  0,              0x006 },
  {     3, 4,  3,              0x00e },
  {    11, 5,  6,              0x01e },
  {     0, 6, jbig2HuffmanOOB, 0x03e },
  {    75, 7, 32,              0x0fe },
  {  -256, 8,  8,              0x0fe },
  {  -257, 8, jbig2HuffmanLOW, 0x0ff },
  {     0, 0, jbig2HuffmanEOT, 0     }
};

JBIG2HuffmanTable huffTableD[] = {
  {     1, 1,  0,              0x000 },
  {     2, 2,  0,              0x002 },
  {     3, 3,  0,              0x006 },
  {     4, 4,  3,              0x00e },
  {    12, 5,  6,              0x01e },
  {    76, 5, 32,              0x01f },
  {     0, 0, jbig2HuffmanEOT, 0     }
};

JBIG2HuffmanTable huffTableE[] = {
  {     1, 1,  0,              0x000 },
  {     2, 2,  0,              0x002 },
  {     3, 3,  0,              0x006 },
  {     4, 4,  3,              0x00e },
  {    12, 5,  6,              0x01e },
  {    76, 6, 32,              0x03e },
  {  -255, 7,  8,              0x07e },
  {  -256, 7, jbig2HuffmanLOW, 0x07f },
  {     0, 0, jbig2HuffmanEOT, 0     }
};

JBIG2HuffmanTable huffTableF[] = {
  {     0, 2,  7,              0x000 },
  {   128, 3,  7,              0x002 },
  {   256, 3,  8,              0x003 },
  { -1024, 4,  9,              0x008 },
  {  -512, 4,  8,              0x009 },
  {  -256, 4,  7,              0x00a },
  {   -32, 4,  5,              0x00b },
  {   512, 4,  9,              0x00c },
  {  1024, 4, 10,              0x00d },
  { -2048, 5, 10,              0x01c },
  {  -128, 5,  6,              0x01d },
  {   -64, 5,  5,              0x01e },
  { -2049, 6, jbig2HuffmanLOW, 0x03e },
  {  2048, 6, 32,              0x03f },
  {     0, 0, jbig2HuffmanEOT, 0     }
};

JBIG2HuffmanTable huffTableG[] = {
  {  -512, 3,  8,              0x000 },
  {   256, 3,  8,              0x001 },
  {   512, 3,  9,              0x002 },
  {  1024, 3, 10,              0x003 },
  { -1024, 4,  9,              0x008 },
  {  -256, 4,  7,              0x009 },
  {   -32, 4,  5,              0x00a },
  {     0, 4,  5,              0x00b },
  {   128, 4,  7,              0x00c },
  {  -128, 5,  6,              0x01a },
  {   -64, 5,  5,              0x01b },
  {    32, 5,  5,              0x01c },
  {    64, 5,  6,              0x01d },
  { -1025, 5, jbig2HuffmanLOW, 0x01e },
  {  2048, 5, 32,              0x01f },
  {     0, 0, jbig2HuffmanEOT, 0     }
};

JBIG2HuffmanTable huffTableH[] = {
  {     0, 2,  1,              0x000 },
  {     0, 2, jbig2HuffmanOOB, 0x001 },
  {     4, 3,  4,              0x004 },
  {    -1, 4,  0,              0x00a },
  {    22, 4,  4,              0x00b },
  {    38, 4,  5,              0x00c },
  {     2, 5,  0,              0x01a },
  {    70, 5,  6,              0x01b },
  {   134, 5,  7,              0x01c },
  {     3, 6,  0,              0x03a },
  {    20, 6,  1,              0x03b },
  {   262, 6,  7,              0x03c },
  {   646, 6, 10,              0x03d },
  {    -2, 7,  0,              0x07c },
  {   390, 7,  8,              0x07d },
  {   -15, 8,  3,              0x0fc },
  {    -5, 8,  1,              0x0fd },
  {    -7, 9,  1,              0x1fc },
  {    -3, 9,  0,              0x1fd },
  {   -16, 9, jbig2HuffmanLOW, 0x1fe },
  {  1670, 9, 32,              0x1ff },
  {     0, 0, jbig2HuffmanEOT, 0     }
};

JBIG2HuffmanTable huffTableI[] = {
  {     0, 2, jbig2HuffmanOOB, 0x000 },
  {    -1, 3,  1,              0x002 },
  {     1, 3,  1,              0x003 },
  {     7, 3,  5,              0x004 },
  {    -3, 4,  1,              0x00a },
  {    43, 4,  5,              0x00b },
  {    75, 4,  6,              0x00c },
  {     3, 5,  1,              0x01a },
  {   139, 5,  7,              0x01b },
  {   267, 5,  8,              0x01c },
  {     5, 6,  1,              0x03a },
  {    39, 6,  2,              0x03b },
  {   523, 6,  8,              0x03c },
  {  1291, 6, 11,              0x03d },
  {    -5, 7,  1,              0x07c },
  {   779, 7,  9,              0x07d },
  {   -31, 8,  4,              0x0fc },
  {   -11, 8,  2,              0x0fd },
  {   -15, 9,  2,              0x1fc },
  {    -7, 9,  1,              0x1fd },
  {   -32, 9, jbig2HuffmanLOW, 0x1fe },
  {  3339, 9, 32,              0x1ff },
  {     0, 0, jbig2HuffmanEOT, 0     }
};

JBIG2HuffmanTable huffTableJ[] = {
  {    -2, 2,  2,              0x000 },
  {     6, 2,  6,              0x001 },
  {     0, 2, jbig2HuffmanOOB, 0x002 },
  {    -3, 5,  0,              0x018 },
  {     2, 5,  0,              0x019 },
  {    70, 5,  5,              0x01a },
  {     3, 6,  0,              0x036 },
  {   102, 6,  5,              0x037 },
  {   134, 6,  6,              0x038 },
  {   198, 6,  7,              0x039 },
  {   326, 6,  8,              0x03a },
  {   582, 6,  9,              0x03b },
  {  1094, 6, 10,              0x03c },
  {   -21, 7,  4,              0x07a },
  {    -4, 7,  0,              0x07b },
  {     4, 7,  0,              0x07c },
  {  2118, 7, 11,              0x07d },
  {    -5, 8,  0,              0x0fc },
  {     5, 8,  0,              0x0fd },
  {   -22, 8, jbig2HuffmanLOW, 0x0fe },
  {  4166, 8, 32,              0x0ff },
  {     0, 0, jbig2HuffmanEOT, 0     }
};

JBIG2HuffmanTable huffTableK[] = {
  {     1, 1,  0,              0x000 },
  {     2, 2,  1,              0x002 },
  {     4, 4,  0,              0x00c },
  {     5, 4,  1,              0x00d },
  {     7, 5,  1,              0x01c },
  {     9, 5,  2,              0x01d },
  {    13, 6,  2,              0x03c },
  {    17, 7,  2,              0x07a },
  {    21, 7,  3,              0x07b },
  {    29, 7,  4,              0x07c },
  {    45, 7,  5,              0x07d },
  {    77, 7,  6,              0x07e },
  {   141, 7, 32,              0x07f },
  {     0, 0, jbig2HuffmanEOT, 0     }
};

JBIG2HuffmanTable huffTableL[] = {
  {     1, 1,  0,              0x000 },
  {     2, 2,  0,              0x002 },
  {     3, 3,  1,              0x006 },
  {     5, 5,  0,              0x01c },
  {     6, 5,  1,              0x01d },
  {     8, 6,  1,              0x03c },
  {    10, 7,  0,              0x07a },
  {    11, 7,  1,              0x07b },
  {    13, 7,  2,              0x07c },
  {    17, 7,  3,              0x07d },
  {    25, 7,  4,              0x07e },
  {    41, 8,  5,              0x0fe },
  {    73, 8, 32,              0x0ff },
  {     0, 0, jbig2HuffmanEOT, 0     }
};

JBIG2HuffmanTable huffTableM[] = {
  {     1, 1,  0,              0x000 },
  {     2, 3,  0,              0x004 },
  {     7, 3,  3,              0x005 },
  {     3, 4,  0,              0x00c },
  {     5, 4,  1,              0x00d },
  {     4, 5,  0,              0x01c },
  {    15, 6,  1,              0x03a },
  {    17, 6,  2,              0x03b },
  {    21, 6,  3,              0x03c },
  {    29, 6,  4,              0x03d },
  {    45, 6,  5,              0x03e },
  {    77, 7,  6,              0x07e },
  {   141, 7, 32,              0x07f },
  {     0, 0, jbig2HuffmanEOT, 0     }
};

JBIG2HuffmanTable huffTableN[] = {
  {     0, 1,  0,              0x000 },
  {    -2, 3,  0,              0x004 },
  {    -1, 3,  0,              0x005 },
  {     1, 3,  0,              0x006 },
  {     2, 3,  0,              0x007 },
  {     0, 0, jbig2HuffmanEOT, 0     }
};

JBIG2HuffmanTable huffTableO[] = {
  {     0, 1,  0,              0x000 },
  {    -1, 3,  0,              0x004 },
  {     1, 3,  0,              0x005 },
  {    -2, 4,  0,              0x00c },
  {     2, 4,  0,              0x00d },
  {    -4, 5,  1,              0x01c },
  {     3, 5,  1,              0x01d },
  {    -8, 6,  2,              0x03c },
  {     5, 6,  2,              0x03d },
  {   -24, 7,  4,              0x07c },
  {     9, 7,  4,              0x07d },
  {   -25, 7, jbig2HuffmanLOW, 0x07e },
  {    25, 7, 32,              0x07f },
  {     0, 0, jbig2HuffmanEOT, 0     }
};

//------------------------------------------------------------------------
// JBIG2HuffmanDecoder
//------------------------------------------------------------------------

class JBIG2HuffmanDecoder {
public:

  JBIG2HuffmanDecoder();
  ~JBIG2HuffmanDecoder();
  void setStream(Stream *strA) { str = strA; }

  void reset();

  // Returns false for OOB, otherwise sets *<x> and returns true.
  GBool decodeInt(int *x, JBIG2HuffmanTable *table);

  Guint readBits(Guint n);
  Guint readBit();

  // Sort the table by prefix length and assign prefix values.
  void buildTable(JBIG2HuffmanTable *table, Guint len);

private:

  Stream *str;
  Guint buf;
  Guint bufLen;
};

JBIG2HuffmanDecoder::JBIG2HuffmanDecoder() {
  str = NULL;
  reset();
}

JBIG2HuffmanDecoder::~JBIG2HuffmanDecoder() {
}

void JBIG2HuffmanDecoder::reset() {
  buf = 0;
  bufLen = 0;
}

//~ optimize this
GBool JBIG2HuffmanDecoder::decodeInt(int *x, JBIG2HuffmanTable *table) {
  Guint i, len, prefix;

  i = 0;
  len = 0;
  prefix = 0;
  while (table[i].rangeLen != jbig2HuffmanEOT) {
    while (len < table[i].prefixLen) {
      prefix = (prefix << 1) | readBit();
      ++len;
    }
    if (prefix == table[i].prefix) {
      if (table[i].rangeLen == jbig2HuffmanOOB) {
	return gFalse;
      }
      if (table[i].rangeLen == jbig2HuffmanLOW) {
	*x = table[i].val - readBits(32);
      } else if (table[i].rangeLen > 0) {
	*x = table[i].val + readBits(table[i].rangeLen);
      } else {
	*x = table[i].val;
      }
      return gTrue;
    }
    ++i;
  }
  return gFalse;
}

Guint JBIG2HuffmanDecoder::readBits(Guint n) {
  Guint x, mask, nLeft;

  mask = (n == 32) ? 0xffffffff : ((1 << n) - 1);
  if (bufLen >= n) {
    x = (buf >> (bufLen - n)) & mask;
    bufLen -= n;
  } else {
    x = buf & ((1 << bufLen) - 1);
    nLeft = n - bufLen;
    bufLen = 0;
    while (nLeft >= 8) {
      x = (x << 8) | (str->getChar() & 0xff);
      nLeft -= 8;
    }
    if (nLeft > 0) {
      buf = str->getChar();
      bufLen = 8 - nLeft;
      x = (x << nLeft) | ((buf >> bufLen) & ((1 << nLeft) - 1));
    }
  }
  return x;
}

Guint JBIG2HuffmanDecoder::readBit() {
  if (bufLen == 0) {
    buf = str->getChar();
    bufLen = 8;
  }
  --bufLen;
  return (buf >> bufLen) & 1;
}

void JBIG2HuffmanDecoder::buildTable(JBIG2HuffmanTable *table, Guint len) {
  Guint i, j, k, prefix;
  JBIG2HuffmanTable tab;

  // stable selection sort:
  // - entries with prefixLen > 0, in ascending prefixLen order
  // - entry with prefixLen = 0, rangeLen = EOT
  // - all other entries with prefixLen = 0
  // (on entry, table[len] has prefixLen = 0, rangeLen = EOT)
  for (i = 0; i < len; ++i) {
    for (j = i; j < len && table[j].prefixLen == 0; ++j) ;
    if (j == len) {
      break;
    }
    for (k = j + 1; k < len; ++k) {
      if (table[k].prefixLen > 0 &&
	  table[k].prefixLen < table[j].prefixLen) {
	j = k;
      }
    }
    if (j != i) {
      tab = table[j];
      for (k = j; k > i; --k) {
	table[k] = table[k - 1];
      }
      table[i] = tab;
    }
  }
  table[i] = table[len];

  // assign prefixes
  i = 0;
  prefix = 0;
  table[i++].prefix = prefix++;
  for (; table[i].rangeLen != jbig2HuffmanEOT; ++i) {
    prefix <<= table[i].prefixLen - table[i-1].prefixLen;
    table[i].prefix = prefix++;
  }
}

//------------------------------------------------------------------------
// JBIG2MMRDecoder
//------------------------------------------------------------------------

class JBIG2MMRDecoder {
public:

  JBIG2MMRDecoder();
  ~JBIG2MMRDecoder();
  void setStream(Stream *strA) { str = strA; }
  void reset();
  int get2DCode();
  int getBlackCode();
  int getWhiteCode();
  Guint get24Bits();
  void skipTo(Guint length);

private:

  Stream *str;
  Guint buf;
  Guint bufLen;
  Guint nBytesRead;
};

JBIG2MMRDecoder::JBIG2MMRDecoder() {
  str = NULL;
  reset();
}

JBIG2MMRDecoder::~JBIG2MMRDecoder() {
}

void JBIG2MMRDecoder::reset() {
  buf = 0;
  bufLen = 0;
  nBytesRead = 0;
}

int JBIG2MMRDecoder::get2DCode() {
  CCITTCode *p;

  if (bufLen == 0) {
    buf = str->getChar() & 0xff;
    bufLen = 8;
    ++nBytesRead;
    p = &twoDimTab1[(buf >> 1) & 0x7f];
  } else if (bufLen == 8) {
    p = &twoDimTab1[(buf >> 1) & 0x7f];
  } else {
    p = &twoDimTab1[(buf << (7 - bufLen)) & 0x7f];
    if (p->bits < 0 || p->bits > (int)bufLen) {
      buf = (buf << 8) | (str->getChar() & 0xff);
      bufLen += 8;
      ++nBytesRead;
      p = &twoDimTab1[(buf >> (bufLen - 7)) & 0x7f];
    }
  }
  if (p->bits < 0) {
    error(str->getPos(), "Bad two dim code in JBIG2 MMR stream");
    return 0;
  }
  bufLen -= p->bits;
  return p->n;
}

int JBIG2MMRDecoder::getWhiteCode() {
  CCITTCode *p;
  Guint code;

  if (bufLen == 0) {
    buf = str->getChar() & 0xff;
    bufLen = 8;
    ++nBytesRead;
  }
  while (1) {
    if (bufLen >= 7 && ((buf >> (bufLen - 7)) & 0x7f) == 0) {
      if (bufLen <= 12) {
	code = buf << (12 - bufLen);
      } else {
	code = buf >> (bufLen - 12);
      }
      p = &whiteTab1[code & 0x1f];
    } else {
      if (bufLen <= 9) {
	code = buf << (9 - bufLen);
      } else {
	code = buf >> (bufLen - 9);
      }
      p = &whiteTab2[code & 0x1ff];
    }
    if (p->bits > 0 && p->bits <= (int)bufLen) {
      bufLen -= p->bits;
      return p->n;
    }
    if (bufLen >= 12) {
      break;
    }
    buf = (buf << 8) | (str->getChar() & 0xff);
    bufLen += 8;
    ++nBytesRead;
  }
  error(str->getPos(), "Bad white code in JBIG2 MMR stream");
  // eat a bit and return a positive number so that the caller doesn't
  // go into an infinite loop
  --bufLen;
  return 1;
}

int JBIG2MMRDecoder::getBlackCode() {
  CCITTCode *p;
  Guint code;

  if (bufLen == 0) {
    buf = str->getChar() & 0xff;
    bufLen = 8;
    ++nBytesRead;
  }
  while (1) {
    if (bufLen >= 6 && ((buf >> (bufLen - 6)) & 0x3f) == 0) {
      if (bufLen <= 13) {
	code = buf << (13 - bufLen);
      } else {
	code = buf >> (bufLen - 13);
      }
      p = &blackTab1[code & 0x7f];
    } else if (bufLen >= 4 && ((buf >> (bufLen - 4)) & 0x0f) == 0) {
      if (bufLen <= 12) {
	code = buf << (12 - bufLen);
      } else {
	code = buf >> (bufLen - 12);
      }
      p = &blackTab2[(code & 0xff) - 64];
    } else {
      if (bufLen <= 6) {
	code = buf << (6 - bufLen);
      } else {
	code = buf >> (bufLen - 6);
      }
      p = &blackTab3[code & 0x3f];
    }
    if (p->bits > 0 && p->bits <= (int)bufLen) {
      bufLen -= p->bits;
      return p->n;
    }
    if (bufLen >= 13) {
      break;
    }
    buf = (buf << 8) | (str->getChar() & 0xff);
    bufLen += 8;
    ++nBytesRead;
  }
  error(str->getPos(), "Bad black code in JBIG2 MMR stream");
  // eat a bit and return a positive number so that the caller doesn't
  // go into an infinite loop
  --bufLen;
  return 1;
}

Guint JBIG2MMRDecoder::get24Bits() {
  while (bufLen < 24) {
    buf = (buf << 8) | (str->getChar() & 0xff);
    bufLen += 8;
    ++nBytesRead;
  }
  return (buf >> (bufLen - 24)) & 0xffffff;
}

void JBIG2MMRDecoder::skipTo(Guint length) {
  while (nBytesRead < length) {
    str->getChar();
    ++nBytesRead;
  }
}

//------------------------------------------------------------------------
// JBIG2Segment
//------------------------------------------------------------------------

enum JBIG2SegmentType {
  jbig2SegBitmap,
  jbig2SegSymbolDict,
  jbig2SegPatternDict,
  jbig2SegCodeTable
};

class JBIG2Segment {
public:

  JBIG2Segment(Guint segNumA) { segNum = segNumA; }
  virtual ~JBIG2Segment() {}
  void setSegNum(Guint segNumA) { segNum = segNumA; }
  Guint getSegNum() { return segNum; }
  virtual JBIG2SegmentType getType() = 0;

private:

  Guint segNum;
};

//------------------------------------------------------------------------
// JBIG2Bitmap
//------------------------------------------------------------------------

struct JBIG2BitmapPtr {
  Guchar *p;
  int shift;
  int x;
};

class JBIG2Bitmap: public JBIG2Segment {
public:

  JBIG2Bitmap(Guint segNumA, int wA, int hA);
  virtual ~JBIG2Bitmap();
  virtual JBIG2SegmentType getType() { return jbig2SegBitmap; }
  JBIG2Bitmap *copy() { return new JBIG2Bitmap(0, this); }
  JBIG2Bitmap *getSlice(Guint x, Guint y, Guint wA, Guint hA);
  void expand(int newH, Guint pixel);
  void clearToZero();
  void clearToOne();
  int getWidth() { return w; }
  int getHeight() { return h; }
  int getPixel(int x, int y)
    { return (x < 0 || x >= w || y < 0 || y >= h) ? 0 :
             (data[y * line + (x >> 3)] >> (7 - (x & 7))) & 1; }
  void setPixel(int x, int y)
    { data[y * line + (x >> 3)] |= 1 << (7 - (x & 7)); }
  void clearPixel(int x, int y)
    { data[y * line + (x >> 3)] &= 0x7f7f >> (x & 7); }
  void getPixelPtr(int x, int y, JBIG2BitmapPtr *ptr);
  int nextPixel(JBIG2BitmapPtr *ptr);
  void duplicateRow(int yDest, int ySrc);
  void combine(JBIG2Bitmap *bitmap, int x, int y, Guint combOp);
  Guchar *getDataPtr() { return data; }
  int getDataSize() { return h * line; }

private:

  JBIG2Bitmap(Guint segNumA, JBIG2Bitmap *bitmap);

  int w, h, line;
  Guchar *data;
};

JBIG2Bitmap::JBIG2Bitmap(Guint segNumA, int wA, int hA):
  JBIG2Segment(segNumA)
{
  w = wA;
  h = hA;
  line = (wA + 7) >> 3;
  if (w <= 0 || h <= 0 || line <= 0 || h >= (INT_MAX - 1) / line) {
    data = NULL;
    return;
  }
  // need to allocate one extra guard byte for use in combine()
  data = (Guchar *)gmalloc(h * line + 1);
  data[h * line] = 0;
}

JBIG2Bitmap::JBIG2Bitmap(Guint segNumA, JBIG2Bitmap *bitmap):
  JBIG2Segment(segNumA)
{
  w = bitmap->w;
  h = bitmap->h;
  line = bitmap->line;
  if (w <= 0 || h <= 0 || line <= 0 || h >= (INT_MAX - 1) / line) {
    data = NULL;
    return;
  }
  // need to allocate one extra guard byte for use in combine()
  data = (Guchar *)gmalloc(h * line + 1);
  memcpy(data, bitmap->data, h * line);
  data[h * line] = 0;
}

JBIG2Bitmap::~JBIG2Bitmap() {
  gfree(data);
}

//~ optimize this
JBIG2Bitmap *JBIG2Bitmap::getSlice(Guint x, Guint y, Guint wA, Guint hA) {
  JBIG2Bitmap *slice;
  Guint xx, yy;

  slice = new JBIG2Bitmap(0, wA, hA);
  slice->clearToZero();
  for (yy = 0; yy < hA; ++yy) {
    for (xx = 0; xx < wA; ++xx) {
      if (getPixel(x + xx, y + yy)) {
	slice->setPixel(xx, yy);
      }
    }
  }
  return slice;
}

void JBIG2Bitmap::expand(int newH, Guint pixel) {
  if (newH <= h || line <= 0 || newH >= (INT_MAX - 1) / line) {
    return;
  }
  // need to allocate one extra guard byte for use in combine()
  data = (Guchar *)grealloc(data, newH * line + 1);
  if (pixel) {
    memset(data + h * line, 0xff, (newH - h) * line);
  } else {
    memset(data + h * line, 0x00, (newH - h) * line);
  }
  h = newH;
  data[h * line] = 0;
}

void JBIG2Bitmap::clearToZero() {
  memset(data, 0, h * line);
}

void JBIG2Bitmap::clearToOne() {
  memset(data, 0xff, h * line);
}

inline void JBIG2Bitmap::getPixelPtr(int x, int y, JBIG2BitmapPtr *ptr) {
  if (y < 0 || y >= h || x >= w) {
    ptr->p = NULL;
  } else if (x < 0) {
    ptr->p = &data[y * line];
    ptr->shift = 7;
    ptr->x = x;
  } else {
    ptr->p = &data[y * line + (x >> 3)];
    ptr->shift = 7 - (x & 7);
    ptr->x = x;
  }
}

inline int JBIG2Bitmap::nextPixel(JBIG2BitmapPtr *ptr) {
  int pix;

  if (!ptr->p) {
    pix = 0;
  } else if (ptr->x < 0) {
    ++ptr->x;
    pix = 0;
  } else {
    pix = (*ptr->p >> ptr->shift) & 1;
    if (++ptr->x == w) {
      ptr->p = NULL;
    } else if (ptr->shift == 0) {
      ++ptr->p;
      ptr->shift = 7;
    } else {
      --ptr->shift;
    }
  }
  return pix;
}

void JBIG2Bitmap::duplicateRow(int yDest, int ySrc) {
  memcpy(data + yDest * line, data + ySrc * line, line);
}

void JBIG2Bitmap::combine(JBIG2Bitmap *bitmap, int x, int y,
			  Guint combOp) {
  int x0, x1, y0, y1, xx, yy;
  Guchar *srcPtr, *destPtr;
  Guint src0, src1, src, dest, s1, s2, m1, m2, m3;
  GBool oneByte;

  if (y < 0) {
    y0 = -y;
  } else {
    y0 = 0;
  }
  if (y + bitmap->h > h) {
    y1 = h - y;
  } else {
    y1 = bitmap->h;
  }
  if (y0 >= y1) {
    return;
  }

  if (x >= 0) {
    x0 = x & ~7;
  } else {
    x0 = 0;
  }
  x1 = x + bitmap->w;
  if (x1 > w) {
    x1 = w;
  }
  if (x0 >= x1) {
    return;
  }

  s1 = x & 7;
  s2 = 8 - s1;
  m1 = 0xff >> (x1 & 7);
  m2 = 0xff << (((x1 & 7) == 0) ? 0 : 8 - (x1 & 7));
  m3 = (0xff >> s1) & m2;

  oneByte = x0 == ((x1 - 1) & ~7);

  for (yy = y0; yy < y1; ++yy) {

    // one byte per line -- need to mask both left and right side
    if (oneByte) {
      if (x >= 0) {
	destPtr = data + (y + yy) * line + (x >> 3);
	srcPtr = bitmap->data + yy * bitmap->line;
	dest = *destPtr;
	src1 = *srcPtr;
	switch (combOp) {
	case 0: // or
	  dest |= (src1 >> s1) & m2;
	  break;
	case 1: // and
	  dest &= ((0xff00 | src1) >> s1) | m1;
	  break;
	case 2: // xor
	  dest ^= (src1 >> s1) & m2;
	  break;
	case 3: // xnor
	  dest ^= ((src1 ^ 0xff) >> s1) & m2;
	  break;
	case 4: // replace
	  dest = (dest & ~m3) | ((src1 >> s1) & m3);
	  break;
	}
	*destPtr = dest;
      } else {
	destPtr = data + (y + yy) * line;
	srcPtr = bitmap->data + yy * bitmap->line + (-x >> 3);
	dest = *destPtr;
	src1 = *srcPtr;
	switch (combOp) {
	case 0: // or
	  dest |= src1 & m2;
	  break;
	case 1: // and
	  dest &= src1 | m1;
	  break;
	case 2: // xor
	  dest ^= src1 & m2;
	  break;
	case 3: // xnor
	  dest ^= (src1 ^ 0xff) & m2;
	  break;
	case 4: // replace
	  dest = (src1 & m2) | (dest & m1);
	  break;
	}
	*destPtr = dest;
      }

    // multiple bytes per line -- need to mask left side of left-most
    // byte and right side of right-most byte
    } else {

      // left-most byte
      if (x >= 0) {
	destPtr = data + (y + yy) * line + (x >> 3);
	srcPtr = bitmap->data + yy * bitmap->line;
	src1 = *srcPtr++;
	dest = *destPtr;
	switch (combOp) {
	case 0: // or
	  dest |= src1 >> s1;
	  break;
	case 1: // and
	  dest &= (0xff00 | src1) >> s1;
	  break;
	case 2: // xor
	  dest ^= src1 >> s1;
	  break;
	case 3: // xnor
	  dest ^= (src1 ^ 0xff) >> s1;
	  break;
	case 4: // replace
	  dest = (dest & (0xff << s2)) | (src1 >> s1);
	  break;
	}
	*destPtr++ = dest;
	xx = x0 + 8;
      } else {
	destPtr = data + (y + yy) * line;
	srcPtr = bitmap->data + yy * bitmap->line + (-x >> 3);
	src1 = *srcPtr++;
	xx = x0;
      }

      // middle bytes
      for (; xx < x1 - 8; xx += 8) {
	dest = *destPtr;
	src0 = src1;
	src1 = *srcPtr++;
	src = (((src0 << 8) | src1) >> s1) & 0xff;
	switch (combOp) {
	case 0: // or
	  dest |= src;
	  break;
	case 1: // and
	  dest &= src;
	  break;
	case 2: // xor
	  dest ^= src;
	  break;
	case 3: // xnor
	  dest ^= src ^ 0xff;
	  break;
	case 4: // replace
	  dest = src;
	  break;
	}
	*destPtr++ = dest;
      }

      // right-most byte
      // note: this last byte (src1) may not actually be used, depending
      // on the values of s1, m1, and m2 - and in fact, it may be off
      // the edge of the source bitmap, which means we need to allocate
      // one extra guard byte at the end of each bitmap
      dest = *destPtr;
      src0 = src1;
      src1 = *srcPtr++;
      src = (((src0 << 8) | src1) >> s1) & 0xff;
      switch (combOp) {
      case 0: // or
	dest |= src & m2;
	break;
      case 1: // and
	dest &= src | m1;
	break;
      case 2: // xor
	dest ^= src & m2;
	break;
      case 3: // xnor
	dest ^= (src ^ 0xff) & m2;
	break;
      case 4: // replace
	dest = (src & m2) | (dest & m1);
	break;
      }
      *destPtr = dest;
    }
  }
}

//------------------------------------------------------------------------
// JBIG2SymbolDict
//------------------------------------------------------------------------

class JBIG2SymbolDict: public JBIG2Segment {
public:

  JBIG2SymbolDict(Guint segNumA, Guint sizeA);
  virtual ~JBIG2SymbolDict();
  virtual JBIG2SegmentType getType() { return jbig2SegSymbolDict; }
  Guint getSize() { return size; }
  void setBitmap(Guint idx, JBIG2Bitmap *bitmap) { bitmaps[idx] = bitmap; }
  JBIG2Bitmap *getBitmap(Guint idx) { return bitmaps[idx]; }
  void setGenericRegionStats(JArithmeticDecoderStats *stats)
    { genericRegionStats = stats; }
  void setRefinementRegionStats(JArithmeticDecoderStats *stats)
    { refinementRegionStats = stats; }
  JArithmeticDecoderStats *getGenericRegionStats()
    { return genericRegionStats; }
  JArithmeticDecoderStats *getRefinementRegionStats()
    { return refinementRegionStats; }

private:

  Guint size;
  JBIG2Bitmap **bitmaps;
  JArithmeticDecoderStats *genericRegionStats;
  JArithmeticDecoderStats *refinementRegionStats;
};

JBIG2SymbolDict::JBIG2SymbolDict(Guint segNumA, Guint sizeA):
  JBIG2Segment(segNumA)
{
  size = sizeA;
  bitmaps = (JBIG2Bitmap **)gmallocn(size, sizeof(JBIG2Bitmap *));
  genericRegionStats = NULL;
  refinementRegionStats = NULL;
}

JBIG2SymbolDict::~JBIG2SymbolDict() {
  Guint i;

  for (i = 0; i < size; ++i) {
    delete bitmaps[i];
  }
  gfree(bitmaps);
  if (genericRegionStats) {
    delete genericRegionStats;
  }
  if (refinementRegionStats) {
    delete refinementRegionStats;
  }
}

//------------------------------------------------------------------------
// JBIG2PatternDict
//------------------------------------------------------------------------

class JBIG2PatternDict: public JBIG2Segment {
public:

  JBIG2PatternDict(Guint segNumA, Guint sizeA);
  virtual ~JBIG2PatternDict();
  virtual JBIG2SegmentType getType() { return jbig2SegPatternDict; }
  Guint getSize() { return size; }
  void setBitmap(Guint idx, JBIG2Bitmap *bitmap) { bitmaps[idx] = bitmap; }
  JBIG2Bitmap *getBitmap(Guint idx) { return bitmaps[idx]; }

private:

  Guint size;
  JBIG2Bitmap **bitmaps;
};

JBIG2PatternDict::JBIG2PatternDict(Guint segNumA, Guint sizeA):
  JBIG2Segment(segNumA)
{
  size = sizeA;
  bitmaps = (JBIG2Bitmap **)gmallocn(size, sizeof(JBIG2Bitmap *));
}

JBIG2PatternDict::~JBIG2PatternDict() {
  Guint i;

  for (i = 0; i < size; ++i) {
    delete bitmaps[i];
  }
  gfree(bitmaps);
}

//------------------------------------------------------------------------
// JBIG2CodeTable
//------------------------------------------------------------------------

class JBIG2CodeTable: public JBIG2Segment {
public:

  JBIG2CodeTable(Guint segNumA, JBIG2HuffmanTable *tableA);
  virtual ~JBIG2CodeTable();
  virtual JBIG2SegmentType getType() { return jbig2SegCodeTable; }
  JBIG2HuffmanTable *getHuffTable() { return table; }

private:

  JBIG2HuffmanTable *table;
};

JBIG2CodeTable::JBIG2CodeTable(Guint segNumA, JBIG2HuffmanTable *tableA):
  JBIG2Segment(segNumA)
{
  table = tableA;
}

JBIG2CodeTable::~JBIG2CodeTable() {
  gfree(table);
}

//------------------------------------------------------------------------
// JBIG2Stream
//------------------------------------------------------------------------

JBIG2Stream::JBIG2Stream(Stream *strA, Object *globalsStreamA):
  FilterStream(strA)
{
  pageBitmap = NULL;

  arithDecoder = new JArithmeticDecoder();
  genericRegionStats = new JArithmeticDecoderStats(1 << 1);
  refinementRegionStats = new JArithmeticDecoderStats(1 << 1);
  iadhStats = new JArithmeticDecoderStats(1 << 9);
  iadwStats = new JArithmeticDecoderStats(1 << 9);
  iaexStats = new JArithmeticDecoderStats(1 << 9);
  iaaiStats = new JArithmeticDecoderStats(1 << 9);
  iadtStats = new JArithmeticDecoderStats(1 << 9);
  iaitStats = new JArithmeticDecoderStats(1 << 9);
  iafsStats = new JArithmeticDecoderStats(1 << 9);
  iadsStats = new JArithmeticDecoderStats(1 << 9);
  iardxStats = new JArithmeticDecoderStats(1 << 9);
  iardyStats = new JArithmeticDecoderStats(1 << 9);
  iardwStats = new JArithmeticDecoderStats(1 << 9);
  iardhStats = new JArithmeticDecoderStats(1 << 9);
  iariStats = new JArithmeticDecoderStats(1 << 9);
  iaidStats = new JArithmeticDecoderStats(1 << 1);
  huffDecoder = new JBIG2HuffmanDecoder();
  mmrDecoder = new JBIG2MMRDecoder();

  globalsStreamA->copy(&globalsStream);
  segments = globalSegments = NULL;
  curStr = NULL;
  dataPtr = dataEnd = NULL;
}

JBIG2Stream::~JBIG2Stream() {
  close();
  globalsStream.free();
  delete arithDecoder;
  delete genericRegionStats;
  delete refinementRegionStats;
  delete iadhStats;
  delete iadwStats;
  delete iaexStats;
  delete iaaiStats;
  delete iadtStats;
  delete iaitStats;
  delete iafsStats;
  delete iadsStats;
  delete iardxStats;
  delete iardyStats;
  delete iardwStats;
  delete iardhStats;
  delete iariStats;
  delete iaidStats;
  delete huffDecoder;
  delete mmrDecoder;
  delete str;
}

void JBIG2Stream::reset() {
  // read the globals stream
  globalSegments = new GList();
  if (globalsStream.isStream()) {
    segments = globalSegments;
    curStr = globalsStream.getStream();
    curStr->reset();
    arithDecoder->setStream(curStr);
    huffDecoder->setStream(curStr);
    mmrDecoder->setStream(curStr);
    readSegments();
    curStr->close();
  }

  // read the main stream
  segments = new GList();
  curStr = str;
  curStr->reset();
  arithDecoder->setStream(curStr);
  huffDecoder->setStream(curStr);
  mmrDecoder->setStream(curStr);
  readSegments();

  if (pageBitmap) {
    dataPtr = pageBitmap->getDataPtr();
    dataEnd = dataPtr + pageBitmap->getDataSize();
  } else {
    dataPtr = dataEnd = NULL;
  }
}

void JBIG2Stream::close() {
  if (pageBitmap) {
    delete pageBitmap;
    pageBitmap = NULL;
  }
  if (segments) {
    deleteGList(segments, JBIG2Segment);
    segments = NULL;
  }
  if (globalSegments) {
    deleteGList(globalSegments, JBIG2Segment);
    globalSegments = NULL;
  }
  dataPtr = dataEnd = NULL;
  FilterStream::close();
}

int JBIG2Stream::getChar() {
  if (dataPtr && dataPtr < dataEnd) {
    return (*dataPtr++ ^ 0xff) & 0xff;
  }
  return EOF;
}

int JBIG2Stream::lookChar() {
  if (dataPtr && dataPtr < dataEnd) {
    return (*dataPtr ^ 0xff) & 0xff;
  }
  return EOF;
}

GString *JBIG2Stream::getPSFilter(int psLevel, char *indent) {
  return NULL;
}

GBool JBIG2Stream::isBinary(GBool last) {
  return str->isBinary(gTrue);
}

void JBIG2Stream::readSegments() {
  Guint segNum, segFlags, segType, page, segLength;
  Guint refFlags, nRefSegs;
  Guint *refSegs;
  int c1, c2, c3;
  Guint i;

  while (readULong(&segNum)) {

    // segment header flags
    if (!readUByte(&segFlags)) {
      goto eofError1;
    }
    segType = segFlags & 0x3f;

    // referred-to segment count and retention flags
    if (!readUByte(&refFlags)) {
      goto eofError1;
    }
    nRefSegs = refFlags >> 5;
    if (nRefSegs == 7) {
      if ((c1 = curStr->getChar()) == EOF ||
	  (c2 = curStr->getChar()) == EOF ||
	  (c3 = curStr->getChar()) == EOF) {
	goto eofError1;
      }
      refFlags = (refFlags << 24) | (c1 << 16) | (c2 << 8) | c3;
      nRefSegs = refFlags & 0x1fffffff;
      for (i = 0; i < (nRefSegs + 9) >> 3; ++i) {
	c1 = curStr->getChar();
      }
    }

    // referred-to segment numbers
    refSegs = (Guint *)gmallocn(nRefSegs, sizeof(Guint));
    if (segNum <= 256) {
      for (i = 0; i < nRefSegs; ++i) {
	if (!readUByte(&refSegs[i])) {
	  goto eofError2;
	}
      }
    } else if (segNum <= 65536) {
      for (i = 0; i < nRefSegs; ++i) {
	if (!readUWord(&refSegs[i])) {
	  goto eofError2;
	}
      }
    } else {
      for (i = 0; i < nRefSegs; ++i) {
	if (!readULong(&refSegs[i])) {
	  goto eofError2;
	}
      }
    }

    // segment page association
    if (segFlags & 0x40) {
      if (!readULong(&page)) {
	goto eofError2;
      }
    } else {
      if (!readUByte(&page)) {
	goto eofError2;
      }
    }

    // segment data length
    if (!readULong(&segLength)) {
      goto eofError2;
    }

    // read the segment data
    switch (segType) {
    case 0:
      if (!readSymbolDictSeg(segNum, segLength, refSegs, nRefSegs)) {
	goto syntaxError;
      }
      break;
    case 4:
      readTextRegionSeg(segNum, gFalse, gFalse, segLength, refSegs, nRefSegs);
      break;
    case 6:
      readTextRegionSeg(segNum, gTrue, gFalse, segLength, refSegs, nRefSegs);
      break;
    case 7:
      readTextRegionSeg(segNum, gTrue, gTrue, segLength, refSegs, nRefSegs);
      break;
    case 16:
      readPatternDictSeg(segNum, segLength);
      break;
    case 20:
      readHalftoneRegionSeg(segNum, gFalse, gFalse, segLength,
			    refSegs, nRefSegs);
      break;
    case 22:
      readHalftoneRegionSeg(segNum, gTrue, gFalse, segLength,
			    refSegs, nRefSegs);
      break;
    case 23:
      readHalftoneRegionSeg(segNum, gTrue, gTrue, segLength,
			    refSegs, nRefSegs);
      break;
    case 36:
      readGenericRegionSeg(segNum, gFalse, gFalse, segLength);
      break;
    case 38:
      readGenericRegionSeg(segNum, gTrue, gFalse, segLength);
      break;
    case 39:
      readGenericRegionSeg(segNum, gTrue, gTrue, segLength);
      break;
    case 40:
      readGenericRefinementRegionSeg(segNum, gFalse, gFalse, segLength,
				     refSegs, nRefSegs);
      break;
    case 42:
      readGenericRefinementRegionSeg(segNum, gTrue, gFalse, segLength,
				     refSegs, nRefSegs);
      break;
    case 43:
      readGenericRefinementRegionSeg(segNum, gTrue, gTrue, segLength,
				     refSegs, nRefSegs);
      break;
    case 48:
      readPageInfoSeg(segLength);
      break;
    case 50:
      readEndOfStripeSeg(segLength);
      break;
    case 52:
      readProfilesSeg(segLength);
      break;
    case 53:
      readCodeTableSeg(segNum, segLength);
      break;
    case 62:
      readExtensionSeg(segLength);
      break;
    default:
      error(getPos(), "Unknown segment type in JBIG2 stream");
      for (i = 0; i < segLength; ++i) {
	if ((c1 = curStr->getChar()) == EOF) {
	  goto eofError2;
	}
      }
      break;
    }

    gfree(refSegs);
  }

  return;

 syntaxError:
  gfree(refSegs);
  return;

 eofError2:
  gfree(refSegs);
 eofError1:
  error(getPos(), "Unexpected EOF in JBIG2 stream");
}

GBool JBIG2Stream::readSymbolDictSeg(Guint segNum, Guint length,
				     Guint *refSegs, Guint nRefSegs) {
  JBIG2SymbolDict *symbolDict;
  JBIG2HuffmanTable *huffDHTable, *huffDWTable;
  JBIG2HuffmanTable *huffBMSizeTable, *huffAggInstTable;
  JBIG2Segment *seg;
  GList *codeTables;
  JBIG2SymbolDict *inputSymbolDict;
  Guint flags, sdTemplate, sdrTemplate, huff, refAgg;
  Guint huffDH, huffDW, huffBMSize, huffAggInst;
  Guint contextUsed, contextRetained;
  int sdATX[4], sdATY[4], sdrATX[2], sdrATY[2];
  Guint numExSyms, numNewSyms, numInputSyms, symCodeLen;
  JBIG2Bitmap **bitmaps;
  JBIG2Bitmap *collBitmap, *refBitmap;
  Guint *symWidths;
  Guint symHeight, symWidth, totalWidth, x, symID;
  int dh, dw, refAggNum, refDX, refDY, bmSize;
  GBool ex;
  int run, cnt;
  Guint i, j, k;
  Guchar *p;

  // symbol dictionary flags
  if (!readUWord(&flags)) {
    goto eofError;
  }
  sdTemplate = (flags >> 10) & 3;
  sdrTemplate = (flags >> 12) & 1;
  huff = flags & 1;
  refAgg = (flags >> 1) & 1;
  huffDH = (flags >> 2) & 3;
  huffDW = (flags >> 4) & 3;
  huffBMSize = (flags >> 6) & 1;
  huffAggInst = (flags >> 7) & 1;
  contextUsed = (flags >> 8) & 1;
  contextRetained = (flags >> 9) & 1;

  // symbol dictionary AT flags
  if (!huff) {
    if (sdTemplate == 0) {
      if (!readByte(&sdATX[0]) ||
	  !readByte(&sdATY[0]) ||
	  !readByte(&sdATX[1]) ||
	  !readByte(&sdATY[1]) ||
	  !readByte(&sdATX[2]) ||
	  !readByte(&sdATY[2]) ||
	  !readByte(&sdATX[3]) ||
	  !readByte(&sdATY[3])) {
	goto eofError;
      }
    } else {
      if (!readByte(&sdATX[0]) ||
	  !readByte(&sdATY[0])) {
	goto eofError;
      }
    }
  }

  // symbol dictionary refinement AT flags
  if (refAgg && !sdrTemplate) {
    if (!readByte(&sdrATX[0]) ||
	!readByte(&sdrATY[0]) ||
	!readByte(&sdrATX[1]) ||
	!readByte(&sdrATY[1])) {
      goto eofError;
    }
  }

  // SDNUMEXSYMS and SDNUMNEWSYMS
  if (!readULong(&numExSyms) || !readULong(&numNewSyms)) {
    goto eofError;
  }

  // get referenced segments: input symbol dictionaries and code tables
  codeTables = new GList();
  numInputSyms = 0;
  for (i = 0; i < nRefSegs; ++i) {
    seg = findSegment(refSegs[i]);
    if (seg->getType() == jbig2SegSymbolDict) {
      numInputSyms += ((JBIG2SymbolDict *)seg)->getSize();
    } else if (seg->getType() == jbig2SegCodeTable) {
      codeTables->append(seg);
    }
  }

  // compute symbol code length
  symCodeLen = 0;
  i = 1;
  while (i < numInputSyms + numNewSyms) {
    ++symCodeLen;
    i <<= 1;
  }

  // get the input symbol bitmaps
  bitmaps = (JBIG2Bitmap **)gmallocn(numInputSyms + numNewSyms,
				     sizeof(JBIG2Bitmap *));
  for (i = 0; i < numInputSyms + numNewSyms; ++i) {
    bitmaps[i] = NULL;
  }
  k = 0;
  inputSymbolDict = NULL;
  for (i = 0; i < nRefSegs; ++i) {
    seg = findSegment(refSegs[i]);
    if (seg->getType() == jbig2SegSymbolDict) {
      inputSymbolDict = (JBIG2SymbolDict *)seg;
      for (j = 0; j < inputSymbolDict->getSize(); ++j) {
	bitmaps[k++] = inputSymbolDict->getBitmap(j);
      }
    }
  }

  // get the Huffman tables
  huffDHTable = huffDWTable = NULL; // make gcc happy
  huffBMSizeTable = huffAggInstTable = NULL; // make gcc happy
  i = 0;
  if (huff) {
    if (huffDH == 0) {
      huffDHTable = huffTableD;
    } else if (huffDH == 1) {
      huffDHTable = huffTableE;
    } else {
      huffDHTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
    }
    if (huffDW == 0) {
      huffDWTable = huffTableB;
    } else if (huffDW == 1) {
      huffDWTable = huffTableC;
    } else {
      huffDWTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
    }
    if (huffBMSize == 0) {
      huffBMSizeTable = huffTableA;
    } else {
      huffBMSizeTable =
	  ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
    }
    if (huffAggInst == 0) {
      huffAggInstTable = huffTableA;
    } else {
      huffAggInstTable =
	  ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
    }
  }
  delete codeTables;

  // set up the Huffman decoder
  if (huff) {
    huffDecoder->reset();

  // set up the arithmetic decoder
  } else {
    if (contextUsed && inputSymbolDict) {
      resetGenericStats(sdTemplate, inputSymbolDict->getGenericRegionStats());
    } else {
      resetGenericStats(sdTemplate, NULL);
    }
    resetIntStats(symCodeLen);
    arithDecoder->start();
  }

  // set up the arithmetic decoder for refinement/aggregation
  if (refAgg) {
    if (contextUsed && inputSymbolDict) {
      resetRefinementStats(sdrTemplate,
			   inputSymbolDict->getRefinementRegionStats());
    } else {
      resetRefinementStats(sdrTemplate, NULL);
    }
  }

  // allocate symbol widths storage
  symWidths = NULL;
  if (huff && !refAgg) {
    symWidths = (Guint *)gmallocn(numNewSyms, sizeof(Guint));
  }

  symHeight = 0;
  i = 0;
  while (i < numNewSyms) {

    // read the height class delta height
    if (huff) {
      huffDecoder->decodeInt(&dh, huffDHTable);
    } else {
      arithDecoder->decodeInt(&dh, iadhStats);
    }
    if (dh < 0 && (Guint)-dh >= symHeight) {
      error(getPos(), "Bad delta-height value in JBIG2 symbol dictionary");
      goto syntaxError;
    }
    symHeight += dh;
    symWidth = 0;
    totalWidth = 0;
    j = i;

    // read the symbols in this height class
    while (1) {

      // read the delta width
      if (huff) {
	if (!huffDecoder->decodeInt(&dw, huffDWTable)) {
	  break;
	}
      } else {
	if (!arithDecoder->decodeInt(&dw, iadwStats)) {
	  break;
	}
      }
      if (dw < 0 && (Guint)-dw >= symWidth) {
	error(getPos(), "Bad delta-height value in JBIG2 symbol dictionary");
	goto syntaxError;
      }
      symWidth += dw;

      // using a collective bitmap, so don't read a bitmap here
      if (huff && !refAgg) {
	symWidths[i] = symWidth;
	totalWidth += symWidth;

      // refinement/aggregate coding
      } else if (refAgg) {
	if (huff) {
	  if (!huffDecoder->decodeInt(&refAggNum, huffAggInstTable)) {
	    break;
	  }
	} else {
	  if (!arithDecoder->decodeInt(&refAggNum, iaaiStats)) {
	    break;
	  }
	}
#if 0 //~ This special case was added about a year before the final draft
      //~ of the JBIG2 spec was released.  I have encountered some old
      //~ JBIG2 images that predate it.
	if (0) {
#else
	if (refAggNum == 1) {
#endif
	  if (huff) {
	    symID = huffDecoder->readBits(symCodeLen);
	    huffDecoder->decodeInt(&refDX, huffTableO);
	    huffDecoder->decodeInt(&refDY, huffTableO);
	    huffDecoder->decodeInt(&bmSize, huffTableA);
	    huffDecoder->reset();
	    arithDecoder->start();
	  } else {
	    symID = arithDecoder->decodeIAID(symCodeLen, iaidStats);
	    arithDecoder->decodeInt(&refDX, iardxStats);
	    arithDecoder->decodeInt(&refDY, iardyStats);
	  }
	  refBitmap = bitmaps[symID];
	  bitmaps[numInputSyms + i] =
	      readGenericRefinementRegion(symWidth, symHeight,
					  sdrTemplate, gFalse,
					  refBitmap, refDX, refDY,
					  sdrATX, sdrATY);
	  //~ do we need to use the bmSize value here (in Huffman mode)?
	} else {
	  bitmaps[numInputSyms + i] =
	      readTextRegion(huff, gTrue, symWidth, symHeight,
			     refAggNum, 0, numInputSyms + i, NULL,
			     symCodeLen, bitmaps, 0, 0, 0, 1, 0,
			     huffTableF, huffTableH, huffTableK, huffTableO,
			     huffTableO, huffTableO, huffTableO, huffTableA,
			     sdrTemplate, sdrATX, sdrATY);
	}

      // non-ref/agg coding
      } else {
	bitmaps[numInputSyms + i] =
	    readGenericBitmap(gFalse, symWidth, symHeight,
			      sdTemplate, gFalse, gFalse, NULL,
			      sdATX, sdATY, 0);
      }

      ++i;
    }

    // read the collective bitmap
    if (huff && !refAgg) {
      huffDecoder->decodeInt(&bmSize, huffBMSizeTable);
      huffDecoder->reset();
      if (bmSize == 0) {
	collBitmap = new JBIG2Bitmap(0, totalWidth, symHeight);
	bmSize = symHeight * ((totalWidth + 7) >> 3);
	p = collBitmap->getDataPtr();
	for (k = 0; k < (Guint)bmSize; ++k) {
	  *p++ = curStr->getChar();
	}
      } else {
	collBitmap = readGenericBitmap(gTrue, totalWidth, symHeight,
				       0, gFalse, gFalse, NULL, NULL, NULL,
				       bmSize);
      }
      x = 0;
      for (; j < i; ++j) {
	bitmaps[numInputSyms + j] =
	    collBitmap->getSlice(x, 0, symWidths[j], symHeight);
	x += symWidths[j];
      }
      delete collBitmap;
    }
  }

  // create the symbol dict object
  symbolDict = new JBIG2SymbolDict(segNum, numExSyms);

  // exported symbol list
  i = j = 0;
  ex = gFalse;
  while (i < numInputSyms + numNewSyms) {
    if (huff) {
      huffDecoder->decodeInt(&run, huffTableA);
    } else {
      arithDecoder->decodeInt(&run, iaexStats);
    }
    if (ex) {
      for (cnt = 0; cnt < run; ++cnt) {
	symbolDict->setBitmap(j++, bitmaps[i++]->copy());
      }
    } else {
      i += run;
    }
    ex = !ex;
  }

  for (i = 0; i < numNewSyms; ++i) {
    delete bitmaps[numInputSyms + i];
  }
  gfree(bitmaps);
  if (symWidths) {
    gfree(symWidths);
  }

  // save the arithmetic decoder stats
  if (!huff && contextRetained) {
    symbolDict->setGenericRegionStats(genericRegionStats->copy());
    if (refAgg) {
      symbolDict->setRefinementRegionStats(refinementRegionStats->copy());
    }
  }

  // store the new symbol dict
  segments->append(symbolDict);

  return gTrue;

 syntaxError:
  for (i = 0; i < numNewSyms; ++i) {
    if (bitmaps[numInputSyms + i]) {
      delete bitmaps[numInputSyms + i];
    }
  }
  gfree(bitmaps);
  if (symWidths) {
    gfree(symWidths);
  }
  return gFalse;

 eofError:
  error(getPos(), "Unexpected EOF in JBIG2 stream");
  return gFalse;
}

void JBIG2Stream::readTextRegionSeg(Guint segNum, GBool imm,
				    GBool lossless, Guint length,
				    Guint *refSegs, Guint nRefSegs) {
  JBIG2Bitmap *bitmap;
  JBIG2HuffmanTable runLengthTab[36];
  JBIG2HuffmanTable *symCodeTab;
  JBIG2HuffmanTable *huffFSTable, *huffDSTable, *huffDTTable;
  JBIG2HuffmanTable *huffRDWTable, *huffRDHTable;
  JBIG2HuffmanTable *huffRDXTable, *huffRDYTable, *huffRSizeTable;
  JBIG2Segment *seg;
  GList *codeTables;
  JBIG2SymbolDict *symbolDict;
  JBIG2Bitmap **syms;
  Guint w, h, x, y, segInfoFlags, extCombOp;
  Guint flags, huff, refine, logStrips, refCorner, transposed;
  Guint combOp, defPixel, templ;
  int sOffset;
  Guint huffFlags, huffFS, huffDS, huffDT;
  Guint huffRDW, huffRDH, huffRDX, huffRDY, huffRSize;
  Guint numInstances, numSyms, symCodeLen;
  int atx[2], aty[2];
  Guint i, k, kk;
  int j;

  // region segment info field
  if (!readULong(&w) || !readULong(&h) ||
      !readULong(&x) || !readULong(&y) ||
      !readUByte(&segInfoFlags)) {
    goto eofError;
  }
  extCombOp = segInfoFlags & 7;

  // rest of the text region header
  if (!readUWord(&flags)) {
    goto eofError;
  }
  huff = flags & 1;
  refine = (flags >> 1) & 1;
  logStrips = (flags >> 2) & 3;
  refCorner = (flags >> 4) & 3;
  transposed = (flags >> 6) & 1;
  combOp = (flags >> 7) & 3;
  defPixel = (flags >> 9) & 1;
  sOffset = (flags >> 10) & 0x1f;
  if (sOffset & 0x10) {
    sOffset |= -1 - 0x0f;
  }
  templ = (flags >> 15) & 1;
  huffFS = huffDS = huffDT = 0; // make gcc happy
  huffRDW = huffRDH = huffRDX = huffRDY = huffRSize = 0; // make gcc happy
  if (huff) {
    if (!readUWord(&huffFlags)) {
      goto eofError;
    }
    huffFS = huffFlags & 3;
    huffDS = (huffFlags >> 2) & 3;
    huffDT = (huffFlags >> 4) & 3;
    huffRDW = (huffFlags >> 6) & 3;
    huffRDH = (huffFlags >> 8) & 3;
    huffRDX = (huffFlags >> 10) & 3;
    huffRDY = (huffFlags >> 12) & 3;
    huffRSize = (huffFlags >> 14) & 1;
  }
  if (refine && templ == 0) {
    if (!readByte(&atx[0]) || !readByte(&aty[0]) ||
	!readByte(&atx[1]) || !readByte(&aty[1])) {
      goto eofError;
    }
  }
  if (!readULong(&numInstances)) {
    goto eofError;
  }

  // get symbol dictionaries and tables
  codeTables = new GList();
  numSyms = 0;
  for (i = 0; i < nRefSegs; ++i) {
    if ((seg = findSegment(refSegs[i]))) {
      if (seg->getType() == jbig2SegSymbolDict) {
	numSyms += ((JBIG2SymbolDict *)seg)->getSize();
      } else if (seg->getType() == jbig2SegCodeTable) {
	codeTables->append(seg);
      }
    } else {
      error(getPos(), "Invalid segment reference in JBIG2 text region");
    }
  }
  symCodeLen = 0;
  i = 1;
  while (i < numSyms) {
    ++symCodeLen;
    i <<= 1;
  }

  // get the symbol bitmaps
  syms = (JBIG2Bitmap **)gmallocn(numSyms, sizeof(JBIG2Bitmap *));
  kk = 0;
  for (i = 0; i < nRefSegs; ++i) {
    if ((seg = findSegment(refSegs[i]))) {
      if (seg->getType() == jbig2SegSymbolDict) {
	symbolDict = (JBIG2SymbolDict *)seg;
	for (k = 0; k < symbolDict->getSize(); ++k) {
	  syms[kk++] = symbolDict->getBitmap(k);
	}
      }
    }
  }

  // get the Huffman tables
  huffFSTable = huffDSTable = huffDTTable = NULL; // make gcc happy
  huffRDWTable = huffRDHTable = NULL; // make gcc happy
  huffRDXTable = huffRDYTable = huffRSizeTable = NULL; // make gcc happy
  i = 0;
  if (huff) {
    if (huffFS == 0) {
      huffFSTable = huffTableF;
    } else if (huffFS == 1) {
      huffFSTable = huffTableG;
    } else {
      huffFSTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
    }
    if (huffDS == 0) {
      huffDSTable = huffTableH;
    } else if (huffDS == 1) {
      huffDSTable = huffTableI;
    } else if (huffDS == 2) {
      huffDSTable = huffTableJ;
    } else {
      huffDSTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
    }
    if (huffDT == 0) {
      huffDTTable = huffTableK;
    } else if (huffDT == 1) {
      huffDTTable = huffTableL;
    } else if (huffDT == 2) {
      huffDTTable = huffTableM;
    } else {
      huffDTTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
    }
    if (huffRDW == 0) {
      huffRDWTable = huffTableN;
    } else if (huffRDW == 1) {
      huffRDWTable = huffTableO;
    } else {
      huffRDWTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
    }
    if (huffRDH == 0) {
      huffRDHTable = huffTableN;
    } else if (huffRDH == 1) {
      huffRDHTable = huffTableO;
    } else {
      huffRDHTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
    }
    if (huffRDX == 0) {
      huffRDXTable = huffTableN;
    } else if (huffRDX == 1) {
      huffRDXTable = huffTableO;
    } else {
      huffRDXTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
    }
    if (huffRDY == 0) {
      huffRDYTable = huffTableN;
    } else if (huffRDY == 1) {
      huffRDYTable = huffTableO;
    } else {
      huffRDYTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
    }
    if (huffRSize == 0) {
      huffRSizeTable = huffTableA;
    } else {
      huffRSizeTable =
	  ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
    }
  }
  delete codeTables;

  // symbol ID Huffman decoding table
  if (huff) {
    huffDecoder->reset();
    for (i = 0; i < 32; ++i) {
      runLengthTab[i].val = i;
      runLengthTab[i].prefixLen = huffDecoder->readBits(4);
      runLengthTab[i].rangeLen = 0;
    }
    runLengthTab[32].val = 0x103;
    runLengthTab[32].prefixLen = huffDecoder->readBits(4);
    runLengthTab[32].rangeLen = 2;
    runLengthTab[33].val = 0x203;
    runLengthTab[33].prefixLen = huffDecoder->readBits(4);
    runLengthTab[33].rangeLen = 3;
    runLengthTab[34].val = 0x20b;
    runLengthTab[34].prefixLen = huffDecoder->readBits(4);
    runLengthTab[34].rangeLen = 7;
    runLengthTab[35].prefixLen = 0;
    runLengthTab[35].rangeLen = jbig2HuffmanEOT;
    huffDecoder->buildTable(runLengthTab, 35);
    symCodeTab = (JBIG2HuffmanTable *)gmallocn(numSyms + 1,
					       sizeof(JBIG2HuffmanTable));
    for (i = 0; i < numSyms; ++i) {
      symCodeTab[i].val = i;
      symCodeTab[i].rangeLen = 0;
    }
    i = 0;
    while (i < numSyms) {
      huffDecoder->decodeInt(&j, runLengthTab);
      if (j > 0x200) {
	for (j -= 0x200; j && i < numSyms; --j) {
	  symCodeTab[i++].prefixLen = 0;
	}
      } else if (j > 0x100) {
	for (j -= 0x100; j && i < numSyms; --j) {
	  symCodeTab[i].prefixLen = symCodeTab[i-1].prefixLen;
	  ++i;
	}
      } else {
	symCodeTab[i++].prefixLen = j;
      }
    }
    symCodeTab[numSyms].prefixLen = 0;
    symCodeTab[numSyms].rangeLen = jbig2HuffmanEOT;
    huffDecoder->buildTable(symCodeTab, numSyms);
    huffDecoder->reset();

  // set up the arithmetic decoder
  } else {
    symCodeTab = NULL;
    resetIntStats(symCodeLen);
    arithDecoder->start();
  }
  if (refine) {
    resetRefinementStats(templ, NULL);
  }

  bitmap = readTextRegion(huff, refine, w, h, numInstances,
			  logStrips, numSyms, symCodeTab, symCodeLen, syms,
			  defPixel, combOp, transposed, refCorner, sOffset,
			  huffFSTable, huffDSTable, huffDTTable,
			  huffRDWTable, huffRDHTable,
			  huffRDXTable, huffRDYTable, huffRSizeTable,
			  templ, atx, aty);

  gfree(syms);

  // combine the region bitmap into the page bitmap
  if (imm) {
    if (pageH == 0xffffffff && y + h > curPageH) {
      pageBitmap->expand(y + h, pageDefPixel);
    }
    pageBitmap->combine(bitmap, x, y, extCombOp);
    delete bitmap;

  // store the region bitmap
  } else {
    bitmap->setSegNum(segNum);
    segments->append(bitmap);
  }

  // clean up the Huffman decoder
  if (huff) {
    gfree(symCodeTab);
  }

  return;

 eofError:
  error(getPos(), "Unexpected EOF in JBIG2 stream");
}

JBIG2Bitmap *JBIG2Stream::readTextRegion(GBool huff, GBool refine,
					 int w, int h,
					 Guint numInstances,
					 Guint logStrips,
					 int numSyms,
					 JBIG2HuffmanTable *symCodeTab,
					 Guint symCodeLen,
					 JBIG2Bitmap **syms,
					 Guint defPixel, Guint combOp,
					 Guint transposed, Guint refCorner,
					 int sOffset,
					 JBIG2HuffmanTable *huffFSTable,
					 JBIG2HuffmanTable *huffDSTable,
					 JBIG2HuffmanTable *huffDTTable,
					 JBIG2HuffmanTable *huffRDWTable,
					 JBIG2HuffmanTable *huffRDHTable,
					 JBIG2HuffmanTable *huffRDXTable,
					 JBIG2HuffmanTable *huffRDYTable,
					 JBIG2HuffmanTable *huffRSizeTable,
					 Guint templ,
					 int *atx, int *aty) {
  JBIG2Bitmap *bitmap;
  JBIG2Bitmap *symbolBitmap;
  Guint strips;
  int t, dt, tt, s, ds, sFirst, j;
  int rdw, rdh, rdx, rdy, ri, refDX, refDY, bmSize;
  Guint symID, inst, bw, bh;

  strips = 1 << logStrips;

  // allocate the bitmap
  bitmap = new JBIG2Bitmap(0, w, h);
  if (defPixel) {
    bitmap->clearToOne();
  } else {
    bitmap->clearToZero();
  }

  // decode initial T value
  if (huff) {
    huffDecoder->decodeInt(&t, huffDTTable);
  } else {
    arithDecoder->decodeInt(&t, iadtStats);
  }
  t *= -(int)strips;

  inst = 0;
  sFirst = 0;
  while (inst < numInstances) {

    // decode delta-T
    if (huff) {
      huffDecoder->decodeInt(&dt, huffDTTable);
    } else {
      arithDecoder->decodeInt(&dt, iadtStats);
    }
    t += dt * strips;

    // first S value
    if (huff) {
      huffDecoder->decodeInt(&ds, huffFSTable);
    } else {
      arithDecoder->decodeInt(&ds, iafsStats);
    }
    sFirst += ds;
    s = sFirst;

    // read the instances
    while (1) {

      // T value
      if (strips == 1) {
	dt = 0;
      } else if (huff) {
	dt = huffDecoder->readBits(logStrips);
      } else {
	arithDecoder->decodeInt(&dt, iaitStats);
      }
      tt = t + dt;

      // symbol ID
      if (huff) {
	if (symCodeTab) {
	  huffDecoder->decodeInt(&j, symCodeTab);
	  symID = (Guint)j;
	} else {
	  symID = huffDecoder->readBits(symCodeLen);
	}
      } else {
	symID = arithDecoder->decodeIAID(symCodeLen, iaidStats);
      }

      if (symID >= (Guint)numSyms) {
	error(getPos(), "Invalid symbol number in JBIG2 text region");
      } else {

	// get the symbol bitmap
	symbolBitmap = NULL;
	if (refine) {
	  if (huff) {
	    ri = (int)huffDecoder->readBit();
	  } else {
	    arithDecoder->decodeInt(&ri, iariStats);
	  }
	} else {
	  ri = 0;
	}
	if (ri) {
	  if (huff) {
	    huffDecoder->decodeInt(&rdw, huffRDWTable);
	    huffDecoder->decodeInt(&rdh, huffRDHTable);
	    huffDecoder->decodeInt(&rdx, huffRDXTable);
	    huffDecoder->decodeInt(&rdy, huffRDYTable);
	    huffDecoder->decodeInt(&bmSize, huffRSizeTable);
	    huffDecoder->reset();
	    arithDecoder->start();
	  } else {
	    arithDecoder->decodeInt(&rdw, iardwStats);
	    arithDecoder->decodeInt(&rdh, iardhStats);
	    arithDecoder->decodeInt(&rdx, iardxStats);
	    arithDecoder->decodeInt(&rdy, iardyStats);
	  }
	  refDX = ((rdw >= 0) ? rdw : rdw - 1) / 2 + rdx;
	  refDY = ((rdh >= 0) ? rdh : rdh - 1) / 2 + rdy;

	  symbolBitmap =
	    readGenericRefinementRegion(rdw + syms[symID]->getWidth(),
					rdh + syms[symID]->getHeight(),
					templ, gFalse, syms[symID],
					refDX, refDY, atx, aty);
	  //~ do we need to use the bmSize value here (in Huffman mode)?
	} else {
	  symbolBitmap = syms[symID];
	}

	// combine the symbol bitmap into the region bitmap
	//~ something is wrong here - refCorner shouldn't degenerate into
	//~   two cases
	bw = symbolBitmap->getWidth() - 1;
	bh = symbolBitmap->getHeight() - 1;
	if (transposed) {
	  switch (refCorner) {
	  case 0: // bottom left
	    bitmap->combine(symbolBitmap, tt, s, combOp);
	    break;
	  case 1: // top left
	    bitmap->combine(symbolBitmap, tt, s, combOp);
	    break;
	  case 2: // bottom right
	    bitmap->combine(symbolBitmap, tt - bw, s, combOp);
	    break;
	  case 3: // top right
	    bitmap->combine(symbolBitmap, tt - bw, s, combOp);
	    break;
	  }
	  s += bh;
	} else {
	  switch (refCorner) {
	  case 0: // bottom left
	    bitmap->combine(symbolBitmap, s, tt - bh, combOp);
	    break;
	  case 1: // top left
	    bitmap->combine(symbolBitmap, s, tt, combOp);
	    break;
	  case 2: // bottom right
	    bitmap->combine(symbolBitmap, s, tt - bh, combOp);
	    break;
	  case 3: // top right
	    bitmap->combine(symbolBitmap, s, tt, combOp);
	    break;
	  }
	  s += bw;
	}
	if (ri) {
	  delete symbolBitmap;
	}
      }

      // next instance
      ++inst;

      // next S value
      if (huff) {
	if (!huffDecoder->decodeInt(&ds, huffDSTable)) {
	  break;
	}
      } else {
	if (!arithDecoder->decodeInt(&ds, iadsStats)) {
	  break;
	}
      }
      s += sOffset + ds;
    }
  }

  return bitmap;
}

void JBIG2Stream::readPatternDictSeg(Guint segNum, Guint length) {
  JBIG2PatternDict *patternDict;
  JBIG2Bitmap *bitmap;
  Guint flags, patternW, patternH, grayMax, templ, mmr;
  int atx[4], aty[4];
  Guint i, x;

  // halftone dictionary flags, pattern width and height, max gray value
  if (!readUByte(&flags) ||
      !readUByte(&patternW) ||
      !readUByte(&patternH) ||
      !readULong(&grayMax)) {
    goto eofError;
  }
  templ = (flags >> 1) & 3;
  mmr = flags & 1;

  // set up the arithmetic decoder
  if (!mmr) {
    resetGenericStats(templ, NULL);
    arithDecoder->start();
  }

  // read the bitmap
  atx[0] = -(int)patternW; aty[0] =  0;
  atx[1] = -3;             aty[1] = -1;
  atx[2] =  2;             aty[2] = -2;
  atx[3] = -2;             aty[3] = -2;
  bitmap = readGenericBitmap(mmr, (grayMax + 1) * patternW, patternH,
			     templ, gFalse, gFalse, NULL,
			     atx, aty, length - 7);

  // create the pattern dict object
  patternDict = new JBIG2PatternDict(segNum, grayMax + 1);

  // split up the bitmap
  x = 0;
  for (i = 0; i <= grayMax; ++i) {
    patternDict->setBitmap(i, bitmap->getSlice(x, 0, patternW, patternH));
    x += patternW;
  }

  // free memory
  delete bitmap;

  // store the new pattern dict
  segments->append(patternDict);

  return;

 eofError:
  error(getPos(), "Unexpected EOF in JBIG2 stream");
}

void JBIG2Stream::readHalftoneRegionSeg(Guint segNum, GBool imm,
					GBool lossless, Guint length,
					Guint *refSegs, Guint nRefSegs) {
  JBIG2Bitmap *bitmap;
  JBIG2Segment *seg;
  JBIG2PatternDict *patternDict;
  JBIG2Bitmap *skipBitmap;
  Guint *grayImg;
  JBIG2Bitmap *grayBitmap;
  JBIG2Bitmap *patternBitmap;
  Guint w, h, x, y, segInfoFlags, extCombOp;
  Guint flags, mmr, templ, enableSkip, combOp;
  Guint gridW, gridH, stepX, stepY, patW, patH;
  int atx[4], aty[4];
  int gridX, gridY, xx, yy, bit, j;
  Guint bpp, m, n, i;

  // region segment info field
  if (!readULong(&w) || !readULong(&h) ||
      !readULong(&x) || !readULong(&y) ||
      !readUByte(&segInfoFlags)) {
    goto eofError;
  }
  extCombOp = segInfoFlags & 7;

  // rest of the halftone region header
  if (!readUByte(&flags)) {
    goto eofError;
  }
  mmr = flags & 1;
  templ = (flags >> 1) & 3;
  enableSkip = (flags >> 3) & 1;
  combOp = (flags >> 4) & 7;
  if (!readULong(&gridW) || !readULong(&gridH) ||
      !readLong(&gridX) || !readLong(&gridY) ||
      !readUWord(&stepX) || !readUWord(&stepY)) {
    goto eofError;
  }
  if (w == 0 || h == 0 || w >= INT_MAX / h) {
    error(getPos(), "Bad bitmap size in JBIG2 halftone segment");
    return;
  }
  if (gridH == 0 || gridW >= INT_MAX / gridH) {
    error(getPos(), "Bad grid size in JBIG2 halftone segment");
    return;
  }

  // get pattern dictionary
  if (nRefSegs != 1) {
    error(getPos(), "Bad symbol dictionary reference in JBIG2 halftone segment");
    return;
  }
  seg = findSegment(refSegs[0]);
  if (seg->getType() != jbig2SegPatternDict) {
    error(getPos(), "Bad symbol dictionary reference in JBIG2 halftone segment");
    return;
  }
  patternDict = (JBIG2PatternDict *)seg;
  bpp = 0;
  i = 1;
  while (i < patternDict->getSize()) {
    ++bpp;
    i <<= 1;
  }
  patW = patternDict->getBitmap(0)->getWidth();
  patH = patternDict->getBitmap(0)->getHeight();

  // set up the arithmetic decoder
  if (!mmr) {
    resetGenericStats(templ, NULL);
    arithDecoder->start();
  }

  // allocate the bitmap
  bitmap = new JBIG2Bitmap(segNum, w, h);
  if (flags & 0x80) { // HDEFPIXEL
    bitmap->clearToOne();
  } else {
    bitmap->clearToZero();
  }

  // compute the skip bitmap
  skipBitmap = NULL;
  if (enableSkip) {
    skipBitmap = new JBIG2Bitmap(0, gridW, gridH);
    skipBitmap->clearToZero();
    for (m = 0; m < gridH; ++m) {
      for (n = 0; n < gridW; ++n) {
	xx = gridX + m * stepY + n * stepX;
	yy = gridY + m * stepX - n * stepY;
	if (((xx + (int)patW) >> 8) <= 0 || (xx >> 8) >= (int)w ||
	    ((yy + (int)patH) >> 8) <= 0 || (yy >> 8) >= (int)h) {
	  skipBitmap->setPixel(n, m);
	}
      }
    }
  }

  // read the gray-scale image
  grayImg = (Guint *)gmallocn(gridW * gridH, sizeof(Guint));
  memset(grayImg, 0, gridW * gridH * sizeof(Guint));
  atx[0] = templ <= 1 ? 3 : 2;  aty[0] = -1;
  atx[1] = -3;                  aty[1] = -1;
  atx[2] =  2;                  aty[2] = -2;
  atx[3] = -2;                  aty[3] = -2;
  for (j = bpp - 1; j >= 0; --j) {
    grayBitmap = readGenericBitmap(mmr, gridW, gridH, templ, gFalse,
				   enableSkip, skipBitmap, atx, aty, -1);
    i = 0;
    for (m = 0; m < gridH; ++m) {
      for (n = 0; n < gridW; ++n) {
	bit = grayBitmap->getPixel(n, m) ^ (grayImg[i] & 1);
	grayImg[i] = (grayImg[i] << 1) | bit;
	++i;
      }
    }
    delete grayBitmap;
  }

  // decode the image
  i = 0;
  for (m = 0; m < gridH; ++m) {
    xx = gridX + m * stepY;
    yy = gridY + m * stepX;
    for (n = 0; n < gridW; ++n) {
      if (!(enableSkip && skipBitmap->getPixel(n, m))) {
	patternBitmap = patternDict->getBitmap(grayImg[i]);
	bitmap->combine(patternBitmap, xx >> 8, yy >> 8, combOp);
      }
      xx += stepX;
      yy -= stepY;
      ++i;
    }
  }

  gfree(grayImg);
  if (skipBitmap) {
    delete skipBitmap;
  }

  // combine the region bitmap into the page bitmap
  if (imm) {
    if (pageH == 0xffffffff && y + h > curPageH) {
      pageBitmap->expand(y + h, pageDefPixel);
    }
    pageBitmap->combine(bitmap, x, y, extCombOp);
    delete bitmap;

  // store the region bitmap
  } else {
    segments->append(bitmap);
  }

  return;

 eofError:
  error(getPos(), "Unexpected EOF in JBIG2 stream");
}

void JBIG2Stream::readGenericRegionSeg(Guint segNum, GBool imm,
				       GBool lossless, Guint length) {
  JBIG2Bitmap *bitmap;
  Guint w, h, x, y, segInfoFlags, extCombOp;
  Guint flags, mmr, templ, tpgdOn;
  int atx[4], aty[4];

  // region segment info field
  if (!readULong(&w) || !readULong(&h) ||
      !readULong(&x) || !readULong(&y) ||
      !readUByte(&segInfoFlags)) {
    goto eofError;
  }
  extCombOp = segInfoFlags & 7;

  // rest of the generic region segment header
  if (!readUByte(&flags)) {
    goto eofError;
  }
  mmr = flags & 1;
  templ = (flags >> 1) & 3;
  tpgdOn = (flags >> 3) & 1;

  // AT flags
  if (!mmr) {
    if (templ == 0) {
      if (!readByte(&atx[0]) ||
	  !readByte(&aty[0]) ||
	  !readByte(&atx[1]) ||
	  !readByte(&aty[1]) ||
	  !readByte(&atx[2]) ||
	  !readByte(&aty[2]) ||
	  !readByte(&atx[3]) ||
	  !readByte(&aty[3])) {
	goto eofError;
      }
    } else {
      if (!readByte(&atx[0]) ||
	  !readByte(&aty[0])) {
	goto eofError;
      }
    }
  }

  // set up the arithmetic decoder
  if (!mmr) {
    resetGenericStats(templ, NULL);
    arithDecoder->start();
  }

  // read the bitmap
  bitmap = readGenericBitmap(mmr, w, h, templ, tpgdOn, gFalse,
			     NULL, atx, aty, mmr ? 0 : length - 18);

  // combine the region bitmap into the page bitmap
  if (imm) {
    if (pageH == 0xffffffff && y + h > curPageH) {
      pageBitmap->expand(y + h, pageDefPixel);
    }
    pageBitmap->combine(bitmap, x, y, extCombOp);
    delete bitmap;

  // store the region bitmap
  } else {
    bitmap->setSegNum(segNum);
    segments->append(bitmap);
  }

  return;

 eofError:
  error(getPos(), "Unexpected EOF in JBIG2 stream");
}

JBIG2Bitmap *JBIG2Stream::readGenericBitmap(GBool mmr, int w, int h,
					    int templ, GBool tpgdOn,
					    GBool useSkip, JBIG2Bitmap *skip,
					    int *atx, int *aty,
					    int mmrDataLength) {
  JBIG2Bitmap *bitmap;
  GBool ltp;
  Guint ltpCX, cx, cx0, cx1, cx2;
  JBIG2BitmapPtr cxPtr0, cxPtr1;
  JBIG2BitmapPtr atPtr0, atPtr1, atPtr2, atPtr3;
  int *refLine, *codingLine;
  int code1, code2, code3;
  int x, y, a0, pix, i, refI, codingI;

  bitmap = new JBIG2Bitmap(0, w, h);
  bitmap->clearToZero();

  //----- MMR decode

  if (mmr) {

    mmrDecoder->reset();
    refLine = (int *)gmallocn(w + 2, sizeof(int));
    codingLine = (int *)gmallocn(w + 2, sizeof(int));
    codingLine[0] = codingLine[1] = w;

    for (y = 0; y < h; ++y) {

      // copy coding line to ref line
      for (i = 0; codingLine[i] < w; ++i) {
	refLine[i] = codingLine[i];
      }
      refLine[i] = refLine[i + 1] = w;

      // decode a line
      refI = 0;     // b1 = refLine[refI]
      codingI = 0;  // a1 = codingLine[codingI]
      a0 = 0;
      do {
	code1 = mmrDecoder->get2DCode();
	switch (code1) {
	case twoDimPass:
	  if (refLine[refI] < w) {
	    a0 = refLine[refI + 1];
	    refI += 2;
	  }
	  break;
	case twoDimHoriz:
	  if (codingI & 1) {
	    code1 = 0;
	    do {
	      code1 += code3 = mmrDecoder->getBlackCode();
	    } while (code3 >= 64);
	    code2 = 0;
	    do {
	      code2 += code3 = mmrDecoder->getWhiteCode();
	    } while (code3 >= 64);
	  } else {
	    code1 = 0;
	    do {
	      code1 += code3 = mmrDecoder->getWhiteCode();
	    } while (code3 >= 64);
	    code2 = 0;
	    do {
	      code2 += code3 = mmrDecoder->getBlackCode();
	    } while (code3 >= 64);
	  }
	  if (code1 > 0 || code2 > 0) {
	    a0 = codingLine[codingI++] = a0 + code1;
	    a0 = codingLine[codingI++] = a0 + code2;
	    while (refLine[refI] <= a0 && refLine[refI] < w) {
	      refI += 2;
	    }
	  }
	  break;
	case twoDimVert0:
	  a0 = codingLine[codingI++] = refLine[refI];
	  if (refLine[refI] < w) {
	    ++refI;
	  }
	  break;
	case twoDimVertR1:
	  a0 = codingLine[codingI++] = refLine[refI] + 1;
	  if (refLine[refI] < w) {
	    ++refI;
	    while (refLine[refI] <= a0 && refLine[refI] < w) {
	      refI += 2;
	    }
	  }
	  break;
	case twoDimVertR2:
	  a0 = codingLine[codingI++] = refLine[refI] + 2;
	  if (refLine[refI] < w) {
	    ++refI;
	    while (refLine[refI] <= a0 && refLine[refI] < w) {
	      refI += 2;
	    }
	  }
	  break;
	case twoDimVertR3:
	  a0 = codingLine[codingI++] = refLine[refI] + 3;
	  if (refLine[refI] < w) {
	    ++refI;
	    while (refLine[refI] <= a0 && refLine[refI] < w) {
	      refI += 2;
	    }
	  }
	  break;
	case twoDimVertL1:
	  a0 = codingLine[codingI++] = refLine[refI] - 1;
	  if (refI > 0) {
	    --refI;
	  } else {
	    ++refI;
	  }
	  while (refLine[refI] <= a0 && refLine[refI] < w) {
	    refI += 2;
	  }
	  break;
	case twoDimVertL2:
	  a0 = codingLine[codingI++] = refLine[refI] - 2;
	  if (refI > 0) {
	    --refI;
	  } else {
	    ++refI;
	  }
	  while (refLine[refI] <= a0 && refLine[refI] < w) {
	    refI += 2;
	  }
	  break;
	case twoDimVertL3:
	  a0 = codingLine[codingI++] = refLine[refI] - 3;
	  if (refI > 0) {
	    --refI;
	  } else {
	    ++refI;
	  }
	  while (refLine[refI] <= a0 && refLine[refI] < w) {
	    refI += 2;
	  }
	  break;
	default:
	  error(getPos(), "Illegal code in JBIG2 MMR bitmap data");
	  break;
	}
      } while (a0 < w);
      codingLine[codingI++] = w;

      // convert the run lengths to a bitmap line
      i = 0;
      while (codingLine[i] < w) {
	for (x = codingLine[i]; x < codingLine[i+1]; ++x) {
	  bitmap->setPixel(x, y);
	}
	i += 2;
      }
    }

    if (mmrDataLength >= 0) {
      mmrDecoder->skipTo(mmrDataLength);
    } else {
      if (mmrDecoder->get24Bits() != 0x001001) {
	error(getPos(), "Missing EOFB in JBIG2 MMR bitmap data");
      }
    }

    gfree(refLine);
    gfree(codingLine);

  //----- arithmetic decode

  } else {
    // set up the typical row context
    ltpCX = 0; // make gcc happy
    if (tpgdOn) {
      switch (templ) {
      case 0:
	ltpCX = 0x3953; // 001 11001 0101 0011
	break;
      case 1:
	ltpCX = 0x079a; // 0011 11001 101 0
	break;
      case 2:
	ltpCX = 0x0e3; // 001 1100 01 1
	break;
      case 3:
	ltpCX = 0x18a; // 01100 0101 1
	break;
      }
    }

    ltp = 0;
    cx = cx0 = cx1 = cx2 = 0; // make gcc happy
    for (y = 0; y < h; ++y) {

      // check for a "typical" (duplicate) row
      if (tpgdOn) {
	if (arithDecoder->decodeBit(ltpCX, genericRegionStats)) {
	  ltp = !ltp;
	}
	if (ltp) {
	  bitmap->duplicateRow(y, y-1);
	  continue;
	}
      }

      switch (templ) {
      case 0:

	// set up the context
	bitmap->getPixelPtr(0, y-2, &cxPtr0);
	cx0 = bitmap->nextPixel(&cxPtr0);
	cx0 = (cx0 << 1) | bitmap->nextPixel(&cxPtr0);
	bitmap->getPixelPtr(0, y-1, &cxPtr1);
	cx1 = bitmap->nextPixel(&cxPtr1);
	cx1 = (cx1 << 1) | bitmap->nextPixel(&cxPtr1);
	cx1 = (cx1 << 1) | bitmap->nextPixel(&cxPtr1);
	cx2 = 0;
	bitmap->getPixelPtr(atx[0], y + aty[0], &atPtr0);
	bitmap->getPixelPtr(atx[1], y + aty[1], &atPtr1);
	bitmap->getPixelPtr(atx[2], y + aty[2], &atPtr2);
	bitmap->getPixelPtr(atx[3], y + aty[3], &atPtr3);

	// decode the row
	for (x = 0; x < w; ++x) {

	  // build the context
	  cx = (cx0 << 13) | (cx1 << 8) | (cx2 << 4) |
	       (bitmap->nextPixel(&atPtr0) << 3) |
	       (bitmap->nextPixel(&atPtr1) << 2) |
	       (bitmap->nextPixel(&atPtr2) << 1) |
	       bitmap->nextPixel(&atPtr3);

	  // check for a skipped pixel
	  if (useSkip && skip->getPixel(x, y)) {
	    pix = 0;

	  // decode the pixel
	  } else if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) {
	    bitmap->setPixel(x, y);
	  }

	  // update the context
	  cx0 = ((cx0 << 1) | bitmap->nextPixel(&cxPtr0)) & 0x07;
	  cx1 = ((cx1 << 1) | bitmap->nextPixel(&cxPtr1)) & 0x1f;
	  cx2 = ((cx2 << 1) | pix) & 0x0f;
	}
	break;

      case 1:

	// set up the context
	bitmap->getPixelPtr(0, y-2, &cxPtr0);
	cx0 = bitmap->nextPixel(&cxPtr0);
	cx0 = (cx0 << 1) | bitmap->nextPixel(&cxPtr0);
	cx0 = (cx0 << 1) | bitmap->nextPixel(&cxPtr0);
	bitmap->getPixelPtr(0, y-1, &cxPtr1);
	cx1 = bitmap->nextPixel(&cxPtr1);
	cx1 = (cx1 << 1) | bitmap->nextPixel(&cxPtr1);
	cx1 = (cx1 << 1) | bitmap->nextPixel(&cxPtr1);
	cx2 = 0;
	bitmap->getPixelPtr(atx[0], y + aty[0], &atPtr0);

	// decode the row
	for (x = 0; x < w; ++x) {

	  // build the context
	  cx = (cx0 << 9) | (cx1 << 4) | (cx2 << 1) |
	       bitmap->nextPixel(&atPtr0);

	  // check for a skipped pixel
	  if (useSkip && skip->getPixel(x, y)) {
	    pix = 0;

	  // decode the pixel
	  } else if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) {
	    bitmap->setPixel(x, y);
	  }

	  // update the context
	  cx0 = ((cx0 << 1) | bitmap->nextPixel(&cxPtr0)) & 0x0f;
	  cx1 = ((cx1 << 1) | bitmap->nextPixel(&cxPtr1)) & 0x1f;
	  cx2 = ((cx2 << 1) | pix) & 0x07;
	}
	break;

      case 2:

	// set up the context
	bitmap->getPixelPtr(0, y-2, &cxPtr0);
	cx0 = bitmap->nextPixel(&cxPtr0);
	cx0 = (cx0 << 1) | bitmap->nextPixel(&cxPtr0);
	bitmap->getPixelPtr(0, y-1, &cxPtr1);
	cx1 = bitmap->nextPixel(&cxPtr1);
	cx1 = (cx1 << 1) | bitmap->nextPixel(&cxPtr1);
	cx2 = 0;
	bitmap->getPixelPtr(atx[0], y + aty[0], &atPtr0);

	// decode the row
	for (x = 0; x < w; ++x) {

	  // build the context
	  cx = (cx0 << 7) | (cx1 << 3) | (cx2 << 1) |
	       bitmap->nextPixel(&atPtr0);

	  // check for a skipped pixel
	  if (useSkip && skip->getPixel(x, y)) {
	    pix = 0;

	  // decode the pixel
	  } else if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) {
	    bitmap->setPixel(x, y);
	  }

	  // update the context
	  cx0 = ((cx0 << 1) | bitmap->nextPixel(&cxPtr0)) & 0x07;
	  cx1 = ((cx1 << 1) | bitmap->nextPixel(&cxPtr1)) & 0x0f;
	  cx2 = ((cx2 << 1) | pix) & 0x03;
	}
	break;

      case 3:

	// set up the context
	bitmap->getPixelPtr(0, y-1, &cxPtr1);
	cx1 = bitmap->nextPixel(&cxPtr1);
	cx1 = (cx1 << 1) | bitmap->nextPixel(&cxPtr1);
	cx2 = 0;
	bitmap->getPixelPtr(atx[0], y + aty[0], &atPtr0);

	// decode the row
	for (x = 0; x < w; ++x) {

	  // build the context
	  cx = (cx1 << 5) | (cx2 << 1) |
	       bitmap->nextPixel(&atPtr0);

	  // check for a skipped pixel
	  if (useSkip && skip->getPixel(x, y)) {
	    pix = 0;

	  // decode the pixel
	  } else if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) {
	    bitmap->setPixel(x, y);
	  }

	  // update the context
	  cx1 = ((cx1 << 1) | bitmap->nextPixel(&cxPtr1)) & 0x1f;
	  cx2 = ((cx2 << 1) | pix) & 0x0f;
	}
	break;
      }
    }
  }

  return bitmap;
}

void JBIG2Stream::readGenericRefinementRegionSeg(Guint segNum, GBool imm,
						 GBool lossless, Guint length,
						 Guint *refSegs,
						 Guint nRefSegs) {
  JBIG2Bitmap *bitmap, *refBitmap;
  Guint w, h, x, y, segInfoFlags, extCombOp;
  Guint flags, templ, tpgrOn;
  int atx[2], aty[2];
  JBIG2Segment *seg;

  // region segment info field
  if (!readULong(&w) || !readULong(&h) ||
      !readULong(&x) || !readULong(&y) ||
      !readUByte(&segInfoFlags)) {
    goto eofError;
  }
  extCombOp = segInfoFlags & 7;

  // rest of the generic refinement region segment header
  if (!readUByte(&flags)) {
    goto eofError;
  }
  templ = flags & 1;
  tpgrOn = (flags >> 1) & 1;

  // AT flags
  if (!templ) {
    if (!readByte(&atx[0]) || !readByte(&aty[0]) ||
	!readByte(&atx[1]) || !readByte(&aty[1])) {
      goto eofError;
    }
  }

  // resize the page bitmap if needed
  if (nRefSegs == 0 || imm) {
    if (pageH == 0xffffffff && y + h > curPageH) {
      pageBitmap->expand(y + h, pageDefPixel);
    }
  }

  // get referenced bitmap
  if (nRefSegs > 1) {
    error(getPos(), "Bad reference in JBIG2 generic refinement segment");
    return;
  }
  if (nRefSegs == 1) {
    seg = findSegment(refSegs[0]);
    if (seg->getType() != jbig2SegBitmap) {
      error(getPos(), "Bad bitmap reference in JBIG2 generic refinement segment");
      return;
    }
    refBitmap = (JBIG2Bitmap *)seg;
  } else {
    refBitmap = pageBitmap->getSlice(x, y, w, h);
  }

  // set up the arithmetic decoder
  resetRefinementStats(templ, NULL);
  arithDecoder->start();

  // read
  bitmap = readGenericRefinementRegion(w, h, templ, tpgrOn,
				       refBitmap, 0, 0, atx, aty);

  // combine the region bitmap into the page bitmap
  if (imm) {
    pageBitmap->combine(bitmap, x, y, extCombOp);
    delete bitmap;

  // store the region bitmap
  } else {
    bitmap->setSegNum(segNum);
    segments->append(bitmap);
  }

  // delete the referenced bitmap
  if (nRefSegs == 1) {
    discardSegment(refSegs[0]);
  } else {
    delete refBitmap;
  }

  return;

 eofError:
  error(getPos(), "Unexpected EOF in JBIG2 stream");
}

JBIG2Bitmap *JBIG2Stream::readGenericRefinementRegion(int w, int h,
						      int templ, GBool tpgrOn,
						      JBIG2Bitmap *refBitmap,
						      int refDX, int refDY,
						      int *atx, int *aty) {
  JBIG2Bitmap *bitmap;
  GBool ltp;
  Guint ltpCX, cx, cx0, cx2, cx3, cx4, tpgrCX0, tpgrCX1, tpgrCX2;
  JBIG2BitmapPtr cxPtr0, cxPtr1, cxPtr2, cxPtr3, cxPtr4, cxPtr5, cxPtr6;
  JBIG2BitmapPtr tpgrCXPtr0, tpgrCXPtr1, tpgrCXPtr2;
  int x, y, pix;

  bitmap = new JBIG2Bitmap(0, w, h);
  bitmap->clearToZero();

  // set up the typical row context
  if (templ) {
    ltpCX = 0x008;
  } else {
    ltpCX = 0x0010;
  }

  ltp = 0;
  for (y = 0; y < h; ++y) {

    if (templ) {

      // set up the context
      bitmap->getPixelPtr(0, y-1, &cxPtr0);
      cx0 = bitmap->nextPixel(&cxPtr0);
      bitmap->getPixelPtr(-1, y, &cxPtr1);
      refBitmap->getPixelPtr(-refDX, y-1-refDY, &cxPtr2);
      refBitmap->getPixelPtr(-1-refDX, y-refDY, &cxPtr3);
      cx3 = refBitmap->nextPixel(&cxPtr3);
      cx3 = (cx3 << 1) | refBitmap->nextPixel(&cxPtr3);
      refBitmap->getPixelPtr(-refDX, y+1-refDY, &cxPtr4);
      cx4 = refBitmap->nextPixel(&cxPtr4);

      // set up the typical prediction context
      tpgrCX0 = tpgrCX1 = tpgrCX2 = 0; // make gcc happy
      if (tpgrOn) {
	refBitmap->getPixelPtr(-1-refDX, y-1-refDY, &tpgrCXPtr0);
	tpgrCX0 = refBitmap->nextPixel(&tpgrCXPtr0);
	tpgrCX0 = (tpgrCX0 << 1) | refBitmap->nextPixel(&tpgrCXPtr0);
	tpgrCX0 = (tpgrCX0 << 1) | refBitmap->nextPixel(&tpgrCXPtr0);
	refBitmap->getPixelPtr(-1-refDX, y-refDY, &tpgrCXPtr1);
	tpgrCX1 = refBitmap->nextPixel(&tpgrCXPtr1);
	tpgrCX1 = (tpgrCX1 << 1) | refBitmap->nextPixel(&tpgrCXPtr1);
	tpgrCX1 = (tpgrCX1 << 1) | refBitmap->nextPixel(&tpgrCXPtr1);
	refBitmap->getPixelPtr(-1-refDX, y+1-refDY, &tpgrCXPtr2);
	tpgrCX2 = refBitmap->nextPixel(&tpgrCXPtr2);
	tpgrCX2 = (tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2);
	tpgrCX2 = (tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2);
      }

      for (x = 0; x < w; ++x) {

	// update the context
	cx0 = ((cx0 << 1) | bitmap->nextPixel(&cxPtr0)) & 7;
	cx3 = ((cx3 << 1) | refBitmap->nextPixel(&cxPtr3)) & 7;
	cx4 = ((cx4 << 1) | refBitmap->nextPixel(&cxPtr4)) & 3;

	if (tpgrOn) {
	  // update the typical predictor context
	  tpgrCX0 = ((tpgrCX0 << 1) | refBitmap->nextPixel(&tpgrCXPtr0)) & 7;
	  tpgrCX1 = ((tpgrCX1 << 1) | refBitmap->nextPixel(&tpgrCXPtr1)) & 7;
	  tpgrCX2 = ((tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2)) & 7;

	  // check for a "typical" pixel
	  if (arithDecoder->decodeBit(ltpCX, refinementRegionStats)) {
	    ltp = !ltp;
	  }
	  if (tpgrCX0 == 0 && tpgrCX1 == 0 && tpgrCX2 == 0) {
	    bitmap->clearPixel(x, y);
	    continue;
	  } else if (tpgrCX0 == 7 && tpgrCX1 == 7 && tpgrCX2 == 7) {
	    bitmap->setPixel(x, y);
	    continue;
	  }
	}

	// build the context
	cx = (cx0 << 7) | (bitmap->nextPixel(&cxPtr1) << 6) |
	     (refBitmap->nextPixel(&cxPtr2) << 5) |
	     (cx3 << 2) | cx4;

	// decode the pixel
	if ((pix = arithDecoder->decodeBit(cx, refinementRegionStats))) {
	  bitmap->setPixel(x, y);
	}
      }

    } else {

      // set up the context
      bitmap->getPixelPtr(0, y-1, &cxPtr0);
      cx0 = bitmap->nextPixel(&cxPtr0);
      bitmap->getPixelPtr(-1, y, &cxPtr1);
      refBitmap->getPixelPtr(-refDX, y-1-refDY, &cxPtr2);
      cx2 = refBitmap->nextPixel(&cxPtr2);
      refBitmap->getPixelPtr(-1-refDX, y-refDY, &cxPtr3);
      cx3 = refBitmap->nextPixel(&cxPtr3);
      cx3 = (cx3 << 1) | refBitmap->nextPixel(&cxPtr3);
      refBitmap->getPixelPtr(-1-refDX, y+1-refDY, &cxPtr4);
      cx4 = refBitmap->nextPixel(&cxPtr4);
      cx4 = (cx4 << 1) | refBitmap->nextPixel(&cxPtr4);
      bitmap->getPixelPtr(atx[0], y+aty[0], &cxPtr5);
      refBitmap->getPixelPtr(atx[1]-refDX, y+aty[1]-refDY, &cxPtr6);

      // set up the typical prediction context
      tpgrCX0 = tpgrCX1 = tpgrCX2 = 0; // make gcc happy
      if (tpgrOn) {
	refBitmap->getPixelPtr(-1-refDX, y-1-refDY, &tpgrCXPtr0);
	tpgrCX0 = refBitmap->nextPixel(&tpgrCXPtr0);
	tpgrCX0 = (tpgrCX0 << 1) | refBitmap->nextPixel(&tpgrCXPtr0);
	tpgrCX0 = (tpgrCX0 << 1) | refBitmap->nextPixel(&tpgrCXPtr0);
	refBitmap->getPixelPtr(-1-refDX, y-refDY, &tpgrCXPtr1);
	tpgrCX1 = refBitmap->nextPixel(&tpgrCXPtr1);
	tpgrCX1 = (tpgrCX1 << 1) | refBitmap->nextPixel(&tpgrCXPtr1);
	tpgrCX1 = (tpgrCX1 << 1) | refBitmap->nextPixel(&tpgrCXPtr1);
	refBitmap->getPixelPtr(-1-refDX, y+1-refDY, &tpgrCXPtr2);
	tpgrCX2 = refBitmap->nextPixel(&tpgrCXPtr2);
	tpgrCX2 = (tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2);
	tpgrCX2 = (tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2);
      }

      for (x = 0; x < w; ++x) {

	// update the context
	cx0 = ((cx0 << 1) | bitmap->nextPixel(&cxPtr0)) & 3;
	cx2 = ((cx2 << 1) | refBitmap->nextPixel(&cxPtr2)) & 3;
	cx3 = ((cx3 << 1) | refBitmap->nextPixel(&cxPtr3)) & 7;
	cx4 = ((cx4 << 1) | refBitmap->nextPixel(&cxPtr4)) & 7;

	if (tpgrOn) {
	  // update the typical predictor context
	  tpgrCX0 = ((tpgrCX0 << 1) | refBitmap->nextPixel(&tpgrCXPtr0)) & 7;
	  tpgrCX1 = ((tpgrCX1 << 1) | refBitmap->nextPixel(&tpgrCXPtr1)) & 7;
	  tpgrCX2 = ((tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2)) & 7;

	  // check for a "typical" pixel
	  if (arithDecoder->decodeBit(ltpCX, refinementRegionStats)) {
	    ltp = !ltp;
	  }
	  if (tpgrCX0 == 0 && tpgrCX1 == 0 && tpgrCX2 == 0) {
	    bitmap->clearPixel(x, y);
	    continue;
	  } else if (tpgrCX0 == 7 && tpgrCX1 == 7 && tpgrCX2 == 7) {
	    bitmap->setPixel(x, y);
	    continue;
	  }
	}

	// build the context
	cx = (cx0 << 11) | (bitmap->nextPixel(&cxPtr1) << 10) |
	     (cx2 << 8) | (cx3 << 5) | (cx4 << 2) |
	     (bitmap->nextPixel(&cxPtr5) << 1) |
	     refBitmap->nextPixel(&cxPtr6);

	// decode the pixel
	if ((pix = arithDecoder->decodeBit(cx, refinementRegionStats))) {
	  bitmap->setPixel(x, y);
	}
      }
    }
  }

  return bitmap;
}

void JBIG2Stream::readPageInfoSeg(Guint length) {
  Guint xRes, yRes, flags, striping;

  if (!readULong(&pageW) || !readULong(&pageH) ||
      !readULong(&xRes) || !readULong(&yRes) ||
      !readUByte(&flags) || !readUWord(&striping)) {
    goto eofError;
  }
  pageDefPixel = (flags >> 2) & 1;
  defCombOp = (flags >> 3) & 3;

  // allocate the page bitmap
  if (pageH == 0xffffffff) {
    curPageH = striping & 0x7fff;
  } else {
    curPageH = pageH;
  }
  pageBitmap = new JBIG2Bitmap(0, pageW, curPageH);

  // default pixel value
  if (pageDefPixel) {
    pageBitmap->clearToOne();
  } else {
    pageBitmap->clearToZero();
  }

  return;

 eofError:
  error(getPos(), "Unexpected EOF in JBIG2 stream");
}

void JBIG2Stream::readEndOfStripeSeg(Guint length) {
  Guint i;

  // skip the segment
  for (i = 0; i < length; ++i) {
    curStr->getChar();
  }
}

void JBIG2Stream::readProfilesSeg(Guint length) {
  Guint i;

  // skip the segment
  for (i = 0; i < length; ++i) {
    curStr->getChar();
  }
}

void JBIG2Stream::readCodeTableSeg(Guint segNum, Guint length) {
  JBIG2HuffmanTable *huffTab;
  Guint flags, oob, prefixBits, rangeBits;
  int lowVal, highVal, val;
  Guint huffTabSize, i;

  if (!readUByte(&flags) || !readLong(&lowVal) || !readLong(&highVal)) {
    goto eofError;
  }
  oob = flags & 1;
  prefixBits = ((flags >> 1) & 7) + 1;
  rangeBits = ((flags >> 4) & 7) + 1;

  huffDecoder->reset();
  huffTabSize = 8;
  huffTab = (JBIG2HuffmanTable *)
                gmallocn(huffTabSize, sizeof(JBIG2HuffmanTable));
  i = 0;
  val = lowVal;
  while (val < highVal) {
    if (i == huffTabSize) {
      huffTabSize *= 2;
      huffTab = (JBIG2HuffmanTable *)
	            greallocn(huffTab, huffTabSize, sizeof(JBIG2HuffmanTable));
    }
    huffTab[i].val = val;
    huffTab[i].prefixLen = huffDecoder->readBits(prefixBits);
    huffTab[i].rangeLen = huffDecoder->readBits(rangeBits);
    val += 1 << huffTab[i].rangeLen;
    ++i;
  }
  if (i + oob + 3 > huffTabSize) {
    huffTabSize = i + oob + 3;
    huffTab = (JBIG2HuffmanTable *)
                  greallocn(huffTab, huffTabSize, sizeof(JBIG2HuffmanTable));
  }
  huffTab[i].val = lowVal - 1;
  huffTab[i].prefixLen = huffDecoder->readBits(prefixBits);
  huffTab[i].rangeLen = jbig2HuffmanLOW;
  ++i;
  huffTab[i].val = highVal;
  huffTab[i].prefixLen = huffDecoder->readBits(prefixBits);
  huffTab[i].rangeLen = 32;
  ++i;
  if (oob) {
    huffTab[i].val = 0;
    huffTab[i].prefixLen = huffDecoder->readBits(prefixBits);
    huffTab[i].rangeLen = jbig2HuffmanOOB;
    ++i;
  }
  huffTab[i].val = 0;
  huffTab[i].prefixLen = 0;
  huffTab[i].rangeLen = jbig2HuffmanEOT;
  huffDecoder->buildTable(huffTab, i);

  // create and store the new table segment
  segments->append(new JBIG2CodeTable(segNum, huffTab));

  return;

 eofError:
  error(getPos(), "Unexpected EOF in JBIG2 stream");
}

void JBIG2Stream::readExtensionSeg(Guint length) {
  Guint i;

  // skip the segment
  for (i = 0; i < length; ++i) {
    curStr->getChar();
  }
}

JBIG2Segment *JBIG2Stream::findSegment(Guint segNum) {
  JBIG2Segment *seg;
  int i;

  for (i = 0; i < globalSegments->getLength(); ++i) {
    seg = (JBIG2Segment *)globalSegments->get(i);
    if (seg->getSegNum() == segNum) {
      return seg;
    }
  }
  for (i = 0; i < segments->getLength(); ++i) {
    seg = (JBIG2Segment *)segments->get(i);
    if (seg->getSegNum() == segNum) {
      return seg;
    }
  }
  return NULL;
}

void JBIG2Stream::discardSegment(Guint segNum) {
  JBIG2Segment *seg;
  int i;

  for (i = 0; i < globalSegments->getLength(); ++i) {
    seg = (JBIG2Segment *)globalSegments->get(i);
    if (seg->getSegNum() == segNum) {
      globalSegments->del(i);
      return;
    }
  }
  for (i = 0; i < segments->getLength(); ++i) {
    seg = (JBIG2Segment *)segments->get(i);
    if (seg->getSegNum() == segNum) {
      segments->del(i);
      return;
    }
  }
}

void JBIG2Stream::resetGenericStats(Guint templ,
				    JArithmeticDecoderStats *prevStats) {
  int size;

  size = contextSize[templ];
  if (prevStats && prevStats->getContextSize() == size) {
    if (genericRegionStats->getContextSize() == size) {
      genericRegionStats->copyFrom(prevStats);
    } else {
      delete genericRegionStats;
      genericRegionStats = prevStats->copy();
    }
  } else {
    if (genericRegionStats->getContextSize() == size) {
      genericRegionStats->reset();
    } else {
      delete genericRegionStats;
      genericRegionStats = new JArithmeticDecoderStats(1 << size);
    }
  }
}

void JBIG2Stream::resetRefinementStats(Guint templ,
				       JArithmeticDecoderStats *prevStats) {
  int size;

  size = refContextSize[templ];
  if (prevStats && prevStats->getContextSize() == size) {
    if (refinementRegionStats->getContextSize() == size) {
      refinementRegionStats->copyFrom(prevStats);
    } else {
      delete refinementRegionStats;
      refinementRegionStats = prevStats->copy();
    }
  } else {
    if (refinementRegionStats->getContextSize() == size) {
      refinementRegionStats->reset();
    } else {
      delete refinementRegionStats;
      refinementRegionStats = new JArithmeticDecoderStats(1 << size);
    }
  }
}

void JBIG2Stream::resetIntStats(int symCodeLen) {
  iadhStats->reset();
  iadwStats->reset();
  iaexStats->reset();
  iaaiStats->reset();
  iadtStats->reset();
  iaitStats->reset();
  iafsStats->reset();
  iadsStats->reset();
  iardxStats->reset();
  iardyStats->reset();
  iardwStats->reset();
  iardhStats->reset();
  iariStats->reset();
  if (iaidStats->getContextSize() == 1 << (symCodeLen + 1)) {
    iaidStats->reset();
  } else {
    delete iaidStats;
    iaidStats = new JArithmeticDecoderStats(1 << (symCodeLen + 1));
  }
}

GBool JBIG2Stream::readUByte(Guint *x) {
  int c0;

  if ((c0 = curStr->getChar()) == EOF) {
    return gFalse;
  }
  *x = (Guint)c0;
  return gTrue;
}

GBool JBIG2Stream::readByte(int *x) {
 int c0;

  if ((c0 = curStr->getChar()) == EOF) {
    return gFalse;
  }
  *x = c0;
  if (c0 & 0x80) {
    *x |= -1 - 0xff;
  }
  return gTrue;
}

GBool JBIG2Stream::readUWord(Guint *x) {
  int c0, c1;

  if ((c0 = curStr->getChar()) == EOF ||
      (c1 = curStr->getChar()) == EOF) {
    return gFalse;
  }
  *x = (Guint)((c0 << 8) | c1);
  return gTrue;
}

GBool JBIG2Stream::readULong(Guint *x) {
  int c0, c1, c2, c3;

  if ((c0 = curStr->getChar()) == EOF ||
      (c1 = curStr->getChar()) == EOF ||
      (c2 = curStr->getChar()) == EOF ||
      (c3 = curStr->getChar()) == EOF) {
    return gFalse;
  }
  *x = (Guint)((c0 << 24) | (c1 << 16) | (c2 << 8) | c3);
  return gTrue;
}

GBool JBIG2Stream::readLong(int *x) {
  int c0, c1, c2, c3;

  if ((c0 = curStr->getChar()) == EOF ||
      (c1 = curStr->getChar()) == EOF ||
      (c2 = curStr->getChar()) == EOF ||
      (c3 = curStr->getChar()) == EOF) {
    return gFalse;
  }
  *x = ((c0 << 24) | (c1 << 16) | (c2 << 8) | c3);
  if (c0 & 0x80) {
    *x |= -1 - (int)0xffffffff;
  }
  return gTrue;
}
//========================================================================
//
// JBIG2Stream.h
//
// Copyright 2002-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef JBIG2STREAM_H
#define JBIG2STREAM_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "gtypes.h"
#include "Object.h"
#include "Stream.h"

class GList;
class JBIG2Segment;
class JBIG2Bitmap;
class JArithmeticDecoder;
class JArithmeticDecoderStats;
class JBIG2HuffmanDecoder;
struct JBIG2HuffmanTable;
class JBIG2MMRDecoder;

//------------------------------------------------------------------------

class JBIG2Stream: public FilterStream {
public:

  JBIG2Stream(Stream *strA, Object *globalsStreamA);
  virtual ~JBIG2Stream();
  virtual StreamKind getKind() { return strJBIG2; }
  virtual void reset();
  virtual void close();
  virtual int getChar();
  virtual int lookChar();
  virtual GString *getPSFilter(int psLevel, char *indent);
  virtual GBool isBinary(GBool last = gTrue);

private:

  void readSegments();
  GBool readSymbolDictSeg(Guint segNum, Guint length,
			  Guint *refSegs, Guint nRefSegs);
  void readTextRegionSeg(Guint segNum, GBool imm,
			 GBool lossless, Guint length,
			 Guint *refSegs, Guint nRefSegs);
  JBIG2Bitmap *readTextRegion(GBool huff, GBool refine,
			      int w, int h,
			      Guint numInstances,
			      Guint logStrips,
			      int numSyms,
			      JBIG2HuffmanTable *symCodeTab,
			      Guint symCodeLen,
			      JBIG2Bitmap **syms,
			      Guint defPixel, Guint combOp,
			      Guint transposed, Guint refCorner,
			      int sOffset,
			      JBIG2HuffmanTable *huffFSTable,
			      JBIG2HuffmanTable *huffDSTable,
			      JBIG2HuffmanTable *huffDTTable,
			      JBIG2HuffmanTable *huffRDWTable,
			      JBIG2HuffmanTable *huffRDHTable,
			      JBIG2HuffmanTable *huffRDXTable,
			      JBIG2HuffmanTable *huffRDYTable,
			      JBIG2HuffmanTable *huffRSizeTable,
			      Guint templ,
			      int *atx, int *aty);
  void readPatternDictSeg(Guint segNum, Guint length);
  void readHalftoneRegionSeg(Guint segNum, GBool imm,
			     GBool lossless, Guint length,
			     Guint *refSegs, Guint nRefSegs);
  void readGenericRegionSeg(Guint segNum, GBool imm,
			    GBool lossless, Guint length);
  JBIG2Bitmap *readGenericBitmap(GBool mmr, int w, int h,
				 int templ, GBool tpgdOn,
				 GBool useSkip, JBIG2Bitmap *skip,
				 int *atx, int *aty,
				 int mmrDataLength);
  void readGenericRefinementRegionSeg(Guint segNum, GBool imm,
				      GBool lossless, Guint length,
				      Guint *refSegs,
				      Guint nRefSegs);
  JBIG2Bitmap *readGenericRefinementRegion(int w, int h,
					   int templ, GBool tpgrOn,
					   JBIG2Bitmap *refBitmap,
					   int refDX, int refDY,
					   int *atx, int *aty);
  void readPageInfoSeg(Guint length);
  void readEndOfStripeSeg(Guint length);
  void readProfilesSeg(Guint length);
  void readCodeTableSeg(Guint segNum, Guint length);
  void readExtensionSeg(Guint length);
  JBIG2Segment *findSegment(Guint segNum);
  void discardSegment(Guint segNum);
  void resetGenericStats(Guint templ,
			 JArithmeticDecoderStats *prevStats);
  void resetRefinementStats(Guint templ,
			    JArithmeticDecoderStats *prevStats);
  void resetIntStats(int symCodeLen);
  GBool readUByte(Guint *x);
  GBool readByte(int *x);
  GBool readUWord(Guint *x);
  GBool readULong(Guint *x);
  GBool readLong(int *x);

  Object globalsStream;
  Guint pageW, pageH, curPageH;
  Guint pageDefPixel;
  JBIG2Bitmap *pageBitmap;
  Guint defCombOp;
  GList *segments;		// [JBIG2Segment]
  GList *globalSegments;	// [JBIG2Segment]
  Stream *curStr;
  Guchar *dataPtr;
  Guchar *dataEnd;

  JArithmeticDecoder *arithDecoder;
  JArithmeticDecoderStats *genericRegionStats;
  JArithmeticDecoderStats *refinementRegionStats;
  JArithmeticDecoderStats *iadhStats;
  JArithmeticDecoderStats *iadwStats;
  JArithmeticDecoderStats *iaexStats;
  JArithmeticDecoderStats *iaaiStats;
  JArithmeticDecoderStats *iadtStats;
  JArithmeticDecoderStats *iaitStats;
  JArithmeticDecoderStats *iafsStats;
  JArithmeticDecoderStats *iadsStats;
  JArithmeticDecoderStats *iardxStats;
  JArithmeticDecoderStats *iardyStats;
  JArithmeticDecoderStats *iardwStats;
  JArithmeticDecoderStats *iardhStats;
  JArithmeticDecoderStats *iariStats;
  JArithmeticDecoderStats *iaidStats;
  JBIG2HuffmanDecoder *huffDecoder;
  JBIG2MMRDecoder *mmrDecoder;
};

#endif
//========================================================================
//
// JPXStream.cc
//
// Copyright 2002-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <limits.h>
#include "gmem.h"
#include "Error.h"
#include "JArithmeticDecoder.h"
#include "JPXStream.h"

//~ to do:
//  - precincts
//  - ROI
//  - progression order changes
//  - packed packet headers
//  - support for palettes, channel maps, etc.
//  - make sure all needed JP2/JPX subboxes are parsed (readBoxes)
//  - can we assume that QCC segments must come after the QCD segment?
//  - skip EPH markers (readTilePartData)
//  - handle tilePartToEOC in readTilePartData
//  - deal with multiple codeword segments (readTilePartData,
//    readCodeBlockData)
//  - progression orders 2, 3, and 4
//  - in coefficient decoding (readCodeBlockData):
//    - termination pattern: terminate after every coding pass
//    - error resilience segmentation symbol
//    - selective arithmetic coding bypass
//    - vertically causal context formation
//    - coeffs longer than 31 bits (should just ignore the extra bits?)
//  - handle boxes larger than 2^32 bytes
//  - the fixed-point arithmetic won't handle 16-bit pixels

//------------------------------------------------------------------------

// number of contexts for the arithmetic decoder
#define jpxNContexts        19

#define jpxContextSigProp    0	// 0 - 8: significance prop and cleanup
#define jpxContextSign       9	// 9 - 13: sign
#define jpxContextMagRef    14	// 14 -16: magnitude refinement
#define jpxContextRunLength 17	// cleanup: run length
#define jpxContextUniform   18	// cleanup: first signif coeff

//------------------------------------------------------------------------

#define jpxPassSigProp       0
#define jpxPassMagRef        1
#define jpxPassCleanup       2

//------------------------------------------------------------------------

// arithmetic decoder context for the significance propagation and
// cleanup passes:
//     [horiz][vert][diag][subband]
// where subband = 0 for HL
//               = 1 for LH and LL
//               = 2 for HH
static Guint sigPropContext[3][3][5][3] = {
  {{{ 0, 0, 0 },   // horiz=0, vert=0, diag=0
    { 1, 1, 3 },   // horiz=0, vert=0, diag=1
    { 2, 2, 6 },   // horiz=0, vert=0, diag=2
    { 2, 2, 8 },   // horiz=0, vert=0, diag=3
    { 2, 2, 8 }},  // horiz=0, vert=0, diag=4
   {{ 5, 3, 1 },   // horiz=0, vert=1, diag=0
    { 6, 3, 4 },   // horiz=0, vert=1, diag=1
    { 6, 3, 7 },   // horiz=0, vert=1, diag=2
    { 6, 3, 8 },   // horiz=0, vert=1, diag=3
    { 6, 3, 8 }},  // horiz=0, vert=1, diag=4
   {{ 8, 4, 2 },   // horiz=0, vert=2, diag=0
    { 8, 4, 5 },   // horiz=0, vert=2, diag=1
    { 8, 4, 7 },   // horiz=0, vert=2, diag=2
    { 8, 4, 8 },   // horiz=0, vert=2, diag=3
    { 8, 4, 8 }}}, // horiz=0, vert=2, diag=4
  {{{ 3, 5, 1 },   // horiz=1, vert=0, diag=0
    { 3, 6, 4 },   // horiz=1, vert=0, diag=1
    { 3, 6, 7 },   // horiz=1, vert=0, diag=2
    { 3, 6, 8 },   // horiz=1, vert=0, diag=3
    { 3, 6, 8 }},  // horiz=1, vert=0, diag=4
   {{ 7, 7, 2 },   // horiz=1, vert=1, diag=0
    { 7, 7, 5 },   // horiz=1, vert=1, diag=1
    { 7, 7, 7 },   // horiz=1, vert=1, diag=2
    { 7, 7, 8 },   // horiz=1, vert=1, diag=3
    { 7, 7, 8 }},  // horiz=1, vert=1, diag=4
   {{ 8, 7, 2 },   // horiz=1, vert=2, diag=0
    { 8, 7, 5 },   // horiz=1, vert=2, diag=1
    { 8, 7, 7 },   // horiz=1, vert=2, diag=2
    { 8, 7, 8 },   // horiz=1, vert=2, diag=3
    { 8, 7, 8 }}}, // horiz=1, vert=2, diag=4
  {{{ 4, 8, 2 },   // horiz=2, vert=0, diag=0
    { 4, 8, 5 },   // horiz=2, vert=0, diag=1
    { 4, 8, 7 },   // horiz=2, vert=0, diag=2
    { 4, 8, 8 },   // horiz=2, vert=0, diag=3
    { 4, 8, 8 }},  // horiz=2, vert=0, diag=4
   {{ 7, 8, 2 },   // horiz=2, vert=1, diag=0
    { 7, 8, 5 },   // horiz=2, vert=1, diag=1
    { 7, 8, 7 },   // horiz=2, vert=1, diag=2
    { 7, 8, 8 },   // horiz=2, vert=1, diag=3
    { 7, 8, 8 }},  // horiz=2, vert=1, diag=4
   {{ 8, 8, 2 },   // horiz=2, vert=2, diag=0
    { 8, 8, 5 },   // horiz=2, vert=2, diag=1
    { 8, 8, 7 },   // horiz=2, vert=2, diag=2
    { 8, 8, 8 },   // horiz=2, vert=2, diag=3
    { 8, 8, 8 }}}  // horiz=2, vert=2, diag=4
};

// arithmetic decoder context and xor bit for the sign bit in the
// significance propagation pass:
//     [horiz][vert][k]
// where horiz/vert are offset by 2 (i.e., range is -2 .. 2)
// and k = 0 for the context
//       = 1 for the xor bit
static Guint signContext[5][5][2] = {
  {{ 13, 1 },  // horiz=-2, vert=-2
   { 13, 1 },  // horiz=-2, vert=-1
   { 12, 1 },  // horiz=-2, vert= 0
   { 11, 1 },  // horiz=-2, vert=+1
   { 11, 1 }}, // horiz=-2, vert=+2
  {{ 13, 1 },  // horiz=-1, vert=-2
   { 13, 1 },  // horiz=-1, vert=-1
   { 12, 1 },  // horiz=-1, vert= 0
   { 11, 1 },  // horiz=-1, vert=+1
   { 11, 1 }}, // horiz=-1, vert=+2
  {{ 10, 1 },  // horiz= 0, vert=-2
   { 10, 1 },  // horiz= 0, vert=-1
   {  9, 0 },  // horiz= 0, vert= 0
   { 10, 0 },  // horiz= 0, vert=+1
   { 10, 0 }}, // horiz= 0, vert=+2
  {{ 11, 0 },  // horiz=+1, vert=-2
   { 11, 0 },  // horiz=+1, vert=-1
   { 12, 0 },  // horiz=+1, vert= 0
   { 13, 0 },  // horiz=+1, vert=+1
   { 13, 0 }}, // horiz=+1, vert=+2
  {{ 11, 0 },  // horiz=+2, vert=-2
   { 11, 0 },  // horiz=+2, vert=-1
   { 12, 0 },  // horiz=+2, vert= 0
   { 13, 0 },  // horiz=+2, vert=+1
   { 13, 0 }}, // horiz=+2, vert=+2
};

//------------------------------------------------------------------------

// constants used in the IDWT
#define idwtAlpha  -1.586134342059924
#define idwtBeta   -0.052980118572961
#define idwtGamma   0.882911075530934
#define idwtDelta   0.443506852043971
#define idwtKappa   1.230174104914001
#define idwtIKappa  (1.0 / idwtKappa)

// number of bits to the right of the decimal point for the fixed
// point arithmetic used in the IDWT
#define fracBits 16

//------------------------------------------------------------------------

// floor(x / y)
#define jpxFloorDiv(x, y) ((x) / (y))

// floor(x / 2^y)
#define jpxFloorDivPow2(x, y) ((x) >> (y))

// ceil(x / y)
#define jpxCeilDiv(x, y) (((x) + (y) - 1) / (y))

// ceil(x / 2^y)
#define jpxCeilDivPow2(x, y) (((x) + (1 << (y)) - 1) >> (y))

//------------------------------------------------------------------------

#if 1 //----- disable coverage tracking

#define cover(idx)

#else //----- enable coverage tracking

class JPXCover {
public:

  JPXCover(int sizeA);
  ~JPXCover();
  void incr(int idx);

private:

  int size, used;
  int *data;
};

JPXCover::JPXCover(int sizeA) {
  size = sizeA;
  used = -1;
  data = (int *)gmallocn(size, sizeof(int));
  memset(data, 0, size * sizeof(int));
}

JPXCover::~JPXCover() {
  int i;

  printf("JPX coverage:\n");
  for (i = 0; i <= used; ++i) {
    printf("  %4d: %8d\n", i, data[i]);
  }
  gfree(data);
}

void JPXCover::incr(int idx) {
  if (idx < size) {
    ++data[idx];
    if (idx > used) {
      used = idx;
    }
  }
}

JPXCover jpxCover(150);

#define cover(idx) jpxCover.incr(idx)

#endif //----- coverage tracking

//------------------------------------------------------------------------

JPXStream::JPXStream(Stream *strA):
  FilterStream(strA)
{
  nComps = 0;
  bpc = NULL;
  width = height = 0;
  haveCS = gFalse;
  havePalette = gFalse;
  haveCompMap = gFalse;
  haveChannelDefn = gFalse;

  img.tiles = NULL;
  bitBuf = 0;
  bitBufLen = 0;
  bitBufSkip = gFalse;
  byteCount = 0;
}

JPXStream::~JPXStream() {
  close();
  delete str;
}

void JPXStream::reset() {
  str->reset();
  if (readBoxes()) {
    curY = img.yOffset;
  } else {
    // readBoxes reported an error, so we go immediately to EOF
    curY = img.ySize;
  }
  curX = img.xOffset;
  curComp = 0;
  readBufLen = 0;
}

void JPXStream::close() {
  JPXTile *tile;
  JPXTileComp *tileComp;
  JPXResLevel *resLevel;
  JPXPrecinct *precinct;
  JPXSubband *subband;
  JPXCodeBlock *cb;
  Guint comp, i, k, r, pre, sb;

  gfree(bpc);
  bpc = NULL;
  if (havePalette) {
    gfree(palette.bpc);
    gfree(palette.c);
    havePalette = gFalse;
  }
  if (haveCompMap) {
    gfree(compMap.comp);
    gfree(compMap.type);
    gfree(compMap.pComp);
    haveCompMap = gFalse;
  }
  if (haveChannelDefn) {
    gfree(channelDefn.idx);
    gfree(channelDefn.type);
    gfree(channelDefn.assoc);
    haveChannelDefn = gFalse;
  }

  if (img.tiles) {
    for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
      tile = &img.tiles[i];
      if (tile->tileComps) {
	for (comp = 0; comp < img.nComps; ++comp) {
	  tileComp = &tile->tileComps[comp];
	  gfree(tileComp->quantSteps);
	  gfree(tileComp->data);
	  gfree(tileComp->buf);
	  if (tileComp->resLevels) {
	    for (r = 0; r <= tileComp->nDecompLevels; ++r) {
	      resLevel = &tileComp->resLevels[r];
	      if (resLevel->precincts) {
		for (pre = 0; pre < 1; ++pre) {
		  precinct = &resLevel->precincts[pre];
		  if (precinct->subbands) {
		    for (sb = 0; sb < (Guint)(r == 0 ? 1 : 3); ++sb) {
		      subband = &precinct->subbands[sb];
		      gfree(subband->inclusion);
		      gfree(subband->zeroBitPlane);
		      if (subband->cbs) {
			for (k = 0; k < subband->nXCBs * subband->nYCBs; ++k) {
			  cb = &subband->cbs[k];
			  gfree(cb->coeffs);
			  if (cb->arithDecoder) {
			    delete cb->arithDecoder;
			  }
			  if (cb->stats) {
			    delete cb->stats;
			  }
			}
			gfree(subband->cbs);
		      }
		    }
		    gfree(precinct->subbands);
		  }
		}
		gfree(img.tiles[i].tileComps[comp].resLevels[r].precincts);
	      }
	    }
	    gfree(img.tiles[i].tileComps[comp].resLevels);
	  }
	}
	gfree(img.tiles[i].tileComps);
      }
    }
    gfree(img.tiles);
    img.tiles = NULL;
  }
  FilterStream::close();
}

int JPXStream::getChar() {
  int c;

  if (readBufLen < 8) {
    fillReadBuf();
  }
  if (readBufLen == 8) {
    c = readBuf & 0xff;
    readBufLen = 0;
  } else if (readBufLen > 8) {
    c = (readBuf >> (readBufLen - 8)) & 0xff;
    readBufLen -= 8;
  } else if (readBufLen == 0) {
    c = EOF;
  } else {
    c = (readBuf << (8 - readBufLen)) & 0xff;
    readBufLen = 0;
  }
  return c;
}

int JPXStream::lookChar() {
  int c;

  if (readBufLen < 8) {
    fillReadBuf();
  }
  if (readBufLen == 8) {
    c = readBuf & 0xff;
  } else if (readBufLen > 8) {
    c = (readBuf >> (readBufLen - 8)) & 0xff;
  } else if (readBufLen == 0) {
    c = EOF;
  } else {
    c = (readBuf << (8 - readBufLen)) & 0xff;
  }
  return c;
}

void JPXStream::fillReadBuf() {
  JPXTileComp *tileComp;
  Guint tileIdx, tx, ty;
  int pix, pixBits;

  do {
    if (curY >= img.ySize) {
      return;
    }
    tileIdx = ((curY - img.yTileOffset) / img.yTileSize) * img.nXTiles
              + (curX - img.xTileOffset) / img.xTileSize;
#if 1 //~ ignore the palette, assume the PDF ColorSpace object is valid
    tileComp = &img.tiles[tileIdx].tileComps[curComp];
#else
    tileComp = &img.tiles[tileIdx].tileComps[havePalette ? 0 : curComp];
#endif
    tx = jpxCeilDiv((curX - img.xTileOffset) % img.xTileSize, tileComp->hSep);
    ty = jpxCeilDiv((curY - img.yTileOffset) % img.yTileSize, tileComp->vSep);
    pix = (int)tileComp->data[ty * (tileComp->x1 - tileComp->x0) + tx];
    pixBits = tileComp->prec;
#if 1 //~ ignore the palette, assume the PDF ColorSpace object is valid
    if (++curComp == img.nComps) {
#else
    if (havePalette) {
      if (pix >= 0 && pix < palette.nEntries) {
	pix = palette.c[pix * palette.nComps + curComp];
      } else {
	pix = 
      pixBits = palette.bpc[curComp];
    }
    if (++curComp == (Guint)(havePalette ? palette.nComps : img.nComps)) {
#endif
      curComp = 0;
      if (++curX == img.xSize) {
	curX = img.xOffset;
	++curY;
      }
    }
    if (pixBits == 8) {
      readBuf = (readBuf << 8) | (pix & 0xff);
    } else {
      readBuf = (readBuf << pixBits) | (pix & ((1 << pixBits) - 1));
    }
    readBufLen += pixBits;
  } while (readBufLen < 8);
}

GString *JPXStream::getPSFilter(int psLevel, char *indent) {
  return NULL;
}

GBool JPXStream::isBinary(GBool last) {
  return str->isBinary(gTrue);
}

void JPXStream::getImageParams(int *bitsPerComponent,
			       StreamColorSpaceMode *csMode) {
  Guint boxType, boxLen, dataLen, csEnum;
  Guint bpc1, dummy, i;
  int csMeth, csPrec, csPrec1, dummy2;
  StreamColorSpaceMode csMode1;
  GBool haveBPC, haveCSMode;

  csPrec = 0; // make gcc happy
  haveBPC = haveCSMode = gFalse;
  str->reset();
  if (str->lookChar() == 0xff) {
    getImageParams2(bitsPerComponent, csMode);
  } else {
    while (readBoxHdr(&boxType, &boxLen, &dataLen)) {
      if (boxType == 0x6a703268) { // JP2 header
	cover(0);
	// skip the superbox
      } else if (boxType == 0x69686472) { // image header
	cover(1);
	if (readULong(&dummy) &&
	    readULong(&dummy) &&
	    readUWord(&dummy) &&
	    readUByte(&bpc1) &&
	    readUByte(&dummy) &&
	    readUByte(&dummy) &&
	    readUByte(&dummy)) {
	  *bitsPerComponent = bpc1 + 1;
	  haveBPC = gTrue;
	}
      } else if (boxType == 0x636F6C72) { // color specification
	cover(2);
	if (readByte(&csMeth) &&
	    readByte(&csPrec1) &&
	    readByte(&dummy2)) {
	  if (csMeth == 1) {
	    if (readULong(&csEnum)) {
	      csMode1 = streamCSNone;
	      if (csEnum == jpxCSBiLevel ||
		  csEnum == jpxCSGrayscale) {
		csMode1 = streamCSDeviceGray;
	      } else if (csEnum == jpxCSCMYK) {
		csMode1 = streamCSDeviceCMYK;
	      } else if (csEnum == jpxCSsRGB ||
			 csEnum == jpxCSCISesRGB ||
			 csEnum == jpxCSROMMRGB) {
		csMode1 = streamCSDeviceRGB;
	      }
	      if (csMode1 != streamCSNone &&
		  (!haveCSMode || csPrec1 > csPrec)) {
		*csMode = csMode1;
		csPrec = csPrec1;
		haveCSMode = gTrue;
	      }
	      for (i = 0; i < dataLen - 7; ++i) {
		str->getChar();
	      }
	    }
	  } else {
	    for (i = 0; i < dataLen - 3; ++i) {
	      str->getChar();
	    }
	  }
	}
      } else if (boxType == 0x6A703263) { // codestream
	cover(3);
	if (!(haveBPC && haveCSMode)) {
	  getImageParams2(bitsPerComponent, csMode);
	}
	break;
      } else {
	cover(4);
	for (i = 0; i < dataLen; ++i) {
	  str->getChar();
	}
      }
    }
  }
  str->close();
}

// Get image parameters from the codestream.
void JPXStream::getImageParams2(int *bitsPerComponent,
				StreamColorSpaceMode *csMode) {
  int segType;
  Guint segLen, nComps1, bpc1, dummy, i;

  while (readMarkerHdr(&segType, &segLen)) {
    if (segType == 0x51) { // SIZ - image and tile size
      cover(5);
      if (readUWord(&dummy) &&
	  readULong(&dummy) &&
	  readULong(&dummy) &&
	  readULong(&dummy) &&
	  readULong(&dummy) &&
	  readULong(&dummy) &&
	  readULong(&dummy) &&
	  readULong(&dummy) &&
	  readULong(&dummy) &&
	  readUWord(&nComps1) &&
	  readUByte(&bpc1)) {
	*bitsPerComponent = (bpc1 & 0x7f) + 1;
	// if there's no color space info, take a guess
	if (nComps1 == 1) {
	  *csMode = streamCSDeviceGray;
	} else if (nComps1 == 3) {
	  *csMode = streamCSDeviceRGB;
	} else if (nComps1 == 4) {
	  *csMode = streamCSDeviceCMYK;
	}
      }
      break;
    } else {
      cover(6);
      if (segLen > 2) {
	for (i = 0; i < segLen - 2; ++i) {
	  str->getChar();
	}
      }
    }
  }
}

GBool JPXStream::readBoxes() {
  Guint boxType, boxLen, dataLen;
  Guint bpc1, compression, unknownColorspace, ipr;
  Guint i, j;

  haveImgHdr = gFalse;

  // check for a naked JPEG 2000 codestream (without the JP2/JPX
  // wrapper) -- this appears to be a violation of the PDF spec, but
  // Acrobat allows it
  if (str->lookChar() == 0xff) {
    cover(7);
    error(getPos(), "Naked JPEG 2000 codestream, missing JP2/JPX wrapper");
    readCodestream(0);
    nComps = img.nComps;
    bpc = (Guint *)gmallocn(nComps, sizeof(Guint));
    for (i = 0; i < nComps; ++i) {
      bpc[i] = img.tiles[0].tileComps[i].prec;
    }
    width = img.xSize - img.xOffset;
    height = img.ySize - img.yOffset;
    return gTrue;
  }

  while (readBoxHdr(&boxType, &boxLen, &dataLen)) {
    switch (boxType) {
    case 0x6a703268:		// JP2 header
      // this is a grouping box ('superbox') which has no real
      // contents and doesn't appear to be used consistently, i.e.,
      // some things which should be subboxes of the JP2 header box
      // show up outside of it - so we simply ignore the JP2 header
      // box
      cover(8);
      break;
    case 0x69686472:		// image header
      cover(9);
      if (!readULong(&height) ||
	  !readULong(&width) ||
	  !readUWord(&nComps) ||
	  !readUByte(&bpc1) ||
	  !readUByte(&compression) ||
	  !readUByte(&unknownColorspace) ||
	  !readUByte(&ipr)) {
	error(getPos(), "Unexpected EOF in JPX stream");
	return gFalse;
      }
      if (compression != 7) {
	error(getPos(), "Unknown compression type in JPX stream");
	return gFalse;
      }
      bpc = (Guint *)gmallocn(nComps, sizeof(Guint));
      for (i = 0; i < nComps; ++i) {
	bpc[i] = bpc1;
      }
      haveImgHdr = gTrue;
      break;
    case 0x62706363:		// bits per component
      cover(10);
      if (!haveImgHdr) {
	error(getPos(), "Found bits per component box before image header box in JPX stream");
	return gFalse;
      }
      if (dataLen != nComps) {
	error(getPos(), "Invalid bits per component box in JPX stream");
	return gFalse;
      }
      for (i = 0; i < nComps; ++i) {
	if (!readUByte(&bpc[i])) {
	  error(getPos(), "Unexpected EOF in JPX stream");
	  return gFalse;
	}
      }
      break;
    case 0x636F6C72:		// color specification
      cover(11);
      if (!readColorSpecBox(dataLen)) {
	return gFalse;
      }
      break;
    case 0x70636c72:		// palette
      cover(12);
      if (!readUWord(&palette.nEntries) ||
	  !readUByte(&palette.nComps)) {
	error(getPos(), "Unexpected EOF in JPX stream");
	return gFalse;
      }
      palette.bpc = (Guint *)gmallocn(palette.nComps, sizeof(Guint));
      palette.c =
          (int *)gmallocn(palette.nEntries * palette.nComps, sizeof(int));
      for (i = 0; i < palette.nComps; ++i) {
	if (!readUByte(&palette.bpc[i])) {
	  error(getPos(), "Unexpected EOF in JPX stream");
	  return gFalse;
	}
	++palette.bpc[i];
      }
      for (i = 0; i < palette.nEntries; ++i) {
	for (j = 0; j < palette.nComps; ++j) {
	  if (!readNBytes(((palette.bpc[j] & 0x7f) + 7) >> 3,
			  (palette.bpc[j] & 0x80) ? gTrue : gFalse,
			  &palette.c[i * palette.nComps + j])) {
	    error(getPos(), "Unexpected EOF in JPX stream");
	    return gFalse;
	  }
	}
      }
      havePalette = gTrue;
      break;
    case 0x636d6170:		// component mapping
      cover(13);
      compMap.nChannels = dataLen / 4;
      compMap.comp = (Guint *)gmallocn(compMap.nChannels, sizeof(Guint));
      compMap.type = (Guint *)gmallocn(compMap.nChannels, sizeof(Guint));
      compMap.pComp = (Guint *)gmallocn(compMap.nChannels, sizeof(Guint));
      for (i = 0; i < compMap.nChannels; ++i) {
	if (!readUWord(&compMap.comp[i]) ||
	    !readUByte(&compMap.type[i]) ||
	    !readUByte(&compMap.pComp[i])) {
	  error(getPos(), "Unexpected EOF in JPX stream");
	  return gFalse;
	}
      }
      haveCompMap = gTrue;
      break;
    case 0x63646566:		// channel definition
      cover(14);
      if (!readUWord(&channelDefn.nChannels)) {
	error(getPos(), "Unexpected EOF in JPX stream");
	return gFalse;
      }
      channelDefn.idx =
	  (Guint *)gmallocn(channelDefn.nChannels, sizeof(Guint));
      channelDefn.type =
	  (Guint *)gmallocn(channelDefn.nChannels, sizeof(Guint));
      channelDefn.assoc =
	  (Guint *)gmallocn(channelDefn.nChannels, sizeof(Guint));
      for (i = 0; i < channelDefn.nChannels; ++i) {
	if (!readUWord(&channelDefn.idx[i]) ||
	    !readUWord(&channelDefn.type[i]) ||
	    !readUWord(&channelDefn.assoc[i])) {
	  error(getPos(), "Unexpected EOF in JPX stream");
	  return gFalse;
	}
      }
      haveChannelDefn = gTrue;
      break;
    case 0x6A703263:		// contiguous codestream
      cover(15);
      if (!bpc) {
	error(getPos(), "JPX stream is missing the image header box");
      }
      if (!haveCS) {
	error(getPos(), "JPX stream has no supported color spec");
      }
      if (!readCodestream(dataLen)) {
	return gFalse;
      }
      break;
    default:
      cover(16);
      for (i = 0; i < dataLen; ++i) {
	if (str->getChar() == EOF) {
	  error(getPos(), "Unexpected EOF in JPX stream");
	  return gFalse;
	}
      }
      break;
    }
  }
  return gTrue;
}

GBool JPXStream::readColorSpecBox(Guint dataLen) {
  JPXColorSpec newCS;
  Guint csApprox, csEnum;
  Guint i;
  GBool ok;

  ok = gFalse;
  if (!readUByte(&newCS.meth) ||
      !readByte(&newCS.prec) ||
      !readUByte(&csApprox)) {
    goto err;
  }
  switch (newCS.meth) {
  case 1:			// enumerated colorspace
    cover(17);
    if (!readULong(&csEnum)) {
      goto err;
    }
    newCS.enumerated.type = (JPXColorSpaceType)csEnum;
    switch (newCS.enumerated.type) {
    case jpxCSBiLevel:
      ok = gTrue;
      break;
    case jpxCSYCbCr1:
      ok = gTrue;
      break;
    case jpxCSYCbCr2:
      ok = gTrue;
      break;
    case jpxCSYCBCr3:
      ok = gTrue;
      break;
    case jpxCSPhotoYCC:
      ok = gTrue;
      break;
    case jpxCSCMY:
      ok = gTrue;
      break;
    case jpxCSCMYK:
      ok = gTrue;
      break;
    case jpxCSYCCK:
      ok = gTrue;
      break;
    case jpxCSCIELab:
      if (dataLen == 7 + 7*4) {
	if (!readULong(&newCS.enumerated.cieLab.rl) ||
	    !readULong(&newCS.enumerated.cieLab.ol) ||
	    !readULong(&newCS.enumerated.cieLab.ra) ||
	    !readULong(&newCS.enumerated.cieLab.oa) ||
	    !readULong(&newCS.enumerated.cieLab.rb) ||
	    !readULong(&newCS.enumerated.cieLab.ob) ||
	    !readULong(&newCS.enumerated.cieLab.il)) {
	  goto err;
	}
      } else if (dataLen == 7) {
	//~ this assumes the 8-bit case
	cover(92);
	newCS.enumerated.cieLab.rl = 100;
	newCS.enumerated.cieLab.ol = 0;
	newCS.enumerated.cieLab.ra = 255;
	newCS.enumerated.cieLab.oa = 128;
	newCS.enumerated.cieLab.rb = 255;
	newCS.enumerated.cieLab.ob = 96;
	newCS.enumerated.cieLab.il = 0x00443530;
      } else {
	goto err;
      }
      ok = gTrue;
      break;
    case jpxCSsRGB:
      ok = gTrue;
      break;
    case jpxCSGrayscale:
      ok = gTrue;
      break;
    case jpxCSBiLevel2:
      ok = gTrue;
      break;
    case jpxCSCIEJab:
      // not allowed in PDF
      goto err;
    case jpxCSCISesRGB:
      ok = gTrue;
      break;
    case jpxCSROMMRGB:
      ok = gTrue;
      break;
    case jpxCSsRGBYCbCr:
      ok = gTrue;
      break;
    case jpxCSYPbPr1125:
      ok = gTrue;
      break;
    case jpxCSYPbPr1250:
      ok = gTrue;
      break;
    default:
      goto err;
    }
    break;
  case 2:			// restricted ICC profile
  case 3: 			// any ICC profile (JPX)
  case 4:			// vendor color (JPX)
    cover(18);
    for (i = 0; i < dataLen - 3; ++i) {
      if (str->getChar() == EOF) {
	goto err;
      }
    }
    break;
  }

  if (ok && (!haveCS || newCS.prec > cs.prec)) {
    cs = newCS;
    haveCS = gTrue;
  }

  return gTrue;

 err:
  error(getPos(), "Error in JPX color spec");
  return gFalse;
}

GBool JPXStream::readCodestream(Guint len) {
  JPXTile *tile;
  JPXTileComp *tileComp;
  int segType;
  GBool haveSIZ, haveCOD, haveQCD, haveSOT;
  Guint precinctSize, style;
  Guint segLen, capabilities, comp, i, j, r;

  //----- main header
  haveSIZ = haveCOD = haveQCD = haveSOT = gFalse;
  do {
    if (!readMarkerHdr(&segType, &segLen)) {
      error(getPos(), "Error in JPX codestream");
      return gFalse;
    }
    switch (segType) {
    case 0x4f:			// SOC - start of codestream
      // marker only
      cover(19);
      break;
    case 0x51:			// SIZ - image and tile size
      cover(20);
      if (!readUWord(&capabilities) ||
	  !readULong(&img.xSize) ||
	  !readULong(&img.ySize) ||
	  !readULong(&img.xOffset) ||
	  !readULong(&img.yOffset) ||
	  !readULong(&img.xTileSize) ||
	  !readULong(&img.yTileSize) ||
	  !readULong(&img.xTileOffset) ||
	  !readULong(&img.yTileOffset) ||
	  !readUWord(&img.nComps)) {
	error(getPos(), "Error in JPX SIZ marker segment");
	return gFalse;
      }
      if (haveImgHdr && img.nComps != nComps) {
	error(getPos(), "Different number of components in JPX SIZ marker segment");
	return gFalse;
      }
      img.nXTiles = (img.xSize - img.xTileOffset + img.xTileSize - 1)
	            / img.xTileSize;
      img.nYTiles = (img.ySize - img.yTileOffset + img.yTileSize - 1)
	            / img.yTileSize;
      // check for overflow before allocating memory
      if (img.nXTiles <= 0 || img.nYTiles <= 0 ||
	  img.nXTiles >= INT_MAX / img.nYTiles) {
	error(getPos(), "Bad tile count in JPX SIZ marker segment");
	return gFalse;
      }
      img.tiles = (JPXTile *)gmallocn(img.nXTiles * img.nYTiles,
				      sizeof(JPXTile));
      for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
	img.tiles[i].tileComps = (JPXTileComp *)gmallocn(img.nComps,
							 sizeof(JPXTileComp));
	for (comp = 0; comp < img.nComps; ++comp) {
	  img.tiles[i].tileComps[comp].quantSteps = NULL;
	  img.tiles[i].tileComps[comp].data = NULL;
	  img.tiles[i].tileComps[comp].buf = NULL;
	  img.tiles[i].tileComps[comp].resLevels = NULL;
	}
      }
      for (comp = 0; comp < img.nComps; ++comp) {
	if (!readUByte(&img.tiles[0].tileComps[comp].prec) ||
	    !readUByte(&img.tiles[0].tileComps[comp].hSep) ||
	    !readUByte(&img.tiles[0].tileComps[comp].vSep)) {
	  error(getPos(), "Error in JPX SIZ marker segment");
	  return gFalse;
	}
	img.tiles[0].tileComps[comp].sgned =
	    (img.tiles[0].tileComps[comp].prec & 0x80) ? gTrue : gFalse;
	img.tiles[0].tileComps[comp].prec =
	    (img.tiles[0].tileComps[comp].prec & 0x7f) + 1;
	for (i = 1; i < img.nXTiles * img.nYTiles; ++i) {
	  img.tiles[i].tileComps[comp] = img.tiles[0].tileComps[comp];
	}
      }
      haveSIZ = gTrue;
      break;
    case 0x52:			// COD - coding style default
      cover(21);
      if (!readUByte(&img.tiles[0].tileComps[0].style) ||
	  !readUByte(&img.tiles[0].progOrder) ||
	  !readUWord(&img.tiles[0].nLayers) ||
	  !readUByte(&img.tiles[0].multiComp) ||
	  !readUByte(&img.tiles[0].tileComps[0].nDecompLevels) ||
	  !readUByte(&img.tiles[0].tileComps[0].codeBlockW) ||
	  !readUByte(&img.tiles[0].tileComps[0].codeBlockH) ||
	  !readUByte(&img.tiles[0].tileComps[0].codeBlockStyle) ||
	  !readUByte(&img.tiles[0].tileComps[0].transform)) {
	error(getPos(), "Error in JPX COD marker segment");
	return gFalse;
      }
      img.tiles[0].tileComps[0].codeBlockW += 2;
      img.tiles[0].tileComps[0].codeBlockH += 2;
      for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
	if (i != 0) {
	  img.tiles[i].progOrder = img.tiles[0].progOrder;
	  img.tiles[i].nLayers = img.tiles[0].nLayers;
	  img.tiles[i].multiComp = img.tiles[0].multiComp;
	}
	for (comp = 0; comp < img.nComps; ++comp) {
	  if (!(i == 0 && comp == 0)) {
	    img.tiles[i].tileComps[comp].style =
	        img.tiles[0].tileComps[0].style;
	    img.tiles[i].tileComps[comp].nDecompLevels =
	        img.tiles[0].tileComps[0].nDecompLevels;
	    img.tiles[i].tileComps[comp].codeBlockW =
	        img.tiles[0].tileComps[0].codeBlockW;
	    img.tiles[i].tileComps[comp].codeBlockH =
	        img.tiles[0].tileComps[0].codeBlockH;
	    img.tiles[i].tileComps[comp].codeBlockStyle =
	        img.tiles[0].tileComps[0].codeBlockStyle;
	    img.tiles[i].tileComps[comp].transform =
	        img.tiles[0].tileComps[0].transform;
	  }
	  img.tiles[i].tileComps[comp].resLevels =
	      (JPXResLevel *)gmallocn(
		     (img.tiles[i].tileComps[comp].nDecompLevels + 1),
		     sizeof(JPXResLevel));
	  for (r = 0; r <= img.tiles[i].tileComps[comp].nDecompLevels; ++r) {
	    img.tiles[i].tileComps[comp].resLevels[r].precincts = NULL;
	  }
	}
      }
      for (r = 0; r <= img.tiles[0].tileComps[0].nDecompLevels; ++r) {
	if (img.tiles[0].tileComps[0].style & 0x01) {
	  cover(91);
	  if (!readUByte(&precinctSize)) {
	    error(getPos(), "Error in JPX COD marker segment");
	    return gFalse;
	  }
	  img.tiles[0].tileComps[0].resLevels[r].precinctWidth =
	      precinctSize & 0x0f;
	  img.tiles[0].tileComps[0].resLevels[r].precinctHeight =
	      (precinctSize >> 4) & 0x0f;
	} else {
	  img.tiles[0].tileComps[0].resLevels[r].precinctWidth = 15;
	  img.tiles[0].tileComps[0].resLevels[r].precinctHeight = 15;
	}
      }
      for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
	for (comp = 0; comp < img.nComps; ++comp) {
	  if (!(i == 0 && comp == 0)) {
	    for (r = 0; r <= img.tiles[i].tileComps[comp].nDecompLevels; ++r) {
	      img.tiles[i].tileComps[comp].resLevels[r].precinctWidth =
		  img.tiles[0].tileComps[0].resLevels[r].precinctWidth;
	      img.tiles[i].tileComps[comp].resLevels[r].precinctHeight =
		  img.tiles[0].tileComps[0].resLevels[r].precinctHeight;
	    }
	  }
	}
      }
      haveCOD = gTrue;
      break;
    case 0x53:			// COC - coding style component
      cover(22);
      if (!haveCOD) {
	error(getPos(), "JPX COC marker segment before COD segment");
	return gFalse;
      }
      if ((img.nComps > 256 && !readUWord(&comp)) ||
	  (img.nComps <= 256 && !readUByte(&comp)) ||
	  comp >= img.nComps ||
	  !readUByte(&style) ||
	  !readUByte(&img.tiles[0].tileComps[comp].nDecompLevels) ||
	  !readUByte(&img.tiles[0].tileComps[comp].codeBlockW) ||
	  !readUByte(&img.tiles[0].tileComps[comp].codeBlockH) ||
	  !readUByte(&img.tiles[0].tileComps[comp].codeBlockStyle) ||
	  !readUByte(&img.tiles[0].tileComps[comp].transform)) {
	error(getPos(), "Error in JPX COC marker segment");
	return gFalse;
      }
      img.tiles[0].tileComps[comp].style =
	  (img.tiles[0].tileComps[comp].style & ~1) | (style & 1);
      img.tiles[0].tileComps[comp].codeBlockW += 2;
      img.tiles[0].tileComps[comp].codeBlockH += 2;
      for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
	if (i != 0) {
	  img.tiles[i].tileComps[comp].style =
	      img.tiles[0].tileComps[comp].style;
	  img.tiles[i].tileComps[comp].nDecompLevels =
	      img.tiles[0].tileComps[comp].nDecompLevels;
	  img.tiles[i].tileComps[comp].codeBlockW =
	      img.tiles[0].tileComps[comp].codeBlockW;
	  img.tiles[i].tileComps[comp].codeBlockH =
	      img.tiles[0].tileComps[comp].codeBlockH;
	  img.tiles[i].tileComps[comp].codeBlockStyle =
	      img.tiles[0].tileComps[comp].codeBlockStyle;
	  img.tiles[i].tileComps[comp].transform =
	      img.tiles[0].tileComps[comp].transform;
	}
	img.tiles[i].tileComps[comp].resLevels =
	    (JPXResLevel *)greallocn(
		     img.tiles[i].tileComps[comp].resLevels,
		     (img.tiles[i].tileComps[comp].nDecompLevels + 1),
		     sizeof(JPXResLevel));
	for (r = 0; r <= img.tiles[i].tileComps[comp].nDecompLevels; ++r) {
	  img.tiles[i].tileComps[comp].resLevels[r].precincts = NULL;
	}
      }
      for (r = 0; r <= img.tiles[0].tileComps[comp].nDecompLevels; ++r) {
	if (img.tiles[0].tileComps[comp].style & 0x01) {
	  if (!readUByte(&precinctSize)) {
	    error(getPos(), "Error in JPX COD marker segment");
	    return gFalse;
	  }
	  img.tiles[0].tileComps[comp].resLevels[r].precinctWidth =
	      precinctSize & 0x0f;
	  img.tiles[0].tileComps[comp].resLevels[r].precinctHeight =
	      (precinctSize >> 4) & 0x0f;
	} else {
	  img.tiles[0].tileComps[comp].resLevels[r].precinctWidth = 15;
	  img.tiles[0].tileComps[comp].resLevels[r].precinctHeight = 15;
	}
      }
      for (i = 1; i < img.nXTiles * img.nYTiles; ++i) {
	for (r = 0; r <= img.tiles[i].tileComps[comp].nDecompLevels; ++r) {
	  img.tiles[i].tileComps[comp].resLevels[r].precinctWidth =
	      img.tiles[0].tileComps[comp].resLevels[r].precinctWidth;
	  img.tiles[i].tileComps[comp].resLevels[r].precinctHeight =
	      img.tiles[0].tileComps[comp].resLevels[r].precinctHeight;
	}
      }
      break;
    case 0x5c:			// QCD - quantization default
      cover(23);
      if (!readUByte(&img.tiles[0].tileComps[0].quantStyle)) {
	error(getPos(), "Error in JPX QCD marker segment");
	return gFalse;
      }
      if ((img.tiles[0].tileComps[0].quantStyle & 0x1f) == 0x00) {
	img.tiles[0].tileComps[0].nQuantSteps = segLen - 3;
	img.tiles[0].tileComps[0].quantSteps =
	    (Guint *)greallocn(img.tiles[0].tileComps[0].quantSteps,
			       img.tiles[0].tileComps[0].nQuantSteps,
			       sizeof(Guint));
	for (i = 0; i < img.tiles[0].tileComps[0].nQuantSteps; ++i) {
	  if (!readUByte(&img.tiles[0].tileComps[0].quantSteps[i])) {
	    error(getPos(), "Error in JPX QCD marker segment");
	    return gFalse;
	  }
	}
      } else if ((img.tiles[0].tileComps[0].quantStyle & 0x1f) == 0x01) {
	img.tiles[0].tileComps[0].nQuantSteps = 1;
	img.tiles[0].tileComps[0].quantSteps =
	    (Guint *)greallocn(img.tiles[0].tileComps[0].quantSteps,
			       img.tiles[0].tileComps[0].nQuantSteps,
			       sizeof(Guint));
	if (!readUWord(&img.tiles[0].tileComps[0].quantSteps[0])) {
	  error(getPos(), "Error in JPX QCD marker segment");
	  return gFalse;
	}
      } else if ((img.tiles[0].tileComps[0].quantStyle & 0x1f) == 0x02) {
	img.tiles[0].tileComps[0].nQuantSteps = (segLen - 3) / 2;
	img.tiles[0].tileComps[0].quantSteps =
	    (Guint *)greallocn(img.tiles[0].tileComps[0].quantSteps,
			       img.tiles[0].tileComps[0].nQuantSteps,
			       sizeof(Guint));
	for (i = 0; i < img.tiles[0].tileComps[0].nQuantSteps; ++i) {
	  if (!readUWord(&img.tiles[0].tileComps[0].quantSteps[i])) {
	    error(getPos(), "Error in JPX QCD marker segment");
	    return gFalse;
	  }
	}
      } else {
	error(getPos(), "Error in JPX QCD marker segment");
	return gFalse;
      }
      for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
	for (comp = 0; comp < img.nComps; ++comp) {
	  if (!(i == 0 && comp == 0)) {
	    img.tiles[i].tileComps[comp].quantStyle =
	        img.tiles[0].tileComps[0].quantStyle;
	    img.tiles[i].tileComps[comp].nQuantSteps =
	        img.tiles[0].tileComps[0].nQuantSteps;
	    img.tiles[i].tileComps[comp].quantSteps = 
	        (Guint *)greallocn(img.tiles[i].tileComps[comp].quantSteps,
				   img.tiles[0].tileComps[0].nQuantSteps,
				   sizeof(Guint));
	    for (j = 0; j < img.tiles[0].tileComps[0].nQuantSteps; ++j) {
	      img.tiles[i].tileComps[comp].quantSteps[j] =
		  img.tiles[0].tileComps[0].quantSteps[j];
	    }
	  }
	}
      }
      haveQCD = gTrue;
      break;
    case 0x5d:			// QCC - quantization component
      cover(24);
      if (!haveQCD) {
	error(getPos(), "JPX QCC marker segment before QCD segment");
	return gFalse;
      }
      if ((img.nComps > 256 && !readUWord(&comp)) ||
	  (img.nComps <= 256 && !readUByte(&comp)) ||
	  comp >= img.nComps ||
	  !readUByte(&img.tiles[0].tileComps[comp].quantStyle)) {
	error(getPos(), "Error in JPX QCC marker segment");
	return gFalse;
      }
      if ((img.tiles[0].tileComps[comp].quantStyle & 0x1f) == 0x00) {
	img.tiles[0].tileComps[comp].nQuantSteps =
	    segLen - (img.nComps > 256 ? 5 : 4);
	img.tiles[0].tileComps[comp].quantSteps =
	    (Guint *)greallocn(img.tiles[0].tileComps[comp].quantSteps,
			       img.tiles[0].tileComps[comp].nQuantSteps,
			       sizeof(Guint));
	for (i = 0; i < img.tiles[0].tileComps[comp].nQuantSteps; ++i) {
	  if (!readUByte(&img.tiles[0].tileComps[comp].quantSteps[i])) {
	    error(getPos(), "Error in JPX QCC marker segment");
	    return gFalse;
	  }
	}
      } else if ((img.tiles[0].tileComps[comp].quantStyle & 0x1f) == 0x01) {
	img.tiles[0].tileComps[comp].nQuantSteps = 1;
	img.tiles[0].tileComps[comp].quantSteps =
	    (Guint *)greallocn(img.tiles[0].tileComps[comp].quantSteps,
			       img.tiles[0].tileComps[comp].nQuantSteps,
			       sizeof(Guint));
	if (!readUWord(&img.tiles[0].tileComps[comp].quantSteps[0])) {
	  error(getPos(), "Error in JPX QCC marker segment");
	  return gFalse;
	}
      } else if ((img.tiles[0].tileComps[comp].quantStyle & 0x1f) == 0x02) {
	img.tiles[0].tileComps[comp].nQuantSteps =
	    (segLen - (img.nComps > 256 ? 5 : 4)) / 2;
	img.tiles[0].tileComps[comp].quantSteps =
	    (Guint *)greallocn(img.tiles[0].tileComps[comp].quantSteps,
			       img.tiles[0].tileComps[comp].nQuantSteps,
			       sizeof(Guint));
	for (i = 0; i < img.tiles[0].tileComps[comp].nQuantSteps; ++i) {
	  if (!readUWord(&img.tiles[0].tileComps[comp].quantSteps[i])) {
	    error(getPos(), "Error in JPX QCD marker segment");
	    return gFalse;
	  }
	}
      } else {
	error(getPos(), "Error in JPX QCC marker segment");
	return gFalse;
      }
      for (i = 1; i < img.nXTiles * img.nYTiles; ++i) {
	img.tiles[i].tileComps[comp].quantStyle =
	    img.tiles[0].tileComps[comp].quantStyle;
	img.tiles[i].tileComps[comp].nQuantSteps =
	    img.tiles[0].tileComps[comp].nQuantSteps;
	img.tiles[i].tileComps[comp].quantSteps = 
	    (Guint *)greallocn(img.tiles[i].tileComps[comp].quantSteps,
			       img.tiles[0].tileComps[comp].nQuantSteps,
			       sizeof(Guint));
	for (j = 0; j < img.tiles[0].tileComps[comp].nQuantSteps; ++j) {
	  img.tiles[i].tileComps[comp].quantSteps[j] =
	      img.tiles[0].tileComps[comp].quantSteps[j];
	}
      }
      break;
    case 0x5e:			// RGN - region of interest
      cover(25);
#if 1 //~ ROI is unimplemented
      fprintf(stderr, "RGN\n");
      for (i = 0; i < segLen - 2; ++i) {
	if (str->getChar() == EOF) {
	  error(getPos(), "Error in JPX PPM marker segment");
	  return gFalse;
	}
      }
#else
      if ((img.nComps > 256 && !readUWord(&comp)) ||
	  (img.nComps <= 256 && !readUByte(&comp)) ||
	  comp >= img.nComps ||
	  !readUByte(&compInfo[comp].defROI.style) ||
	  !readUByte(&compInfo[comp].defROI.shift)) {
	error(getPos(), "Error in JPX RGN marker segment");
	return gFalse;
      }
#endif
      break;
    case 0x5f:			// POC - progression order change
      cover(26);
#if 1 //~ progression order changes are unimplemented
      fprintf(stderr, "POC\n");
      for (i = 0; i < segLen - 2; ++i) {
	if (str->getChar() == EOF) {
	  error(getPos(), "Error in JPX PPM marker segment");
	  return gFalse;
	}
      }
#else
      nProgs = (segLen - 2) / (img.nComps > 256 ? 9 : 7);
      progs = (JPXProgOrder *)gmallocn(nProgs, sizeof(JPXProgOrder));
      for (i = 0; i < nProgs; ++i) {
	if (!readUByte(&progs[i].startRes) ||
	    !(img.nComps > 256 && readUWord(&progs[i].startComp)) ||
	    !(img.nComps <= 256 && readUByte(&progs[i].startComp)) ||
	    !readUWord(&progs[i].endLayer) ||
	    !readUByte(&progs[i].endRes) ||
	    !(img.nComps > 256 && readUWord(&progs[i].endComp)) ||
	    !(img.nComps <= 256 && readUByte(&progs[i].endComp)) ||
	    !readUByte(&progs[i].progOrder)) {
	  error(getPos(), "Error in JPX POC marker segment");
	  return gFalse;
	}
      }
#endif
      break;
    case 0x60:			// PPM - packed packet headers, main header
      cover(27);
#if 1 //~ packed packet headers are unimplemented
      fprintf(stderr, "PPM\n");
      for (i = 0; i < segLen - 2; ++i) {
	if (str->getChar() == EOF) {
	  error(getPos(), "Error in JPX PPM marker segment");
	  return gFalse;
	}
      }
#endif
      break;
    case 0x55:			// TLM - tile-part lengths
      // skipped
      cover(28);
      for (i = 0; i < segLen - 2; ++i) {
	if (str->getChar() == EOF) {
	  error(getPos(), "Error in JPX TLM marker segment");
	  return gFalse;
	}
      }
      break;
    case 0x57:			// PLM - packet length, main header
      // skipped
      cover(29);
      for (i = 0; i < segLen - 2; ++i) {
	if (str->getChar() == EOF) {
	  error(getPos(), "Error in JPX PLM marker segment");
	  return gFalse;
	}
      }
      break;
    case 0x63:			// CRG - component registration
      // skipped
      cover(30);
      for (i = 0; i < segLen - 2; ++i) {
	if (str->getChar() == EOF) {
	  error(getPos(), "Error in JPX CRG marker segment");
	  return gFalse;
	}
      }
      break;
    case 0x64:			// COM - comment
      // skipped
      cover(31);
      for (i = 0; i < segLen - 2; ++i) {
	if (str->getChar() == EOF) {
	  error(getPos(), "Error in JPX COM marker segment");
	  return gFalse;
	}
      }
      break;
    case 0x90:			// SOT - start of tile
      cover(32);
      haveSOT = gTrue;
      break;
    default:
      cover(33);
      error(getPos(), "Unknown marker segment %02x in JPX stream", segType);
      for (i = 0; i < segLen - 2; ++i) {
	if (str->getChar() == EOF) {
	  break;
	}
      }
      break;
    }
  } while (!haveSOT);

  if (!haveSIZ) {
    error(getPos(), "Missing SIZ marker segment in JPX stream");
    return gFalse;
  }
  if (!haveCOD) {
    error(getPos(), "Missing COD marker segment in JPX stream");
    return gFalse;
  }
  if (!haveQCD) {
    error(getPos(), "Missing QCD marker segment in JPX stream");
    return gFalse;
  }

  //----- read the tile-parts
  while (1) {
    if (!readTilePart()) {
      return gFalse;
    }
    if (!readMarkerHdr(&segType, &segLen)) {
      error(getPos(), "Error in JPX codestream");
      return gFalse;
    }
    if (segType != 0x90) {	// SOT - start of tile
      break;
    }
  }

  if (segType != 0xd9) {	// EOC - end of codestream
    error(getPos(), "Missing EOC marker in JPX codestream");
    return gFalse;
  }

  //----- finish decoding the image
  for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
    tile = &img.tiles[i];
    for (comp = 0; comp < img.nComps; ++comp) {
      tileComp = &tile->tileComps[comp];
      inverseTransform(tileComp);
    }
    if (!inverseMultiCompAndDC(tile)) {
      return gFalse;
    }
  }

  //~ can free memory below tileComps here, and also tileComp.buf

  return gTrue;
}

GBool JPXStream::readTilePart() {
  JPXTile *tile;
  JPXTileComp *tileComp;
  JPXResLevel *resLevel;
  JPXPrecinct *precinct;
  JPXSubband *subband;
  JPXCodeBlock *cb;
  GBool haveSOD;
  Guint tileIdx, tilePartLen, tilePartIdx, nTileParts;
  GBool tilePartToEOC;
  Guint precinctSize, style;
  Guint n, nSBs, nx, ny, sbx0, sby0, comp, segLen;
  Guint i, j, k, cbX, cbY, r, pre, sb, cbi;
  int segType, level;

  // process the SOT marker segment
  if (!readUWord(&tileIdx) ||
      !readULong(&tilePartLen) ||
      !readUByte(&tilePartIdx) ||
      !readUByte(&nTileParts)) {
    error(getPos(), "Error in JPX SOT marker segment");
    return gFalse;
  }

  if (tileIdx >= img.nXTiles * img.nYTiles) {
    error(getPos(), "Weird tile index in JPX stream");
    return gFalse;
  }

  tilePartToEOC = tilePartLen == 0;
  tilePartLen -= 12; // subtract size of SOT segment

  haveSOD = gFalse;
  do {
    if (!readMarkerHdr(&segType, &segLen)) {
      error(getPos(), "Error in JPX tile-part codestream");
      return gFalse;
    }
    tilePartLen -= 2 + segLen;
    switch (segType) {
    case 0x52:			// COD - coding style default
      cover(34);
      if (!readUByte(&img.tiles[tileIdx].tileComps[0].style) ||
	  !readUByte(&img.tiles[tileIdx].progOrder) ||
	  !readUWord(&img.tiles[tileIdx].nLayers) ||
	  !readUByte(&img.tiles[tileIdx].multiComp) ||
	  !readUByte(&img.tiles[tileIdx].tileComps[0].nDecompLevels) ||
	  !readUByte(&img.tiles[tileIdx].tileComps[0].codeBlockW) ||
	  !readUByte(&img.tiles[tileIdx].tileComps[0].codeBlockH) ||
	  !readUByte(&img.tiles[tileIdx].tileComps[0].codeBlockStyle) ||
	  !readUByte(&img.tiles[tileIdx].tileComps[0].transform)) {
	error(getPos(), "Error in JPX COD marker segment");
	return gFalse;
      }
      img.tiles[tileIdx].tileComps[0].codeBlockW += 2;
      img.tiles[tileIdx].tileComps[0].codeBlockH += 2;
      for (comp = 0; comp < img.nComps; ++comp) {
	if (comp != 0) {
	  img.tiles[tileIdx].tileComps[comp].style =
	      img.tiles[tileIdx].tileComps[0].style;
	  img.tiles[tileIdx].tileComps[comp].nDecompLevels =
	      img.tiles[tileIdx].tileComps[0].nDecompLevels;
	  img.tiles[tileIdx].tileComps[comp].codeBlockW =
	      img.tiles[tileIdx].tileComps[0].codeBlockW;
	  img.tiles[tileIdx].tileComps[comp].codeBlockH =
	      img.tiles[tileIdx].tileComps[0].codeBlockH;
	  img.tiles[tileIdx].tileComps[comp].codeBlockStyle =
	      img.tiles[tileIdx].tileComps[0].codeBlockStyle;
	  img.tiles[tileIdx].tileComps[comp].transform =
	      img.tiles[tileIdx].tileComps[0].transform;
	}
	img.tiles[tileIdx].tileComps[comp].resLevels =
	    (JPXResLevel *)greallocn(
		     img.tiles[tileIdx].tileComps[comp].resLevels,
		     (img.tiles[tileIdx].tileComps[comp].nDecompLevels + 1),
		     sizeof(JPXResLevel));
	for (r = 0;
	     r <= img.tiles[tileIdx].tileComps[comp].nDecompLevels;
	     ++r) {
	  img.tiles[tileIdx].tileComps[comp].resLevels[r].precincts = NULL;
	}
      }
      for (r = 0; r <= img.tiles[tileIdx].tileComps[0].nDecompLevels; ++r) {
	if (img.tiles[tileIdx].tileComps[0].style & 0x01) {
	  if (!readUByte(&precinctSize)) {
	    error(getPos(), "Error in JPX COD marker segment");
	    return gFalse;
	  }
	  img.tiles[tileIdx].tileComps[0].resLevels[r].precinctWidth =
	      precinctSize & 0x0f;
	  img.tiles[tileIdx].tileComps[0].resLevels[r].precinctHeight =
	      (precinctSize >> 4) & 0x0f;
	} else {
	  img.tiles[tileIdx].tileComps[0].resLevels[r].precinctWidth = 15;
	  img.tiles[tileIdx].tileComps[0].resLevels[r].precinctHeight = 15;
	}
      }
      for (comp = 1; comp < img.nComps; ++comp) {
	for (r = 0;
	     r <= img.tiles[tileIdx].tileComps[comp].nDecompLevels;
	     ++r) {
	  img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctWidth =
	      img.tiles[tileIdx].tileComps[0].resLevels[r].precinctWidth;
	  img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctHeight =
	      img.tiles[tileIdx].tileComps[0].resLevels[r].precinctHeight;
	}
      }
      break;
    case 0x53:			// COC - coding style component
      cover(35);
      if ((img.nComps > 256 && !readUWord(&comp)) ||
	  (img.nComps <= 256 && !readUByte(&comp)) ||
	  comp >= img.nComps ||
	  !readUByte(&style) ||
	  !readUByte(&img.tiles[tileIdx].tileComps[comp].nDecompLevels) ||
	  !readUByte(&img.tiles[tileIdx].tileComps[comp].codeBlockW) ||
	  !readUByte(&img.tiles[tileIdx].tileComps[comp].codeBlockH) ||
	  !readUByte(&img.tiles[tileIdx].tileComps[comp].codeBlockStyle) ||
	  !readUByte(&img.tiles[tileIdx].tileComps[comp].transform)) {
	error(getPos(), "Error in JPX COC marker segment");
	return gFalse;
      }
      img.tiles[tileIdx].tileComps[comp].style =
	  (img.tiles[tileIdx].tileComps[comp].style & ~1) | (style & 1);
      img.tiles[tileIdx].tileComps[comp].codeBlockW += 2;
      img.tiles[tileIdx].tileComps[comp].codeBlockH += 2;
      img.tiles[tileIdx].tileComps[comp].resLevels =
	  (JPXResLevel *)greallocn(
		     img.tiles[tileIdx].tileComps[comp].resLevels,
		     (img.tiles[tileIdx].tileComps[comp].nDecompLevels + 1),
		     sizeof(JPXResLevel));
      for (r = 0; r <= img.tiles[tileIdx].tileComps[comp].nDecompLevels; ++r) {
	img.tiles[tileIdx].tileComps[comp].resLevels[r].precincts = NULL;
      }
      for (r = 0; r <= img.tiles[tileIdx].tileComps[comp].nDecompLevels; ++r) {
	if (img.tiles[tileIdx].tileComps[comp].style & 0x01) {
	  if (!readUByte(&precinctSize)) {
	    error(getPos(), "Error in JPX COD marker segment");
	    return gFalse;
	  }
	  img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctWidth =
	      precinctSize & 0x0f;
	  img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctHeight =
	      (precinctSize >> 4) & 0x0f;
	} else {
	  img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctWidth = 15;
	  img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctHeight = 15;
	}
      }
      break;
    case 0x5c:			// QCD - quantization default
      cover(36);
      if (!readUByte(&img.tiles[tileIdx].tileComps[0].quantStyle)) {
	error(getPos(), "Error in JPX QCD marker segment");
	return gFalse;
      }
      if ((img.tiles[tileIdx].tileComps[0].quantStyle & 0x1f) == 0x00) {
	img.tiles[tileIdx].tileComps[0].nQuantSteps =
	    segLen - 3;
	img.tiles[tileIdx].tileComps[0].quantSteps =
	    (Guint *)greallocn(img.tiles[tileIdx].tileComps[0].quantSteps,
			       img.tiles[tileIdx].tileComps[0].nQuantSteps,
			       sizeof(Guint));
	for (i = 0; i < img.tiles[tileIdx].tileComps[0].nQuantSteps; ++i) {
	  if (!readUByte(&img.tiles[tileIdx].tileComps[0].quantSteps[i])) {
	    error(getPos(), "Error in JPX QCD marker segment");
	    return gFalse;
	  }
	}
      } else if ((img.tiles[tileIdx].tileComps[0].quantStyle & 0x1f) == 0x01) {
	img.tiles[tileIdx].tileComps[0].nQuantSteps = 1;
	img.tiles[tileIdx].tileComps[0].quantSteps =
	    (Guint *)greallocn(img.tiles[tileIdx].tileComps[0].quantSteps,
			       img.tiles[tileIdx].tileComps[0].nQuantSteps,
			       sizeof(Guint));
	if (!readUWord(&img.tiles[tileIdx].tileComps[0].quantSteps[0])) {
	  error(getPos(), "Error in JPX QCD marker segment");
	  return gFalse;
	}
      } else if ((img.tiles[tileIdx].tileComps[0].quantStyle & 0x1f) == 0x02) {
	img.tiles[tileIdx].tileComps[0].nQuantSteps = (segLen - 3) / 2;
	img.tiles[tileIdx].tileComps[0].quantSteps =
	    (Guint *)greallocn(img.tiles[tileIdx].tileComps[0].quantSteps,
			       img.tiles[tileIdx].tileComps[0].nQuantSteps,
			       sizeof(Guint));
	for (i = 0; i < img.tiles[tileIdx].tileComps[0].nQuantSteps; ++i) {
	  if (!readUWord(&img.tiles[tileIdx].tileComps[0].quantSteps[i])) {
	    error(getPos(), "Error in JPX QCD marker segment");
	    return gFalse;
	  }
	}
      } else {
	error(getPos(), "Error in JPX QCD marker segment");
	return gFalse;
      }
      for (comp = 1; comp < img.nComps; ++comp) {
	img.tiles[tileIdx].tileComps[comp].quantStyle =
	    img.tiles[tileIdx].tileComps[0].quantStyle;
	img.tiles[tileIdx].tileComps[comp].nQuantSteps =
	    img.tiles[tileIdx].tileComps[0].nQuantSteps;
	img.tiles[tileIdx].tileComps[comp].quantSteps = 
	    (Guint *)greallocn(img.tiles[tileIdx].tileComps[comp].quantSteps,
			       img.tiles[tileIdx].tileComps[0].nQuantSteps,
			       sizeof(Guint));
	for (j = 0; j < img.tiles[tileIdx].tileComps[0].nQuantSteps; ++j) {
	  img.tiles[tileIdx].tileComps[comp].quantSteps[j] =
	      img.tiles[tileIdx].tileComps[0].quantSteps[j];
	}
      }
      break;
    case 0x5d:			// QCC - quantization component
      cover(37);
      if ((img.nComps > 256 && !readUWord(&comp)) ||
	  (img.nComps <= 256 && !readUByte(&comp)) ||
	  comp >= img.nComps ||
	  !readUByte(&img.tiles[tileIdx].tileComps[comp].quantStyle)) {
	error(getPos(), "Error in JPX QCC marker segment");
	return gFalse;
      }
      if ((img.tiles[tileIdx].tileComps[comp].quantStyle & 0x1f) == 0x00) {
	img.tiles[tileIdx].tileComps[comp].nQuantSteps =
	    segLen - (img.nComps > 256 ? 5 : 4);
	img.tiles[tileIdx].tileComps[comp].quantSteps =
	    (Guint *)greallocn(img.tiles[tileIdx].tileComps[comp].quantSteps,
			       img.tiles[tileIdx].tileComps[comp].nQuantSteps,
			       sizeof(Guint));
	for (i = 0; i < img.tiles[tileIdx].tileComps[comp].nQuantSteps; ++i) {
	  if (!readUByte(&img.tiles[tileIdx].tileComps[comp].quantSteps[i])) {
	    error(getPos(), "Error in JPX QCC marker segment");
	    return gFalse;
	  }
	}
      } else if ((img.tiles[tileIdx].tileComps[comp].quantStyle & 0x1f)
		 == 0x01) {
	img.tiles[tileIdx].tileComps[comp].nQuantSteps = 1;
	img.tiles[tileIdx].tileComps[comp].quantSteps =
	    (Guint *)greallocn(img.tiles[tileIdx].tileComps[comp].quantSteps,
			       img.tiles[tileIdx].tileComps[comp].nQuantSteps,
			       sizeof(Guint));
	if (!readUWord(&img.tiles[tileIdx].tileComps[comp].quantSteps[0])) {
	  error(getPos(), "Error in JPX QCC marker segment");
	  return gFalse;
	}
      } else if ((img.tiles[tileIdx].tileComps[comp].quantStyle & 0x1f)
		 == 0x02) {
	img.tiles[tileIdx].tileComps[comp].nQuantSteps =
	    (segLen - (img.nComps > 256 ? 5 : 4)) / 2;
	img.tiles[tileIdx].tileComps[comp].quantSteps =
	    (Guint *)greallocn(img.tiles[tileIdx].tileComps[comp].quantSteps,
			       img.tiles[tileIdx].tileComps[comp].nQuantSteps,
			       sizeof(Guint));
	for (i = 0; i < img.tiles[tileIdx].tileComps[comp].nQuantSteps; ++i) {
	  if (!readUWord(&img.tiles[tileIdx].tileComps[comp].quantSteps[i])) {
	    error(getPos(), "Error in JPX QCD marker segment");
	    return gFalse;
	  }
	}
      } else {
	error(getPos(), "Error in JPX QCC marker segment");
	return gFalse;
      }
      break;
    case 0x5e:			// RGN - region of interest
      cover(38);
#if 1 //~ ROI is unimplemented
      fprintf(stderr, "RGN\n");
      for (i = 0; i < segLen - 2; ++i) {
	if (str->getChar() == EOF) {
	  error(getPos(), "Error in JPX PPM marker segment");
	  return gFalse;
	}
      }
#else
      if ((img.nComps > 256 && !readUWord(&comp)) ||
	  (img.nComps <= 256 && !readUByte(&comp)) ||
	  comp >= img.nComps ||
	  !readUByte(&compInfo[comp].roi.style) ||
	  !readUByte(&compInfo[comp].roi.shift)) {
	error(getPos(), "Error in JPX RGN marker segment");
	return gFalse;
      }
#endif
      break;
    case 0x5f:			// POC - progression order change
      cover(39);
#if 1 //~ progression order changes are unimplemented
      fprintf(stderr, "POC\n");
      for (i = 0; i < segLen - 2; ++i) {
	if (str->getChar() == EOF) {
	  error(getPos(), "Error in JPX PPM marker segment");
	  return gFalse;
	}
      }
#else
      nTileProgs = (segLen - 2) / (img.nComps > 256 ? 9 : 7);
      tileProgs = (JPXProgOrder *)gmallocn(nTileProgs, sizeof(JPXProgOrder));
      for (i = 0; i < nTileProgs; ++i) {
	if (!readUByte(&tileProgs[i].startRes) ||
	    !(img.nComps > 256 && readUWord(&tileProgs[i].startComp)) ||
	    !(img.nComps <= 256 && readUByte(&tileProgs[i].startComp)) ||
	    !readUWord(&tileProgs[i].endLayer) ||
	    !readUByte(&tileProgs[i].endRes) ||
	    !(img.nComps > 256 && readUWord(&tileProgs[i].endComp)) ||
	    !(img.nComps <= 256 && readUByte(&tileProgs[i].endComp)) ||
	    !readUByte(&tileProgs[i].progOrder)) {
	  error(getPos(), "Error in JPX POC marker segment");
	  return gFalse;
	}
      }
#endif
      break;
    case 0x61:			// PPT - packed packet headers, tile-part hdr
      cover(40);
#if 1 //~ packed packet headers are unimplemented
      fprintf(stderr, "PPT\n");
      for (i = 0; i < segLen - 2; ++i) {
	if (str->getChar() == EOF) {
	  error(getPos(), "Error in JPX PPT marker segment");
	  return gFalse;
	}
      }
#endif
    case 0x58:			// PLT - packet length, tile-part header
      // skipped
      cover(41);
      for (i = 0; i < segLen - 2; ++i) {
	if (str->getChar() == EOF) {
	  error(getPos(), "Error in JPX PLT marker segment");
	  return gFalse;
	}
      }
      break;
    case 0x64:			// COM - comment
      // skipped
      cover(42);
      for (i = 0; i < segLen - 2; ++i) {
	if (str->getChar() == EOF) {
	  error(getPos(), "Error in JPX COM marker segment");
	  return gFalse;
	}
      }
      break;
    case 0x93:			// SOD - start of data
      cover(43);
      haveSOD = gTrue;
      break;
    default:
      cover(44);
      error(getPos(), "Unknown marker segment %02x in JPX tile-part stream",
	    segType);
      for (i = 0; i < segLen - 2; ++i) {
	if (str->getChar() == EOF) {
	  break;
	}
      }
      break;
    }
  } while (!haveSOD);

  //----- initialize the tile, precincts, and code-blocks
  if (tilePartIdx == 0) {
    tile = &img.tiles[tileIdx];
    i = tileIdx / img.nXTiles;
    j = tileIdx % img.nXTiles;
    if ((tile->x0 = img.xTileOffset + j * img.xTileSize) < img.xOffset) {
      tile->x0 = img.xOffset;
    }
    if ((tile->y0 = img.yTileOffset + i * img.yTileSize) < img.yOffset) {
      tile->y0 = img.yOffset;
    }
    if ((tile->x1 = img.xTileOffset + (j + 1) * img.xTileSize) > img.xSize) {
      tile->x1 = img.xSize;
    }
    if ((tile->y1 = img.yTileOffset + (i + 1) * img.yTileSize) > img.ySize) {
      tile->y1 = img.ySize;
    }
    tile->comp = 0;
    tile->res = 0;
    tile->precinct = 0;
    tile->layer = 0;
    tile->maxNDecompLevels = 0;
    for (comp = 0; comp < img.nComps; ++comp) {
      tileComp = &tile->tileComps[comp];
      if (tileComp->nDecompLevels > tile->maxNDecompLevels) {
	tile->maxNDecompLevels = tileComp->nDecompLevels;
      }
      tileComp->x0 = jpxCeilDiv(tile->x0, tileComp->hSep);
      tileComp->y0 = jpxCeilDiv(tile->y0, tileComp->hSep);
      tileComp->x1 = jpxCeilDiv(tile->x1, tileComp->hSep);
      tileComp->y1 = jpxCeilDiv(tile->y1, tileComp->hSep);
      tileComp->cbW = 1 << tileComp->codeBlockW;
      tileComp->cbH = 1 << tileComp->codeBlockH;
      tileComp->data = (int *)gmallocn((tileComp->x1 - tileComp->x0) *
				       (tileComp->y1 - tileComp->y0),
				       sizeof(int));
      if (tileComp->x1 - tileComp->x0 > tileComp->y1 - tileComp->y0) {
	n = tileComp->x1 - tileComp->x0;
      } else {
	n = tileComp->y1 - tileComp->y0;
      }
      tileComp->buf = (int *)gmallocn(n + 8, sizeof(int));
      for (r = 0; r <= tileComp->nDecompLevels; ++r) {
	resLevel = &tileComp->resLevels[r];
	k = r == 0 ? tileComp->nDecompLevels
	           : tileComp->nDecompLevels - r + 1;
	resLevel->x0 = jpxCeilDivPow2(tileComp->x0, k);
	resLevel->y0 = jpxCeilDivPow2(tileComp->y0, k);
	resLevel->x1 = jpxCeilDivPow2(tileComp->x1, k);
	resLevel->y1 = jpxCeilDivPow2(tileComp->y1, k);
	if (r == 0) {
	  resLevel->bx0[0] = resLevel->x0;
	  resLevel->by0[0] = resLevel->y0;
	  resLevel->bx1[0] = resLevel->x1;
	  resLevel->by1[0] = resLevel->y1;
	} else {
	  resLevel->bx0[0] = jpxCeilDivPow2(tileComp->x0 - (1 << (k-1)), k);
	  resLevel->by0[0] = resLevel->y0;
	  resLevel->bx1[0] = jpxCeilDivPow2(tileComp->x1 - (1 << (k-1)), k);
	  resLevel->by1[0] = resLevel->y1;
	  resLevel->bx0[1] = resLevel->x0;
	  resLevel->by0[1] = jpxCeilDivPow2(tileComp->y0 - (1 << (k-1)), k);
	  resLevel->bx1[1] = resLevel->x1;
	  resLevel->by1[1] = jpxCeilDivPow2(tileComp->y1 - (1 << (k-1)), k);
	  resLevel->bx0[2] = jpxCeilDivPow2(tileComp->x0 - (1 << (k-1)), k);
	  resLevel->by0[2] = jpxCeilDivPow2(tileComp->y0 - (1 << (k-1)), k);
	  resLevel->bx1[2] = jpxCeilDivPow2(tileComp->x1 - (1 << (k-1)), k);
	  resLevel->by1[2] = jpxCeilDivPow2(tileComp->y1 - (1 << (k-1)), k);
	}
	resLevel->precincts = (JPXPrecinct *)gmallocn(1, sizeof(JPXPrecinct));
	for (pre = 0; pre < 1; ++pre) {
	  precinct = &resLevel->precincts[pre];
	  precinct->x0 = resLevel->x0;
	  precinct->y0 = resLevel->y0;
	  precinct->x1 = resLevel->x1;
	  precinct->y1 = resLevel->y1;
	  nSBs = r == 0 ? 1 : 3;
	  precinct->subbands =
	      (JPXSubband *)gmallocn(nSBs, sizeof(JPXSubband));
	  for (sb = 0; sb < nSBs; ++sb) {
	    subband = &precinct->subbands[sb];
	    subband->x0 = resLevel->bx0[sb];
	    subband->y0 = resLevel->by0[sb];
	    subband->x1 = resLevel->bx1[sb];
	    subband->y1 = resLevel->by1[sb];
	    subband->nXCBs = jpxCeilDivPow2(subband->x1,
					    tileComp->codeBlockW)
	                     - jpxFloorDivPow2(subband->x0,
					       tileComp->codeBlockW);
	    subband->nYCBs = jpxCeilDivPow2(subband->y1,
					    tileComp->codeBlockH)
	                     - jpxFloorDivPow2(subband->y0,
					       tileComp->codeBlockH);
	    n = subband->nXCBs > subband->nYCBs ? subband->nXCBs
	                                        : subband->nYCBs;
	    for (subband->maxTTLevel = 0, --n;
		 n;
		 ++subband->maxTTLevel, n >>= 1) ;
	    n = 0;
	    for (level = subband->maxTTLevel; level >= 0; --level) {
	      nx = jpxCeilDivPow2(subband->nXCBs, level);
	      ny = jpxCeilDivPow2(subband->nYCBs, level);
	      n += nx * ny;
	    }
	    subband->inclusion =
	        (JPXTagTreeNode *)gmallocn(n, sizeof(JPXTagTreeNode));
	    subband->zeroBitPlane =
	        (JPXTagTreeNode *)gmallocn(n, sizeof(JPXTagTreeNode));
	    for (k = 0; k < n; ++k) {
	      subband->inclusion[k].finished = gFalse;
	      subband->inclusion[k].val = 0;
	      subband->zeroBitPlane[k].finished = gFalse;
	      subband->zeroBitPlane[k].val = 0;
	    }
	    subband->cbs = (JPXCodeBlock *)gmallocn(subband->nXCBs *
						      subband->nYCBs,
						    sizeof(JPXCodeBlock));
	    sbx0 = jpxFloorDivPow2(subband->x0, tileComp->codeBlockW);
	    sby0 = jpxFloorDivPow2(subband->y0, tileComp->codeBlockH);
	    cb = subband->cbs;
	    for (cbY = 0; cbY < subband->nYCBs; ++cbY) {
	      for (cbX = 0; cbX < subband->nXCBs; ++cbX) {
		cb->x0 = (sbx0 + cbX) << tileComp->codeBlockW;
		cb->x1 = cb->x0 + tileComp->cbW;
		if (subband->x0 > cb->x0) {
		  cb->x0 = subband->x0;
		}
		if (subband->x1 < cb->x1) {
		  cb->x1 = subband->x1;
		}
		cb->y0 = (sby0 + cbY) << tileComp->codeBlockH;
		cb->y1 = cb->y0 + tileComp->cbH;
		if (subband->y0 > cb->y0) {
		  cb->y0 = subband->y0;
		}
		if (subband->y1 < cb->y1) {
		  cb->y1 = subband->y1;
		}
		cb->seen = gFalse;
		cb->lBlock = 3;
		cb->nextPass = jpxPassCleanup;
		cb->nZeroBitPlanes = 0;
		cb->coeffs =
		    (JPXCoeff *)gmallocn((1 << (tileComp->codeBlockW
						+ tileComp->codeBlockH)),
					 sizeof(JPXCoeff));
		for (cbi = 0;
		     cbi < (Guint)(1 << (tileComp->codeBlockW
					 + tileComp->codeBlockH));
		     ++cbi) {
		  cb->coeffs[cbi].flags = 0;
		  cb->coeffs[cbi].len = 0;
		  cb->coeffs[cbi].mag = 0;
		}
		cb->arithDecoder = NULL;
		cb->stats = NULL;
		++cb;
	      }
	    }
	  }
	}
      }
    }
  }

  return readTilePartData(tileIdx, tilePartLen, tilePartToEOC);
}

GBool JPXStream::readTilePartData(Guint tileIdx,
				  Guint tilePartLen, GBool tilePartToEOC) {
  JPXTile *tile;
  JPXTileComp *tileComp;
  JPXResLevel *resLevel;
  JPXPrecinct *precinct;
  JPXSubband *subband;
  JPXCodeBlock *cb;
  Guint ttVal;
  Guint bits, cbX, cbY, nx, ny, i, j, n, sb;
  int level;

  tile = &img.tiles[tileIdx];

  // read all packets from this tile-part
  while (1) {
    if (tilePartToEOC) {
      //~ peek for an EOC marker
      cover(93);
    } else if (tilePartLen == 0) {
      break;
    }

    tileComp = &tile->tileComps[tile->comp];
    resLevel = &tileComp->resLevels[tile->res];
    precinct = &resLevel->precincts[tile->precinct];

    //----- packet header

    // setup
    startBitBuf(tilePartLen);

    // zero-length flag
    if (!readBits(1, &bits)) {
      goto err;
    }
    if (!bits) {
      // packet is empty -- clear all code-block inclusion flags
      cover(45);
      for (sb = 0; sb < (Guint)(tile->res == 0 ? 1 : 3); ++sb) {
	subband = &precinct->subbands[sb];
	for (cbY = 0; cbY < subband->nYCBs; ++cbY) {
	  for (cbX = 0; cbX < subband->nXCBs; ++cbX) {
	    cb = &subband->cbs[cbY * subband->nXCBs + cbX];
	    cb->included = gFalse;
	  }
	}
      }
    } else {

      for (sb = 0; sb < (Guint)(tile->res == 0 ? 1 : 3); ++sb) {
	subband = &precinct->subbands[sb];
	for (cbY = 0; cbY < subband->nYCBs; ++cbY) {
	  for (cbX = 0; cbX < subband->nXCBs; ++cbX) {
	    cb = &subband->cbs[cbY * subband->nXCBs + cbX];

	    // skip code-blocks with no coefficients
	    if (cb->x0 >= cb->x1 || cb->y0 >= cb->y1) {
	      cover(46);
	      cb->included = gFalse;
	      continue;
	    }

	    // code-block inclusion
	    if (cb->seen) {
	      cover(47);
	      if (!readBits(1, &cb->included)) {
		goto err;
	      }
	    } else {
	      cover(48);
	      ttVal = 0;
	      i = 0;
	      for (level = subband->maxTTLevel; level >= 0; --level) {
		nx = jpxCeilDivPow2(subband->nXCBs, level);
		ny = jpxCeilDivPow2(subband->nYCBs, level);
		j = i + (cbY >> level) * nx + (cbX >> level);
		if (!subband->inclusion[j].finished &&
		    !subband->inclusion[j].val) {
		  subband->inclusion[j].val = ttVal;
		} else {
		  ttVal = subband->inclusion[j].val;
		}
		while (!subband->inclusion[j].finished &&
		       ttVal <= tile->layer) {
		  if (!readBits(1, &bits)) {
		    goto err;
		  }
		  if (bits == 1) {
		    subband->inclusion[j].finished = gTrue;
		  } else {
		    ++ttVal;
		  }
		}
		subband->inclusion[j].val = ttVal;
		if (ttVal > tile->layer) {
		  break;
		}
		i += nx * ny;
	      }
	      cb->included = level < 0;
	    }

	    if (cb->included) {
	      cover(49);

	      // zero bit-plane count
	      if (!cb->seen) {
		cover(50);
		ttVal = 0;
		i = 0;
		for (level = subband->maxTTLevel; level >= 0; --level) {
		  nx = jpxCeilDivPow2(subband->nXCBs, level);
		  ny = jpxCeilDivPow2(subband->nYCBs, level);
		  j = i + (cbY >> level) * nx + (cbX >> level);
		  if (!subband->zeroBitPlane[j].finished &&
		      !subband->zeroBitPlane[j].val) {
		    subband->zeroBitPlane[j].val = ttVal;
		  } else {
		    ttVal = subband->zeroBitPlane[j].val;
		  }
		  while (!subband->zeroBitPlane[j].finished) {
		    if (!readBits(1, &bits)) {
		      goto err;
		    }
		    if (bits == 1) {
		      subband->zeroBitPlane[j].finished = gTrue;
		    } else {
		      ++ttVal;
		    }
		  }
		  subband->zeroBitPlane[j].val = ttVal;
		  i += nx * ny;
		}
		cb->nZeroBitPlanes = ttVal;
	      }

	      // number of coding passes
	      if (!readBits(1, &bits)) {
		goto err;
	      }
	      if (bits == 0) {
		cover(51);
		cb->nCodingPasses = 1;
	      } else {
		if (!readBits(1, &bits)) {
		  goto err;
		}
		if (bits == 0) {
		  cover(52);
		  cb->nCodingPasses = 2;
		} else {
		  cover(53);
		  if (!readBits(2, &bits)) {
		    goto err;
		  }
		  if (bits < 3) {
		    cover(54);
		    cb->nCodingPasses = 3 + bits;
		  } else {
		    cover(55);
		    if (!readBits(5, &bits)) {
		      goto err;
		    }
		    if (bits < 31) {
		      cover(56);
		      cb->nCodingPasses = 6 + bits;
		    } else {
		      cover(57);
		      if (!readBits(7, &bits)) {
			goto err;
		      }
		      cb->nCodingPasses = 37 + bits;
		    }
		  }
		}
	      }

	      // update Lblock
	      while (1) {
		if (!readBits(1, &bits)) {
		  goto err;
		}
		if (!bits) {
		  break;
		}
		++cb->lBlock;
	      }

	      // length of compressed data
	      //~ deal with multiple codeword segments
	      for (n = cb->lBlock, i = cb->nCodingPasses >> 1;
		   i;
		   ++n, i >>= 1) ;
	      if (!readBits(n, &cb->dataLen)) {
		goto err;
	      }
	    }
	  }
	}
      }
    }
    tilePartLen = finishBitBuf();

    //----- packet data

    for (sb = 0; sb < (Guint)(tile->res == 0 ? 1 : 3); ++sb) {
      subband = &precinct->subbands[sb];
      for (cbY = 0; cbY < subband->nYCBs; ++cbY) {
	for (cbX = 0; cbX < subband->nXCBs; ++cbX) {
	  cb = &subband->cbs[cbY * subband->nXCBs + cbX];
	  if (cb->included) {
	    if (!readCodeBlockData(tileComp, resLevel, precinct, subband,
				   tile->res, sb, cb)) {
	      return gFalse;
	    }
	    tilePartLen -= cb->dataLen;
	    cb->seen = gTrue;
	  }
	}
      }
    }

    //----- next packet

    switch (tile->progOrder) {
    case 0: // layer, resolution level, component, precinct
      cover(58);
      if (++tile->comp == img.nComps) {
	tile->comp = 0;
	if (++tile->res == tile->maxNDecompLevels + 1) {
	  tile->res = 0;
	  if (++tile->layer == tile->nLayers) {
	    tile->layer = 0;
	  }
	}
      }
      break;
    case 1: // resolution level, layer, component, precinct
      cover(59);
      if (++tile->comp == img.nComps) {
	tile->comp = 0;
	if (++tile->layer == tile->nLayers) {
	  tile->layer = 0;
	  if (++tile->res == tile->maxNDecompLevels + 1) {
	    tile->res = 0;
	  }
	}
      }
      break;
    case 2: // resolution level, precinct, component, layer
      //~ this isn't correct -- see B.12.1.3
      cover(60);
      if (++tile->layer == tile->nLayers) {
	tile->layer = 0;
	if (++tile->comp == img.nComps) {
	  tile->comp = 0;
	  if (++tile->res == tile->maxNDecompLevels + 1) {
	    tile->res = 0;
	  }
	}
      }
      break;
    case 3: // precinct, component, resolution level, layer
      //~ this isn't correct -- see B.12.1.4
      cover(61);
      if (++tile->layer == tile->nLayers) {
	tile->layer = 0;
	if (++tile->res == tile->maxNDecompLevels + 1) {
	  tile->res = 0;
	  if (++tile->comp == img.nComps) {
	    tile->comp = 0;
	  }
	}
      }
      break;
    case 4: // component, precinct, resolution level, layer
      //~ this isn't correct -- see B.12.1.5
      cover(62);
      if (++tile->layer == tile->nLayers) {
	tile->layer = 0;
	if (++tile->res == tile->maxNDecompLevels + 1) {
	  tile->res = 0;
	  if (++tile->comp == img.nComps) {
	    tile->comp = 0;
	  }
	}
      }
      break;
    }
  }

  return gTrue;

 err:
  error(getPos(), "Error in JPX stream");
  return gFalse;
}

GBool JPXStream::readCodeBlockData(JPXTileComp *tileComp,
				   JPXResLevel *resLevel,
				   JPXPrecinct *precinct,
				   JPXSubband *subband,
				   Guint res, Guint sb,
				   JPXCodeBlock *cb) {
  JPXCoeff *coeff0, *coeff1, *coeff;
  Guint horiz, vert, diag, all, cx, xorBit;
  int horizSign, vertSign;
  Guint i, x, y0, y1, y2;

  if (cb->arithDecoder) {
    cover(63);
    cb->arithDecoder->restart(cb->dataLen);
  } else {
    cover(64);
    cb->arithDecoder = new JArithmeticDecoder();
    cb->arithDecoder->setStream(str, cb->dataLen);
    cb->arithDecoder->start();
    cb->stats = new JArithmeticDecoderStats(jpxNContexts);
    cb->stats->setEntry(jpxContextSigProp, 4, 0);
    cb->stats->setEntry(jpxContextRunLength, 3, 0);
    cb->stats->setEntry(jpxContextUniform, 46, 0);
  }

  for (i = 0; i < cb->nCodingPasses; ++i) {
    switch (cb->nextPass) {

    //----- significance propagation pass
    case jpxPassSigProp:
      cover(65);
      for (y0 = cb->y0, coeff0 = cb->coeffs;
	   y0 < cb->y1;
	   y0 += 4, coeff0 += 4 << tileComp->codeBlockW) {
	for (x = cb->x0, coeff1 = coeff0;
	     x < cb->x1;
	     ++x, ++coeff1) {
	  for (y1 = 0, coeff = coeff1;
	       y1 < 4 && y0+y1 < cb->y1;
	       ++y1, coeff += tileComp->cbW) {
	    if (!(coeff->flags & jpxCoeffSignificant)) {
	      horiz = vert = diag = 0;
	      horizSign = vertSign = 2;
	      if (x > cb->x0) {
		if (coeff[-1].flags & jpxCoeffSignificant) {
		  ++horiz;
		  horizSign += (coeff[-1].flags & jpxCoeffSign) ? -1 : 1;
		}
		if (y0+y1 > cb->y0) {
		  diag += (coeff[-(int)tileComp->cbW - 1].flags
			   >> jpxCoeffSignificantB) & 1;
		}
		if (y0+y1 < cb->y1 - 1) {
		  diag += (coeff[tileComp->cbW - 1].flags
			   >> jpxCoeffSignificantB) & 1;
		}
	      }
	      if (x < cb->x1 - 1) {
		if (coeff[1].flags & jpxCoeffSignificant) {
		  ++horiz;
		  horizSign += (coeff[1].flags & jpxCoeffSign) ? -1 : 1;
		}
		if (y0+y1 > cb->y0) {
		  diag += (coeff[-(int)tileComp->cbW + 1].flags
			   >> jpxCoeffSignificantB) & 1;
		}
		if (y0+y1 < cb->y1 - 1) {
		  diag += (coeff[tileComp->cbW + 1].flags
			   >> jpxCoeffSignificantB) & 1;
		}
	      }
	      if (y0+y1 > cb->y0) {
		if (coeff[-(int)tileComp->cbW].flags & jpxCoeffSignificant) {
		  ++vert;
		  vertSign += (coeff[-(int)tileComp->cbW].flags & jpxCoeffSign)
		              ? -1 : 1;
		}
	      }
	      if (y0+y1 < cb->y1 - 1) {
		if (coeff[tileComp->cbW].flags & jpxCoeffSignificant) {
		  ++vert;
		  vertSign += (coeff[tileComp->cbW].flags & jpxCoeffSign)
		              ? -1 : 1;
		}
	      }
	      cx = sigPropContext[horiz][vert][diag][res == 0 ? 1 : sb];
	      if (cx != 0) {
		if (cb->arithDecoder->decodeBit(cx, cb->stats)) {
		  coeff->flags |= jpxCoeffSignificant | jpxCoeffFirstMagRef;
		  coeff->mag = (coeff->mag << 1) | 1;
		  cx = signContext[horizSign][vertSign][0];
		  xorBit = signContext[horizSign][vertSign][1];
		  if (cb->arithDecoder->decodeBit(cx, cb->stats) ^ xorBit) {
		    coeff->flags |= jpxCoeffSign;
		  }
		}
		++coeff->len;
		coeff->flags |= jpxCoeffTouched;
	      }
	    }
	  }
	}
      }
      ++cb->nextPass;
      break;

    //----- magnitude refinement pass
    case jpxPassMagRef:
      cover(66);
      for (y0 = cb->y0, coeff0 = cb->coeffs;
	   y0 < cb->y1;
	   y0 += 4, coeff0 += 4 << tileComp->codeBlockW) {
	for (x = cb->x0, coeff1 = coeff0;
	     x < cb->x1;
	     ++x, ++coeff1) {
	  for (y1 = 0, coeff = coeff1;
	       y1 < 4 && y0+y1 < cb->y1;
	       ++y1, coeff += tileComp->cbW) {
	    if ((coeff->flags & jpxCoeffSignificant) &&
		!(coeff->flags & jpxCoeffTouched)) {
	      if (coeff->flags & jpxCoeffFirstMagRef) {
		all = 0;
		if (x > cb->x0) {
		  all += (coeff[-1].flags >> jpxCoeffSignificantB) & 1;
		  if (y0+y1 > cb->y0) {
		    all += (coeff[-(int)tileComp->cbW - 1].flags
			    >> jpxCoeffSignificantB) & 1;
		  }
		  if (y0+y1 < cb->y1 - 1) {
		    all += (coeff[tileComp->cbW - 1].flags
			    >> jpxCoeffSignificantB) & 1;
		  }
		}
		if (x < cb->x1 - 1) {
		  all += (coeff[1].flags >> jpxCoeffSignificantB) & 1;
		  if (y0+y1 > cb->y0) {
		    all += (coeff[-(int)tileComp->cbW + 1].flags
			    >> jpxCoeffSignificantB) & 1;
		  }
		  if (y0+y1 < cb->y1 - 1) {
		    all += (coeff[tileComp->cbW + 1].flags
			    >> jpxCoeffSignificantB) & 1;
		  }
		}
		if (y0+y1 > cb->y0) {
		  all += (coeff[-(int)tileComp->cbW].flags
			  >> jpxCoeffSignificantB) & 1;
		}
		if (y0+y1 < cb->y1 - 1) {
		  all += (coeff[tileComp->cbW].flags
			  >> jpxCoeffSignificantB) & 1;
		}
		cx = all ? 15 : 14;
	      } else {
		cx = 16;
	      }
	      coeff->mag = (coeff->mag << 1) |
		           cb->arithDecoder->decodeBit(cx, cb->stats);
	      ++coeff->len;
	      coeff->flags |= jpxCoeffTouched;
	      coeff->flags &= ~jpxCoeffFirstMagRef;
	    }
	  }
	}
      }
      ++cb->nextPass;
      break;

    //----- cleanup pass
    case jpxPassCleanup:
      cover(67);
      for (y0 = cb->y0, coeff0 = cb->coeffs;
	   y0 < cb->y1;
	   y0 += 4, coeff0 += 4 << tileComp->codeBlockW) {
	for (x = cb->x0, coeff1 = coeff0;
	     x < cb->x1;
	     ++x, ++coeff1) {
	  y1 = 0;
	  if (y0 + 3 < cb->y1 &&
	      !(coeff1->flags & jpxCoeffTouched) &&
	      !(coeff1[tileComp->cbW].flags & jpxCoeffTouched) &&
	      !(coeff1[2 * tileComp->cbW].flags & jpxCoeffTouched) &&
	      !(coeff1[3 * tileComp->cbW].flags & jpxCoeffTouched) &&
	      (x == cb->x0 || y0 == cb->y0 ||
	       !(coeff1[-(int)tileComp->cbW - 1].flags
		 & jpxCoeffSignificant)) &&
	      (y0 == cb->y0 ||
	       !(coeff1[-(int)tileComp->cbW].flags
		 & jpxCoeffSignificant)) &&
	      (x == cb->x1 - 1 || y0 == cb->y0 ||
	       !(coeff1[-(int)tileComp->cbW + 1].flags
		 & jpxCoeffSignificant)) &&
	      (x == cb->x0 ||
	       (!(coeff1[-1].flags & jpxCoeffSignificant) &&
		!(coeff1[tileComp->cbW - 1].flags
		  & jpxCoeffSignificant) &&
		!(coeff1[2 * tileComp->cbW - 1].flags
		  & jpxCoeffSignificant) && 
		!(coeff1[3 * tileComp->cbW - 1].flags
		  & jpxCoeffSignificant))) &&
	      (x == cb->x1 - 1 ||
	       (!(coeff1[1].flags & jpxCoeffSignificant) &&
		!(coeff1[tileComp->cbW + 1].flags
		  & jpxCoeffSignificant) &&
		!(coeff1[2 * tileComp->cbW + 1].flags
		  & jpxCoeffSignificant) &&
		!(coeff1[3 * tileComp->cbW + 1].flags
		  & jpxCoeffSignificant))) &&
	      (x == cb->x0 || y0+4 == cb->y1 ||
	       !(coeff1[4 * tileComp->cbW - 1].flags & jpxCoeffSignificant)) &&
	      (y0+4 == cb->y1 ||
	       !(coeff1[4 * tileComp->cbW].flags & jpxCoeffSignificant)) &&
	      (x == cb->x1 - 1 || y0+4 == cb->y1 ||
	       !(coeff1[4 * tileComp->cbW + 1].flags
		 & jpxCoeffSignificant))) {
	    if (cb->arithDecoder->decodeBit(jpxContextRunLength, cb->stats)) {
	      y1 = cb->arithDecoder->decodeBit(jpxContextUniform, cb->stats);
	      y1 = (y1 << 1) |
		   cb->arithDecoder->decodeBit(jpxContextUniform, cb->stats);
	      for (y2 = 0, coeff = coeff1;
		   y2 < y1;
		   ++y2, coeff += tileComp->cbW) {
		++coeff->len;
	      }
	      coeff->flags |= jpxCoeffSignificant | jpxCoeffFirstMagRef;
	      coeff->mag = (coeff->mag << 1) | 1;
	      ++coeff->len;
	      cx = signContext[2][2][0];
	      xorBit = signContext[2][2][1];
	      if (cb->arithDecoder->decodeBit(cx, cb->stats) ^ xorBit) {
		coeff->flags |= jpxCoeffSign;
	      }
	      ++y1;
	    } else {
	      for (y1 = 0, coeff = coeff1;
		   y1 < 4;
		   ++y1, coeff += tileComp->cbW) {
		++coeff->len;
	      }
	      y1 = 4;
	    }
	  }
	  for (coeff = &coeff1[y1 << tileComp->codeBlockW];
	       y1 < 4 && y0 + y1 < cb->y1;
	       ++y1, coeff += tileComp->cbW) {
	    if (!(coeff->flags & jpxCoeffTouched)) {
	      horiz = vert = diag = 0;
	      horizSign = vertSign = 2;
	      if (x > cb->x0) {
		if (coeff[-1].flags & jpxCoeffSignificant) {
		  ++horiz;
		  horizSign += (coeff[-1].flags & jpxCoeffSign) ? -1 : 1;
		}
		if (y0+y1 > cb->y0) {
		  diag += (coeff[-(int)tileComp->cbW - 1].flags
			   >> jpxCoeffSignificantB) & 1;
		}
		if (y0+y1 < cb->y1 - 1) {
		  diag += (coeff[tileComp->cbW - 1].flags
			   >> jpxCoeffSignificantB) & 1;
		}
	      }
	      if (x < cb->x1 - 1) {
		if (coeff[1].flags & jpxCoeffSignificant) {
		  ++horiz;
		  horizSign += (coeff[1].flags & jpxCoeffSign) ? -1 : 1;
		}
		if (y0+y1 > cb->y0) {
		  diag += (coeff[-(int)tileComp->cbW + 1].flags
			   >> jpxCoeffSignificantB) & 1;
		}
		if (y0+y1 < cb->y1 - 1) {
		  diag += (coeff[tileComp->cbW + 1].flags
			   >> jpxCoeffSignificantB) & 1;
		}
	      }
	      if (y0+y1 > cb->y0) {
		if (coeff[-(int)tileComp->cbW].flags & jpxCoeffSignificant) {
		  ++vert;
		  vertSign += (coeff[-(int)tileComp->cbW].flags & jpxCoeffSign)
		              ? -1 : 1;
		}
	      }
	      if (y0+y1 < cb->y1 - 1) {
		if (coeff[tileComp->cbW].flags & jpxCoeffSignificant) {
		  ++vert;
		  vertSign += (coeff[tileComp->cbW].flags & jpxCoeffSign)
		              ? -1 : 1;
		}
	      }
	      cx = sigPropContext[horiz][vert][diag][res == 0 ? 1 : sb];
	      if (cb->arithDecoder->decodeBit(cx, cb->stats)) {
		coeff->flags |= jpxCoeffSignificant | jpxCoeffFirstMagRef;
		coeff->mag = (coeff->mag << 1) | 1;
		cx = signContext[horizSign][vertSign][0];
		xorBit = signContext[horizSign][vertSign][1];
		if (cb->arithDecoder->decodeBit(cx, cb->stats) ^ xorBit) {
		  coeff->flags |= jpxCoeffSign;
		}
	      }
	      ++coeff->len;
	    } else {
	      coeff->flags &= ~jpxCoeffTouched;
	    }
	  }
	}
      }
      cb->nextPass = jpxPassSigProp;
      break;
    }
  }

  cb->arithDecoder->cleanup();
  return gTrue;
}

// Inverse quantization, and wavelet transform (IDWT).  This also does
// the initial shift to convert to fixed point format.
void JPXStream::inverseTransform(JPXTileComp *tileComp) {
  JPXResLevel *resLevel;
  JPXPrecinct *precinct;
  JPXSubband *subband;
  JPXCodeBlock *cb;
  JPXCoeff *coeff0, *coeff;
  Guint qStyle, guard, eps, shift;
  int shift2;
  double mu;
  int val;
  int *dataPtr;
  Guint nx0, ny0, nx1, ny1;
  Guint r, cbX, cbY, x, y;

  cover(68);

  //----- (NL)LL subband (resolution level 0)

  resLevel = &tileComp->resLevels[0];
  precinct = &resLevel->precincts[0];
  subband = &precinct->subbands[0];

  // i-quant parameters
  qStyle = tileComp->quantStyle & 0x1f;
  guard = (tileComp->quantStyle >> 5) & 7;
  if (qStyle == 0) {
    cover(69);
    eps = (tileComp->quantSteps[0] >> 3) & 0x1f;
    shift = guard + eps - 1;
    mu = 0; // make gcc happy
  } else {
    cover(70);
    shift = guard - 1 + tileComp->prec;
    mu = (double)(0x800 + (tileComp->quantSteps[0] & 0x7ff)) / 2048.0;
  }
  if (tileComp->transform == 0) {
    cover(71);
    shift += fracBits;
  }

  // copy (NL)LL into the upper-left corner of the data array, doing
  // the fixed point adjustment and dequantization along the way
  cb = subband->cbs;
  for (cbY = 0; cbY < subband->nYCBs; ++cbY) {
    for (cbX = 0; cbX < subband->nXCBs; ++cbX) {
      for (y = cb->y0, coeff0 = cb->coeffs;
	   y < cb->y1;
	   ++y, coeff0 += tileComp->cbW) {
	dataPtr = &tileComp->data[(y - subband->y0)
				  * (tileComp->x1 - tileComp->x0)
				  + (cb->x0 - subband->x0)];
	for (x = cb->x0, coeff = coeff0; x < cb->x1; ++x, ++coeff) {
	  val = (int)coeff->mag;
	  if (val != 0) {
	    shift2 = shift - (cb->nZeroBitPlanes + coeff->len);
	    if (shift2 > 0) {
	      cover(94);
	      val = (val << shift2) + (1 << (shift2 - 1));
	    } else {
	      cover(95);
	      val >>= -shift2;
	    }
	    if (qStyle == 0) {
	      cover(96);
	      if (tileComp->transform == 0) {
		cover(97);
		val &= -1 << fracBits;
	      }
	    } else {
	      cover(98);
	      val = (int)((double)val * mu);
	    }
	    if (coeff->flags & jpxCoeffSign) {
	      cover(99);
	      val = -val;
	    }
	  }
	  *dataPtr++ = val;
	}
      }
      ++cb;
    }
  }

  //----- IDWT for each level

  for (r = 1; r <= tileComp->nDecompLevels; ++r) {
    resLevel = &tileComp->resLevels[r];

    // (n)LL is already in the upper-left corner of the
    // tile-component data array -- interleave with (n)HL/LH/HH
    // and inverse transform to get (n-1)LL, which will be stored
    // in the upper-left corner of the tile-component data array
    if (r == tileComp->nDecompLevels) {
      cover(72);
      nx0 = tileComp->x0;
      ny0 = tileComp->y0;
      nx1 = tileComp->x1;
      ny1 = tileComp->y1;
    } else {
      cover(73);
      nx0 = tileComp->resLevels[r+1].x0;
      ny0 = tileComp->resLevels[r+1].y0;
      nx1 = tileComp->resLevels[r+1].x1;
      ny1 = tileComp->resLevels[r+1].y1;
    }
    inverseTransformLevel(tileComp, r, resLevel, nx0, ny0, nx1, ny1);
  }
}

// Do one level of the inverse transform:
// - take (n)LL from the tile-component data array
// - take (n)HL/LH/HH from <resLevel>
// - leave the resulting (n-1)LL in the tile-component data array
void JPXStream::inverseTransformLevel(JPXTileComp *tileComp,
				      Guint r, JPXResLevel *resLevel,
				      Guint nx0, Guint ny0,
				      Guint nx1, Guint ny1) {
  JPXPrecinct *precinct;
  JPXSubband *subband;
  JPXCodeBlock *cb;
  JPXCoeff *coeff0, *coeff;
  Guint qStyle, guard, eps, shift, t;
  int shift2;
  double mu;
  int val;
  int *dataPtr;
  Guint xo, yo;
  Guint x, y, sb, cbX, cbY;
  int xx, yy;

  //----- interleave

  // spread out LL
  for (yy = resLevel->y1 - 1; yy >= (int)resLevel->y0; --yy) {
    for (xx = resLevel->x1 - 1; xx >= (int)resLevel->x0; --xx) {
      tileComp->data[(2 * yy - ny0) * (tileComp->x1 - tileComp->x0)
		     + (2 * xx - nx0)] =
	  tileComp->data[(yy - resLevel->y0) * (tileComp->x1 - tileComp->x0)
			 + (xx - resLevel->x0)];
    }
  }

  // i-quant parameters
  qStyle = tileComp->quantStyle & 0x1f;
  guard = (tileComp->quantStyle >> 5) & 7;

  // interleave HL/LH/HH
  precinct = &resLevel->precincts[0];
  for (sb = 0; sb < 3; ++sb) {

    // i-quant parameters
    if (qStyle == 0) {
      cover(100);
      eps = (tileComp->quantSteps[3*r - 2 + sb] >> 3) & 0x1f;
      shift = guard + eps - 1;
      mu = 0; // make gcc happy
    } else {
      cover(101);
      shift = guard + tileComp->prec;
      if (sb == 2) {
	cover(102);
	++shift;
      }
      t = tileComp->quantSteps[qStyle == 1 ? 0 : (3*r - 2 + sb)];
      mu = (double)(0x800 + (t & 0x7ff)) / 2048.0;
    }
    if (tileComp->transform == 0) {
      cover(103);
      shift += fracBits;
    }

    // copy the subband coefficients into the data array, doing the
    // fixed point adjustment and dequantization along the way
    xo = (sb & 1) ? 0 : 1;
    yo = (sb > 0) ? 1 : 0;
    subband = &precinct->subbands[sb];
    cb = subband->cbs;
    for (cbY = 0; cbY < subband->nYCBs; ++cbY) {
      for (cbX = 0; cbX < subband->nXCBs; ++cbX) {
	for (y = cb->y0, coeff0 = cb->coeffs;
	     y < cb->y1;
	     ++y, coeff0 += tileComp->cbW) {
	  dataPtr = &tileComp->data[(2 * y + yo - ny0)
				    * (tileComp->x1 - tileComp->x0)
				    + (2 * cb->x0 + xo - nx0)];
	  for (x = cb->x0, coeff = coeff0; x < cb->x1; ++x, ++coeff) {
	    val = (int)coeff->mag;
	    if (val != 0) {
	      shift2 = shift - (cb->nZeroBitPlanes + coeff->len);
	      if (shift2 > 0) {
		cover(74);
		val = (val << shift2) + (1 << (shift2 - 1));
	      } else {
		cover(75);
		val >>= -shift2;
	      }
	      if (qStyle == 0) {
		cover(76);
		if (tileComp->transform == 0) {
		  val &= -1 << fracBits;
		}
	      } else {
		cover(77);
		val = (int)((double)val * mu);
	      }
	      if (coeff->flags & jpxCoeffSign) {
		cover(78);
		val = -val;
	      }
	    }
	    *dataPtr = val;
	    dataPtr += 2;
	  }
	}
	++cb;
      }
    }
  }

  //----- horizontal (row) transforms
  dataPtr = tileComp->data;
  for (y = 0; y < ny1 - ny0; ++y) {
    inverseTransform1D(tileComp, dataPtr, 1, nx0, nx1);
    dataPtr += tileComp->x1 - tileComp->x0;
  }

  //----- vertical (column) transforms
  dataPtr = tileComp->data;
  for (x = 0; x < nx1 - nx0; ++x) {
    inverseTransform1D(tileComp, dataPtr,
		       tileComp->x1 - tileComp->x0, ny0, ny1);
    ++dataPtr;
  }
}

void JPXStream::inverseTransform1D(JPXTileComp *tileComp,
				   int *data, Guint stride,
				   Guint i0, Guint i1) {
  int *buf;
  Guint offset, end, i;

  //----- special case for length = 1
  if (i1 - i0 == 1) {
    cover(79);
    if (i0 & 1) {
      cover(104);
      *data >>= 1;
    }

  } else {
    cover(80);

    // choose an offset: this makes even buf[] indexes correspond to
    // odd values of i, and vice versa
    offset = 3 + (i0 & 1);
    end = offset + i1 - i0;

    //----- gather
    buf = tileComp->buf;
    for (i = 0; i < i1 - i0; ++i) {
      buf[offset + i] = data[i * stride];
    }

    //----- extend right
    buf[end] = buf[end - 2];
    if (i1 - i0 == 2) {
      cover(81);
      buf[end+1] = buf[offset + 1];
      buf[end+2] = buf[offset];
      buf[end+3] = buf[offset + 1];
    } else {
      cover(82);
      buf[end+1] = buf[end - 3];
      if (i1 - i0 == 3) {
	cover(105);
	buf[end+2] = buf[offset + 1];
	buf[end+3] = buf[offset + 2];
      } else {
	cover(106);
	buf[end+2] = buf[end - 4];
	if (i1 - i0 == 4) {
	  cover(107);
	  buf[end+3] = buf[offset + 1];
	} else {
	  cover(108);
	  buf[end+3] = buf[end - 5];
	}
      }
    }

    //----- extend left
    buf[offset - 1] = buf[offset + 1];
    buf[offset - 2] = buf[offset + 2];
    buf[offset - 3] = buf[offset + 3];
    if (offset == 4) {
      cover(83);
      buf[0] = buf[offset + 4];
    }

    //----- 9-7 irreversible filter

    if (tileComp->transform == 0) {
      cover(84);
      // step 1 (even)
      for (i = 1; i <= end + 2; i += 2) {
	buf[i] = (int)(idwtKappa * buf[i]);
      }
      // step 2 (odd)
      for (i = 0; i <= end + 3; i += 2) {
	buf[i] = (int)(idwtIKappa * buf[i]);
      }
      // step 3 (even)
      for (i = 1; i <= end + 2; i += 2) {
	buf[i] = (int)(buf[i] - idwtDelta * (buf[i-1] + buf[i+1]));
      }
      // step 4 (odd)
      for (i = 2; i <= end + 1; i += 2) {
	buf[i] = (int)(buf[i] - idwtGamma * (buf[i-1] + buf[i+1]));
      }
      // step 5 (even)
      for (i = 3; i <= end; i += 2) {
	buf[i] = (int)(buf[i] - idwtBeta * (buf[i-1] + buf[i+1]));
      }
      // step 6 (odd)
      for (i = 4; i <= end - 1; i += 2) {
	buf[i] = (int)(buf[i] - idwtAlpha * (buf[i-1] + buf[i+1]));
      }

    //----- 5-3 reversible filter

    } else {
      cover(85);
      // step 1 (even)
      for (i = 3; i <= end; i += 2) {
	buf[i] -= (buf[i-1] + buf[i+1] + 2) >> 2;
      }
      // step 2 (odd)
      for (i = 4; i < end; i += 2) {
	buf[i] += (buf[i-1] + buf[i+1]) >> 1;
      }
    }

    //----- scatter
    for (i = 0; i < i1 - i0; ++i) {
      data[i * stride] = buf[offset + i];
    }
  }
}

// Inverse multi-component transform and DC level shift.  This also
// converts fixed point samples back to integers.
GBool JPXStream::inverseMultiCompAndDC(JPXTile *tile) {
  JPXTileComp *tileComp;
  int coeff, d0, d1, d2, t, minVal, maxVal, zeroVal;
  int *dataPtr;
  Guint j, comp, x, y;

  //----- inverse multi-component transform

  if (tile->multiComp == 1) {
    cover(86);
    if (img.nComps < 3 ||
	tile->tileComps[0].hSep != tile->tileComps[1].hSep ||
	tile->tileComps[0].vSep != tile->tileComps[1].vSep ||
	tile->tileComps[1].hSep != tile->tileComps[2].hSep ||
	tile->tileComps[1].vSep != tile->tileComps[2].vSep) {
      return gFalse;
    }

    // inverse irreversible multiple component transform
    if (tile->tileComps[0].transform == 0) {
      cover(87);
      j = 0;
      for (y = 0; y < tile->tileComps[0].y1 - tile->tileComps[0].y0; ++y) {
	for (x = 0; x < tile->tileComps[0].x1 - tile->tileComps[0].x0; ++x) {
	  d0 = tile->tileComps[0].data[j];
	  d1 = tile->tileComps[1].data[j];
	  d2 = tile->tileComps[2].data[j];
	  tile->tileComps[0].data[j] = (int)(d0 + 1.402 * d2 + 0.5);
	  tile->tileComps[1].data[j] =
	      (int)(d0 - 0.34413 * d1 - 0.71414 * d2 + 0.5);
	  tile->tileComps[2].data[j] = (int)(d0 + 1.772 * d1 + 0.5);
	  ++j;
	}
      }

    // inverse reversible multiple component transform
    } else {
      cover(88);
      j = 0;
      for (y = 0; y < tile->tileComps[0].y1 - tile->tileComps[0].y0; ++y) {
	for (x = 0; x < tile->tileComps[0].x1 - tile->tileComps[0].x0; ++x) {
	  d0 = tile->tileComps[0].data[j];
	  d1 = tile->tileComps[1].data[j];
	  d2 = tile->tileComps[2].data[j];
	  tile->tileComps[1].data[j] = t = d0 - ((d2 + d1) >> 2);
	  tile->tileComps[0].data[j] = d2 + t;
	  tile->tileComps[2].data[j] = d1 + t;
	  ++j;
	}
      }
    }
  }

  //----- DC level shift
  for (comp = 0; comp < img.nComps; ++comp) {
    tileComp = &tile->tileComps[comp];

    // signed: clip
    if (tileComp->sgned) {
      cover(89);
      minVal = -(1 << (tileComp->prec - 1));
      maxVal = (1 << (tileComp->prec - 1)) - 1;
      dataPtr = tileComp->data;
      for (y = 0; y < tileComp->y1 - tileComp->y0; ++y) {
	for (x = 0; x < tileComp->x1 - tileComp->x0; ++x) {
	  coeff = *dataPtr;
	  if (tileComp->transform == 0) {
	    cover(109);
	    coeff >>= fracBits;
	  }
	  if (coeff < minVal) {
	    cover(110);
	    coeff = minVal;
	  } else if (coeff > maxVal) {
	    cover(111);
	    coeff = maxVal;
	  }
	  *dataPtr++ = coeff;
	}
      }

    // unsigned: inverse DC level shift and clip
    } else {
      cover(90);
      maxVal = (1 << tileComp->prec) - 1;
      zeroVal = 1 << (tileComp->prec - 1);
      dataPtr = tileComp->data;
      for (y = 0; y < tileComp->y1 - tileComp->y0; ++y) {
	for (x = 0; x < tileComp->x1 - tileComp->x0; ++x) {
	  coeff = *dataPtr;
	  if (tileComp->transform == 0) {
	    cover(112);
	    coeff >>= fracBits;
	  }
	  coeff += zeroVal;
	  if (coeff < 0) {
	    cover(113);
	    coeff = 0;
	  } else if (coeff > maxVal) {
	    cover(114);
	    coeff = maxVal;
	  }
	  *dataPtr++ = coeff;
	}
      }
    }
  }

  return gTrue;
}

GBool JPXStream::readBoxHdr(Guint *boxType, Guint *boxLen, Guint *dataLen) {
  Guint len, lenH;

  if (!readULong(&len) ||
      !readULong(boxType)) {
    return gFalse;
  }
  if (len == 1) {
    if (!readULong(&lenH) || !readULong(&len)) {
      return gFalse;
    }
    if (lenH) {
      error(getPos(), "JPX stream contains a box larger than 2^32 bytes");
      return gFalse;
    }
    *boxLen = len;
    *dataLen = len - 16;
  } else if (len == 0) {
    *boxLen = 0;
    *dataLen = 0;
  } else {
    *boxLen = len;
    *dataLen = len - 8;
  }
  return gTrue;
}

int JPXStream::readMarkerHdr(int *segType, Guint *segLen) {
  int c;

  do {
    do {
      if ((c = str->getChar()) == EOF) {
	return gFalse;
      }
    } while (c != 0xff);
    do {
      if ((c = str->getChar()) == EOF) {
	return gFalse;
      }
    } while (c == 0xff);
  } while (c == 0x00);
  *segType = c;
  if ((c >= 0x30 && c <= 0x3f) ||
      c == 0x4f || c == 0x92 || c == 0x93 || c == 0xd9) {
    *segLen = 0;
    return gTrue;
  }
  return readUWord(segLen);
}

GBool JPXStream::readUByte(Guint *x) {
  int c0;

  if ((c0 = str->getChar()) == EOF) {
    return gFalse;
  }
  *x = (Guint)c0;
  return gTrue;
}

GBool JPXStream::readByte(int *x) {
 int c0;

  if ((c0 = str->getChar()) == EOF) {
    return gFalse;
  }
  *x = c0;
  if (c0 & 0x80) {
    *x |= -1 - 0xff;
  }
  return gTrue;
}

GBool JPXStream::readUWord(Guint *x) {
  int c0, c1;

  if ((c0 = str->getChar()) == EOF ||
      (c1 = str->getChar()) == EOF) {
    return gFalse;
  }
  *x = (Guint)((c0 << 8) | c1);
  return gTrue;
}

GBool JPXStream::readULong(Guint *x) {
  int c0, c1, c2, c3;

  if ((c0 = str->getChar()) == EOF ||
      (c1 = str->getChar()) == EOF ||
      (c2 = str->getChar()) == EOF ||
      (c3 = str->getChar()) == EOF) {
    return gFalse;
  }
  *x = (Guint)((c0 << 24) | (c1 << 16) | (c2 << 8) | c3);
  return gTrue;
}

GBool JPXStream::readNBytes(int nBytes, GBool signd, int *x) {
  int y, c, i;

  y = 0;
  for (i = 0; i < nBytes; ++i) {
    if ((c = str->getChar()) == EOF) {
      return gFalse;
    }
    y = (y << 8) + c;
  }
  if (signd) {
    if (y & (1 << (8 * nBytes - 1))) {
      y |= -1 << (8 * nBytes);
    }
  }
  *x = y;
  return gTrue;
}

GBool JPXStream::readBits(int nBits, Guint *x) {
  int c;

  while (bitBufLen < nBits) {
    if (byteCount == 0 || (c = str->getChar()) == EOF) {
      return gFalse;
    }
    --byteCount;
    if (bitBufSkip) {
      bitBuf = (bitBuf << 7) | (c & 0x7f);
      bitBufLen += 7;
    } else {
      bitBuf = (bitBuf << 8) | (c & 0xff);
      bitBufLen += 8;
    }
    bitBufSkip = c == 0xff;
  }
  *x = (bitBuf >> (bitBufLen - nBits)) & ((1 << nBits) - 1);
  bitBufLen -= nBits;
  return gTrue;
}

void JPXStream::startBitBuf(Guint byteCountA) {
  bitBufLen = 0;
  bitBufSkip = gFalse;
  byteCount = byteCountA;
}

Guint JPXStream::finishBitBuf() {
  if (bitBufSkip) {
    str->getChar();
    --byteCount;
  }
  return byteCount;
}
//========================================================================
//
// JPXStream.h
//
// Copyright 2002-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef JPXSTREAM_H
#define JPXSTREAM_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "gtypes.h"
#include "Object.h"
#include "Stream.h"

class JArithmeticDecoder;
class JArithmeticDecoderStats;

//------------------------------------------------------------------------

enum JPXColorSpaceType {
  jpxCSBiLevel = 0,
  jpxCSYCbCr1 = 1,
  jpxCSYCbCr2 = 3,
  jpxCSYCBCr3 = 4,
  jpxCSPhotoYCC = 9,
  jpxCSCMY = 11,
  jpxCSCMYK = 12,
  jpxCSYCCK = 13,
  jpxCSCIELab = 14,
  jpxCSsRGB = 16,
  jpxCSGrayscale = 17,
  jpxCSBiLevel2 = 18,
  jpxCSCIEJab = 19,
  jpxCSCISesRGB = 20,
  jpxCSROMMRGB = 21,
  jpxCSsRGBYCbCr = 22,
  jpxCSYPbPr1125 = 23,
  jpxCSYPbPr1250 = 24
};

struct JPXColorSpecCIELab {
  Guint rl, ol, ra, oa, rb, ob, il;
};

struct JPXColorSpecEnumerated {
  JPXColorSpaceType type;	// color space type
  union {
    JPXColorSpecCIELab cieLab;
  };
};

struct JPXColorSpec {
  Guint meth;			// method
  int prec;			// precedence
  union {
    JPXColorSpecEnumerated enumerated;
  };
};

//------------------------------------------------------------------------

struct JPXPalette {
  Guint nEntries;		// number of entries in the palette
  Guint nComps;			// number of components in each entry
  Guint *bpc;			// bits per component, for each component
  int *c;			// color data:
				//   c[i*nComps+j] = entry i, component j
};

//------------------------------------------------------------------------

struct JPXCompMap {
  Guint nChannels;		// number of channels
  Guint *comp;			// codestream components mapped to each channel
  Guint *type;			// 0 for direct use, 1 for palette mapping
  Guint *pComp;			// palette components to use
};

//------------------------------------------------------------------------

struct JPXChannelDefn {
  Guint nChannels;		// number of channels
  Guint *idx;			// channel indexes
  Guint *type;			// channel types
  Guint *assoc;			// channel associations
};

//------------------------------------------------------------------------

struct JPXTagTreeNode {
  GBool finished;		// true if this node is finished
  Guint val;			// current value
};

//------------------------------------------------------------------------

struct JPXCoeff {
  Gushort flags;		// flag bits
  Gushort len;			// number of significant bits in mag
  Guint mag;			// magnitude value
};

// coefficient flags
#define jpxCoeffSignificantB  0
#define jpxCoeffTouchedB      1
#define jpxCoeffFirstMagRefB  2
#define jpxCoeffSignB         7
#define jpxCoeffSignificant   (1 << jpxCoeffSignificantB)
#define jpxCoeffTouched       (1 << jpxCoeffTouchedB)
#define jpxCoeffFirstMagRef   (1 << jpxCoeffFirstMagRefB)
#define jpxCoeffSign          (1 << jpxCoeffSignB)

//------------------------------------------------------------------------

struct JPXCodeBlock {
  //----- size
  Guint x0, y0, x1, y1;		// bounds

  //----- persistent state
  GBool seen;			// true if this code-block has already
				//   been seen
  Guint lBlock;			// base number of bits used for pkt data length
  Guint nextPass;		// next coding pass

  //---- info from first packet
  Guint nZeroBitPlanes;		// number of zero bit planes

  //----- info for the current packet
  Guint included;		// code-block inclusion in this packet:
				//   0=not included, 1=included
  Guint nCodingPasses;		// number of coding passes in this pkt
  Guint dataLen;		// pkt data length

  //----- coefficient data
  JPXCoeff *coeffs;		// the coefficients
  JArithmeticDecoder		// arithmetic decoder
    *arithDecoder;
  JArithmeticDecoderStats	// arithmetic decoder stats
    *stats;
};

//------------------------------------------------------------------------

struct JPXSubband {
  //----- computed
  Guint x0, y0, x1, y1;		// bounds
  Guint nXCBs, nYCBs;		// number of code-blocks in the x and y
				//   directions

  //----- tag trees
  Guint maxTTLevel;		// max tag tree level
  JPXTagTreeNode *inclusion;	// inclusion tag tree for each subband
  JPXTagTreeNode *zeroBitPlane;	// zero-bit plane tag tree for each
				//   subband

  //----- children
  JPXCodeBlock *cbs;		// the code-blocks (len = nXCBs * nYCBs)
};

//------------------------------------------------------------------------

struct JPXPrecinct {
  //----- computed
  Guint x0, y0, x1, y1;		// bounds of the precinct

  //----- children
  JPXSubband *subbands;		// the subbands
};

//------------------------------------------------------------------------

struct JPXResLevel {
  //----- from the COD and COC segments (main and tile)
  Guint precinctWidth;		// log2(precinct width)
  Guint precinctHeight;		// log2(precinct height)

  //----- computed
  Guint x0, y0, x1, y1;		// bounds of the tile-comp (for this res level)
  Guint bx0[3], by0[3],		// subband bounds
        bx1[3], by1[3];

  //---- children
  JPXPrecinct *precincts;	// the precincts
};

//------------------------------------------------------------------------

struct JPXTileComp {
  //----- from the SIZ segment
  GBool sgned;			// 1 for signed, 0 for unsigned
  Guint prec;			// precision, in bits
  Guint hSep;			// horizontal separation of samples
  Guint vSep;			// vertical separation of samples

  //----- from the COD and COC segments (main and tile)
  Guint style;			// coding style parameter (Scod / Scoc)
  Guint nDecompLevels;		// number of decomposition levels
  Guint codeBlockW;		// log2(code-block width)
  Guint codeBlockH;		// log2(code-block height)
  Guint codeBlockStyle;		// code-block style
  Guint transform;		// wavelet transformation

  //----- from the QCD and QCC segments (main and tile)
  Guint quantStyle;		// quantization style
  Guint *quantSteps;		// quantization step size for each subband
  Guint nQuantSteps;		// number of entries in quantSteps

  //----- computed
  Guint x0, y0, x1, y1;		// bounds of the tile-comp, in ref coords
  Guint cbW;			// code-block width
  Guint cbH;			// code-block height

  //----- image data
  int *data;			// the decoded image data
  int *buf;			// intermediate buffer for the inverse
				//   transform

  //----- children
  JPXResLevel *resLevels;	// the resolution levels
				//   (len = nDecompLevels + 1)
};

//------------------------------------------------------------------------

struct JPXTile {
  //----- from the COD segments (main and tile)
  Guint progOrder;		// progression order
  Guint nLayers;		// number of layers
  Guint multiComp;		// multiple component transformation

  //----- computed
  Guint x0, y0, x1, y1;		// bounds of the tile, in ref coords
  Guint maxNDecompLevels;	// max number of decomposition levels used
				//   in any component in this tile

  //----- progression order loop counters
  Guint comp;			//   component
  Guint res;			//   resolution level
  Guint precinct;		//   precinct
  Guint layer;			//   layer

  //----- children
  JPXTileComp *tileComps;	// the tile-components (len = JPXImage.nComps)
};

//------------------------------------------------------------------------

struct JPXImage {
  //----- from the SIZ segment
  Guint xSize, ySize;		// size of reference grid
  Guint xOffset, yOffset;	// image offset
  Guint xTileSize, yTileSize;	// size of tiles
  Guint xTileOffset,		// offset of first tile
        yTileOffset;
  Guint nComps;			// number of components

  //----- computed
  Guint nXTiles;		// number of tiles in x direction
  Guint nYTiles;		// number of tiles in y direction

  //----- children
  JPXTile *tiles;		// the tiles (len = nXTiles * nYTiles)
};

//------------------------------------------------------------------------

class JPXStream: public FilterStream {
public:

  JPXStream(Stream *strA);
  virtual ~JPXStream();
  virtual StreamKind getKind() { return strJPX; }
  virtual void reset();
  virtual void close();
  virtual int getChar();
  virtual int lookChar();
  virtual GString *getPSFilter(int psLevel, char *indent);
  virtual GBool isBinary(GBool last = gTrue);
  virtual void getImageParams(int *bitsPerComponent,
			      StreamColorSpaceMode *csMode);

private:

  void fillReadBuf();
  void getImageParams2(int *bitsPerComponent, StreamColorSpaceMode *csMode);
  GBool readBoxes();
  GBool readColorSpecBox(Guint dataLen);
  GBool readCodestream(Guint len);
  GBool readTilePart();
  GBool readTilePartData(Guint tileIdx,
			 Guint tilePartLen, GBool tilePartToEOC);
  GBool readCodeBlockData(JPXTileComp *tileComp,
			  JPXResLevel *resLevel,
			  JPXPrecinct *precinct,
			  JPXSubband *subband,
			  Guint res, Guint sb,
			  JPXCodeBlock *cb);
  void inverseTransform(JPXTileComp *tileComp);
  void inverseTransformLevel(JPXTileComp *tileComp,
			     Guint r, JPXResLevel *resLevel,
			     Guint nx0, Guint ny0,
			     Guint nx1, Guint ny1);
  void inverseTransform1D(JPXTileComp *tileComp,
			  int *data, Guint stride,
			  Guint i0, Guint i1);
  GBool inverseMultiCompAndDC(JPXTile *tile);
  GBool readBoxHdr(Guint *boxType, Guint *boxLen, Guint *dataLen);
  int readMarkerHdr(int *segType, Guint *segLen);
  GBool readUByte(Guint *x);
  GBool readByte(int *x);
  GBool readUWord(Guint *x);
  GBool readULong(Guint *x);
  GBool readNBytes(int nBytes, GBool signd, int *x);
  GBool readBits(int nBits, Guint *x);
  void startBitBuf(Guint byteCountA);
  Guint finishBitBuf();

  Guint nComps;			// number of components
  Guint *bpc;			// bits per component, for each component
  Guint width, height;		// image size
  GBool haveImgHdr;		// set if a JP2/JPX image header has been
				//   found
  JPXColorSpec cs;		// color specification
  GBool haveCS;			// set if a color spec has been found
  JPXPalette palette;		// the palette
  GBool havePalette;		// set if a palette has been found
  JPXCompMap compMap;		// the component mapping
  GBool haveCompMap;		// set if a component mapping has been found
  JPXChannelDefn channelDefn;	// channel definition
  GBool haveChannelDefn;	// set if a channel defn has been found

  JPXImage img;			// JPEG2000 decoder data
  Guint bitBuf;			// buffer for bit reads
  int bitBufLen;		// number of bits in bitBuf
  GBool bitBufSkip;		// true if next bit should be skipped
				//   (for bit stuffing)
  Guint byteCount;		// number of available bytes left

  Guint curX, curY, curComp;	// current position for lookChar/getChar
  Guint readBuf;		// read buffer
  Guint readBufLen;		// number of valid bits in readBuf
};

#endif
//========================================================================
//
// Lexer.cc
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <ctype.h>
#include "Lexer.h"
#include "Error.h"

//------------------------------------------------------------------------

// A '1' in this array means the character is white space.  A '1' or
// '2' means the character ends a name or command.
static char specialChars[256] = {
  1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0,   // 0x
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // 1x
  1, 0, 0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2,   // 2x
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0,   // 3x
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // 4x
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0,   // 5x
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // 6x
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0,   // 7x
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // 8x
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // 9x
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // ax
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // bx
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // cx
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // dx
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // ex
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0    // fx
};

//------------------------------------------------------------------------
// Lexer
//------------------------------------------------------------------------

Lexer::Lexer(XRef *xref, Stream *str) {
  Object obj;

  curStr.initStream(str);
  streams = new Array(xref);
  streams->add(curStr.copy(&obj));
  strPtr = 0;
  freeArray = gTrue;
  curStr.streamReset();
}

Lexer::Lexer(XRef *xref, Object *obj) {
  Object obj2;

  if (obj->isStream()) {
    streams = new Array(xref);
    freeArray = gTrue;
    streams->add(obj->copy(&obj2));
  } else {
    streams = obj->getArray();
    freeArray = gFalse;
  }
  strPtr = 0;
  if (streams->getLength() > 0) {
    streams->get(strPtr, &curStr);
    curStr.streamReset();
  }
}

Lexer::~Lexer() {
  if (!curStr.isNone()) {
    curStr.streamClose();
    curStr.free();
  }
  if (freeArray) {
    delete streams;
  }
}

int Lexer::getChar() {
  int c;

  c = EOF;
  while (!curStr.isNone() && (c = curStr.streamGetChar()) == EOF) {
    curStr.streamClose();
    curStr.free();
    ++strPtr;
    if (strPtr < streams->getLength()) {
      streams->get(strPtr, &curStr);
      curStr.streamReset();
    }
  }
  return c;
}

int Lexer::lookChar() {
  if (curStr.isNone()) {
    return EOF;
  }
  return curStr.streamLookChar();
}

Object *Lexer::getObj(Object *obj) {
  char *p;
  int c, c2;
  GBool comment, neg, done;
  int numParen;
  int xi;
  double xf, scale;
  GString *s;
  int n, m;

  // skip whitespace and comments
  comment = gFalse;
  while (1) {
    if ((c = getChar()) == EOF) {
      return obj->initEOF();
    }
    if (comment) {
      if (c == '\r' || c == '\n')
	comment = gFalse;
    } else if (c == '%') {
      comment = gTrue;
    } else if (specialChars[c] != 1) {
      break;
    }
  }

  // start reading token
  switch (c) {

  // number
  case '0': case '1': case '2': case '3': case '4':
  case '5': case '6': case '7': case '8': case '9':
  case '-': case '.':
    neg = gFalse;
    xi = 0;
    if (c == '-') {
      neg = gTrue;
    } else if (c == '.') {
      goto doReal;
    } else {
      xi = c - '0';
    }
    while (1) {
      c = lookChar();
      if (isdigit(c)) {
	getChar();
	xi = xi * 10 + (c - '0');
      } else if (c == '.') {
	getChar();
	goto doReal;
      } else {
	break;
      }
    }
    if (neg)
      xi = -xi;
    obj->initInt(xi);
    break;
  doReal:
    xf = xi;
    scale = 0.1;
    while (1) {
      c = lookChar();
      if (c == '-') {
	// ignore minus signs in the middle of numbers to match
	// Adobe's behavior
	error(getPos(), "Badly formatted number");
	getChar();
	continue;
      }
      if (!isdigit(c)) {
	break;
      }
      getChar();
      xf = xf + scale * (c - '0');
      scale *= 0.1;
    }
    if (neg)
      xf = -xf;
    obj->initReal(xf);
    break;

  // string
  case '(':
    p = tokBuf;
    n = 0;
    numParen = 1;
    done = gFalse;
    s = NULL;
    do {
      c2 = EOF;
      switch (c = getChar()) {

      case EOF:
#if 0
      // This breaks some PDF files, e.g., ones from Photoshop.
      case '\r':
      case '\n':
#endif
	error(getPos(), "Unterminated string");
	done = gTrue;
	break;

      case '(':
	++numParen;
	c2 = c;
	break;

      case ')':
	if (--numParen == 0) {
	  done = gTrue;
	} else {
	  c2 = c;
	}
	break;

      case '\\':
	switch (c = getChar()) {
	case 'n':
	  c2 = '\n';
	  break;
	case 'r':
	  c2 = '\r';
	  break;
	case 't':
	  c2 = '\t';
	  break;
	case 'b':
	  c2 = '\b';
	  break;
	case 'f':
	  c2 = '\f';
	  break;
	case '\\':
	case '(':
	case ')':
	  c2 = c;
	  break;
	case '0': case '1': case '2': case '3':
	case '4': case '5': case '6': case '7':
	  c2 = c - '0';
	  c = lookChar();
	  if (c >= '0' && c <= '7') {
	    getChar();
	    c2 = (c2 << 3) + (c - '0');
	    c = lookChar();
	    if (c >= '0' && c <= '7') {
	      getChar();
	      c2 = (c2 << 3) + (c - '0');
	    }
	  }
	  break;
	case '\r':
	  c = lookChar();
	  if (c == '\n') {
	    getChar();
	  }
	  break;
	case '\n':
	  break;
	case EOF:
	  error(getPos(), "Unterminated string");
	  done = gTrue;
	  break;
	default:
	  c2 = c;
	  break;
	}
	break;

      default:
	c2 = c;
	break;
      }

      if (c2 != EOF) {
	if (n == tokBufSize) {
	  if (!s)
	    s = new GString(tokBuf, tokBufSize);
	  else
	    s->append(tokBuf, tokBufSize);
	  p = tokBuf;
	  n = 0;
	}
	*p++ = (char)c2;
	++n;
      }
    } while (!done);
    if (!s)
      s = new GString(tokBuf, n);
    else
      s->append(tokBuf, n);
    obj->initString(s);
    break;

  // name
  case '/':
    p = tokBuf;
    n = 0;
    while ((c = lookChar()) != EOF && !specialChars[c]) {
      getChar();
      if (c == '#') {
	c2 = lookChar();
	if (c2 >= '0' && c2 <= '9') {
	  c = c2 - '0';
	} else if (c2 >= 'A' && c2 <= 'F') {
	  c = c2 - 'A' + 10;
	} else if (c2 >= 'a' && c2 <= 'f') {
	  c = c2 - 'a' + 10;
	} else {
	  goto notEscChar;
	}
	getChar();
	c <<= 4;
	c2 = getChar();
	if (c2 >= '0' && c2 <= '9') {
	  c += c2 - '0';
	} else if (c2 >= 'A' && c2 <= 'F') {
	  c += c2 - 'A' + 10;
	} else if (c2 >= 'a' && c2 <= 'f') {
	  c += c2 - 'a' + 10;
	} else {
	  error(getPos(), "Illegal digit in hex char in name");
	}
      }
     notEscChar:
      if (++n == tokBufSize) {
	error(getPos(), "Name token too long");
	break;
      }
      *p++ = c;
    }
    *p = '\0';
    obj->initName(tokBuf);
    break;

  // array punctuation
  case '[':
  case ']':
    tokBuf[0] = c;
    tokBuf[1] = '\0';
    obj->initCmd(tokBuf);
    break;

  // hex string or dict punctuation
  case '<':
    c = lookChar();

    // dict punctuation
    if (c == '<') {
      getChar();
      tokBuf[0] = tokBuf[1] = '<';
      tokBuf[2] = '\0';
      obj->initCmd(tokBuf);

    // hex string
    } else {
      p = tokBuf;
      m = n = 0;
      c2 = 0;
      s = NULL;
      while (1) {
	c = getChar();
	if (c == '>') {
	  break;
	} else if (c == EOF) {
	  error(getPos(), "Unterminated hex string");
	  break;
	} else if (specialChars[c] != 1) {
	  c2 = c2 << 4;
	  if (c >= '0' && c <= '9')
	    c2 += c - '0';
	  else if (c >= 'A' && c <= 'F')
	    c2 += c - 'A' + 10;
	  else if (c >= 'a' && c <= 'f')
	    c2 += c - 'a' + 10;
	  else
	    error(getPos(), "Illegal character <%02x> in hex string", c);
	  if (++m == 2) {
	    if (n == tokBufSize) {
	      if (!s)
		s = new GString(tokBuf, tokBufSize);
	      else
		s->append(tokBuf, tokBufSize);
	      p = tokBuf;
	      n = 0;
	    }
	    *p++ = (char)c2;
	    ++n;
	    c2 = 0;
	    m = 0;
	  }
	}
      }
      if (!s)
	s = new GString(tokBuf, n);
      else
	s->append(tokBuf, n);
      if (m == 1)
	s->append((char)(c2 << 4));
      obj->initString(s);
    }
    break;

  // dict punctuation
  case '>':
    c = lookChar();
    if (c == '>') {
      getChar();
      tokBuf[0] = tokBuf[1] = '>';
      tokBuf[2] = '\0';
      obj->initCmd(tokBuf);
    } else {
      error(getPos(), "Illegal character '>'");
      obj->initError();
    }
    break;

  // error
  case ')':
  case '{':
  case '}':
    error(getPos(), "Illegal character '%c'", c);
    obj->initError();
    break;

  // command
  default:
    p = tokBuf;
    *p++ = c;
    n = 1;
    while ((c = lookChar()) != EOF && !specialChars[c]) {
      getChar();
      if (++n == tokBufSize) {
	error(getPos(), "Command token too long");
	break;
      }
      *p++ = c;
    }
    *p = '\0';
    if (tokBuf[0] == 't' && !strcmp(tokBuf, "true")) {
      obj->initBool(gTrue);
    } else if (tokBuf[0] == 'f' && !strcmp(tokBuf, "false")) {
      obj->initBool(gFalse);
    } else if (tokBuf[0] == 'n' && !strcmp(tokBuf, "null")) {
      obj->initNull();
    } else {
      obj->initCmd(tokBuf);
    }
    break;
  }

  return obj;
}

void Lexer::skipToNextLine() {
  int c;

  while (1) {
    c = getChar();
    if (c == EOF || c == '\n') {
      return;
    }
    if (c == '\r') {
      if ((c = lookChar()) == '\n') {
	getChar();
      }
      return;
    }
  }
}

GBool Lexer::isSpace(int c) {
  return c >= 0 && c <= 0xff && specialChars[c] == 1;
}
//========================================================================
//
// Lexer.h
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef LEXER_H
#define LEXER_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "Object.h"
#include "Stream.h"

class XRef;

#define tokBufSize 128		// size of token buffer

//------------------------------------------------------------------------
// Lexer
//------------------------------------------------------------------------

class Lexer {
public:

  // Construct a lexer for a single stream.  Deletes the stream when
  // lexer is deleted.
  Lexer(XRef *xref, Stream *str);

  // Construct a lexer for a stream or array of streams (assumes obj
  // is either a stream or array of streams).
  Lexer(XRef *xref, Object *obj);

  // Destructor.
  ~Lexer();

  // Get the next object from the input stream.
  Object *getObj(Object *obj);

  // Skip to the beginning of the next line in the input stream.
  void skipToNextLine();

  // Skip over one character.
  void skipChar() { getChar(); }

  // Get stream.
  Stream *getStream()
    { return curStr.isNone() ? (Stream *)NULL : curStr.getStream(); }

  // Get current position in file.  This is only used for error
  // messages, so it returns an int instead of a Guint.
  int getPos()
    { return curStr.isNone() ? -1 : (int)curStr.streamGetPos(); }

  // Set position in file.
  void setPos(Guint pos, int dir = 0)
    { if (!curStr.isNone()) curStr.streamSetPos(pos, dir); }

  // Returns true if <c> is a whitespace character.
  static GBool isSpace(int c);

private:

  int getChar();
  int lookChar();

  Array *streams;		// array of input streams
  int strPtr;			// index of current stream
  Object curStr;		// current stream
  GBool freeArray;		// should lexer free the streams array?
  char tokBuf[tokBufSize];	// temporary token buffer
};

#endif
//========================================================================
//
// Link.cc
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stddef.h>
#include <string.h>
#include "gmem.h"
#include "GString.h"
#include "Error.h"
#include "Object.h"
#include "Array.h"
#include "Dict.h"
#include "Link.h"

//------------------------------------------------------------------------
// LinkAction
//------------------------------------------------------------------------

LinkAction *LinkAction::parseDest(Object *obj) {
  LinkAction *action;

  action = new LinkGoTo(obj);
  if (!action->isOk()) {
    delete action;
    return NULL;
  }
  return action;
}

LinkAction *LinkAction::parseAction(Object *obj, GString *baseURI) {
  LinkAction *action;
  Object obj2, obj3, obj4;

  if (!obj->isDict()) {
    error(-1, "Bad annotation action");
    return NULL;
  }

  obj->dictLookup("S", &obj2);

  // GoTo action
  if (obj2.isName("GoTo")) {
    obj->dictLookup("D", &obj3);
    action = new LinkGoTo(&obj3);
    obj3.free();

  // GoToR action
  } else if (obj2.isName("GoToR")) {
    obj->dictLookup("F", &obj3);
    obj->dictLookup("D", &obj4);
    action = new LinkGoToR(&obj3, &obj4);
    obj3.free();
    obj4.free();

  // Launch action
  } else if (obj2.isName("Launch")) {
    action = new LinkLaunch(obj);

  // URI action
  } else if (obj2.isName("URI")) {
    obj->dictLookup("URI", &obj3);
    action = new LinkURI(&obj3, baseURI);
    obj3.free();

  // Named action
  } else if (obj2.isName("Named")) {
    obj->dictLookup("N", &obj3);
    action = new LinkNamed(&obj3);
    obj3.free();

  // Movie action
  } else if (obj2.isName("Movie")) {
    obj->dictLookupNF("Annot", &obj3);
    obj->dictLookup("T", &obj4);
    action = new LinkMovie(&obj3, &obj4);
    obj3.free();
    obj4.free();

  // unknown action
  } else if (obj2.isName()) {
    action = new LinkUnknown(obj2.getName());

  // action is missing or wrong type
  } else {
    error(-1, "Bad annotation action");
    action = NULL;
  }

  obj2.free();

  if (action && !action->isOk()) {
    delete action;
    return NULL;
  }
  return action;
}

GString *LinkAction::getFileSpecName(Object *fileSpecObj) {
  GString *name;
  Object obj1;

  name = NULL;

  // string
  if (fileSpecObj->isString()) {
    name = fileSpecObj->getString()->copy();

  // dictionary
  } else if (fileSpecObj->isDict()) {
#ifdef WIN32
    if (!fileSpecObj->dictLookup("DOS", &obj1)->isString()) {
#else
    if (!fileSpecObj->dictLookup("Unix", &obj1)->isString()) {
#endif
      obj1.free();
      fileSpecObj->dictLookup("F", &obj1);
    }
    if (obj1.isString()) {
      name = obj1.getString()->copy();
    } else {
      error(-1, "Illegal file spec in link");
    }
    obj1.free();

  // error
  } else {
    error(-1, "Illegal file spec in link");
  }

  // system-dependent path manipulation
  if (name) {
#ifdef WIN32
    int i, j;

    // "//...."             --> "\...."
    // "/x/...."            --> "x:\...."
    // "/server/share/...." --> "\\server\share\...."
    // convert escaped slashes to slashes and unescaped slashes to backslashes
    i = 0;
    if (name->getChar(0) == '/') {
      if (name->getLength() >= 2 && name->getChar(1) == '/') {
	name->del(0);
	i = 0;
      } else if (name->getLength() >= 2 &&
		 ((name->getChar(1) >= 'a' && name->getChar(1) <= 'z') ||
		  (name->getChar(1) >= 'A' && name->getChar(1) <= 'Z')) &&
		 (name->getLength() == 2 || name->getChar(2) == '/')) {
	name->setChar(0, name->getChar(1));
	name->setChar(1, ':');
	i = 2;
      } else {
	for (j = 2; j < name->getLength(); ++j) {
	  if (name->getChar(j-1) != '\\' &&
	      name->getChar(j) == '/') {
	    break;
	  }
	}
	if (j < name->getLength()) {
	  name->setChar(0, '\\');
	  name->insert(0, '\\');
	  i = 2;
	}
      }
    }
    for (; i < name->getLength(); ++i) {
      if (name->getChar(i) == '/') {
	name->setChar(i, '\\');
      } else if (name->getChar(i) == '\\' &&
		 i+1 < name->getLength() &&
		 name->getChar(i+1) == '/') {
	name->del(i);
      }
    }
#else
    // no manipulation needed for Unix
#endif
  }

  return name;
}

//------------------------------------------------------------------------
// LinkDest
//------------------------------------------------------------------------

LinkDest::LinkDest(Array *a) {
  Object obj1, obj2;

  // initialize fields
  left = bottom = right = top = zoom = 0;
  ok = gFalse;

  // get page
  if (a->getLength() < 2) {
    error(-1, "Annotation destination array is too short");
    return;
  }
  a->getNF(0, &obj1);
  if (obj1.isInt()) {
    pageNum = obj1.getInt() + 1;
    pageIsRef = gFalse;
  } else if (obj1.isRef()) {
    pageRef.num = obj1.getRefNum();
    pageRef.gen = obj1.getRefGen();
    pageIsRef = gTrue;
  } else {
    error(-1, "Bad annotation destination");
    goto err2;
  }
  obj1.free();

  // get destination type
  a->get(1, &obj1);

  // XYZ link
  if (obj1.isName("XYZ")) {
    kind = destXYZ;
    if (a->getLength() < 3) {
      changeLeft = gFalse;
    } else {
      a->get(2, &obj2);
      if (obj2.isNull()) {
	changeLeft = gFalse;
      } else if (obj2.isNum()) {
	changeLeft = gTrue;
	left = obj2.getNum();
      } else {
	error(-1, "Bad annotation destination position");
	goto err1;
      }
      obj2.free();
    }
    if (a->getLength() < 4) {
      changeTop = gFalse;
    } else {
      a->get(3, &obj2);
      if (obj2.isNull()) {
	changeTop = gFalse;
      } else if (obj2.isNum()) {
	changeTop = gTrue;
	top = obj2.getNum();
      } else {
	error(-1, "Bad annotation destination position");
	goto err1;
      }
      obj2.free();
    }
    if (a->getLength() < 5) {
      changeZoom = gFalse;
    } else {
      a->get(4, &obj2);
      if (obj2.isNull()) {
	changeZoom = gFalse;
      } else if (obj2.isNum()) {
	changeZoom = gTrue;
	zoom = obj2.getNum();
      } else {
	error(-1, "Bad annotation destination position");
	goto err1;
      }
      obj2.free();
    }

  // Fit link
  } else if (obj1.isName("Fit")) {
    if (a->getLength() < 2) {
      error(-1, "Annotation destination array is too short");
      goto err2;
    }
    kind = destFit;

  // FitH link
  } else if (obj1.isName("FitH")) {
    if (a->getLength() < 3) {
      error(-1, "Annotation destination array is too short");
      goto err2;
    }
    kind = destFitH;
    if (!a->get(2, &obj2)->isNum()) {
      error(-1, "Bad annotation destination position");
      kind = destFit;
    }
    top = obj2.getNum();
    obj2.free();

  // FitV link
  } else if (obj1.isName("FitV")) {
    if (a->getLength() < 3) {
      error(-1, "Annotation destination array is too short");
      goto err2;
    }
    kind = destFitV;
    if (!a->get(2, &obj2)->isNum()) {
      error(-1, "Bad annotation destination position");
      kind = destFit;
    }
    left = obj2.getNum();
    obj2.free();

  // FitR link
  } else if (obj1.isName("FitR")) {
    if (a->getLength() < 6) {
      error(-1, "Annotation destination array is too short");
      goto err2;
    }
    kind = destFitR;
    if (!a->get(2, &obj2)->isNum()) {
      error(-1, "Bad annotation destination position");
      kind = destFit;
    }
    left = obj2.getNum();
    obj2.free();
    if (!a->get(3, &obj2)->isNum()) {
      error(-1, "Bad annotation destination position");
      kind = destFit;
    }
    bottom = obj2.getNum();
    obj2.free();
    if (!a->get(4, &obj2)->isNum()) {
      error(-1, "Bad annotation destination position");
      kind = destFit;
    }
    right = obj2.getNum();
    obj2.free();
    if (!a->get(5, &obj2)->isNum()) {
      error(-1, "Bad annotation destination position");
      kind = destFit;
    }
    top = obj2.getNum();
    obj2.free();

  // FitB link
  } else if (obj1.isName("FitB")) {
    if (a->getLength() < 2) {
      error(-1, "Annotation destination array is too short");
      goto err2;
    }
    kind = destFitB;

  // FitBH link
  } else if (obj1.isName("FitBH")) {
    if (a->getLength() < 3) {
      error(-1, "Annotation destination array is too short");
      goto err2;
    }
    kind = destFitBH;
    if (!a->get(2, &obj2)->isNum()) {
      error(-1, "Bad annotation destination position");
      kind = destFit;
    }
    top = obj2.getNum();
    obj2.free();

  // FitBV link
  } else if (obj1.isName("FitBV")) {
    if (a->getLength() < 3) {
      error(-1, "Annotation destination array is too short");
      goto err2;
    }
    kind = destFitBV;
    if (!a->get(2, &obj2)->isNum()) {
      error(-1, "Bad annotation destination position");
      kind = destFit;
    }
    left = obj2.getNum();
    obj2.free();

  // unknown link kind
  } else {
    error(-1, "Unknown annotation destination type");
    goto err2;
  }

  obj1.free();
  ok = gTrue;
  return;

 err1:
  obj2.free();
 err2:
  obj1.free();
}

LinkDest::LinkDest(LinkDest *dest) {
  kind = dest->kind;
  pageIsRef = dest->pageIsRef;
  if (pageIsRef)
    pageRef = dest->pageRef;
  else
    pageNum = dest->pageNum;
  left = dest->left;
  bottom = dest->bottom;
  right = dest->right;
  top = dest->top;
  zoom = dest->zoom;
  changeLeft = dest->changeLeft;
  changeTop = dest->changeTop;
  changeZoom = dest->changeZoom;
  ok = gTrue;
}

//------------------------------------------------------------------------
// LinkGoTo
//------------------------------------------------------------------------

LinkGoTo::LinkGoTo(Object *destObj) {
  dest = NULL;
  namedDest = NULL;

  // named destination
  if (destObj->isName()) {
    namedDest = new GString(destObj->getName());
  } else if (destObj->isString()) {
    namedDest = destObj->getString()->copy();

  // destination dictionary
  } else if (destObj->isArray()) {
    dest = new LinkDest(destObj->getArray());
    if (!dest->isOk()) {
      delete dest;
      dest = NULL;
    }

  // error
  } else {
    error(-1, "Illegal annotation destination");
  }
}

LinkGoTo::~LinkGoTo() {
  if (dest)
    delete dest;
  if (namedDest)
    delete namedDest;
}

//------------------------------------------------------------------------
// LinkGoToR
//------------------------------------------------------------------------

LinkGoToR::LinkGoToR(Object *fileSpecObj, Object *destObj) {
  dest = NULL;
  namedDest = NULL;

  // get file name
  fileName = getFileSpecName(fileSpecObj);

  // named destination
  if (destObj->isName()) {
    namedDest = new GString(destObj->getName());
  } else if (destObj->isString()) {
    namedDest = destObj->getString()->copy();

  // destination dictionary
  } else if (destObj->isArray()) {
    dest = new LinkDest(destObj->getArray());
    if (!dest->isOk()) {
      delete dest;
      dest = NULL;
    }

  // error
  } else {
    error(-1, "Illegal annotation destination");
  }
}

LinkGoToR::~LinkGoToR() {
  if (fileName)
    delete fileName;
  if (dest)
    delete dest;
  if (namedDest)
    delete namedDest;
}


//------------------------------------------------------------------------
// LinkLaunch
//------------------------------------------------------------------------

LinkLaunch::LinkLaunch(Object *actionObj) {
  Object obj1, obj2;

  fileName = NULL;
  params = NULL;

  if (actionObj->isDict()) {
    if (!actionObj->dictLookup("F", &obj1)->isNull()) {
      fileName = getFileSpecName(&obj1);
    } else {
      obj1.free();
#ifdef WIN32
      if (actionObj->dictLookup("Win", &obj1)->isDict()) {
	obj1.dictLookup("F", &obj2);
	fileName = getFileSpecName(&obj2);
	obj2.free();
	if (obj1.dictLookup("P", &obj2)->isString()) {
	  params = obj2.getString()->copy();
	}
	obj2.free();
      } else {
	error(-1, "Bad launch-type link action");
      }
#else
      //~ This hasn't been defined by Adobe yet, so assume it looks
      //~ just like the Win dictionary until they say otherwise.
      if (actionObj->dictLookup("Unix", &obj1)->isDict()) {
	obj1.dictLookup("F", &obj2);
	fileName = getFileSpecName(&obj2);
	obj2.free();
	if (obj1.dictLookup("P", &obj2)->isString()) {
	  params = obj2.getString()->copy();
	}
	obj2.free();
      } else {
	error(-1, "Bad launch-type link action");
      }
#endif
    }
    obj1.free();
  }
}

LinkLaunch::~LinkLaunch() {
  if (fileName)
    delete fileName;
  if (params)
    delete params;
}

//------------------------------------------------------------------------
// LinkURI
//------------------------------------------------------------------------

LinkURI::LinkURI(Object *uriObj, GString *baseURI) {
  GString *uri2;
  int n;
  char c;

  uri = NULL;
  if (uriObj->isString()) {
    uri2 = uriObj->getString()->copy();
    if (baseURI && baseURI->getLength() > 0) {
      n = strcspn(uri2->getCString(), "/:");
      if (n == uri2->getLength() || uri2->getChar(n) == '/') {
	uri = baseURI->copy();
	c = uri->getChar(uri->getLength() - 1);
	if (c == '/' || c == '?') {
	  if (uri2->getChar(0) == '/') {
	    uri2->del(0);
	  }
	} else {
	  if (uri2->getChar(0) != '/') {
	    uri->append('/');
	  }
	}
	uri->append(uri2);
	delete uri2;
      } else {
	uri = uri2;
      }
    } else {
      uri = uri2;
    }
  } else {
    error(-1, "Illegal URI-type link");
  }
}

LinkURI::~LinkURI() {
  if (uri)
    delete uri;
}

//------------------------------------------------------------------------
// LinkNamed
//------------------------------------------------------------------------

LinkNamed::LinkNamed(Object *nameObj) {
  name = NULL;
  if (nameObj->isName()) {
    name = new GString(nameObj->getName());
  }
}

LinkNamed::~LinkNamed() {
  if (name) {
    delete name;
  }
}

//------------------------------------------------------------------------
// LinkMovie
//------------------------------------------------------------------------

LinkMovie::LinkMovie(Object *annotObj, Object *titleObj) {
  annotRef.num = -1;
  title = NULL;
  if (annotObj->isRef()) {
    annotRef = annotObj->getRef();
  } else if (titleObj->isString()) {
    title = titleObj->getString()->copy();
  } else {
    error(-1, "Movie action is missing both the Annot and T keys");
  }
}

LinkMovie::~LinkMovie() {
  if (title) {
    delete title;
  }
}

//------------------------------------------------------------------------
// LinkUnknown
//------------------------------------------------------------------------

LinkUnknown::LinkUnknown(char *actionA) {
  action = new GString(actionA);
}

LinkUnknown::~LinkUnknown() {
  delete action;
}

//------------------------------------------------------------------------
// LinkBorderStyle
//------------------------------------------------------------------------

LinkBorderStyle::LinkBorderStyle(LinkBorderType typeA, double widthA,
				 double *dashA, int dashLengthA,
				 double rA, double gA, double bA) {
  type = typeA;
  width = widthA;
  dash = dashA;
  dashLength = dashLengthA;
  r = rA;
  g = gA;
  b = bA;
}

LinkBorderStyle::~LinkBorderStyle() {
  if (dash) {
    gfree(dash);
  }
}

//------------------------------------------------------------------------
// Link
//------------------------------------------------------------------------

Link::Link(Dict *dict, GString *baseURI) {
  Object obj1, obj2;
  double t;

  action = NULL;
  ok = gFalse;

  // get rectangle
  if (!dict->lookup("Rect", &obj1)->isArray()) {
    error(-1, "Annotation rectangle is wrong type");
    goto err2;
  }
  if (!obj1.arrayGet(0, &obj2)->isNum()) {
    error(-1, "Bad annotation rectangle");
    goto err1;
  }
  x1 = obj2.getNum();
  obj2.free();
  if (!obj1.arrayGet(1, &obj2)->isNum()) {
    error(-1, "Bad annotation rectangle");
    goto err1;
  }
  y1 = obj2.getNum();
  obj2.free();
  if (!obj1.arrayGet(2, &obj2)->isNum()) {
    error(-1, "Bad annotation rectangle");
    goto err1;
  }
  x2 = obj2.getNum();
  obj2.free();
  if (!obj1.arrayGet(3, &obj2)->isNum()) {
    error(-1, "Bad annotation rectangle");
    goto err1;
  }
  y2 = obj2.getNum();
  obj2.free();
  obj1.free();
  if (x1 > x2) {
    t = x1;
    x1 = x2;
    x2 = t;
  }
  if (y1 > y2) {
    t = y1;
    y1 = y2;
    y2 = t;
  }

  // look for destination
  if (!dict->lookup("Dest", &obj1)->isNull()) {
    action = LinkAction::parseDest(&obj1);

  // look for action
  } else {
    obj1.free();
    if (dict->lookup("A", &obj1)->isDict()) {
      action = LinkAction::parseAction(&obj1, baseURI);
    }
  }
  obj1.free();

  // check for bad action
  if (action) {
    ok = gTrue;
  }

  return;

 err1:
  obj2.free();
 err2:
  obj1.free();
}

Link::~Link() {
  if (action) {
    delete action;
  }
}

//------------------------------------------------------------------------
// Links
//------------------------------------------------------------------------

Links::Links(Object *annots, GString *baseURI) {
  Link *link;
  Object obj1, obj2;
  int size;
  int i;

  links = NULL;
  size = 0;
  numLinks = 0;

  if (annots->isArray()) {
    for (i = 0; i < annots->arrayGetLength(); ++i) {
      if (annots->arrayGet(i, &obj1)->isDict()) {
	if (obj1.dictLookup("Subtype", &obj2)->isName("Link")) {
	  link = new Link(obj1.getDict(), baseURI);
	  if (link->isOk()) {
	    if (numLinks >= size) {
	      size += 16;
	      links = (Link **)greallocn(links, size, sizeof(Link *));
	    }
	    links[numLinks++] = link;
	  } else {
	    delete link;
	  }
	}
	obj2.free();
      }
      obj1.free();
    }
  }
}

Links::~Links() {
  int i;

  for (i = 0; i < numLinks; ++i)
    delete links[i];
  gfree(links);
}

LinkAction *Links::find(double x, double y) {
  int i;

  for (i = numLinks - 1; i >= 0; --i) {
    if (links[i]->inRect(x, y)) {
      return links[i]->getAction();
    }
  }
  return NULL;
}

GBool Links::onLink(double x, double y) {
  int i;

  for (i = 0; i < numLinks; ++i) {
    if (links[i]->inRect(x, y))
      return gTrue;
  }
  return gFalse;
}
//========================================================================
//
// Link.cc
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stddef.h>
#include <string.h>
#include "gmem.h"
#include "GString.h"
#include "Error.h"
#include "Object.h"
#include "Array.h"
#include "Dict.h"
#include "Link.h"

//------------------------------------------------------------------------
// LinkAction
//------------------------------------------------------------------------

LinkAction *LinkAction::parseDest(Object *obj) {
  LinkAction *action;

  action = new LinkGoTo(obj);
  if (!action->isOk()) {
    delete action;
    return NULL;
  }
  return action;
}

LinkAction *LinkAction::parseAction(Object *obj, GString *baseURI) {
  LinkAction *action;
  Object obj2, obj3, obj4;

  if (!obj->isDict()) {
    error(-1, "Bad annotation action");
    return NULL;
  }

  obj->dictLookup("S", &obj2);

  // GoTo action
  if (obj2.isName("GoTo")) {
    obj->dictLookup("D", &obj3);
    action = new LinkGoTo(&obj3);
    obj3.free();

  // GoToR action
  } else if (obj2.isName("GoToR")) {
    obj->dictLookup("F", &obj3);
    obj->dictLookup("D", &obj4);
    action = new LinkGoToR(&obj3, &obj4);
    obj3.free();
    obj4.free();

  // Launch action
  } else if (obj2.isName("Launch")) {
    action = new LinkLaunch(obj);

  // URI action
  } else if (obj2.isName("URI")) {
    obj->dictLookup("URI", &obj3);
    action = new LinkURI(&obj3, baseURI);
    obj3.free();

  // Named action
  } else if (obj2.isName("Named")) {
    obj->dictLookup("N", &obj3);
    action = new LinkNamed(&obj3);
    obj3.free();

  // Movie action
  } else if (obj2.isName("Movie")) {
    obj->dictLookupNF("Annot", &obj3);
    obj->dictLookup("T", &obj4);
    action = new LinkMovie(&obj3, &obj4);
    obj3.free();
    obj4.free();

  // unknown action
  } else if (obj2.isName()) {
    action = new LinkUnknown(obj2.getName());

  // action is missing or wrong type
  } else {
    error(-1, "Bad annotation action");
    action = NULL;
  }

  obj2.free();

  if (action && !action->isOk()) {
    delete action;
    return NULL;
  }
  return action;
}

GString *LinkAction::getFileSpecName(Object *fileSpecObj) {
  GString *name;
  Object obj1;

  name = NULL;

  // string
  if (fileSpecObj->isString()) {
    name = fileSpecObj->getString()->copy();

  // dictionary
  } else if (fileSpecObj->isDict()) {
#ifdef WIN32
    if (!fileSpecObj->dictLookup("DOS", &obj1)->isString()) {
#else
    if (!fileSpecObj->dictLookup("Unix", &obj1)->isString()) {
#endif
      obj1.free();
      fileSpecObj->dictLookup("F", &obj1);
    }
    if (obj1.isString()) {
      name = obj1.getString()->copy();
    } else {
      error(-1, "Illegal file spec in link");
    }
    obj1.free();

  // error
  } else {
    error(-1, "Illegal file spec in link");
  }

  // system-dependent path manipulation
  if (name) {
#ifdef WIN32
    int i, j;

    // "//...."             --> "\...."
    // "/x/...."            --> "x:\...."
    // "/server/share/...." --> "\\server\share\...."
    // convert escaped slashes to slashes and unescaped slashes to backslashes
    i = 0;
    if (name->getChar(0) == '/') {
      if (name->getLength() >= 2 && name->getChar(1) == '/') {
	name->del(0);
	i = 0;
      } else if (name->getLength() >= 2 &&
		 ((name->getChar(1) >= 'a' && name->getChar(1) <= 'z') ||
		  (name->getChar(1) >= 'A' && name->getChar(1) <= 'Z')) &&
		 (name->getLength() == 2 || name->getChar(2) == '/')) {
	name->setChar(0, name->getChar(1));
	name->setChar(1, ':');
	i = 2;
      } else {
	for (j = 2; j < name->getLength(); ++j) {
	  if (name->getChar(j-1) != '\\' &&
	      name->getChar(j) == '/') {
	    break;
	  }
	}
	if (j < name->getLength()) {
	  name->setChar(0, '\\');
	  name->insert(0, '\\');
	  i = 2;
	}
      }
    }
    for (; i < name->getLength(); ++i) {
      if (name->getChar(i) == '/') {
	name->setChar(i, '\\');
      } else if (name->getChar(i) == '\\' &&
		 i+1 < name->getLength() &&
		 name->getChar(i+1) == '/') {
	name->del(i);
      }
    }
#else
    // no manipulation needed for Unix
#endif
  }

  return name;
}

//------------------------------------------------------------------------
// LinkDest
//------------------------------------------------------------------------

LinkDest::LinkDest(Array *a) {
  Object obj1, obj2;

  // initialize fields
  left = bottom = right = top = zoom = 0;
  ok = gFalse;

  // get page
  if (a->getLength() < 2) {
    error(-1, "Annotation destination array is too short");
    return;
  }
  a->getNF(0, &obj1);
  if (obj1.isInt()) {
    pageNum = obj1.getInt() + 1;
    pageIsRef = gFalse;
  } else if (obj1.isRef()) {
    pageRef.num = obj1.getRefNum();
    pageRef.gen = obj1.getRefGen();
    pageIsRef = gTrue;
  } else {
    error(-1, "Bad annotation destination");
    goto err2;
  }
  obj1.free();

  // get destination type
  a->get(1, &obj1);

  // XYZ link
  if (obj1.isName("XYZ")) {
    kind = destXYZ;
    if (a->getLength() < 3) {
      changeLeft = gFalse;
    } else {
      a->get(2, &obj2);
      if (obj2.isNull()) {
	changeLeft = gFalse;
      } else if (obj2.isNum()) {
	changeLeft = gTrue;
	left = obj2.getNum();
      } else {
	error(-1, "Bad annotation destination position");
	goto err1;
      }
      obj2.free();
    }
    if (a->getLength() < 4) {
      changeTop = gFalse;
    } else {
      a->get(3, &obj2);
      if (obj2.isNull()) {
	changeTop = gFalse;
      } else if (obj2.isNum()) {
	changeTop = gTrue;
	top = obj2.getNum();
      } else {
	error(-1, "Bad annotation destination position");
	goto err1;
      }
      obj2.free();
    }
    if (a->getLength() < 5) {
      changeZoom = gFalse;
    } else {
      a->get(4, &obj2);
      if (obj2.isNull()) {
	changeZoom = gFalse;
      } else if (obj2.isNum()) {
	changeZoom = gTrue;
	zoom = obj2.getNum();
      } else {
	error(-1, "Bad annotation destination position");
	goto err1;
      }
      obj2.free();
    }

  // Fit link
  } else if (obj1.isName("Fit")) {
    if (a->getLength() < 2) {
      error(-1, "Annotation destination array is too short");
      goto err2;
    }
    kind = destFit;

  // FitH link
  } else if (obj1.isName("FitH")) {
    if (a->getLength() < 3) {
      error(-1, "Annotation destination array is too short");
      goto err2;
    }
    kind = destFitH;
    if (!a->get(2, &obj2)->isNum()) {
      error(-1, "Bad annotation destination position");
      kind = destFit;
    }
    top = obj2.getNum();
    obj2.free();

  // FitV link
  } else if (obj1.isName("FitV")) {
    if (a->getLength() < 3) {
      error(-1, "Annotation destination array is too short");
      goto err2;
    }
    kind = destFitV;
    if (!a->get(2, &obj2)->isNum()) {
      error(-1, "Bad annotation destination position");
      kind = destFit;
    }
    left = obj2.getNum();
    obj2.free();

  // FitR link
  } else if (obj1.isName("FitR")) {
    if (a->getLength() < 6) {
      error(-1, "Annotation destination array is too short");
      goto err2;
    }
    kind = destFitR;
    if (!a->get(2, &obj2)->isNum()) {
      error(-1, "Bad annotation destination position");
      kind = destFit;
    }
    left = obj2.getNum();
    obj2.free();
    if (!a->get(3, &obj2)->isNum()) {
      error(-1, "Bad annotation destination position");
      kind = destFit;
    }
    bottom = obj2.getNum();
    obj2.free();
    if (!a->get(4, &obj2)->isNum()) {
      error(-1, "Bad annotation destination position");
      kind = destFit;
    }
    right = obj2.getNum();
    obj2.free();
    if (!a->get(5, &obj2)->isNum()) {
      error(-1, "Bad annotation destination position");
      kind = destFit;
    }
    top = obj2.getNum();
    obj2.free();

  // FitB link
  } else if (obj1.isName("FitB")) {
    if (a->getLength() < 2) {
      error(-1, "Annotation destination array is too short");
      goto err2;
    }
    kind = destFitB;

  // FitBH link
  } else if (obj1.isName("FitBH")) {
    if (a->getLength() < 3) {
      error(-1, "Annotation destination array is too short");
      goto err2;
    }
    kind = destFitBH;
    if (!a->get(2, &obj2)->isNum()) {
      error(-1, "Bad annotation destination position");
      kind = destFit;
    }
    top = obj2.getNum();
    obj2.free();

  // FitBV link
  } else if (obj1.isName("FitBV")) {
    if (a->getLength() < 3) {
      error(-1, "Annotation destination array is too short");
      goto err2;
    }
    kind = destFitBV;
    if (!a->get(2, &obj2)->isNum()) {
      error(-1, "Bad annotation destination position");
      kind = destFit;
    }
    left = obj2.getNum();
    obj2.free();

  // unknown link kind
  } else {
    error(-1, "Unknown annotation destination type");
    goto err2;
  }

  obj1.free();
  ok = gTrue;
  return;

 err1:
  obj2.free();
 err2:
  obj1.free();
}

LinkDest::LinkDest(LinkDest *dest) {
  kind = dest->kind;
  pageIsRef = dest->pageIsRef;
  if (pageIsRef)
    pageRef = dest->pageRef;
  else
    pageNum = dest->pageNum;
  left = dest->left;
  bottom = dest->bottom;
  right = dest->right;
  top = dest->top;
  zoom = dest->zoom;
  changeLeft = dest->changeLeft;
  changeTop = dest->changeTop;
  changeZoom = dest->changeZoom;
  ok = gTrue;
}

//------------------------------------------------------------------------
// LinkGoTo
//------------------------------------------------------------------------

LinkGoTo::LinkGoTo(Object *destObj) {
  dest = NULL;
  namedDest = NULL;

  // named destination
  if (destObj->isName()) {
    namedDest = new GString(destObj->getName());
  } else if (destObj->isString()) {
    namedDest = destObj->getString()->copy();

  // destination dictionary
  } else if (destObj->isArray()) {
    dest = new LinkDest(destObj->getArray());
    if (!dest->isOk()) {
      delete dest;
      dest = NULL;
    }

  // error
  } else {
    error(-1, "Illegal annotation destination");
  }
}

LinkGoTo::~LinkGoTo() {
  if (dest)
    delete dest;
  if (namedDest)
    delete namedDest;
}

//------------------------------------------------------------------------
// LinkGoToR
//------------------------------------------------------------------------

LinkGoToR::LinkGoToR(Object *fileSpecObj, Object *destObj) {
  dest = NULL;
  namedDest = NULL;

  // get file name
  fileName = getFileSpecName(fileSpecObj);

  // named destination
  if (destObj->isName()) {
    namedDest = new GString(destObj->getName());
  } else if (destObj->isString()) {
    namedDest = destObj->getString()->copy();

  // destination dictionary
  } else if (destObj->isArray()) {
    dest = new LinkDest(destObj->getArray());
    if (!dest->isOk()) {
      delete dest;
      dest = NULL;
    }

  // error
  } else {
    error(-1, "Illegal annotation destination");
  }
}

LinkGoToR::~LinkGoToR() {
  if (fileName)
    delete fileName;
  if (dest)
    delete dest;
  if (namedDest)
    delete namedDest;
}


//------------------------------------------------------------------------
// LinkLaunch
//------------------------------------------------------------------------

LinkLaunch::LinkLaunch(Object *actionObj) {
  Object obj1, obj2;

  fileName = NULL;
  params = NULL;

  if (actionObj->isDict()) {
    if (!actionObj->dictLookup("F", &obj1)->isNull()) {
      fileName = getFileSpecName(&obj1);
    } else {
      obj1.free();
#ifdef WIN32
      if (actionObj->dictLookup("Win", &obj1)->isDict()) {
	obj1.dictLookup("F", &obj2);
	fileName = getFileSpecName(&obj2);
	obj2.free();
	if (obj1.dictLookup("P", &obj2)->isString()) {
	  params = obj2.getString()->copy();
	}
	obj2.free();
      } else {
	error(-1, "Bad launch-type link action");
      }
#else
      //~ This hasn't been defined by Adobe yet, so assume it looks
      //~ just like the Win dictionary until they say otherwise.
      if (actionObj->dictLookup("Unix", &obj1)->isDict()) {
	obj1.dictLookup("F", &obj2);
	fileName = getFileSpecName(&obj2);
	obj2.free();
	if (obj1.dictLookup("P", &obj2)->isString()) {
	  params = obj2.getString()->copy();
	}
	obj2.free();
      } else {
	error(-1, "Bad launch-type link action");
      }
#endif
    }
    obj1.free();
  }
}

LinkLaunch::~LinkLaunch() {
  if (fileName)
    delete fileName;
  if (params)
    delete params;
}

//------------------------------------------------------------------------
// LinkURI
//------------------------------------------------------------------------

LinkURI::LinkURI(Object *uriObj, GString *baseURI) {
  GString *uri2;
  int n;
  char c;

  uri = NULL;
  if (uriObj->isString()) {
    uri2 = uriObj->getString()->copy();
    if (baseURI && baseURI->getLength() > 0) {
      n = strcspn(uri2->getCString(), "/:");
      if (n == uri2->getLength() || uri2->getChar(n) == '/') {
	uri = baseURI->copy();
	c = uri->getChar(uri->getLength() - 1);
	if (c == '/' || c == '?') {
	  if (uri2->getChar(0) == '/') {
	    uri2->del(0);
	  }
	} else {
	  if (uri2->getChar(0) != '/') {
	    uri->append('/');
	  }
	}
	uri->append(uri2);
	delete uri2;
      } else {
	uri = uri2;
      }
    } else {
      uri = uri2;
    }
  } else {
    error(-1, "Illegal URI-type link");
  }
}

LinkURI::~LinkURI() {
  if (uri)
    delete uri;
}

//------------------------------------------------------------------------
// LinkNamed
//------------------------------------------------------------------------

LinkNamed::LinkNamed(Object *nameObj) {
  name = NULL;
  if (nameObj->isName()) {
    name = new GString(nameObj->getName());
  }
}

LinkNamed::~LinkNamed() {
  if (name) {
    delete name;
  }
}

//------------------------------------------------------------------------
// LinkMovie
//------------------------------------------------------------------------

LinkMovie::LinkMovie(Object *annotObj, Object *titleObj) {
  annotRef.num = -1;
  title = NULL;
  if (annotObj->isRef()) {
    annotRef = annotObj->getRef();
  } else if (titleObj->isString()) {
    title = titleObj->getString()->copy();
  } else {
    error(-1, "Movie action is missing both the Annot and T keys");
  }
}

LinkMovie::~LinkMovie() {
  if (title) {
    delete title;
  }
}

//------------------------------------------------------------------------
// LinkUnknown
//------------------------------------------------------------------------

LinkUnknown::LinkUnknown(char *actionA) {
  action = new GString(actionA);
}

LinkUnknown::~LinkUnknown() {
  delete action;
}

//------------------------------------------------------------------------
// Link
//------------------------------------------------------------------------

Link::Link(Dict *dict, GString *baseURI) {
  Object obj1, obj2;
  double t;

  action = NULL;
  ok = gFalse;

  // get rectangle
  if (!dict->lookup("Rect", &obj1)->isArray()) {
    error(-1, "Annotation rectangle is wrong type");
    goto err2;
  }
  if (!obj1.arrayGet(0, &obj2)->isNum()) {
    error(-1, "Bad annotation rectangle");
    goto err1;
  }
  x1 = obj2.getNum();
  obj2.free();
  if (!obj1.arrayGet(1, &obj2)->isNum()) {
    error(-1, "Bad annotation rectangle");
    goto err1;
  }
  y1 = obj2.getNum();
  obj2.free();
  if (!obj1.arrayGet(2, &obj2)->isNum()) {
    error(-1, "Bad annotation rectangle");
    goto err1;
  }
  x2 = obj2.getNum();
  obj2.free();
  if (!obj1.arrayGet(3, &obj2)->isNum()) {
    error(-1, "Bad annotation rectangle");
    goto err1;
  }
  y2 = obj2.getNum();
  obj2.free();
  obj1.free();
  if (x1 > x2) {
    t = x1;
    x1 = x2;
    x2 = t;
  }
  if (y1 > y2) {
    t = y1;
    y1 = y2;
    y2 = t;
  }

  // look for destination
  if (!dict->lookup("Dest", &obj1)->isNull()) {
    action = LinkAction::parseDest(&obj1);

  // look for action
  } else {
    obj1.free();
    if (dict->lookup("A", &obj1)->isDict()) {
      action = LinkAction::parseAction(&obj1, baseURI);
    }
  }
  obj1.free();

  // check for bad action
  if (action) {
    ok = gTrue;
  }

  return;

 err1:
  obj2.free();
 err2:
  obj1.free();
}

Link::~Link() {
  if (action) {
    delete action;
  }
}

//------------------------------------------------------------------------
// Links
//------------------------------------------------------------------------

Links::Links(Object *annots, GString *baseURI) {
  Link *link;
  Object obj1, obj2;
  int size;
  int i;

  links = NULL;
  size = 0;
  numLinks = 0;

  if (annots->isArray()) {
    for (i = 0; i < annots->arrayGetLength(); ++i) {
      if (annots->arrayGet(i, &obj1)->isDict()) {
	if (obj1.dictLookup("Subtype", &obj2)->isName("Link")) {
	  link = new Link(obj1.getDict(), baseURI);
	  if (link->isOk()) {
	    if (numLinks >= size) {
	      size += 16;
	      links = (Link **)greallocn(links, size, sizeof(Link *));
	    }
	    links[numLinks++] = link;
	  } else {
	    delete link;
	  }
	}
	obj2.free();
      }
      obj1.free();
    }
  }
}

Links::~Links() {
  int i;

  for (i = 0; i < numLinks; ++i)
    delete links[i];
  gfree(links);
}

LinkAction *Links::find(double x, double y) {
  int i;

  for (i = numLinks - 1; i >= 0; --i) {
    if (links[i]->inRect(x, y)) {
      return links[i]->getAction();
    }
  }
  return NULL;
}

GBool Links::onLink(double x, double y) {
  int i;

  for (i = 0; i < numLinks; ++i) {
    if (links[i]->inRect(x, y))
      return gTrue;
  }
  return gFalse;
}
//========================================================================
//
// Link.h
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef LINK_H
#define LINK_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "Object.h"

class GString;
class Array;
class Dict;

//------------------------------------------------------------------------
// LinkAction
//------------------------------------------------------------------------

enum LinkActionKind {
  actionGoTo,			// go to destination
  actionGoToR,			// go to destination in new file
  actionLaunch,			// launch app (or open document)
  actionURI,			// URI
  actionNamed,			// named action
  actionMovie,			// movie action
  actionUnknown			// anything else
};

class LinkAction {
public:

  // Destructor.
  virtual ~LinkAction() {}

  // Was the LinkAction created successfully?
  virtual GBool isOk() = 0;

  // Check link action type.
  virtual LinkActionKind getKind() = 0;

  // Parse a destination (old-style action) name, string, or array.
  static LinkAction *parseDest(Object *obj);

  // Parse an action dictionary.
  static LinkAction *parseAction(Object *obj, GString *baseURI = NULL);

  // Extract a file name from a file specification (string or
  // dictionary).
  static GString *getFileSpecName(Object *fileSpecObj);
};

//------------------------------------------------------------------------
// LinkDest
//------------------------------------------------------------------------

enum LinkDestKind {
  destXYZ,
  destFit,
  destFitH,
  destFitV,
  destFitR,
  destFitB,
  destFitBH,
  destFitBV
};

class LinkDest {
public:

  // Build a LinkDest from the array.
  LinkDest(Array *a);

  // Copy a LinkDest.
  LinkDest *copy() { return new LinkDest(this); }

  // Was the LinkDest created successfully?
  GBool isOk() { return ok; }

  // Accessors.
  LinkDestKind getKind() { return kind; }
  GBool isPageRef() { return pageIsRef; }
  int getPageNum() { return pageNum; }
  Ref getPageRef() { return pageRef; }
  double getLeft() { return left; }
  double getBottom() { return bottom; }
  double getRight() { return right; }
  double getTop() { return top; }
  double getZoom() { return zoom; }
  GBool getChangeLeft() { return changeLeft; }
  GBool getChangeTop() { return changeTop; }
  GBool getChangeZoom() { return changeZoom; }

private:

  LinkDestKind kind;		// destination type
  GBool pageIsRef;		// is the page a reference or number?
  union {
    Ref pageRef;		// reference to page
    int pageNum;		// one-relative page number
  };
  double left, bottom;		// position
  double right, top;
  double zoom;			// zoom factor
  GBool changeLeft, changeTop;	// for destXYZ links, which position
  GBool changeZoom;		//   components to change
  GBool ok;			// set if created successfully

  LinkDest(LinkDest *dest);
};

//------------------------------------------------------------------------
// LinkGoTo
//------------------------------------------------------------------------

class LinkGoTo: public LinkAction {
public:

  // Build a LinkGoTo from a destination (dictionary, name, or string).
  LinkGoTo(Object *destObj);

  // Destructor.
  virtual ~LinkGoTo();

  // Was the LinkGoTo created successfully?
  virtual GBool isOk() { return dest || namedDest; }

  // Accessors.
  virtual LinkActionKind getKind() { return actionGoTo; }
  LinkDest *getDest() { return dest; }
  GString *getNamedDest() { return namedDest; }

private:

  LinkDest *dest;		// regular destination (NULL for remote
				//   link with bad destination)
  GString *namedDest;		// named destination (only one of dest and
				//   and namedDest may be non-NULL)
};

//------------------------------------------------------------------------
// LinkGoToR
//------------------------------------------------------------------------

class LinkGoToR: public LinkAction {
public:

  // Build a LinkGoToR from a file spec (dictionary) and destination
  // (dictionary, name, or string).
  LinkGoToR(Object *fileSpecObj, Object *destObj);

  // Destructor.
  virtual ~LinkGoToR();

  // Was the LinkGoToR created successfully?
  virtual GBool isOk() { return fileName && (dest || namedDest); }

  // Accessors.
  virtual LinkActionKind getKind() { return actionGoToR; }
  GString *getFileName() { return fileName; }
  LinkDest *getDest() { return dest; }
  GString *getNamedDest() { return namedDest; }

private:

  GString *fileName;		// file name
  LinkDest *dest;		// regular destination (NULL for remote
				//   link with bad destination)
  GString *namedDest;		// named destination (only one of dest and
				//   and namedDest may be non-NULL)
};

//------------------------------------------------------------------------
// LinkLaunch
//------------------------------------------------------------------------

class LinkLaunch: public LinkAction {
public:

  // Build a LinkLaunch from an action dictionary.
  LinkLaunch(Object *actionObj);

  // Destructor.
  virtual ~LinkLaunch();

  // Was the LinkLaunch created successfully?
  virtual GBool isOk() { return fileName != NULL; }

  // Accessors.
  virtual LinkActionKind getKind() { return actionLaunch; }
  GString *getFileName() { return fileName; }
  GString *getParams() { return params; }

private:

  GString *fileName;		// file name
  GString *params;		// parameters
};

//------------------------------------------------------------------------
// LinkURI
//------------------------------------------------------------------------

class LinkURI: public LinkAction {
public:

  // Build a LinkURI given the URI (string) and base URI.
  LinkURI(Object *uriObj, GString *baseURI);

  // Destructor.
  virtual ~LinkURI();

  // Was the LinkURI created successfully?
  virtual GBool isOk() { return uri != NULL; }

  // Accessors.
  virtual LinkActionKind getKind() { return actionURI; }
  GString *getURI() { return uri; }

private:

  GString *uri;			// the URI
};

//------------------------------------------------------------------------
// LinkNamed
//------------------------------------------------------------------------

class LinkNamed: public LinkAction {
public:

  // Build a LinkNamed given the action name.
  LinkNamed(Object *nameObj);

  virtual ~LinkNamed();

  virtual GBool isOk() { return name != NULL; }

  virtual LinkActionKind getKind() { return actionNamed; }
  GString *getName() { return name; }

private:

  GString *name;
};

//------------------------------------------------------------------------
// LinkMovie
//------------------------------------------------------------------------

class LinkMovie: public LinkAction {
public:

  LinkMovie(Object *annotObj, Object *titleObj);

  virtual ~LinkMovie();

  virtual GBool isOk() { return annotRef.num >= 0 || title != NULL; }

  virtual LinkActionKind getKind() { return actionMovie; }
  GBool hasAnnotRef() { return annotRef.num >= 0; }
  Ref *getAnnotRef() { return &annotRef; }
  GString *getTitle() { return title; }

private:

  Ref annotRef;
  GString *title;
};

//------------------------------------------------------------------------
// LinkUnknown
//------------------------------------------------------------------------

class LinkUnknown: public LinkAction {
public:

  // Build a LinkUnknown with the specified action type.
  LinkUnknown(char *actionA);

  // Destructor.
  virtual ~LinkUnknown();

  // Was the LinkUnknown create successfully?
  virtual GBool isOk() { return action != NULL; }

  // Accessors.
  virtual LinkActionKind getKind() { return actionUnknown; }
  GString *getAction() { return action; }

private:

  GString *action;		// action subtype
};

//------------------------------------------------------------------------
// LinkBorderStyle
//------------------------------------------------------------------------

enum LinkBorderType {
  linkBorderSolid,
  linkBorderDashed,
  linkBorderEmbossed,
  linkBorderEngraved,
  linkBorderUnderlined
};

class LinkBorderStyle {
public:

  LinkBorderStyle(LinkBorderType typeA, double widthA,
		  double *dashA, int dashLengthA,
		  double rA, double gA, double bA);
  ~LinkBorderStyle();

  LinkBorderType getType() { return type; }
  double getWidth() { return width; }
  void getDash(double **dashA, int *dashLengthA)
    { *dashA = dash; *dashLengthA = dashLength; }
  void getColor(double *rA, double *gA, double *bA)
    { *rA = r; *gA = g; *bA = b; }

private:

  LinkBorderType type;
  double width;
  double *dash;
  int dashLength;
  double r, g, b;
};

//------------------------------------------------------------------------
// Link
//------------------------------------------------------------------------

class Link {
public:

  // Construct a link, given its dictionary.
  Link(Dict *dict, GString *baseURI);

  // Destructor.
  ~Link();

  // Was the link created successfully?
  GBool isOk() { return ok; }

  // Check if point is inside the link rectangle.
  GBool inRect(double x, double y)
    { return x1 <= x && x <= x2 && y1 <= y && y <= y2; }

  // Get action.
  LinkAction *getAction() { return action; }

  // Get the link rectangle.
  void getRect(double *xa1, double *ya1, double *xa2, double *ya2)
    { *xa1 = x1; *ya1 = y1; *xa2 = x2; *ya2 = y2; }

  // Get the border style info.
  LinkBorderStyle *getBorderStyle() { return borderStyle; }

   // Get border corners and width.
  void getBorder(double *xa1, double *ya1, double *xa2, double *ya2,
		 double *wa)
    { *xa1 = x1; *ya1 = y1; *xa2 = x2; *ya2 = y2; *wa = borderW; }

private:

  double x1, y1;		// lower left corner
  double x2, y2;		// upper right corner
  double borderW;		// border width
  LinkBorderStyle *borderStyle;	// border style
  LinkAction *action;		// action
  GBool ok;			// is link valid?
};

//------------------------------------------------------------------------
// Links
//------------------------------------------------------------------------

class Links {
public:

  // Extract links from array of annotations.
  Links(Object *annots, GString *baseURI);

  // Destructor.
  ~Links();

  // Iterate through list of links.
  int getNumLinks() { return numLinks; }
  Link *getLink(int i) { return links[i]; }

  // If point <x>,<y> is in a link, return the associated action;
  // else return NULL.
  LinkAction *find(double x, double y);

  // Return true if <x>,<y> is in a link.
  GBool onLink(double x, double y);

private:

  Link **links;
  int numLinks;
};

#endif
//========================================================================
//
// Link.h
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef LINK_H
#define LINK_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "Object.h"

class GString;
class Array;
class Dict;

//------------------------------------------------------------------------
// LinkAction
//------------------------------------------------------------------------

enum LinkActionKind {
  actionGoTo,			// go to destination
  actionGoToR,			// go to destination in new file
  actionLaunch,			// launch app (or open document)
  actionURI,			// URI
  actionNamed,			// named action
  actionMovie,			// movie action
  actionUnknown			// anything else
};

class LinkAction {
public:

  // Destructor.
  virtual ~LinkAction() {}

  // Was the LinkAction created successfully?
  virtual GBool isOk() = 0;

  // Check link action type.
  virtual LinkActionKind getKind() = 0;

  // Parse a destination (old-style action) name, string, or array.
  static LinkAction *parseDest(Object *obj);

  // Parse an action dictionary.
  static LinkAction *parseAction(Object *obj, GString *baseURI = NULL);

  // Extract a file name from a file specification (string or
  // dictionary).
  static GString *getFileSpecName(Object *fileSpecObj);
};

//------------------------------------------------------------------------
// LinkDest
//------------------------------------------------------------------------

enum LinkDestKind {
  destXYZ,
  destFit,
  destFitH,
  destFitV,
  destFitR,
  destFitB,
  destFitBH,
  destFitBV
};

class LinkDest {
public:

  // Build a LinkDest from the array.
  LinkDest(Array *a);

  // Copy a LinkDest.
  LinkDest *copy() { return new LinkDest(this); }

  // Was the LinkDest created successfully?
  GBool isOk() { return ok; }

  // Accessors.
  LinkDestKind getKind() { return kind; }
  GBool isPageRef() { return pageIsRef; }
  int getPageNum() { return pageNum; }
  Ref getPageRef() { return pageRef; }
  double getLeft() { return left; }
  double getBottom() { return bottom; }
  double getRight() { return right; }
  double getTop() { return top; }
  double getZoom() { return zoom; }
  GBool getChangeLeft() { return changeLeft; }
  GBool getChangeTop() { return changeTop; }
  GBool getChangeZoom() { return changeZoom; }

private:

  LinkDestKind kind;		// destination type
  GBool pageIsRef;		// is the page a reference or number?
  union {
    Ref pageRef;		// reference to page
    int pageNum;		// one-relative page number
  };
  double left, bottom;		// position
  double right, top;
  double zoom;			// zoom factor
  GBool changeLeft, changeTop;	// for destXYZ links, which position
  GBool changeZoom;		//   components to change
  GBool ok;			// set if created successfully

  LinkDest(LinkDest *dest);
};

//------------------------------------------------------------------------
// LinkGoTo
//------------------------------------------------------------------------

class LinkGoTo: public LinkAction {
public:

  // Build a LinkGoTo from a destination (dictionary, name, or string).
  LinkGoTo(Object *destObj);

  // Destructor.
  virtual ~LinkGoTo();

  // Was the LinkGoTo created successfully?
  virtual GBool isOk() { return dest || namedDest; }

  // Accessors.
  virtual LinkActionKind getKind() { return actionGoTo; }
  LinkDest *getDest() { return dest; }
  GString *getNamedDest() { return namedDest; }

private:

  LinkDest *dest;		// regular destination (NULL for remote
				//   link with bad destination)
  GString *namedDest;		// named destination (only one of dest and
				//   and namedDest may be non-NULL)
};

//------------------------------------------------------------------------
// LinkGoToR
//------------------------------------------------------------------------

class LinkGoToR: public LinkAction {
public:

  // Build a LinkGoToR from a file spec (dictionary) and destination
  // (dictionary, name, or string).
  LinkGoToR(Object *fileSpecObj, Object *destObj);

  // Destructor.
  virtual ~LinkGoToR();

  // Was the LinkGoToR created successfully?
  virtual GBool isOk() { return fileName && (dest || namedDest); }

  // Accessors.
  virtual LinkActionKind getKind() { return actionGoToR; }
  GString *getFileName() { return fileName; }
  LinkDest *getDest() { return dest; }
  GString *getNamedDest() { return namedDest; }

private:

  GString *fileName;		// file name
  LinkDest *dest;		// regular destination (NULL for remote
				//   link with bad destination)
  GString *namedDest;		// named destination (only one of dest and
				//   and namedDest may be non-NULL)
};

//------------------------------------------------------------------------
// LinkLaunch
//------------------------------------------------------------------------

class LinkLaunch: public LinkAction {
public:

  // Build a LinkLaunch from an action dictionary.
  LinkLaunch(Object *actionObj);

  // Destructor.
  virtual ~LinkLaunch();

  // Was the LinkLaunch created successfully?
  virtual GBool isOk() { return fileName != NULL; }

  // Accessors.
  virtual LinkActionKind getKind() { return actionLaunch; }
  GString *getFileName() { return fileName; }
  GString *getParams() { return params; }

private:

  GString *fileName;		// file name
  GString *params;		// parameters
};

//------------------------------------------------------------------------
// LinkURI
//------------------------------------------------------------------------

class LinkURI: public LinkAction {
public:

  // Build a LinkURI given the URI (string) and base URI.
  LinkURI(Object *uriObj, GString *baseURI);

  // Destructor.
  virtual ~LinkURI();

  // Was the LinkURI created successfully?
  virtual GBool isOk() { return uri != NULL; }

  // Accessors.
  virtual LinkActionKind getKind() { return actionURI; }
  GString *getURI() { return uri; }

private:

  GString *uri;			// the URI
};

//------------------------------------------------------------------------
// LinkNamed
//------------------------------------------------------------------------

class LinkNamed: public LinkAction {
public:

  // Build a LinkNamed given the action name.
  LinkNamed(Object *nameObj);

  virtual ~LinkNamed();

  virtual GBool isOk() { return name != NULL; }

  virtual LinkActionKind getKind() { return actionNamed; }
  GString *getName() { return name; }

private:

  GString *name;
};

//------------------------------------------------------------------------
// LinkMovie
//------------------------------------------------------------------------

class LinkMovie: public LinkAction {
public:

  LinkMovie(Object *annotObj, Object *titleObj);

  virtual ~LinkMovie();

  virtual GBool isOk() { return annotRef.num >= 0 || title != NULL; }

  virtual LinkActionKind getKind() { return actionMovie; }
  GBool hasAnnotRef() { return annotRef.num >= 0; }
  Ref *getAnnotRef() { return &annotRef; }
  GString *getTitle() { return title; }

private:

  Ref annotRef;
  GString *title;
};

//------------------------------------------------------------------------
// LinkUnknown
//------------------------------------------------------------------------

class LinkUnknown: public LinkAction {
public:

  // Build a LinkUnknown with the specified action type.
  LinkUnknown(char *actionA);

  // Destructor.
  virtual ~LinkUnknown();

  // Was the LinkUnknown create successfully?
  virtual GBool isOk() { return action != NULL; }

  // Accessors.
  virtual LinkActionKind getKind() { return actionUnknown; }
  GString *getAction() { return action; }

private:

  GString *action;		// action subtype
};

//------------------------------------------------------------------------
// Link
//------------------------------------------------------------------------

class Link {
public:

  // Construct a link, given its dictionary.
  Link(Dict *dict, GString *baseURI);

  // Destructor.
  ~Link();

  // Was the link created successfully?
  GBool isOk() { return ok; }

  // Check if point is inside the link rectangle.
  GBool inRect(double x, double y)
    { return x1 <= x && x <= x2 && y1 <= y && y <= y2; }

  // Get action.
  LinkAction *getAction() { return action; }

  // Get the link rectangle.
  void getRect(double *xa1, double *ya1, double *xa2, double *ya2)
    { *xa1 = x1; *ya1 = y1; *xa2 = x2; *ya2 = y2; }

private:

  double x1, y1;		// lower left corner
  double x2, y2;		// upper right corner
  LinkAction *action;		// action
  GBool ok;			// is link valid?
};

//------------------------------------------------------------------------
// Links
//------------------------------------------------------------------------

class Links {
public:

  // Extract links from array of annotations.
  Links(Object *annots, GString *baseURI);

  // Destructor.
  ~Links();

  // Iterate through list of links.
  int getNumLinks() { return numLinks; }
  Link *getLink(int i) { return links[i]; }

  // If point <x>,<y> is in a link, return the associated action;
  // else return NULL.
  LinkAction *find(double x, double y);

  // Return true if <x>,<y> is in a link.
  GBool onLink(double x, double y);

private:

  Link **links;
  int numLinks;
};

#endif
#========================================================================
#
# Xpdf Makefile
#
# Copyright 1996-2003 Glyph & Cog, LLC
#
#========================================================================

SHELL = /bin/sh

prefix = @prefix@
srcdir = @srcdir@
VPATH = @srcdir@

GOOSRCDIR = $(srcdir)/../goo
GOOLIBDIR = ../goo
FOFISRCDIR = $(srcdir)/../fofi
FOFILIBDIR = ../fofi
SPLASHSRCDIR = $(srcdir)/../splash
SPLASHLIBDIR = ../splash

CXXFLAGS = -I/usr/local/include @CXXFLAGS@ @DEFS@ -I.. -I$(GOOSRCDIR) -I$(FOFISRCDIR) -I$(SPLASHSRCDIR) -I$(srcdir) 

LDFLAGS = @LDFLAGS@

T1LIBS = 
FTLIBS = 

XLIBS = -lX11

SPLASHLIBS = -L$(SPLASHLIBDIR) -lsplash

OTHERLIBS = \
	-L$(FOFILIBDIR) -lfofi \
	-L$(GOOLIBDIR) -lGoo

CXX = @CXX@

AR = ar rc
RANLIB = ranlib

LIBPREFIX = lib
EXE = 

#------------------------------------------------------------------------

.SUFFIXES: .cc

.cc.o:
	$(CXX) $(CXXFLAGS) -c $<

#------------------------------------------------------------------------

CXX_SRC = \
	$(srcdir)/Annot.cc \
	$(srcdir)/Array.cc \
	$(srcdir)/BuiltinFont.cc \
	$(srcdir)/BuiltinFontTables.cc \
	$(srcdir)/CMap.cc \
	$(srcdir)/Catalog.cc \
	$(srcdir)/CharCodeToUnicode.cc \
	$(srcdir)/CoreOutputDev.cc \
	$(srcdir)/Decrypt.cc \
	$(srcdir)/Dict.cc \
	$(srcdir)/Error.cc \
	$(srcdir)/FontEncodingTables.cc \
	$(srcdir)/Function.cc \
	$(srcdir)/Gfx.cc \
	$(srcdir)/GfxFont.cc \
	$(srcdir)/GfxState.cc \
	$(srcdir)/GlobalParams.cc \
	$(srcdir)/ImageOutputDev.cc \
	$(srcdir)/JArithmeticDecoder.cc \
	$(srcdir)/JBIG2Stream.cc \
	$(srcdir)/JPXStream.cc \
	$(srcdir)/Lexer.cc \
	$(srcdir)/Link.cc \
	$(srcdir)/NameToCharCode.cc \
	$(srcdir)/Object.cc \
	$(srcdir)/Outline.cc \
	$(srcdir)/OutputDev.cc \
	$(srcdir)/PDFCore.cc \
	$(srcdir)/PDFDoc.cc \
	$(srcdir)/PDFDocEncoding.cc \
	$(srcdir)/PSOutputDev.cc \
	$(srcdir)/PSTokenizer.cc \
	$(srcdir)/Page.cc \
	$(srcdir)/Parser.cc \
	$(srcdir)/PreScanOutputDev.cc \
	$(srcdir)/SecurityHandler.cc \
	$(srcdir)/SplashOutputDev.cc \
	$(srcdir)/Stream.cc \
	$(srcdir)/TextOutputDev.cc \
	$(srcdir)/UnicodeMap.cc \
	$(srcdir)/UnicodeTypeTable.cc \
	$(srcdir)/XPDFApp.cc \
	$(srcdir)/XPDFCore.cc \
	$(srcdir)/XPDFTree.cc \
	$(srcdir)/XPDFViewer.cc \
	$(srcdir)/XpdfPluginAPI.cc \
	$(srcdir)/XRef.cc \
	$(srcdir)/pdftops.cc \
	$(srcdir)/pdftotext.cc \
	$(srcdir)/pdfinfo.cc \
	$(srcdir)/pdffonts.cc \
	$(srcdir)/pdftoppm.cc \
	$(srcdir)/pdfimages.cc \
	$(srcdir)/xpdf.cc

#------------------------------------------------------------------------

all: pdftops$(EXE) pdftotext$(EXE) pdfinfo$(EXE) pdffonts$(EXE) pdfimages$(EXE) $(LIBPREFIX)Xpdf.a

all-no-x: pdftops$(EXE) pdftotext$(EXE) pdfinfo$(EXE) pdffonts$(EXE) pdfimages$(EXE) $(LIBPREFIX)Xpdf.a


#------------------------------------------------------------------------

PDFTOPS_OBJS = Annot.o Array.o BuiltinFont.o BuiltinFontTables.o \
	Catalog.o CharCodeToUnicode.o CMap.o Decrypt.o Dict.o Error.o \
	FontEncodingTables.o Function.o Gfx.o GfxFont.o \
	GfxState.o GlobalParams.o JArithmeticDecoder.o JBIG2Stream.o \
	JPXStream.o Lexer.o Link.o NameToCharCode.o Outline.o Object.o \
	OutputDev.o Page.o Parser.o PDFDoc.o PDFDocEncoding.o PreScanOutputDev.o PSOutputDev.o \
	PSTokenizer.o SecurityHandler.o SplashOutputDev.o Stream.o UnicodeMap.o \
	XpdfPluginAPI.o XRef.o pdftops.o
PDFTOPS_LIBS = -L$(GOOLIBDIR) -lGoo $(SPLASHLIBS) $(FTLIBS) -lGoo $(OTHERLIBS) -lm

pdftops$(EXE): $(PDFTOPS_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
	$(CXX) $(CXXFLAGS) $(LDFLAGS) -o pdftops$(EXE) $(PDFTOPS_OBJS) \
		$(PDFTOPS_LIBS)

#------------------------------------------------------------------------

PDFTOTEXT_OBJS = Annot.o Array.o BuiltinFont.o BuiltinFontTables.o \
	Catalog.o CharCodeToUnicode.o CMap.o Decrypt.o Dict.o Error.o \
	FontEncodingTables.o Function.o Gfx.o GfxFont.o \
	GfxState.o GlobalParams.o JArithmeticDecoder.o JBIG2Stream.o \
	JPXStream.o Lexer.o Link.o NameToCharCode.o Object.o Outline.o \
	OutputDev.o Page.o Parser.o PDFDoc.o PDFDocEncoding.o PSTokenizer.o \
	SecurityHandler.o Stream.o TextOutputDev.o UnicodeMap.o \
	UnicodeTypeTable.o XpdfPluginAPI.o XRef.o pdftotext.o
PDFTOTEXT_LIBS = -L$(GOOLIBDIR) -lGoo $(OTHERLIBS) -lm

pdftotext$(EXE): $(PDFTOTEXT_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
	$(CXX) $(CXXFLAGS) $(LDFLAGS) -o pdftotext$(EXE) $(PDFTOTEXT_OBJS) \
		$(PDFTOTEXT_LIBS)

#------------------------------------------------------------------------

PDFINFO_OBJS = Annot.o Array.o BuiltinFont.o BuiltinFontTables.o \
	Catalog.o CharCodeToUnicode.o CMap.o Decrypt.o Dict.o Error.o \
	FontEncodingTables.o Function.o Gfx.o GfxFont.o \
	GfxState.o GlobalParams.o JArithmeticDecoder.o JBIG2Stream.o \
	JPXStream.o Lexer.o Link.o NameToCharCode.o Object.o Outline.o \
	OutputDev.o Page.o Parser.o PDFDoc.o PDFDocEncoding.o PSTokenizer.o \
	SecurityHandler.o Stream.o UnicodeMap.o XpdfPluginAPI.o XRef.o \
	pdfinfo.o
PDFINFO_LIBS = -L$(GOOLIBDIR) -lGoo $(OTHERLIBS) -lm

pdfinfo$(EXE): $(PDFINFO_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
	$(CXX) $(CXXFLAGS) $(LDFLAGS) -o pdfinfo$(EXE) $(PDFINFO_OBJS) \
		$(PDFINFO_LIBS)

#------------------------------------------------------------------------

PDFFONTS_OBJS = Annot.o Array.o BuiltinFont.o BuiltinFontTables.o \
	Catalog.o CharCodeToUnicode.o CMap.o Decrypt.o Dict.o Error.o \
	FontEncodingTables.o Function.o Gfx.o GfxFont.o \
	GfxState.o GlobalParams.o JArithmeticDecoder.o JBIG2Stream.o \
	JPXStream.o Lexer.o Link.o NameToCharCode.o Object.o Outline.o \
	OutputDev.o Page.o Parser.o PDFDoc.o PDFDocEncoding.o PSTokenizer.o \
	SecurityHandler.o Stream.o UnicodeMap.o XpdfPluginAPI.o XRef.o \
	pdffonts.o
PDFFONTS_LIBS = -L$(GOOLIBDIR) -lGoo $(OTHERLIBS) -lm

pdffonts$(EXE): $(PDFFONTS_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
	$(CXX) $(CXXFLAGS) $(LDFLAGS) -o pdffonts$(EXE) $(PDFFONTS_OBJS) \
		$(PDFFONTS_LIBS)

#------------------------------------------------------------------------

PDFIMAGES_OBJS = Annot.o Array.o BuiltinFont.o BuiltinFontTables.o \
	Catalog.o CharCodeToUnicode.o CMap.o Decrypt.o Dict.o Error.o \
	FontEncodingTables.o Function.o Gfx.o GfxFont.o GfxState.o \
	GlobalParams.o ImageOutputDev.o JArithmeticDecoder.o \
	JBIG2Stream.o JPXStream.o Lexer.o Link.o NameToCharCode.o Object.o \
	Outline.o OutputDev.o Page.o Parser.o PDFDoc.o PDFDocEncoding.o \
	PSTokenizer.o SecurityHandler.o Stream.o UnicodeMap.o \
	XpdfPluginAPI.o XRef.o pdfimages.o
PDFIMAGES_LIBS = -L$(GOOLIBDIR) -lGoo $(OTHERLIBS) -lm

pdfimages$(EXE): $(PDFIMAGES_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
	$(CXX) $(CXXFLAGS) $(LDFLAGS) -o pdfimages$(EXE) $(PDFIMAGES_OBJS) \
		$(PDFIMAGES_LIBS)

#------------------------------------------------------------------------

XPDF_OBJS = Annot.o Array.o BuiltinFont.o BuiltinFontTables.o Catalog.o \
	CharCodeToUnicode.o CMap.o Decrypt.o Dict.o Error.o \
	FontEncodingTables.o Function.o Gfx.o GfxFont.o \
	GfxState.o GlobalParams.o JArithmeticDecoder.o JBIG2Stream.o \
	JPXStream.o Lexer.o Link.o NameToCharCode.o Object.o Outline.o \
	OutputDev.o Page.o Parser.o PDFDoc.o PDFDocEncoding.o PreScanOutputDev.o PSOutputDev.o \
	PSTokenizer.o SplashOutputDev.o Stream.o UnicodeMap.o SecurityHandler.o UnicodeTypeTable.o \
	XRef.o 
XPDF_LIBS = -L$(GOOLIBDIR) -lGoo $(SPLASHLIBS) $(FTLIBS) -lGoo $(OTHERLIBS) -lm

$(LIBPREFIX)Xpdf.a: $(XPDF_OBJS)
	rm -f $(LIBPREFIX)Xpdf.a
	$(AR) $(LIBPREFIX)Xpdf.a $(XPDF_OBJS)
	$(RANLIB) $(LIBPREFIX)Xpdf.a

#------------------------------------------------------------------------

XPDF_WINOBJS = libxpdf.lib

clean:
	rm -f $(XPDF_OBJS) xpdf$(EXE)
	rm -f $(PDFTOPS_OBJS) pdftops$(EXE)
	rm -f $(PDFTOTEXT_OBJS) pdftotext$(EXE)
	rm -f $(PDFINFO_OBJS) pdfinfo$(EXE)
	rm -f $(PDFFONTS_OBJS) pdffonts$(EXE)
	rm -f $(PDFIMAGES_OBJS) pdfimages$(EXE)
	rm -f $(XPDF_WINOBJS)

#------------------------------------------------------------------------

depend:
	$(CXX) $(CXXFLAGS) -MM $(CXX_SRC) >Makefile.dep

include Makefile.dep
//========================================================================
//
// NameToCharCode.cc
//
// Copyright 2001-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <string.h>
#include "gmem.h"
#include "NameToCharCode.h"

//------------------------------------------------------------------------

struct NameToCharCodeEntry {
  char *name;
  CharCode c;
};

//------------------------------------------------------------------------

NameToCharCode::NameToCharCode() {
  int i;

  size = 31;
  len = 0;
  tab = (NameToCharCodeEntry *)gmallocn(size, sizeof(NameToCharCodeEntry));
  for (i = 0; i < size; ++i) {
    tab[i].name = NULL;
  }
}

NameToCharCode::~NameToCharCode() {
  int i;

  for (i = 0; i < size; ++i) {
    if (tab[i].name) {
      gfree(tab[i].name);
    }
  }
  gfree(tab);
}

void NameToCharCode::add(char *name, CharCode c) {
  NameToCharCodeEntry *oldTab;
  int h, i, oldSize;

  // expand the table if necessary
  if (len >= size / 2) {
    oldSize = size;
    oldTab = tab;
    size = 2*size + 1;
    tab = (NameToCharCodeEntry *)gmallocn(size, sizeof(NameToCharCodeEntry));
    for (h = 0; h < size; ++h) {
      tab[h].name = NULL;
    }
    for (i = 0; i < oldSize; ++i) {
      if (oldTab[i].name) {
	h = hash(oldTab[i].name);
	while (tab[h].name) {
	  if (++h == size) {
	    h = 0;
	  }
	}
	tab[h] = oldTab[i];
      }
    }
    gfree(oldTab);
  }

  // add the new name
  h = hash(name);
  while (tab[h].name && strcmp(tab[h].name, name)) {
    if (++h == size) {
      h = 0;
    }
  }
  if (!tab[h].name) {
    tab[h].name = copyString(name);
  }
  tab[h].c = c;

  ++len;
}

CharCode NameToCharCode::lookup(char *name) {
  int h;

  h = hash(name);
  while (tab[h].name) {
    if (!strcmp(tab[h].name, name)) {
      return tab[h].c;
    }
    if (++h == size) {
      h = 0;
    }
  }
  return 0;
}

int NameToCharCode::hash(char *name) {
  char *p;
  unsigned int h;

  h = 0;
  for (p = name; *p; ++p) {
    h = 17 * h + (int)(*p & 0xff);
  }
  return (int)(h % size);
}
//========================================================================
//
// NameToCharCode.h
//
// Copyright 2001-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef NAMETOCHARCODE_H
#define NAMETOCHARCODE_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "CharTypes.h"

struct NameToCharCodeEntry;

//------------------------------------------------------------------------

class NameToCharCode {
public:

  NameToCharCode();
  ~NameToCharCode();

  void add(char *name, CharCode c);
  CharCode lookup(char *name);

private:

  int hash(char *name);

  NameToCharCodeEntry *tab;
  int size;
  int len;
};

#endif
//========================================================================
//
// NameToUnicodeTable.h
//
// Copyright 2001-2004 Glyph & Cog, LLC
//
//========================================================================

static struct {
  Unicode u;
  char *name;
} nameToUnicodeTab[] = {
  {0x0021, "!"},
  {0x0023, "#"},
  {0x0024, "$"},
  {0x0025, "%"},
  {0x0026, "&"},
  {0x0027, "'"},
  {0x0028, "("},
  {0x0029, ")"},
  {0x002a, "*"},
  {0x002b, "+"},
  {0x002c, ","},
  {0x002d, "-"},
  {0x002e, "."},
  {0x002f, "/"},
  {0x0030, "0"},
  {0x0031, "1"},
  {0x0032, "2"},
  {0x0033, "3"},
  {0x0034, "4"},
  {0x0035, "5"},
  {0x0036, "6"},
  {0x0037, "7"},
  {0x0038, "8"},
  {0x0039, "9"},
  {0x003a, ":"},
  {0x003b, ";"},
  {0x003c, "<"},
  {0x003d, "="},
  {0x003e, ">"},
  {0x003f, "?"},
  {0x0040, "@"},
  {0x0041, "A"},
  {0x00c6, "AE"},
  {0x01fc, "AEacute"},
  {0xf7e6, "AEsmall"},
  {0x00c1, "Aacute"},
  {0xf7e1, "Aacutesmall"},
  {0x0102, "Abreve"},
  {0x00c2, "Acircumflex"},
  {0xf7e2, "Acircumflexsmall"},
  {0xf6c9, "Acute"},
  {0xf7b4, "Acutesmall"},
  {0x00c4, "Adieresis"},
  {0xf7e4, "Adieresissmall"},
  {0x00c0, "Agrave"},
  {0xf7e0, "Agravesmall"},
  {0x0391, "Alpha"},
  {0x0386, "Alphatonos"},
  {0x0100, "Amacron"},
  {0x0104, "Aogonek"},
  {0x00c5, "Aring"},
  {0x01fa, "Aringacute"},
  {0xf7e5, "Aringsmall"},
  {0xf761, "Asmall"},
  {0x00c3, "Atilde"},
  {0xf7e3, "Atildesmall"},
  {0x0042, "B"},
  {0x0392, "Beta"},
  {0xf6f4, "Brevesmall"},
  {0xf762, "Bsmall"},
  {0x0043, "C"},
  {0x0106, "Cacute"},
  {0xf6ca, "Caron"},
  {0xf6f5, "Caronsmall"},
  {0x010c, "Ccaron"},
  {0x00c7, "Ccedilla"},
  {0xf7e7, "Ccedillasmall"},
  {0x0108, "Ccircumflex"},
  {0x010a, "Cdotaccent"},
  {0xf7b8, "Cedillasmall"},
  {0x03a7, "Chi"},
  {0xf6f6, "Circumflexsmall"},
  {0xf763, "Csmall"},
  {0x0044, "D"},
  {0x010e, "Dcaron"},
  {0x0110, "Dcroat"},
  {0x2206, "Delta"},
  {0xf6cb, "Dieresis"},
  {0xf6cc, "DieresisAcute"},
  {0xf6cd, "DieresisGrave"},
  {0xf7a8, "Dieresissmall"},
  {0xf6f7, "Dotaccentsmall"},
  {0xf764, "Dsmall"},
  {0x0045, "E"},
  {0x00c9, "Eacute"},
  {0xf7e9, "Eacutesmall"},
  {0x0114, "Ebreve"},
  {0x011a, "Ecaron"},
  {0x00ca, "Ecircumflex"},
  {0xf7ea, "Ecircumflexsmall"},
  {0x00cb, "Edieresis"},
  {0xf7eb, "Edieresissmall"},
  {0x0116, "Edotaccent"},
  {0x00c8, "Egrave"},
  {0xf7e8, "Egravesmall"},
  {0x0112, "Emacron"},
  {0x014a, "Eng"},
  {0x0118, "Eogonek"},
  {0x0395, "Epsilon"},
  {0x0388, "Epsilontonos"},
  {0xf765, "Esmall"},
  {0x0397, "Eta"},
  {0x0389, "Etatonos"},
  {0x00d0, "Eth"},
  {0xf7f0, "Ethsmall"},
  {0x20ac, "Euro"},
  {0x0046, "F"},
  {0xf766, "Fsmall"},
  {0x0047, "G"},
  {0x0393, "Gamma"},
  {0x011e, "Gbreve"},
  {0x01e6, "Gcaron"},
  {0x011c, "Gcircumflex"},
  {0x0122, "Gcommaaccent"},
  {0x0120, "Gdotaccent"},
  {0xf6ce, "Grave"},
  {0xf760, "Gravesmall"},
  {0xf767, "Gsmall"},
  {0x0048, "H"},
  {0x25cf, "H18533"},
  {0x25aa, "H18543"},
  {0x25ab, "H18551"},
  {0x25a1, "H22073"},
  {0x0126, "Hbar"},
  {0x0124, "Hcircumflex"},
  {0xf768, "Hsmall"},
  {0xf6cf, "Hungarumlaut"},
  {0xf6f8, "Hungarumlautsmall"},
  {0x0049, "I"},
  {0x0132, "IJ"},
  {0x00cd, "Iacute"},
  {0xf7ed, "Iacutesmall"},
  {0x012c, "Ibreve"},
  {0x00ce, "Icircumflex"},
  {0xf7ee, "Icircumflexsmall"},
  {0x00cf, "Idieresis"},
  {0xf7ef, "Idieresissmall"},
  {0x0130, "Idotaccent"},
  {0x2111, "Ifraktur"},
  {0x00cc, "Igrave"},
  {0xf7ec, "Igravesmall"},
  {0x012a, "Imacron"},
  {0x012e, "Iogonek"},
  {0x0399, "Iota"},
  {0x03aa, "Iotadieresis"},
  {0x038a, "Iotatonos"},
  {0xf769, "Ismall"},
  {0x0128, "Itilde"},
  {0x004a, "J"},
  {0x0134, "Jcircumflex"},
  {0xf76a, "Jsmall"},
  {0x004b, "K"},
  {0x039a, "Kappa"},
  {0x0136, "Kcommaaccent"},
  {0xf76b, "Ksmall"},
  {0x004c, "L"},
  {0xf6bf, "LL"},
  {0x0139, "Lacute"},
  {0x039b, "Lambda"},
  {0x013d, "Lcaron"},
  {0x013b, "Lcommaaccent"},
  {0x013f, "Ldot"},
  {0x0141, "Lslash"},
  {0xf6f9, "Lslashsmall"},
  {0xf76c, "Lsmall"},
  {0x004d, "M"},
  {0xf6d0, "Macron"},
  {0xf7af, "Macronsmall"},
  {0xf76d, "Msmall"},
  {0x039c, "Mu"},
  {0x004e, "N"},
  {0x0143, "Nacute"},
  {0x0147, "Ncaron"},
  {0x0145, "Ncommaaccent"},
  {0xf76e, "Nsmall"},
  {0x00d1, "Ntilde"},
  {0xf7f1, "Ntildesmall"},
  {0x039d, "Nu"},
  {0x004f, "O"},
  {0x0152, "OE"},
  {0xf6fa, "OEsmall"},
  {0x00d3, "Oacute"},
  {0xf7f3, "Oacutesmall"},
  {0x014e, "Obreve"},
  {0x00d4, "Ocircumflex"},
  {0xf7f4, "Ocircumflexsmall"},
  {0x00d6, "Odieresis"},
  {0xf7f6, "Odieresissmall"},
  {0xf6fb, "Ogoneksmall"},
  {0x00d2, "Ograve"},
  {0xf7f2, "Ogravesmall"},
  {0x01a0, "Ohorn"},
  {0x0150, "Ohungarumlaut"},
  {0x014c, "Omacron"},
  {0x2126, "Omega"},
  {0x038f, "Omegatonos"},
  {0x039f, "Omicron"},
  {0x038c, "Omicrontonos"},
  {0x00d8, "Oslash"},
  {0x01fe, "Oslashacute"},
  {0xf7f8, "Oslashsmall"},
  {0xf76f, "Osmall"},
  {0x00d5, "Otilde"},
  {0xf7f5, "Otildesmall"},
  {0x0050, "P"},
  {0x03a6, "Phi"},
  {0x03a0, "Pi"},
  {0x03a8, "Psi"},
  {0xf770, "Psmall"},
  {0x0051, "Q"},
  {0xf771, "Qsmall"},
  {0x0052, "R"},
  {0x0154, "Racute"},
  {0x0158, "Rcaron"},
  {0x0156, "Rcommaaccent"},
  {0x211c, "Rfraktur"},
  {0x03a1, "Rho"},
  {0xf6fc, "Ringsmall"},
  {0xf772, "Rsmall"},
  {0x0053, "S"},
  {0x250c, "SF010000"},
  {0x2514, "SF020000"},
  {0x2510, "SF030000"},
  {0x2518, "SF040000"},
  {0x253c, "SF050000"},
  {0x252c, "SF060000"},
  {0x2534, "SF070000"},
  {0x251c, "SF080000"},
  {0x2524, "SF090000"},
  {0x2500, "SF100000"},
  {0x2502, "SF110000"},
  {0x2561, "SF190000"},
  {0x2562, "SF200000"},
  {0x2556, "SF210000"},
  {0x2555, "SF220000"},
  {0x2563, "SF230000"},
  {0x2551, "SF240000"},
  {0x2557, "SF250000"},
  {0x255d, "SF260000"},
  {0x255c, "SF270000"},
  {0x255b, "SF280000"},
  {0x255e, "SF360000"},
  {0x255f, "SF370000"},
  {0x255a, "SF380000"},
  {0x2554, "SF390000"},
  {0x2569, "SF400000"},
  {0x2566, "SF410000"},
  {0x2560, "SF420000"},
  {0x2550, "SF430000"},
  {0x256c, "SF440000"},
  {0x2567, "SF450000"},
  {0x2568, "SF460000"},
  {0x2564, "SF470000"},
  {0x2565, "SF480000"},
  {0x2559, "SF490000"},
  {0x2558, "SF500000"},
  {0x2552, "SF510000"},
  {0x2553, "SF520000"},
  {0x256b, "SF530000"},
  {0x256a, "SF540000"},
  {0x015a, "Sacute"},
  {0x0160, "Scaron"},
  {0xf6fd, "Scaronsmall"},
  {0x015e, "Scedilla"},
  {0x015c, "Scircumflex"},
  {0x0218, "Scommaaccent"},
  {0x03a3, "Sigma"},
  {0xf773, "Ssmall"},
  {0x0054, "T"},
  {0x03a4, "Tau"},
  {0x0166, "Tbar"},
  {0x0164, "Tcaron"},
  {0x0162, "Tcommaaccent"},
  {0x0398, "Theta"},
  {0x00de, "Thorn"},
  {0xf7fe, "Thornsmall"},
  {0xf6fe, "Tildesmall"},
  {0xf774, "Tsmall"},
  {0x0055, "U"},
  {0x00da, "Uacute"},
  {0xf7fa, "Uacutesmall"},
  {0x016c, "Ubreve"},
  {0x00db, "Ucircumflex"},
  {0xf7fb, "Ucircumflexsmall"},
  {0x00dc, "Udieresis"},
  {0xf7fc, "Udieresissmall"},
  {0x00d9, "Ugrave"},
  {0xf7f9, "Ugravesmall"},
  {0x01af, "Uhorn"},
  {0x0170, "Uhungarumlaut"},
  {0x016a, "Umacron"},
  {0x0172, "Uogonek"},
  {0x03a5, "Upsilon"},
  {0x03d2, "Upsilon1"},
  {0x03ab, "Upsilondieresis"},
  {0x038e, "Upsilontonos"},
  {0x016e, "Uring"},
  {0xf775, "Usmall"},
  {0x0168, "Utilde"},
  {0x0056, "V"},
  {0xf776, "Vsmall"},
  {0x0057, "W"},
  {0x1e82, "Wacute"},
  {0x0174, "Wcircumflex"},
  {0x1e84, "Wdieresis"},
  {0x1e80, "Wgrave"},
  {0xf777, "Wsmall"},
  {0x0058, "X"},
  {0x039e, "Xi"},
  {0xf778, "Xsmall"},
  {0x0059, "Y"},
  {0x00dd, "Yacute"},
  {0xf7fd, "Yacutesmall"},
  {0x0176, "Ycircumflex"},
  {0x0178, "Ydieresis"},
  {0xf7ff, "Ydieresissmall"},
  {0x1ef2, "Ygrave"},
  {0xf779, "Ysmall"},
  {0x005a, "Z"},
  {0x0179, "Zacute"},
  {0x017d, "Zcaron"},
  {0xf6ff, "Zcaronsmall"},
  {0x017b, "Zdotaccent"},
  {0x0396, "Zeta"},
  {0xf77a, "Zsmall"},
  {0x0022, "\""},
  {0x005c, "\\"},
  {0x005d, "]"},
  {0x005e, "^"},
  {0x005f, "_"},
  {0x0060, "`"},
  {0x0061, "a"},
  {0x00e1, "aacute"},
  {0x0103, "abreve"},
  {0x00e2, "acircumflex"},
  {0x00b4, "acute"},
  {0x0301, "acutecomb"},
  {0x00e4, "adieresis"},
  {0x00e6, "ae"},
  {0x01fd, "aeacute"},
  {0x2015, "afii00208"},
  {0x0410, "afii10017"},
  {0x0411, "afii10018"},
  {0x0412, "afii10019"},
  {0x0413, "afii10020"},
  {0x0414, "afii10021"},
  {0x0415, "afii10022"},
  {0x0401, "afii10023"},
  {0x0416, "afii10024"},
  {0x0417, "afii10025"},
  {0x0418, "afii10026"},
  {0x0419, "afii10027"},
  {0x041a, "afii10028"},
  {0x041b, "afii10029"},
  {0x041c, "afii10030"},
  {0x041d, "afii10031"},
  {0x041e, "afii10032"},
  {0x041f, "afii10033"},
  {0x0420, "afii10034"},
  {0x0421, "afii10035"},
  {0x0422, "afii10036"},
  {0x0423, "afii10037"},
  {0x0424, "afii10038"},
  {0x0425, "afii10039"},
  {0x0426, "afii10040"},
  {0x0427, "afii10041"},
  {0x0428, "afii10042"},
  {0x0429, "afii10043"},
  {0x042a, "afii10044"},
  {0x042b, "afii10045"},
  {0x042c, "afii10046"},
  {0x042d, "afii10047"},
  {0x042e, "afii10048"},
  {0x042f, "afii10049"},
  {0x0490, "afii10050"},
  {0x0402, "afii10051"},
  {0x0403, "afii10052"},
  {0x0404, "afii10053"},
  {0x0405, "afii10054"},
  {0x0406, "afii10055"},
  {0x0407, "afii10056"},
  {0x0408, "afii10057"},
  {0x0409, "afii10058"},
  {0x040a, "afii10059"},
  {0x040b, "afii10060"},
  {0x040c, "afii10061"},
  {0x040e, "afii10062"},
  {0xf6c4, "afii10063"},
  {0xf6c5, "afii10064"},
  {0x0430, "afii10065"},
  {0x0431, "afii10066"},
  {0x0432, "afii10067"},
  {0x0433, "afii10068"},
  {0x0434, "afii10069"},
  {0x0435, "afii10070"},
  {0x0451, "afii10071"},
  {0x0436, "afii10072"},
  {0x0437, "afii10073"},
  {0x0438, "afii10074"},
  {0x0439, "afii10075"},
  {0x043a, "afii10076"},
  {0x043b, "afii10077"},
  {0x043c, "afii10078"},
  {0x043d, "afii10079"},
  {0x043e, "afii10080"},
  {0x043f, "afii10081"},
  {0x0440, "afii10082"},
  {0x0441, "afii10083"},
  {0x0442, "afii10084"},
  {0x0443, "afii10085"},
  {0x0444, "afii10086"},
  {0x0445, "afii10087"},
  {0x0446, "afii10088"},
  {0x0447, "afii10089"},
  {0x0448, "afii10090"},
  {0x0449, "afii10091"},
  {0x044a, "afii10092"},
  {0x044b, "afii10093"},
  {0x044c, "afii10094"},
  {0x044d, "afii10095"},
  {0x044e, "afii10096"},
  {0x044f, "afii10097"},
  {0x0491, "afii10098"},
  {0x0452, "afii10099"},
  {0x0453, "afii10100"},
  {0x0454, "afii10101"},
  {0x0455, "afii10102"},
  {0x0456, "afii10103"},
  {0x0457, "afii10104"},
  {0x0458, "afii10105"},
  {0x0459, "afii10106"},
  {0x045a, "afii10107"},
  {0x045b, "afii10108"},
  {0x045c, "afii10109"},
  {0x045e, "afii10110"},
  {0x040f, "afii10145"},
  {0x0462, "afii10146"},
  {0x0472, "afii10147"},
  {0x0474, "afii10148"},
  {0xf6c6, "afii10192"},
  {0x045f, "afii10193"},
  {0x0463, "afii10194"},
  {0x0473, "afii10195"},
  {0x0475, "afii10196"},
  {0xf6c7, "afii10831"},
  {0xf6c8, "afii10832"},
  {0x04d9, "afii10846"},
  {0x200e, "afii299"},
  {0x200f, "afii300"},
  {0x200d, "afii301"},
  {0x066a, "afii57381"},
  {0x060c, "afii57388"},
  {0x0660, "afii57392"},
  {0x0661, "afii57393"},
  {0x0662, "afii57394"},
  {0x0663, "afii57395"},
  {0x0664, "afii57396"},
  {0x0665, "afii57397"},
  {0x0666, "afii57398"},
  {0x0667, "afii57399"},
  {0x0668, "afii57400"},
  {0x0669, "afii57401"},
  {0x061b, "afii57403"},
  {0x061f, "afii57407"},
  {0x0621, "afii57409"},
  {0x0622, "afii57410"},
  {0x0623, "afii57411"},
  {0x0624, "afii57412"},
  {0x0625, "afii57413"},
  {0x0626, "afii57414"},
  {0x0627, "afii57415"},
  {0x0628, "afii57416"},
  {0x0629, "afii57417"},
  {0x062a, "afii57418"},
  {0x062b, "afii57419"},
  {0x062c, "afii57420"},
  {0x062d, "afii57421"},
  {0x062e, "afii57422"},
  {0x062f, "afii57423"},
  {0x0630, "afii57424"},
  {0x0631, "afii57425"},
  {0x0632, "afii57426"},
  {0x0633, "afii57427"},
  {0x0634, "afii57428"},
  {0x0635, "afii57429"},
  {0x0636, "afii57430"},
  {0x0637, "afii57431"},
  {0x0638, "afii57432"},
  {0x0639, "afii57433"},
  {0x063a, "afii57434"},
  {0x0640, "afii57440"},
  {0x0641, "afii57441"},
  {0x0642, "afii57442"},
  {0x0643, "afii57443"},
  {0x0644, "afii57444"},
  {0x0645, "afii57445"},
  {0x0646, "afii57446"},
  {0x0648, "afii57448"},
  {0x0649, "afii57449"},
  {0x064a, "afii57450"},
  {0x064b, "afii57451"},
  {0x064c, "afii57452"},
  {0x064d, "afii57453"},
  {0x064e, "afii57454"},
  {0x064f, "afii57455"},
  {0x0650, "afii57456"},
  {0x0651, "afii57457"},
  {0x0652, "afii57458"},
  {0x0647, "afii57470"},
  {0x06a4, "afii57505"},
  {0x067e, "afii57506"},
  {0x0686, "afii57507"},
  {0x0698, "afii57508"},
  {0x06af, "afii57509"},
  {0x0679, "afii57511"},
  {0x0688, "afii57512"},
  {0x0691, "afii57513"},
  {0x06ba, "afii57514"},
  {0x06d2, "afii57519"},
  {0x06d5, "afii57534"},
  {0x20aa, "afii57636"},
  {0x05be, "afii57645"},
  {0x05c3, "afii57658"},
  {0x05d0, "afii57664"},
  {0x05d1, "afii57665"},
  {0x05d2, "afii57666"},
  {0x05d3, "afii57667"},
  {0x05d4, "afii57668"},
  {0x05d5, "afii57669"},
  {0x05d6, "afii57670"},
  {0x05d7, "afii57671"},
  {0x05d8, "afii57672"},
  {0x05d9, "afii57673"},
  {0x05da, "afii57674"},
  {0x05db, "afii57675"},
  {0x05dc, "afii57676"},
  {0x05dd, "afii57677"},
  {0x05de, "afii57678"},
  {0x05df, "afii57679"},
  {0x05e0, "afii57680"},
  {0x05e1, "afii57681"},
  {0x05e2, "afii57682"},
  {0x05e3, "afii57683"},
  {0x05e4, "afii57684"},
  {0x05e5, "afii57685"},
  {0x05e6, "afii57686"},
  {0x05e7, "afii57687"},
  {0x05e8, "afii57688"},
  {0x05e9, "afii57689"},
  {0x05ea, "afii57690"},
  {0xfb2a, "afii57694"},
  {0xfb2b, "afii57695"},
  {0xfb4b, "afii57700"},
  {0xfb1f, "afii57705"},
  {0x05f0, "afii57716"},
  {0x05f1, "afii57717"},
  {0x05f2, "afii57718"},
  {0xfb35, "afii57723"},
  {0x05b4, "afii57793"},
  {0x05b5, "afii57794"},
  {0x05b6, "afii57795"},
  {0x05bb, "afii57796"},
  {0x05b8, "afii57797"},
  {0x05b7, "afii57798"},
  {0x05b0, "afii57799"},
  {0x05b2, "afii57800"},
  {0x05b1, "afii57801"},
  {0x05b3, "afii57802"},
  {0x05c2, "afii57803"},
  {0x05c1, "afii57804"},
  {0x05b9, "afii57806"},
  {0x05bc, "afii57807"},
  {0x05bd, "afii57839"},
  {0x05bf, "afii57841"},
  {0x05c0, "afii57842"},
  {0x02bc, "afii57929"},
  {0x2105, "afii61248"},
  {0x2113, "afii61289"},
  {0x2116, "afii61352"},
  {0x202c, "afii61573"},
  {0x202d, "afii61574"},
  {0x202e, "afii61575"},
  {0x200c, "afii61664"},
  {0x066d, "afii63167"},
  {0x02bd, "afii64937"},
  {0x00e0, "agrave"},
  {0x2135, "aleph"},
  {0x03b1, "alpha"},
  {0x03ac, "alphatonos"},
  {0x0101, "amacron"},
  {0x0026, "ampersand"},
  {0xf726, "ampersandsmall"},
  {0x2220, "angle"},
  {0x2329, "angleleft"},
  {0x232a, "angleright"},
  {0x0387, "anoteleia"},
  {0x0105, "aogonek"},
  {0x2248, "approxequal"},
  {0x00e5, "aring"},
  {0x01fb, "aringacute"},
  {0x2194, "arrowboth"},
  {0x21d4, "arrowdblboth"},
  {0x21d3, "arrowdbldown"},
  {0x21d0, "arrowdblleft"},
  {0x21d2, "arrowdblright"},
  {0x21d1, "arrowdblup"},
  {0x2193, "arrowdown"},
  {0xf8e7, "arrowhorizex"},
  {0x2190, "arrowleft"},
  {0x2192, "arrowright"},
  {0x2191, "arrowup"},
  {0x2195, "arrowupdn"},
  {0x21a8, "arrowupdnbse"},
  {0xf8e6, "arrowvertex"},
  {0x005e, "asciicircum"},
  {0x007e, "asciitilde"},
  {0x002a, "asterisk"},
  {0x2217, "asteriskmath"},
  {0xf6e9, "asuperior"},
  {0x0040, "at"},
  {0x00e3, "atilde"},
  {0x0062, "b"},
  {0x005c, "backslash"},
  {0x007c, "bar"},
  {0x03b2, "beta"},
  {0x2588, "block"},
  {0xf8f4, "braceex"},
  {0x007b, "braceleft"},
  {0xf8f3, "braceleftbt"},
  {0xf8f2, "braceleftmid"},
  {0xf8f1, "bracelefttp"},
  {0x007d, "braceright"},
  {0xf8fe, "bracerightbt"},
  {0xf8fd, "bracerightmid"},
  {0xf8fc, "bracerighttp"},
  {0x005b, "bracketleft"},
  {0xf8f0, "bracketleftbt"},
  {0xf8ef, "bracketleftex"},
  {0xf8ee, "bracketlefttp"},
  {0x005d, "bracketright"},
  {0xf8fb, "bracketrightbt"},
  {0xf8fa, "bracketrightex"},
  {0xf8f9, "bracketrighttp"},
  {0x02d8, "breve"},
  {0x00a6, "brokenbar"},
  {0xf6ea, "bsuperior"},
  {0x2022, "bullet"},
  {0x0063, "c"},
  {0x0107, "cacute"},
  {0x02c7, "caron"},
  {0x21b5, "carriagereturn"},
  {0x010d, "ccaron"},
  {0x00e7, "ccedilla"},
  {0x0109, "ccircumflex"},
  {0x010b, "cdotaccent"},
  {0x00b8, "cedilla"},
  {0x00a2, "cent"},
  {0xf6df, "centinferior"},
  {0xf7a2, "centoldstyle"},
  {0xf6e0, "centsuperior"},
  {0x03c7, "chi"},
  {0x25cb, "circle"},
  {0x2297, "circlemultiply"},
  {0x2295, "circleplus"},
  {0x02c6, "circumflex"},
  {0x2663, "club"},
  {0x003a, "colon"},
  {0x20a1, "colonmonetary"},
  {0x002c, "comma"},
  {0xf6c3, "commaaccent"},
  {0xf6e1, "commainferior"},
  {0xf6e2, "commasuperior"},
  {0x2245, "congruent"},
  {0x00a9, "copyright"},
  {0x00a9, "copyrightsans"},
  {0x00a9, "copyrightserif"},
  {0x00a4, "currency"},
  {0xf6d1, "cyrBreve"},
  {0xf6d2, "cyrFlex"},
  {0xf6d4, "cyrbreve"},
  {0xf6d5, "cyrflex"},
  {0x0064, "d"},
  {0x2020, "dagger"},
  {0x2021, "daggerdbl"},
  {0xf6d3, "dblGrave"},
  {0xf6d6, "dblgrave"},
  {0x010f, "dcaron"},
  {0x0111, "dcroat"},
  {0x00b0, "degree"},
  {0x03b4, "delta"},
  {0x2666, "diamond"},
  {0x00a8, "dieresis"},
  {0xf6d7, "dieresisacute"},
  {0xf6d8, "dieresisgrave"},
  {0x0385, "dieresistonos"},
  {0x00f7, "divide"},
  {0x2593, "dkshade"},
  {0x2584, "dnblock"},
  {0x0024, "dollar"},
  {0xf6e3, "dollarinferior"},
  {0xf724, "dollaroldstyle"},
  {0xf6e4, "dollarsuperior"},
  {0x20ab, "dong"},
  {0x02d9, "dotaccent"},
  {0x0323, "dotbelowcomb"},
  {0x0131, "dotlessi"},
  {0xf6be, "dotlessj"},
  {0x22c5, "dotmath"},
  {0xf6eb, "dsuperior"},
  {0x0065, "e"},
  {0x00e9, "eacute"},
  {0x0115, "ebreve"},
  {0x011b, "ecaron"},
  {0x00ea, "ecircumflex"},
  {0x00eb, "edieresis"},
  {0x0117, "edotaccent"},
  {0x00e8, "egrave"},
  {0x0038, "eight"},
  {0x2088, "eightinferior"},
  {0xf738, "eightoldstyle"},
  {0x2078, "eightsuperior"},
  {0x2208, "element"},
  {0x2026, "ellipsis"},
  {0x0113, "emacron"},
  {0x2014, "emdash"},
  {0x2205, "emptyset"},
  {0x2013, "endash"},
  {0x014b, "eng"},
  {0x0119, "eogonek"},
  {0x03b5, "epsilon"},
  {0x03ad, "epsilontonos"},
  {0x003d, "equal"},
  {0x2261, "equivalence"},
  {0x212e, "estimated"},
  {0xf6ec, "esuperior"},
  {0x03b7, "eta"},
  {0x03ae, "etatonos"},
  {0x00f0, "eth"},
  {0x0021, "exclam"},
  {0x203c, "exclamdbl"},
  {0x00a1, "exclamdown"},
  {0xf7a1, "exclamdownsmall"},
  {0x0021, "exclamleft"},
  {0xf721, "exclamsmall"},
  {0x2203, "existential"},
  {0x0066, "f"},
  {0x2640, "female"},
  {0xfb00, "ff"},
  {0xfb03, "ffi"},
  {0xfb04, "ffl"},
  {0xfb01, "fi"},
  {0x2012, "figuredash"},
  {0x25a0, "filledbox"},
  {0x25ac, "filledrect"},
  {0x0035, "five"},
  {0x215d, "fiveeighths"},
  {0x2085, "fiveinferior"},
  {0xf735, "fiveoldstyle"},
  {0x2075, "fivesuperior"},
  {0xfb02, "fl"},
  {0x0192, "florin"},
  {0x0034, "four"},
  {0x2084, "fourinferior"},
  {0xf734, "fouroldstyle"},
  {0x2074, "foursuperior"},
  {0x2044, "fraction"},
  {0x20a3, "franc"},
  {0x0067, "g"},
  {0x03b3, "gamma"},
  {0x011f, "gbreve"},
  {0x01e7, "gcaron"},
  {0x011d, "gcircumflex"},
  {0x0123, "gcommaaccent"},
  {0x0121, "gdotaccent"},
  {0x00df, "germandbls"},
  {0x2207, "gradient"},
  {0x0060, "grave"},
  {0x0300, "gravecomb"},
  {0x003e, "greater"},
  {0x2265, "greaterequal"},
  {0x00ab, "guillemotleft"},
  {0x00bb, "guillemotright"},
  {0x2039, "guilsinglleft"},
  {0x203a, "guilsinglright"},
  {0x0068, "h"},
  {0x0127, "hbar"},
  {0x0125, "hcircumflex"},
  {0x2665, "heart"},
  {0x0309, "hookabovecomb"},
  {0x2302, "house"},
  {0x02dd, "hungarumlaut"},
  {0x002d, "hyphen"},
  {0xf6e5, "hypheninferior"},
  {0xf6e6, "hyphensuperior"},
  {0x0069, "i"},
  {0x00ed, "iacute"},
  {0x012d, "ibreve"},
  {0x00ee, "icircumflex"},
  {0x00ef, "idieresis"},
  {0x00ec, "igrave"},
  {0x0133, "ij"},
  {0x012b, "imacron"},
  {0x221e, "infinity"},
  {0x222b, "integral"},
  {0x2321, "integralbt"},
  {0xf8f5, "integralex"},
  {0x2320, "integraltp"},
  {0x2229, "intersection"},
  {0x25d8, "invbullet"},
  {0x25d9, "invcircle"},
  {0x263b, "invsmileface"},
  {0x012f, "iogonek"},
  {0x03b9, "iota"},
  {0x03ca, "iotadieresis"},
  {0x0390, "iotadieresistonos"},
  {0x03af, "iotatonos"},
  {0xf6ed, "isuperior"},
  {0x0129, "itilde"},
  {0x006a, "j"},
  {0x0135, "jcircumflex"},
  {0x006b, "k"},
  {0x03ba, "kappa"},
  {0x0137, "kcommaaccent"},
  {0x0138, "kgreenlandic"},
  {0x006c, "l"},
  {0x013a, "lacute"},
  {0x03bb, "lambda"},
  {0x013e, "lcaron"},
  {0x013c, "lcommaaccent"},
  {0x0140, "ldot"},
  {0x003c, "less"},
  {0x2264, "lessequal"},
  {0x258c, "lfblock"},
  {0x20a4, "lira"},
  {0xf6c0, "ll"},
  {0x2227, "logicaland"},
  {0x00ac, "logicalnot"},
  {0x2228, "logicalor"},
  {0x017f, "longs"},
  {0x25ca, "lozenge"},
  {0x0142, "lslash"},
  {0xf6ee, "lsuperior"},
  {0x2591, "ltshade"},
  {0x006d, "m"},
  {0x00af, "macron"},
  {0x2642, "male"},
  {0x2212, "minus"},
  {0x2032, "minute"},
  {0xf6ef, "msuperior"},
  {0x00b5, "mu"},
  {0x00d7, "multiply"},
  {0x266a, "musicalnote"},
  {0x266b, "musicalnotedbl"},
  {0x006e, "n"},
  {0x0144, "nacute"},
  {0x0149, "napostrophe"},
  {0x00a0, "nbspace"},
  {0x0148, "ncaron"},
  {0x0146, "ncommaaccent"},
  {0x0039, "nine"},
  {0x2089, "nineinferior"},
  {0xf739, "nineoldstyle"},
  {0x2079, "ninesuperior"},
  {0x00a0, "nonbreakingspace"},
  {0x2209, "notelement"},
  {0x2260, "notequal"},
  {0x2284, "notsubset"},
  {0x207f, "nsuperior"},
  {0x00f1, "ntilde"},
  {0x03bd, "nu"},
  {0x0023, "numbersign"},
  {0x006f, "o"},
  {0x00f3, "oacute"},
  {0x014f, "obreve"},
  {0x00f4, "ocircumflex"},
  {0x00f6, "odieresis"},
  {0x0153, "oe"},
  {0x02db, "ogonek"},
  {0x00f2, "ograve"},
  {0x01a1, "ohorn"},
  {0x0151, "ohungarumlaut"},
  {0x014d, "omacron"},
  {0x03c9, "omega"},
  {0x03d6, "omega1"},
  {0x03ce, "omegatonos"},
  {0x03bf, "omicron"},
  {0x03cc, "omicrontonos"},
  {0x0031, "one"},
  {0x2024, "onedotenleader"},
  {0x215b, "oneeighth"},
  {0xf6dc, "onefitted"},
  {0x00bd, "onehalf"},
  {0x2081, "oneinferior"},
  {0xf731, "oneoldstyle"},
  {0x00bc, "onequarter"},
  {0x00b9, "onesuperior"},
  {0x2153, "onethird"},
  {0x25e6, "openbullet"},
  {0x00aa, "ordfeminine"},
  {0x00ba, "ordmasculine"},
  {0x221f, "orthogonal"},
  {0x00f8, "oslash"},
  {0x01ff, "oslashacute"},
  {0xf6f0, "osuperior"},
  {0x00f5, "otilde"},
  {0x0070, "p"},
  {0x00b6, "paragraph"},
  {0x0028, "parenleft"},
  {0xf8ed, "parenleftbt"},
  {0xf8ec, "parenleftex"},
  {0x208d, "parenleftinferior"},
  {0x207d, "parenleftsuperior"},
  {0xf8eb, "parenlefttp"},
  {0x0029, "parenright"},
  {0xf8f8, "parenrightbt"},
  {0xf8f7, "parenrightex"},
  {0x208e, "parenrightinferior"},
  {0x207e, "parenrightsuperior"},
  {0xf8f6, "parenrighttp"},
  {0x2202, "partialdiff"},
  {0x0025, "percent"},
  {0x002e, "period"},
  {0x00b7, "periodcentered"},
  {0xf6e7, "periodinferior"},
  {0xf6e8, "periodsuperior"},
  {0x22a5, "perpendicular"},
  {0x2030, "perthousand"},
  {0x20a7, "peseta"},
  {0x03c6, "phi"},
  {0x03d5, "phi1"},
  {0x03c0, "pi"},
  {0x002b, "plus"},
  {0x00b1, "plusminus"},
  {0x211e, "prescription"},
  {0x220f, "product"},
  {0x2282, "propersubset"},
  {0x2283, "propersuperset"},
  {0x221d, "proportional"},
  {0x03c8, "psi"},
  {0x0071, "q"},
  {0x003f, "question"},
  {0x00bf, "questiondown"},
  {0xf7bf, "questiondownsmall"},
  {0xf73f, "questionsmall"},
  {0x0022, "quotedbl"},
  {0x201e, "quotedblbase"},
  {0x201c, "quotedblleft"},
  {0x201d, "quotedblright"},
  {0x2018, "quoteleft"},
  {0x201b, "quotereversed"},
  {0x2019, "quoteright"},
  {0x201a, "quotesinglbase"},
  {0x0027, "quotesingle"},
  {0x0072, "r"},
  {0x0155, "racute"},
  {0x221a, "radical"},
  {0xf8e5, "radicalex"},
  {0x0159, "rcaron"},
  {0x0157, "rcommaaccent"},
  {0x2286, "reflexsubset"},
  {0x2287, "reflexsuperset"},
  {0x00ae, "registered"},
  {0x00ae, "registersans"},
  {0x00ae, "registerserif"},
  {0x2310, "revlogicalnot"},
  {0x03c1, "rho"},
  {0x02da, "ring"},
  {0xf6f1, "rsuperior"},
  {0x2590, "rtblock"},
  {0xf6dd, "rupiah"},
  {0x0073, "s"},
  {0x015b, "sacute"},
  {0x0161, "scaron"},
  {0x015f, "scedilla"},
  {0x015d, "scircumflex"},
  {0x0219, "scommaaccent"},
  {0x2033, "second"},
  {0x00a7, "section"},
  {0x003b, "semicolon"},
  {0x0037, "seven"},
  {0x215e, "seveneighths"},
  {0x2087, "seveninferior"},
  {0xf737, "sevenoldstyle"},
  {0x2077, "sevensuperior"},
  {0x2592, "shade"},
  {0x03c3, "sigma"},
  {0x03c2, "sigma1"},
  {0x223c, "similar"},
  {0x0036, "six"},
  {0x2086, "sixinferior"},
  {0xf736, "sixoldstyle"},
  {0x2076, "sixsuperior"},
  {0x002f, "slash"},
  {0x263a, "smileface"},
  {0x0020, "space"},
  {0x2660, "spade"},
  {0xf6f2, "ssuperior"},
  {0x00a3, "sterling"},
  {0x220b, "suchthat"},
  {0x2211, "summation"},
  {0x263c, "sun"},
  {0x0074, "t"},
  {0x03c4, "tau"},
  {0x0167, "tbar"},
  {0x0165, "tcaron"},
  {0x0163, "tcommaaccent"},
  {0x2234, "therefore"},
  {0x03b8, "theta"},
  {0x03d1, "theta1"},
  {0x00fe, "thorn"},
  {0x0033, "three"},
  {0x215c, "threeeighths"},
  {0x2083, "threeinferior"},
  {0xf733, "threeoldstyle"},
  {0x00be, "threequarters"},
  {0xf6de, "threequartersemdash"},
  {0x00b3, "threesuperior"},
  {0x02dc, "tilde"},
  {0x0303, "tildecomb"},
  {0x0384, "tonos"},
  {0x2122, "trademark"},
  {0x2122, "trademarksans"},
  {0x2122, "trademarkserif"},
  {0x25bc, "triagdn"},
  {0x25c4, "triaglf"},
  {0x25ba, "triagrt"},
  {0x25b2, "triagup"},
  {0xf6f3, "tsuperior"},
  {0x0032, "two"},
  {0x2025, "twodotenleader"},
  {0x2082, "twoinferior"},
  {0xf732, "twooldstyle"},
  {0x00b2, "twosuperior"},
  {0x2154, "twothirds"},
  {0x0075, "u"},
  {0x00fa, "uacute"},
  {0x016d, "ubreve"},
  {0x00fb, "ucircumflex"},
  {0x00fc, "udieresis"},
  {0x00f9, "ugrave"},
  {0x01b0, "uhorn"},
  {0x0171, "uhungarumlaut"},
  {0x016b, "umacron"},
  {0x005f, "underscore"},
  {0x2017, "underscoredbl"},
  {0x222a, "union"},
  {0x2200, "universal"},
  {0x0173, "uogonek"},
  {0x2580, "upblock"},
  {0x03c5, "upsilon"},
  {0x03cb, "upsilondieresis"},
  {0x03b0, "upsilondieresistonos"},
  {0x03cd, "upsilontonos"},
  {0x016f, "uring"},
  {0x0169, "utilde"},
  {0x0076, "v"},
  {0x0077, "w"},
  {0x1e83, "wacute"},
  {0x0175, "wcircumflex"},
  {0x1e85, "wdieresis"},
  {0x2118, "weierstrass"},
  {0x1e81, "wgrave"},
  {0x0078, "x"},
  {0x03be, "xi"},
  {0x0079, "y"},
  {0x00fd, "yacute"},
  {0x0177, "ycircumflex"},
  {0x00ff, "ydieresis"},
  {0x00a5, "yen"},
  {0x1ef3, "ygrave"},
  {0x007a, "z"},
  {0x017a, "zacute"},
  {0x017e, "zcaron"},
  {0x017c, "zdotaccent"},
  {0x0030, "zero"},
  {0x2080, "zeroinferior"},
  {0xf730, "zerooldstyle"},
  {0x2070, "zerosuperior"},
  {0x03b6, "zeta"},
  {0x007b, "{"},
  {0x007c, "|"},
  {0x007d, "}"},
  {0x007e, "~"},
  { 0, NULL }
};
//========================================================================
//
// Object.cc
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stddef.h>
#include "Object.h"
#include "Array.h"
#include "Dict.h"
#include "Error.h"
#include "Stream.h"
#include "XRef.h"

//------------------------------------------------------------------------
// Object
//------------------------------------------------------------------------

char *objTypeNames[numObjTypes] = {
  "boolean",
  "integer",
  "real",
  "string",
  "name",
  "null",
  "array",
  "dictionary",
  "stream",
  "ref",
  "cmd",
  "error",
  "eof",
  "none"
};

#ifdef DEBUG_MEM
int Object::numAlloc[numObjTypes] =
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
#endif

Object *Object::initArray(XRef *xref) {
  initObj(objArray);
  array = new Array(xref);
  return this;
}

Object *Object::initDict(XRef *xref) {
  initObj(objDict);
  dict = new Dict(xref);
  return this;
}

Object *Object::initDict(Dict *dictA) {
  initObj(objDict);
  dict = dictA;
  dict->incRef();
  return this;
}

Object *Object::initStream(Stream *streamA) {
  initObj(objStream);
  stream = streamA;
  return this;
}

Object *Object::copy(Object *obj) {
  *obj = *this;
  switch (type) {
  case objString:
    obj->string = string->copy();
    break;
  case objName:
    obj->name = copyString(name);
    break;
  case objArray:
    array->incRef();
    break;
  case objDict:
    dict->incRef();
    break;
  case objStream:
    stream->incRef();
    break;
  case objCmd:
    obj->cmd = copyString(cmd);
    break;
  default:
    break;
  }
#ifdef DEBUG_MEM
  ++numAlloc[type];
#endif
  return obj;
}

Object *Object::fetch(XRef *xref, Object *obj) {
  return (type == objRef && xref) ?
         xref->fetch(ref.num, ref.gen, obj) : copy(obj);
}

void Object::free() {
  switch (type) {
  case objString:
    delete string;
    break;
  case objName:
    gfree(name);
    break;
  case objArray:
    if (!array->decRef()) {
      delete array;
    }
    break;
  case objDict:
    if (!dict->decRef()) {
      delete dict;
    }
    break;
  case objStream:
    if (!stream->decRef()) {
      delete stream;
    }
    break;
  case objCmd:
    gfree(cmd);
    break;
  default:
    break;
  }
#ifdef DEBUG_MEM
  --numAlloc[type];
#endif
  type = objNone;
}

char *Object::getTypeName() {
  return objTypeNames[type];
}

void Object::print(FILE *f) {
  Object obj;
  int i;

  switch (type) {
  case objBool:
    fprintf(f, "%s", booln ? "true" : "false");
    break;
  case objInt:
    fprintf(f, "%d", intg);
    break;
  case objReal:
    fprintf(f, "%g", real);
    break;
  case objString:
    fprintf(f, "(");
    fwrite(string->getCString(), 1, string->getLength(), f);
    fprintf(f, ")");
    break;
  case objName:
    fprintf(f, "/%s", name);
    break;
  case objNull:
    fprintf(f, "null");
    break;
  case objArray:
    fprintf(f, "[");
    for (i = 0; i < arrayGetLength(); ++i) {
      if (i > 0)
	fprintf(f, " ");
      arrayGetNF(i, &obj);
      obj.print(f);
      obj.free();
    }
    fprintf(f, "]");
    break;
  case objDict:
    fprintf(f, "<<");
    for (i = 0; i < dictGetLength(); ++i) {
      fprintf(f, " /%s ", dictGetKey(i));
      dictGetValNF(i, &obj);
      obj.print(f);
      obj.free();
    }
    fprintf(f, " >>");
    break;
  case objStream:
    fprintf(f, "<stream>");
    break;
  case objRef:
    fprintf(f, "%d %d R", ref.num, ref.gen);
    break;
  case objCmd:
    fprintf(f, "%s", cmd);
    break;
  case objError:
    fprintf(f, "<error>");
    break;
  case objEOF:
    fprintf(f, "<EOF>");
    break;
  case objNone:
    fprintf(f, "<none>");
    break;
  }
}

void Object::memCheck(FILE *f) {
#ifdef DEBUG_MEM
  int i;
  int t;

  t = 0;
  for (i = 0; i < numObjTypes; ++i)
    t += numAlloc[i];
  if (t > 0) {
    fprintf(f, "Allocated objects:\n");
    for (i = 0; i < numObjTypes; ++i) {
      if (numAlloc[i] > 0)
	fprintf(f, "  %-20s: %6d\n", objTypeNames[i], numAlloc[i]);
    }
  }
#endif
}
//========================================================================
//
// Object.h
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef OBJECT_H
#define OBJECT_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include <stdio.h>
#include <string.h>
#include "gtypes.h"
#include "gmem.h"
#include "GString.h"

class XRef;
class Array;
class Dict;
class Stream;

//------------------------------------------------------------------------
// Ref
//------------------------------------------------------------------------

struct Ref {
  int num;			// object number
  int gen;			// generation number
};

//------------------------------------------------------------------------
// object types
//------------------------------------------------------------------------

enum ObjType {
  // simple objects
  objBool,			// boolean
  objInt,			// integer
  objReal,			// real
  objString,			// string
  objName,			// name
  objNull,			// null

  // complex objects
  objArray,			// array
  objDict,			// dictionary
  objStream,			// stream
  objRef,			// indirect reference

  // special objects
  objCmd,			// command name
  objError,			// error return from Lexer
  objEOF,			// end of file return from Lexer
  objNone			// uninitialized object
};

#define numObjTypes 14		// total number of object types

//------------------------------------------------------------------------
// Object
//------------------------------------------------------------------------

#ifdef DEBUG_MEM
#define initObj(t) ++numAlloc[type = t]
#else
#define initObj(t) type = t
#endif

class Object {
public:

  // Default constructor.
  Object():
    type(objNone) {}

  // Initialize an object.
  Object *initBool(GBool boolnA)
    { initObj(objBool); booln = boolnA; return this; }
  Object *initInt(int intgA)
    { initObj(objInt); intg = intgA; return this; }
  Object *initReal(double realA)
    { initObj(objReal); real = realA; return this; }
  Object *initString(GString *stringA)
    { initObj(objString); string = stringA; return this; }
  Object *initName(char *nameA)
    { initObj(objName); name = copyString(nameA); return this; }
  Object *initNull()
    { initObj(objNull); return this; }
  Object *initArray(XRef *xref);
  Object *initDict(XRef *xref);
  Object *initDict(Dict *dictA);
  Object *initStream(Stream *streamA);
  Object *initRef(int numA, int genA)
    { initObj(objRef); ref.num = numA; ref.gen = genA; return this; }
  Object *initCmd(char *cmdA)
    { initObj(objCmd); cmd = copyString(cmdA); return this; }
  Object *initError()
    { initObj(objError); return this; }
  Object *initEOF()
    { initObj(objEOF); return this; }

  // Copy an object.
  Object *copy(Object *obj);

  // If object is a Ref, fetch and return the referenced object.
  // Otherwise, return a copy of the object.
  Object *fetch(XRef *xref, Object *obj);

  // Free object contents.
  void free();

  // Type checking.
  ObjType getType() { return type; }
  GBool isBool() { return type == objBool; }
  GBool isInt() { return type == objInt; }
  GBool isReal() { return type == objReal; }
  GBool isNum() { return type == objInt || type == objReal; }
  GBool isString() { return type == objString; }
  GBool isName() { return type == objName; }
  GBool isNull() { return type == objNull; }
  GBool isArray() { return type == objArray; }
  GBool isDict() { return type == objDict; }
  GBool isStream() { return type == objStream; }
  GBool isRef() { return type == objRef; }
  GBool isCmd() { return type == objCmd; }
  GBool isError() { return type == objError; }
  GBool isEOF() { return type == objEOF; }
  GBool isNone() { return type == objNone; }

  // Special type checking.
  GBool isName(char *nameA)
    { return type == objName && !strcmp(name, nameA); }
  GBool isDict(char *dictType);
  GBool isStream(char *dictType);
  GBool isCmd(char *cmdA)
    { return type == objCmd && !strcmp(cmd, cmdA); }

  // Accessors.  NB: these assume object is of correct type.
  GBool getBool() { return booln; }
  int getInt() { return intg; }
  double getReal() { return real; }
  double getNum() { return type == objInt ? (double)intg : real; }
  GString *getString() { return string; }
  char *getName() { return name; }
  Array *getArray() { return array; }
  Dict *getDict() { return dict; }
  Stream *getStream() { return stream; }
  Ref getRef() { return ref; }
  int getRefNum() { return ref.num; }
  int getRefGen() { return ref.gen; }
  char *getCmd() { return cmd; }

  // Array accessors.
  int arrayGetLength();
  void arrayAdd(Object *elem);
  Object *arrayGet(int i, Object *obj);
  Object *arrayGetNF(int i, Object *obj);

  // Dict accessors.
  int dictGetLength();
  void dictAdd(char *key, Object *val);
  GBool dictIs(char *dictType);
  Object *dictLookup(char *key, Object *obj);
  Object *dictLookupNF(char *key, Object *obj);
  char *dictGetKey(int i);
  Object *dictGetVal(int i, Object *obj);
  Object *dictGetValNF(int i, Object *obj);

  // Stream accessors.
  GBool streamIs(char *dictType);
  void streamReset();
  void streamClose();
  int streamGetChar();
  int streamLookChar();
  char *streamGetLine(char *buf, int size);
  Guint streamGetPos();
  void streamSetPos(Guint pos, int dir = 0);
  Dict *streamGetDict();

  // Output.
  char *getTypeName();
  void print(FILE *f = stdout);

  // Memory testing.
  static void memCheck(FILE *f);

private:

  ObjType type;			// object type
  union {			// value for each type:
    GBool booln;		//   boolean
    int intg;			//   integer
    double real;		//   real
    GString *string;		//   string
    char *name;			//   name
    Array *array;		//   array
    Dict *dict;			//   dictionary
    Stream *stream;		//   stream
    Ref ref;			//   indirect reference
    char *cmd;			//   command
  };

#ifdef DEBUG_MEM
  static int			// number of each type of object
    numAlloc[numObjTypes];	//   currently allocated
#endif
};

//------------------------------------------------------------------------
// Array accessors.
//------------------------------------------------------------------------

#include "Array.h"

inline int Object::arrayGetLength()
  { return array->getLength(); }

inline void Object::arrayAdd(Object *elem)
  { array->add(elem); }

inline Object *Object::arrayGet(int i, Object *obj)
  { return array->get(i, obj); }

inline Object *Object::arrayGetNF(int i, Object *obj)
  { return array->getNF(i, obj); }

//------------------------------------------------------------------------
// Dict accessors.
//------------------------------------------------------------------------

#include "Dict.h"

inline int Object::dictGetLength()
  { return dict->getLength(); }

inline void Object::dictAdd(char *key, Object *val)
  { dict->add(key, val); }

inline GBool Object::dictIs(char *dictType)
  { return dict->is(dictType); }

inline GBool Object::isDict(char *dictType)
  { return type == objDict && dictIs(dictType); }

inline Object *Object::dictLookup(char *key, Object *obj)
  { return dict->lookup(key, obj); }

inline Object *Object::dictLookupNF(char *key, Object *obj)
  { return dict->lookupNF(key, obj); }

inline char *Object::dictGetKey(int i)
  { return dict->getKey(i); }

inline Object *Object::dictGetVal(int i, Object *obj)
  { return dict->getVal(i, obj); }

inline Object *Object::dictGetValNF(int i, Object *obj)
  { return dict->getValNF(i, obj); }

//------------------------------------------------------------------------
// Stream accessors.
//------------------------------------------------------------------------

#include "Stream.h"

inline GBool Object::streamIs(char *dictType)
  { return stream->getDict()->is(dictType); }

inline GBool Object::isStream(char *dictType)
  { return type == objStream && streamIs(dictType); }

inline void Object::streamReset()
  { stream->reset(); }

inline void Object::streamClose()
  { stream->close(); }

inline int Object::streamGetChar()
  { return stream->getChar(); }

inline int Object::streamLookChar()
  { return stream->lookChar(); }

inline char *Object::streamGetLine(char *buf, int size)
  { return stream->getLine(buf, size); }

inline Guint Object::streamGetPos()
  { return stream->getPos(); }

inline void Object::streamSetPos(Guint pos, int dir)
  { stream->setPos(pos, dir); }

inline Dict *Object::streamGetDict()
  { return stream->getDict(); }

#endif
//========================================================================
//
// Outline.cc
//
// Copyright 2002-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include "gmem.h"
#include "GString.h"
#include "GList.h"
#include "Link.h"
#include "PDFDocEncoding.h"
#include "Outline.h"

//------------------------------------------------------------------------

Outline::Outline(Object *outlineObj, XRef *xref) {
  Object first, last;

  items = NULL;
  if (!outlineObj->isDict()) {
    return;
  }
  items = OutlineItem::readItemList(outlineObj->dictLookupNF("First", &first),
				    outlineObj->dictLookupNF("Last", &last),
				    xref);
  first.free();
  last.free();
}

Outline::~Outline() {
  if (items) {
    deleteGList(items, OutlineItem);
  }
}

//------------------------------------------------------------------------

OutlineItem::OutlineItem(Dict *dict, XRef *xrefA) {
  Object obj1;
  GString *s;
  int i;

  xref = xrefA;
  title = NULL;
  action = NULL;
  kids = NULL;

  if (dict->lookup("Title", &obj1)->isString()) {
    s = obj1.getString();
    if ((s->getChar(0) & 0xff) == 0xfe &&
	(s->getChar(1) & 0xff) == 0xff) {
      titleLen = (s->getLength() - 2) / 2;
      title = (Unicode *)gmallocn(titleLen, sizeof(Unicode));
      for (i = 0; i < titleLen; ++i) {
	title[i] = ((s->getChar(2 + 2*i) & 0xff) << 8) |
	           (s->getChar(3 + 2*i) & 0xff);
      }
    } else {
      titleLen = s->getLength();
      title = (Unicode *)gmallocn(titleLen, sizeof(Unicode));
      for (i = 0; i < titleLen; ++i) {
	title[i] = pdfDocEncoding[s->getChar(i) & 0xff];
      }
    }
  } else {
    titleLen = 0;
  }
  obj1.free();

  if (!dict->lookup("Dest", &obj1)->isNull()) {
    action = LinkAction::parseDest(&obj1);
  } else {
    obj1.free();
    if (!dict->lookup("A", &obj1)->isNull()) {
      action = LinkAction::parseAction(&obj1);
    }
  }
  obj1.free();

  dict->lookupNF("First", &firstRef);
  dict->lookupNF("Last", &lastRef);
  dict->lookupNF("Next", &nextRef);

  startsOpen = gFalse;
  if (dict->lookup("Count", &obj1)->isInt()) {
    if (obj1.getInt() > 0) {
      startsOpen = gTrue;
    }
  }
  obj1.free();
}

OutlineItem::~OutlineItem() {
  close();
  if (title) {
    gfree(title);
  }
  if (action) {
    delete action;
  }
  firstRef.free();
  lastRef.free();
  nextRef.free();
}

GList *OutlineItem::readItemList(Object *firstItemRef, Object *lastItemRef,
				 XRef *xrefA) {
  GList *items;
  OutlineItem *item;
  Object obj;
  Object *p;

  items = new GList();
  p = firstItemRef;
  while (p->isRef()) {
    if (!p->fetch(xrefA, &obj)->isDict()) {
      obj.free();
      break;
    }
    item = new OutlineItem(obj.getDict(), xrefA);
    obj.free();
    items->append(item);
    if (p->getRef().num == lastItemRef->getRef().num &&
	p->getRef().gen == lastItemRef->getRef().gen) {
      break;
    }
    p = &item->nextRef;
  }
  return items;
}

void OutlineItem::open() {
  if (!kids) {
    kids = readItemList(&firstRef, &lastRef, xref);
  }
}

void OutlineItem::close() {
  if (kids) {
    deleteGList(kids, OutlineItem);
    kids = NULL;
  }
}
//========================================================================
//
// Outline.h
//
// Copyright 2002-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef OUTLINE_H
#define OUTLINE_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "Object.h"
#include "CharTypes.h"

class GString;
class GList;
class XRef;
class LinkAction;

//------------------------------------------------------------------------

class Outline {
public:

  Outline(Object *outlineObj, XRef *xref);
  ~Outline();

  GList *getItems() { return items; }

private:

  GList *items;			// NULL if document has no outline
				//   [OutlineItem]
};

//------------------------------------------------------------------------

class OutlineItem {
public:

  OutlineItem(Dict *dict, XRef *xrefA);
  ~OutlineItem();

  static GList *readItemList(Object *firstItemRef, Object *lastItemRef,
			     XRef *xrefA);

  void open();
  void close();

  Unicode *getTitle() { return title; }
  int getTitleLength() { return titleLen; }
  LinkAction *getAction() { return action; }
  GBool isOpen() { return startsOpen; }
  GBool hasKids() { return firstRef.isRef(); }
  GList *getKids() { return kids; }

private:

  XRef *xref;
  Unicode *title;
  int titleLen;
  LinkAction *action;
  Object firstRef;
  Object lastRef;
  Object nextRef;
  GBool startsOpen;
  GList *kids;			// NULL unless this item is open [OutlineItem]
};

#endif
//========================================================================
//
// OutputDev.cc
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stddef.h>
#include "Object.h"
#include "Stream.h"
#include "GfxState.h"
#include "OutputDev.h"

//------------------------------------------------------------------------
// OutputDev
//------------------------------------------------------------------------

void OutputDev::setDefaultCTM(double *ctm) {
  int i;
  double det;

  for (i = 0; i < 6; ++i) {
    defCTM[i] = ctm[i];
  }
  det = 1 / (defCTM[0] * defCTM[3] - defCTM[1] * defCTM[2]);
  defICTM[0] = defCTM[3] * det;
  defICTM[1] = -defCTM[1] * det;
  defICTM[2] = -defCTM[2] * det;
  defICTM[3] = defCTM[0] * det;
  defICTM[4] = (defCTM[2] * defCTM[5] - defCTM[3] * defCTM[4]) * det;
  defICTM[5] = (defCTM[1] * defCTM[4] - defCTM[0] * defCTM[5]) * det;
}

void OutputDev::cvtDevToUser(double dx, double dy, double *ux, double *uy) {
  *ux = defICTM[0] * dx + defICTM[2] * dy + defICTM[4];
  *uy = defICTM[1] * dx + defICTM[3] * dy + defICTM[5];
}

void OutputDev::cvtUserToDev(double ux, double uy, int *dx, int *dy) {
  *dx = (int)(defCTM[0] * ux + defCTM[2] * uy + defCTM[4] + 0.5);
  *dy = (int)(defCTM[1] * ux + defCTM[3] * uy + defCTM[5] + 0.5);
}

void OutputDev::updateAll(GfxState *state) {
  updateLineDash(state);
  updateFlatness(state);
  updateLineJoin(state);
  updateLineCap(state);
  updateMiterLimit(state);
  updateLineWidth(state);
  updateStrokeAdjust(state);
  updateFillColorSpace(state);
  updateFillColor(state);
  updateStrokeColorSpace(state);
  updateStrokeColor(state);
  updateBlendMode(state);
  updateFillOpacity(state);
  updateStrokeOpacity(state);
  updateFillOverprint(state);
  updateStrokeOverprint(state);
  updateTransfer(state);
  updateFont(state);
}

GBool OutputDev::beginType3Char(GfxState *state, double x, double y,
				double dx, double dy,
				CharCode code, Unicode *u, int uLen) {
  return gFalse;
}

void OutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
			      int width, int height, GBool invert,
			      GBool inlineImg) {
  int i, j;

  if (inlineImg) {
    str->reset();
    j = height * ((width + 7) / 8);
    for (i = 0; i < j; ++i)
      str->getChar();
    str->close();
  }
}

void OutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
			  int width, int height, GfxImageColorMap *colorMap,
			  int *maskColors, GBool inlineImg) {
  int i, j;

  if (inlineImg) {
    str->reset();
    j = height * ((width * colorMap->getNumPixelComps() *
		   colorMap->getBits() + 7) / 8);
    for (i = 0; i < j; ++i)
      str->getChar();
    str->close();
  }
}

void OutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
				int width, int height,
				GfxImageColorMap *colorMap,
				Stream *maskStr,
				int maskWidth, int maskHeight,
				GBool maskInvert) {
  drawImage(state, ref, str, width, height, colorMap, NULL, gFalse);
}

void OutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
				    int width, int height,
				    GfxImageColorMap *colorMap,
				    Stream *maskStr,
				    int maskWidth, int maskHeight,
				    GfxImageColorMap *maskColorMap) {
  drawImage(state, ref, str, width, height, colorMap, NULL, gFalse);
}

#if OPI_SUPPORT
void OutputDev::opiBegin(GfxState *state, Dict *opiDict) {
}

void OutputDev::opiEnd(GfxState *state, Dict *opiDict) {
}
#endif
//========================================================================
//
// OutputDev.h
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef OUTPUTDEV_H
#define OUTPUTDEV_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "gtypes.h"
#include "CharTypes.h"

class GString;
class GfxState;
struct GfxColor;
class GfxColorSpace;
class GfxImageColorMap;
class GfxFunctionShading;
class GfxAxialShading;
class GfxRadialShading;
class Stream;
class Links;
class Link;
class Catalog;
class Page;
class Function;

//------------------------------------------------------------------------
// OutputDev
//------------------------------------------------------------------------

class OutputDev {
public:

  // Constructor.
  OutputDev() {}

  // Destructor.
  virtual ~OutputDev() {}

  //----- get info about output device

  // Does this device use upside-down coordinates?
  // (Upside-down means (0,0) is the top left corner of the page.)
  virtual GBool upsideDown() = 0;

  // Does this device use drawChar() or drawString()?
  virtual GBool useDrawChar() = 0;

  // Does this device use tilingPatternFill()?  If this returns false,
  // tiling pattern fills will be reduced to a series of other drawing
  // operations.
  virtual GBool useTilingPatternFill() { return gFalse; }

  // Does this device use functionShadedFill(), axialShadedFill(), and
  // radialShadedFill()?  If this returns false, these shaded fills
  // will be reduced to a series of other drawing operations.
  virtual GBool useShadedFills() { return gFalse; }

  // Does this device use drawForm()?  If this returns false,
  // form-type XObjects will be interpreted (i.e., unrolled).
  virtual GBool useDrawForm() { return gFalse; }

  // Does this device use beginType3Char/endType3Char?  Otherwise,
  // text in Type 3 fonts will be drawn with drawChar/drawString.
  virtual GBool interpretType3Chars() = 0;

  // Does this device need non-text content?
  virtual GBool needNonText() { return gTrue; }

  //----- initialization and control

  // Set default transform matrix.
  virtual void setDefaultCTM(double *ctm);

  // Check to see if a page slice should be displayed.  If this
  // returns false, the page display is aborted.  Typically, an
  // OutputDev will use some alternate means to display the page
  // before returning false.
  virtual GBool checkPageSlice(Page *page, double hDPI, double vDPI,
			       int rotate, GBool useMediaBox, GBool crop,
			       int sliceX, int sliceY, int sliceW, int sliceH,
			       GBool printing, Catalog *catalog,
			       GBool (*abortCheckCbk)(void *data) = NULL,
			       void *abortCheckCbkData = NULL)
    { return gTrue; }

  // Start a page.
  virtual void startPage(int pageNum, GfxState *state, double x1,double y1,double x2,double y2) {}

  // End a page.
  virtual void endPage() {}

  // Dump page contents to display.
  virtual void dump() {}

  //----- coordinate conversion

  // Convert between device and user coordinates.
  virtual void cvtDevToUser(double dx, double dy, double *ux, double *uy);
  virtual void cvtUserToDev(double ux, double uy, int *dx, int *dy);

  double *getDefCTM() { return defCTM; }
  double *getDefICTM() { return defICTM; }
    
  //----- link borders
  virtual void drawLink(Link *link, Catalog *catalog) {}    

  //----- save/restore graphics state
  virtual void saveState(GfxState *state) {}
  virtual void restoreState(GfxState *state) {}

  //----- update graphics state
  virtual void updateAll(GfxState *state);
  virtual void updateCTM(GfxState *state, double m11, double m12,
			 double m21, double m22, double m31, double m32) {}
  virtual void updateLineDash(GfxState *state) {}
  virtual void updateFlatness(GfxState *state) {}
  virtual void updateLineJoin(GfxState *state) {}
  virtual void updateLineCap(GfxState *state) {}
  virtual void updateMiterLimit(GfxState *state) {}
  virtual void updateLineWidth(GfxState *state) {}
  virtual void updateStrokeAdjust(GfxState *state) {}
  virtual void updateFillColorSpace(GfxState *state) {}
  virtual void updateStrokeColorSpace(GfxState *state) {}
  virtual void updateFillColor(GfxState *state) {}
  virtual void updateStrokeColor(GfxState *state) {}
  virtual void updateBlendMode(GfxState *state) {}
  virtual void updateFillOpacity(GfxState *state) {}
  virtual void updateStrokeOpacity(GfxState *state) {}
  virtual void updateFillOverprint(GfxState *state) {}
  virtual void updateStrokeOverprint(GfxState *state) {}
  virtual void updateTransfer(GfxState *state) {}

  //----- update text state
  virtual void updateFont(GfxState *state) {}
  virtual void updateTextMat(GfxState *state) {}
  virtual void updateCharSpace(GfxState *state) {}
  virtual void updateRender(GfxState *state) {}
  virtual void updateRise(GfxState *state) {}
  virtual void updateWordSpace(GfxState *state) {}
  virtual void updateHorizScaling(GfxState *state) {}
  virtual void updateTextPos(GfxState *state) {}
  virtual void updateTextShift(GfxState *state, double shift) {}

  //----- path painting
  virtual void stroke(GfxState *state) {}
  virtual void fill(GfxState *state) {}
  virtual void eoFill(GfxState *state) {}
  virtual void tilingPatternFill(GfxState *state, Object *str,
				 int paintType, Dict *resDict,
				 double *mat, double *bbox,
				 int x0, int y0, int x1, int y1,
				 double xStep, double yStep) {}
  virtual GBool functionShadedFill(GfxState *state,
				   GfxFunctionShading *shading)
    { return gFalse; }
  virtual GBool axialShadedFill(GfxState *state, GfxAxialShading *shading)
    { return gFalse; }
  virtual GBool radialShadedFill(GfxState *state, GfxRadialShading *shading)
    { return gFalse; }

  //----- path clipping
  virtual void clip(GfxState *state) {}
  virtual void eoClip(GfxState *state) {}
  virtual void clipToStrokePath(GfxState *state) {}

  //----- text drawing
  virtual void beginStringOp(GfxState *state) {}
  virtual void endStringOp(GfxState *state) {}
  virtual void beginString(GfxState *state, GString *s) {}
  virtual void endString(GfxState *state) {}
  virtual void drawChar(GfxState *state, double x, double y,
			double dx, double dy,
			double originX, double originY,
			CharCode code, int nBytes, Unicode *u, int uLen) {}
  virtual void drawString(GfxState *state, GString *s) {}
  virtual GBool beginType3Char(GfxState *state, double x, double y,
			       double dx, double dy,
			       CharCode code, Unicode *u, int uLen);
  virtual void endType3Char(GfxState *state) {}
  virtual void endTextObject(GfxState *state) {}

  //----- image drawing
  virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
			     int width, int height, GBool invert,
			     GBool inlineImg);
  virtual void drawImage(GfxState *state, Object *ref, Stream *str,
			 int width, int height, GfxImageColorMap *colorMap,
			 int *maskColors, GBool inlineImg);
  virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str,
			       int width, int height,
			       GfxImageColorMap *colorMap,
			       Stream *maskStr, int maskWidth, int maskHeight,
			       GBool maskInvert);
  virtual void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
				   int width, int height,
				   GfxImageColorMap *colorMap,
				   Stream *maskStr,
				   int maskWidth, int maskHeight,
				   GfxImageColorMap *maskColorMap);

#if OPI_SUPPORT
  //----- OPI functions
  virtual void opiBegin(GfxState *state, Dict *opiDict);
  virtual void opiEnd(GfxState *state, Dict *opiDict);
#endif

  //----- Type 3 font operators
  virtual void type3D0(GfxState *state, double wx, double wy) {}
  virtual void type3D1(GfxState *state, double wx, double wy,
		       double llx, double lly, double urx, double ury) {}

  //----- form XObjects
  virtual void drawForm(Ref id) {}

  //----- PostScript XObjects
  virtual void psXObject(Stream *psStream, Stream *level1Stream) {}

  //----- transparency groups and soft masks
  virtual void beginTransparencyGroup(GfxState *state, double *bbox,
				      GfxColorSpace *blendingColorSpace,
				      GBool isolated, GBool knockout,
				      GBool forSoftMask) {}
  virtual void endTransparencyGroup(GfxState *state) {}
  virtual void paintTransparencyGroup(GfxState *state, double *bbox) {}
  virtual void setSoftMask(GfxState *state, double *bbox, GBool alpha,
			   Function *transferFunc, GfxColor *backdropColor) {}
  virtual void clearSoftMask(GfxState *state) {}

  //----- links
  virtual void processLink(Link *link, Catalog *catalog) {}

#if 1 //~tmp: turn off anti-aliasing temporarily
  virtual GBool getVectorAntialias() { return gFalse; }
  virtual void setVectorAntialias(GBool vaa) {}
#endif

private:

  double defCTM[6];		// default coordinate transform matrix
  double defICTM[6];		// inverse of default CTM
};

#endif
//========================================================================
//
// PDFCore.cc
//
// Copyright 2004 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <math.h>
#include "GString.h"
#include "GList.h"
#include "GlobalParams.h"
#include "Splash.h"
#include "SplashBitmap.h"
#include "SplashPattern.h"
#include "SplashPath.h"
#include "Error.h"
#include "ErrorCodes.h"
#include "PDFDoc.h"
#include "Link.h"
#include "TextOutputDev.h"
#include "CoreOutputDev.h"
#include "PDFCore.h"

//------------------------------------------------------------------------
// PDFCorePage
//------------------------------------------------------------------------

PDFCorePage::PDFCorePage(int pageA, int wA, int hA, int tileWA, int tileHA) {
  page = pageA;
  tiles = new GList();
  w = wA;
  h = hA;
  tileW = tileWA;
  tileH = tileHA;
  links = NULL;
  text = NULL;
}

PDFCorePage::~PDFCorePage() {
  deleteGList(tiles, PDFCoreTile);
  if (links) {
    delete links;
  }
  if (text) {
    delete text;
  }
}

//------------------------------------------------------------------------
// PDFCoreTile
//------------------------------------------------------------------------

PDFCoreTile::PDFCoreTile(int xDestA, int yDestA) {
  xMin = 0;
  yMin = 0;
  xMax = 0;
  yMax = 0;
  xDest = xDestA;
  yDest = yDestA;
  bitmap = NULL;
}

PDFCoreTile::~PDFCoreTile() {
  if (bitmap) {
    delete bitmap;
  }
}


//------------------------------------------------------------------------
// PDFCore
//------------------------------------------------------------------------

PDFCore::PDFCore(SplashColorMode colorModeA, int bitmapRowPadA,
		 GBool reverseVideoA, SplashColorPtr paperColorA,
		 GBool incrementalUpdate) {
  int i;

  doc = NULL;
  continuousMode = globalParams->getContinuousView();
  drawAreaWidth = drawAreaHeight = 0;
  maxPageW = totalDocH = 0;
  pageY = NULL;
  topPage = 0;
  scrollX = scrollY = 0;
  zoom = defZoom;
  dpi = 0;
  rotate = 0;

  selectPage = 0;
  selectULX = selectLRX = 0;
  selectULY = selectLRY = 0;
  dragging = gFalse;
  lastDragLeft = lastDragTop = gTrue;

  historyCur = pdfHistorySize - 1;
  historyBLen = historyFLen = 0;
  for (i = 0; i < pdfHistorySize; ++i) {
    history[i].fileName = NULL;
  }


  pages = new GList();
  curTile = NULL;

  splashColorCopy(paperColor, paperColorA);
  out = new CoreOutputDev(colorModeA, bitmapRowPadA,
			  reverseVideoA, paperColorA, incrementalUpdate,
			  &redrawCbk, this);
  out->startDoc(NULL);
}

PDFCore::~PDFCore() {
  int i;

  if (doc) {
    delete doc;
  }
  for (i = 0; i < pdfHistorySize; ++i) {
    if (history[i].fileName) {
      delete history[i].fileName;
    }
  }
  gfree(pageY);
  deleteGList(pages, PDFCorePage);
  delete out;
}

int PDFCore::loadFile(GString *fileName, GString *ownerPassword,
		      GString *userPassword) {
  int err;

  setBusyCursor(gTrue);
  err = loadFile2(new PDFDoc(fileName->copy(), ownerPassword, userPassword,
			     this));
  setBusyCursor(gFalse);
  return err;
}

#ifdef WIN32
int PDFCore::loadFile(wchar_t *fileName, int fileNameLen,
		      GString *ownerPassword, GString *userPassword) {
  int err;

  setBusyCursor(gTrue);
  err = loadFile2(new PDFDoc(fileName, fileNameLen,
			     ownerPassword, userPassword, this));
  setBusyCursor(gFalse);
  return err;
}
#endif

int PDFCore::loadFile(BaseStream *stream, GString *ownerPassword,
		      GString *userPassword) {
  int err;

  setBusyCursor(gTrue);
  err = loadFile2(new PDFDoc(stream, ownerPassword, userPassword, this));
  setBusyCursor(gFalse);
  return err;
}

void PDFCore::loadDoc(PDFDoc *docA) {
  setBusyCursor(gTrue);
  loadFile2(docA);
  setBusyCursor(gFalse);
}

int PDFCore::loadFile2(PDFDoc *newDoc) {
  int err;
  double w, h, t;
  int i;

  // open the PDF file
  if (!newDoc->isOk()) {
    err = newDoc->getErrorCode();
    delete newDoc;
    return err;
  }

  // replace old document
  if (doc) {
    delete doc;
  }
  doc = newDoc;
  if (out) {
    out->startDoc(doc->getXRef());
  }

  // nothing displayed yet
  topPage = -99;
  while (pages->getLength() > 0) {
    delete (PDFCorePage *)pages->del(0);
  }

  // compute the max unscaled page size
  maxUnscaledPageW = maxUnscaledPageH = 0;
  for (i = 1; i <= doc->getNumPages(); ++i) {
    w = doc->getPageCropWidth(i);
    h = doc->getPageCropHeight(i);
    if (doc->getPageRotate(i) == 90 || doc->getPageRotate(i) == 270) {
      t = w; w = h; h = t;
    }
    if (w > maxUnscaledPageW) {
      maxUnscaledPageW = w;
    }
    if (h > maxUnscaledPageH) {
      maxUnscaledPageH = h;
    }
  }

  return errNone;
}

void PDFCore::clear() {
  if (!doc) {
    return;
  }

  // no document
  delete doc;
  doc = NULL;
  out->clear();

  // no page displayed
  topPage = -99;
  while (pages->getLength() > 0) {
    delete (PDFCorePage *)pages->del(0);
  }

  // redraw
  scrollX = scrollY = 0;
  redrawWindow(0, 0, drawAreaWidth, drawAreaHeight, gTrue);
  updateScrollbars();
}

PDFDoc *PDFCore::takeDoc(GBool redraw) {
  PDFDoc *docA;

  if (!doc) {
    return NULL;
  }

  // no document
  docA = doc;
  doc = NULL;
  out->clear();

  // no page displayed
  topPage = -99;
  while (pages->getLength() > 0) {
    delete (PDFCorePage *)pages->del(0);
  }

  // redraw
  scrollX = scrollY = 0;
  if (redraw) {
    redrawWindow(0, 0, drawAreaWidth, drawAreaHeight, gTrue);
    updateScrollbars();
  }

  return docA;
}

void PDFCore::displayPage(int topPageA, double zoomA, int rotateA,
			  GBool scrollToTop, GBool addToHist) {
  int scrollXA, scrollYA;

  scrollXA = scrollX;
  if (continuousMode) {
    scrollYA = -1;
  } else if (scrollToTop) {
    scrollYA = 0;
  } else {
    scrollYA = scrollY;
  }
  if (zoomA != zoom) {
    scrollXA = 0;
    scrollYA = continuousMode ? -1 : 0;
  }

  dragging = gFalse;
  lastDragLeft = lastDragTop = gTrue;

  update(topPageA, scrollXA, scrollYA, zoomA, rotateA, gTrue, addToHist);
}

void PDFCore::displayDest(LinkDest *dest, double zoomA, int rotateA,
			  GBool addToHist) {
  Ref pageRef;
  int topPageA;
  int dx, dy, scrollXA, scrollYA;

  if (dest->isPageRef()) {
    pageRef = dest->getPageRef();
    topPageA = doc->findPage(pageRef.num, pageRef.gen);
  } else {
    topPageA = dest->getPageNum();
  }
  if (topPageA <= 0 || topPageA > doc->getNumPages()) {
    topPageA = 1;
  }
  scrollXA = scrollX;
  scrollYA = continuousMode ? -1 : scrollY;
  switch (dest->getKind()) {
  case destXYZ:
    cvtUserToDev(topPageA, dest->getLeft(), dest->getTop(), &dx, &dy);
    scrollXA = dest->getChangeLeft() ? dx : scrollX;
    if (dest->getChangeTop()) {
      scrollYA = dy;
    } else {
      if (topPage <= 0) {
	scrollYA = 0;
      } else if (continuousMode) {
	scrollYA = scrollY - pageY[topPage - 1];
      } else {
	scrollYA = scrollY;
      }
    }
    if (continuousMode && topPage > 0) {
      scrollYA += pageY[topPageA - 1];
    }
    //~ what is the zoom parameter?
    break;
  case destFit:
  case destFitB:
    //~ do fit
    scrollXA = 0;
    scrollYA = continuousMode ? -1 : 0;
    break;
  case destFitH:
  case destFitBH:
    //~ do fit
    cvtUserToDev(topPageA, 0, dest->getTop(), &dx, &dy);
    if (continuousMode && topPage > 0) {
      dy += pageY[topPageA - 1];
    }
    scrollXA = 0;
    scrollYA = dy;
    break;
  case destFitV:
  case destFitBV:
    //~ do fit
    cvtUserToDev(topPageA, dest->getLeft(), 0, &dx, &dy);
    scrollXA = dx;
    scrollYA = continuousMode ? -1 : 0;
    break;
  case destFitR:
    //~ do fit
    cvtUserToDev(topPageA, dest->getLeft(), dest->getTop(), &dx, &dy);
    if (continuousMode && topPage > 0) {
      dy += pageY[topPageA - 1];
    }
    scrollXA = dx;
    scrollYA = dy;
    break;
  }
  update(topPageA, scrollXA, scrollYA, zoom, rotate, gFalse,
	 addToHist && topPageA != topPage);
}

void PDFCore::update(int topPageA, int scrollXA, int scrollYA,
		     double zoomA, int rotateA, GBool force, GBool addToHist) {
  double hDPI, vDPI, dpiA, uw, uh, ut;
  int w, h, t, x0, x1, y0, y1, x, y;
  int rot;
  int pg0, pg1;
  PDFCoreTile *tile;
  PDFCorePage *page;
  PDFHistory *hist;
  SplashColor xorColor;
  GBool needUpdate;
  int i, j;

  // check for document and valid page number
  if (!doc) {
    // save the new settings
    zoom = zoomA;
    rotate = rotateA;
    return;
  }
  if (topPageA <= 0 || topPageA > doc->getNumPages()) {
    return;
  }

  needUpdate = gFalse;

  // check for changes to the PDF file
  if ((force || (!continuousMode && topPage != topPageA)) &&
      checkForNewFile()) {
    if (loadFile(doc->getFileName()) == errNone) {
      if (topPageA > doc->getNumPages()) {
	topPageA = doc->getNumPages();
      }
      needUpdate = gTrue;
    }
  }

  // compute the DPI
  if (continuousMode) {
    uw = maxUnscaledPageW;
    uh = maxUnscaledPageH;
    rot = rotateA;
  } else {
    uw = doc->getPageCropWidth(topPageA);
    uh = doc->getPageCropHeight(topPageA);
    rot = rotateA + doc->getPageRotate(topPageA);
    if (rot >= 360) {
      rot -= 360;
    } else if (rot < 0) {
      rot += 360;
    }
  }
  if (rot == 90 || rot == 270) {
    ut = uw; uw = uh; uh = ut;
  }
  if (zoomA == zoomPage) {
    hDPI = (drawAreaWidth / uw) * 72;
    if (continuousMode) {
      vDPI = ((drawAreaHeight - continuousModePageSpacing) / uh) * 72;
    } else {
      vDPI = (drawAreaHeight / uh) * 72;
    }
    dpiA = (hDPI < vDPI) ? hDPI : vDPI;
  } else if (zoomA == zoomWidth) {
    dpiA = (drawAreaWidth / uw) * 72;
  } else {
    dpiA = 0.01 * zoomA * 72;
  }
  // this can happen if the window hasn't been sized yet
  if (dpiA <= 0) {
    dpiA = 1;
  }

  // if the display properties have changed, create a new PDFCorePage
  // object
  if (force || pages->getLength() == 0 ||
      (!continuousMode && topPageA != topPage) ||
      zoomA != zoom || dpiA != dpi || rotateA != rotate) {
    needUpdate = gTrue;
    setSelection(0, 0, 0, 0, 0);
    while (pages->getLength() > 0) {
      delete (PDFCorePage *)pages->del(0);
    }
    zoom = zoomA;
    rotate = rotateA;
    dpi = dpiA;
    if (continuousMode) {
      maxPageW = totalDocH = 0;
      pageY = (int *)greallocn(pageY, doc->getNumPages(), sizeof(int));
      for (i = 1; i <= doc->getNumPages(); ++i) {
	pageY[i-1] = totalDocH;
	w = (int)((doc->getPageCropWidth(i) * dpi) / 72 + 0.5);
	h = (int)((doc->getPageCropHeight(i) * dpi) / 72 + 0.5);
	rot = rotate + doc->getPageRotate(i);
	if (rot >= 360) {
	  rot -= 360;
	} else if (rot < 0) {
	  rot += 360;
	}
	if (rot == 90 || rot == 270) {
	  t = w; w = h; h = t;
	}
	if (w > maxPageW) {
	  maxPageW = w;
	}
	totalDocH += h;
	if (i < doc->getNumPages()) {
	  totalDocH += continuousModePageSpacing;
	}
      }
    } else {
      rot = rotate + doc->getPageRotate(topPageA);
      if (rot >= 360) {
	rot -= 360;
      } else if (rot < 0) {
	rot += 360;
      }
      addPage(topPageA, rot);
    }
  } else {
    // erase the selection
    if (selectULX != selectLRX && selectULY != selectLRY) {
      xorColor[0] = xorColor[1] = xorColor[2] = 0xff;
      xorRectangle(selectPage, selectULX, selectULY, selectLRX, selectLRY,
		   new SplashSolidColor(xorColor));
    }
  }
  if (continuousMode) {
    page = NULL; // make gcc happy
  } else {
    page = (PDFCorePage *)pages->get(0);
  }
  topPage = topPageA;

  // adjust the scroll position
  scrollX = scrollXA;
  if (continuousMode && scrollYA < 0) {
    scrollY = pageY[topPage - 1];
  } else {
    scrollY = scrollYA;
  }
  w = continuousMode ? maxPageW : page->w;
  if (scrollX > w - drawAreaWidth) {
    scrollX = w - drawAreaWidth;
  }
  if (scrollX < 0) {
    scrollX = 0;
  }
  h = continuousMode ? totalDocH : page->h;
  if (scrollY > h - drawAreaHeight) {
    scrollY = h - drawAreaHeight;
  }
  if (scrollY < 0) {
    scrollY = 0;
  }

  // find topPage, and the first and last pages to be rasterized
  if (continuousMode) {
    //~ should use a binary search
    for (i = 2; i <= doc->getNumPages(); ++i) {
      if (pageY[i-1] > scrollY - drawAreaHeight / 2) {
	break;
      }
    }
    pg0 = i - 1;
    for (i = pg0 + 1; i <= doc->getNumPages(); ++i) {
      if (pageY[i-1] > scrollY) {
	break;
      }
    }
    topPage = i - 1;
    for (i = topPage + 1; i <= doc->getNumPages(); ++i) {
      if (pageY[i-1] > scrollY + drawAreaHeight + drawAreaHeight / 2) {
	break;
      }
    }
    pg1 = i - 1;

    // delete pages that are no longer needed and insert new pages
    // objects that are needed
    while (pages->getLength() > 0 &&
	   ((PDFCorePage *)pages->get(0))->page < pg0) {
      delete (PDFCorePage *)pages->del(0);
    }
    i = pages->getLength() - 1;
    while (i > 0 && ((PDFCorePage *)pages->get(i))->page > pg1) {
      delete (PDFCorePage *)pages->del(i--);
    }
    j = pages->getLength() > 0 ? ((PDFCorePage *)pages->get(0))->page - 1
                               : pg1;
    for (i = pg0; i <= j; ++i) {
      rot = rotate + doc->getPageRotate(i);
      if (rot >= 360) {
	rot -= 360;
      } else if (rot < 0) {
	rot += 360;
      }
      addPage(i, rot);
    }      
    j = ((PDFCorePage *)pages->get(pages->getLength() - 1))->page;
    for (i = j + 1; i <= pg1; ++i) {
      rot = rotate + doc->getPageRotate(i);
      if (rot >= 360) {
	rot -= 360;
      } else if (rot < 0) {
	rot += 360;
      }
      addPage(i, rot);
    }

  } else {
    pg0 = pg1 = topPage;
  }

  // delete tiles that are no longer needed
  for (i = 0; i < pages->getLength(); ++i) {
    page = (PDFCorePage *)pages->get(i);
    j = 0;
    while (j < page->tiles->getLength()) {
      tile = (PDFCoreTile *)page->tiles->get(j);
      if (continuousMode) {
	y0 = pageY[page->page - 1] + tile->yMin;
	y1 = pageY[page->page - 1] + tile->yMax;
      } else {
	y0 = tile->yMin;
	y1 = tile->yMax;
      }
      if (tile->xMax < scrollX - drawAreaWidth / 2 ||
	  tile->xMin > scrollX + drawAreaWidth + drawAreaWidth / 2 ||
	  y1 < scrollY - drawAreaHeight / 2 ||
	  y0 > scrollY + drawAreaHeight + drawAreaHeight / 2) {
	delete (PDFCoreTile *)page->tiles->del(j);
      } else {
	++j;
      }
    }
  }

  // update page positions
  for (i = 0; i < pages->getLength(); ++i) {
    page = (PDFCorePage *)pages->get(i);
    page->xDest = -scrollX;
    if (continuousMode) {
      page->yDest = pageY[page->page - 1] - scrollY;
    } else {
      page->yDest = -scrollY;
    }
    if (continuousMode) {
      if (page->w < maxPageW) {
	page->xDest += (maxPageW - page->w) / 2;
      }
      if (maxPageW < drawAreaWidth) {
	page->xDest += (drawAreaWidth - maxPageW) / 2;
      }
    } else if (page->w < drawAreaWidth) {
      page->xDest += (drawAreaWidth - page->w) / 2;
    }
    if (continuousMode && totalDocH < drawAreaHeight) {
      page->yDest += (drawAreaHeight - totalDocH) / 2;
    } else if (!continuousMode && page->h < drawAreaHeight) {
      page->yDest += (drawAreaHeight - page->h) / 2;
    }
  }

  // rasterize any new tiles
  for (i = 0; i < pages->getLength(); ++i) {
    page = (PDFCorePage *)pages->get(i);
    x0 = page->xDest;
    x1 = x0 + page->w - 1;
    if (x0 < -drawAreaWidth / 2) {
      x0 = -drawAreaWidth / 2;
    }
    if (x1 > drawAreaWidth + drawAreaWidth / 2) {
      x1 = drawAreaWidth + drawAreaWidth / 2;
    }
    x0 = ((x0 - page->xDest) / page->tileW) * page->tileW;
    x1 = ((x1 - page->xDest) / page->tileW) * page->tileW;
    y0 = page->yDest;
    y1 = y0 + page->h - 1;
    if (y0 < -drawAreaHeight / 2) {
      y0 = -drawAreaHeight / 2;
    }
    if (y1 > drawAreaHeight + drawAreaHeight / 2) {
      y1 = drawAreaHeight + drawAreaHeight / 2;
    }
    y0 = ((y0 - page->yDest) / page->tileH) * page->tileH;
    y1 = ((y1 - page->yDest) / page->tileH) * page->tileH;
    for (y = y0; y <= y1; y += page->tileH) {
      for (x = x0; x <= x1; x += page->tileW) {
	needTile(page, x, y);
      }
    }
  }

  // update tile positions
  for (i = 0; i < pages->getLength(); ++i) {
    page = (PDFCorePage *)pages->get(i);
    for (j = 0; j < page->tiles->getLength(); ++j) {
      tile = (PDFCoreTile *)page->tiles->get(j);
      tile->xDest = tile->xMin - scrollX;
      if (continuousMode) {
	tile->yDest = tile->yMin + pageY[page->page - 1] - scrollY;
      } else {
	tile->yDest = tile->yMin - scrollY;
      }
      if (continuousMode) {
	if (page->w < maxPageW) {
	  tile->xDest += (maxPageW - page->w) / 2;
	}
	if (maxPageW < drawAreaWidth) {
	  tile->xDest += (drawAreaWidth - maxPageW) / 2;
	}
      } else if (page->w < drawAreaWidth) {
	tile->xDest += (drawAreaWidth - page->w) / 2;
      }
      if (continuousMode && totalDocH < drawAreaHeight) {
	tile->yDest += (drawAreaHeight - totalDocH) / 2;
      } else if (!continuousMode && page->h < drawAreaHeight) {
	tile->yDest += (drawAreaHeight - page->h) / 2;
      }
    }
  }

  // redraw the selection
  if (selectULX != selectLRX && selectULY != selectLRY) {
    xorColor[0] = xorColor[1] = xorColor[2] = 0xff;
    xorRectangle(selectPage, selectULX, selectULY, selectLRX, selectLRY,
		 new SplashSolidColor(xorColor));
  }

  // redraw the window
  redrawWindow(0, 0, drawAreaWidth, drawAreaHeight, needUpdate);
  updateScrollbars();

  // add to history
  if (addToHist) {
    if (++historyCur == pdfHistorySize) {
      historyCur = 0;
    }
    hist = &history[historyCur];
    if (hist->fileName) {
      delete hist->fileName;
    }
    if (doc->getFileName()) {
      hist->fileName = doc->getFileName()->copy();
    } else {
      hist->fileName = NULL;
    }
    hist->page = topPage;
    if (historyBLen < pdfHistorySize) {
      ++historyBLen;
    }
    historyFLen = 0;
  }
}

void PDFCore::addPage(int pg, int rot) {
  PDFCorePage *page;
  int w, h, t, tileW, tileH, i;

  w = (int)((doc->getPageCropWidth(pg) * dpi) / 72 + 0.5);
  h = (int)((doc->getPageCropHeight(pg) * dpi) / 72 + 0.5);
  if (rot == 90 || rot == 270) {
    t = w; w = h; h = t;
  }
  tileW = 2 * drawAreaWidth;
  if (tileW < 1500) {
    tileW = 1500;
  }
  if (tileW > w) {
    tileW = w;
  }
  tileH = 2 * drawAreaHeight;
  if (tileH < 1500) {
    tileH = 1500;
  }
  if (tileH > h) {
    tileH = h;
  }
  page = new PDFCorePage(pg, w, h, tileW, tileH);
  for (i = 0;
       i < pages->getLength() && pg > ((PDFCorePage *)pages->get(i))->page;
       ++i) ;
  pages->insert(i, page);
}

void PDFCore::needTile(PDFCorePage *page, int x, int y) {
  PDFCoreTile *tile;
  TextOutputDev *textOut;
  int xDest, yDest, sliceW, sliceH;
  int i;

  for (i = 0; i < page->tiles->getLength(); ++i) {
    tile = (PDFCoreTile *)page->tiles->get(i);
    if (x == tile->xMin && y == tile->yMin) {
      return;
    }
  }

  setBusyCursor(gTrue);

  sliceW = page->tileW;
  if (x + sliceW > page->w) {
    sliceW = page->w - x;
  }
  sliceH = page->tileH;
  if (y + sliceH > page->h) {
    sliceH = page->h - y;
  }

  xDest = x - scrollX;
  if (continuousMode) {
    yDest = y + pageY[page->page - 1] - scrollY;
  } else {
    yDest = y - scrollY;
  }
  if (continuousMode) {
    if (page->w < maxPageW) {
      xDest += (maxPageW - page->w) / 2;
    }
    if (maxPageW < drawAreaWidth) {
      xDest += (drawAreaWidth - maxPageW) / 2;
    }
  } else if (page->w < drawAreaWidth) {
    xDest += (drawAreaWidth - page->w) / 2;
  }
  if (continuousMode && totalDocH < drawAreaHeight) {
    yDest += (drawAreaHeight - totalDocH) / 2;
  } else if (!continuousMode && page->h < drawAreaHeight) {
    yDest += (drawAreaHeight - page->h) / 2;
  }
  curTile = tile = newTile(xDest, yDest);
  curPage = page;
  tile->xMin = x;
  tile->yMin = y;
  tile->xMax = x + sliceW;
  tile->yMax = y + sliceH;
  tile->edges = 0;
  if (tile->xMin == 0) {
    tile->edges |= pdfCoreTileLeftEdge;
  }
  if (tile->xMax == page->w) {
    tile->edges |= pdfCoreTileRightEdge;
  }
  if (continuousMode) {
    if (tile->yMin == 0) {
      tile->edges |= pdfCoreTileTopSpace;
      if (page->page == 1) {
	tile->edges |= pdfCoreTileTopEdge;
      }
    }
    if (tile->yMax == page->h) {
      tile->edges |= pdfCoreTileBottomSpace;
      if (page->page == doc->getNumPages()) {
	tile->edges |= pdfCoreTileBottomEdge;
      }
    }
  } else {
    if (tile->yMin == 0) {
      tile->edges |= pdfCoreTileTopEdge;
    }
    if (tile->yMax == page->h) {
      tile->edges |= pdfCoreTileBottomEdge;
    }
  }
  doc->displayPageSlice(out, page->page, dpi, dpi, rotate,
			gFalse, gTrue, gFalse, x, y, sliceW, sliceH);
  tile->bitmap = out->takeBitmap();
  memcpy(tile->ctm, out->getDefCTM(), 6 * sizeof(double));
  memcpy(tile->ictm, out->getDefICTM(), 6 * sizeof(double));
  if (!page->links) {
    page->links = doc->getLinks(page->page);
  }
  if (!page->text) {
    if ((textOut = new TextOutputDev(NULL, gTrue, gFalse, gFalse))) {
      doc->displayPage(textOut, page->page, dpi, dpi, rotate,
		       gFalse, gTrue, gFalse);
      page->text = textOut->takeText();
      delete textOut;
    }
  }
  page->tiles->append(tile);
  curTile = NULL;
  curPage = NULL;

  setBusyCursor(gFalse);
}

GBool PDFCore::gotoNextPage(int inc, GBool top) {
  int pg, scrollYA;

  if (!doc || doc->getNumPages() == 0 || topPage >= doc->getNumPages()) {
    return gFalse;
  }
  if ((pg = topPage + inc) > doc->getNumPages()) {
    pg = doc->getNumPages();
  }
  if (continuousMode) {
    scrollYA = -1;
  } else if (top) {
    scrollYA = 0;
  } else {
    scrollYA = scrollY;
  }
  update(pg, scrollX, scrollYA, zoom, rotate, gFalse, gTrue);
  return gTrue;
}

GBool PDFCore::gotoPrevPage(int dec, GBool top, GBool bottom) {
  int pg, scrollYA;

  if (!doc || doc->getNumPages() == 0 || topPage <= 1) {
    return gFalse;
  }
  if ((pg = topPage - dec) < 1) {
    pg = 1;
  }
  if (continuousMode) {
    scrollYA = -1;
  } else if (top) {
    scrollYA = 0;
  } else if (bottom) {
    scrollYA = ((PDFCorePage *)pages->get(0))->h - drawAreaHeight;
    if (scrollYA < 0) {
      scrollYA = 0;
    }
  } else {
    scrollYA = scrollY;
  }
  update(pg, scrollX, scrollYA, zoom, rotate, gFalse, gTrue);
  return gTrue;
}

GBool PDFCore::gotoNamedDestination(GString *dest) {
  LinkDest *d;

  if (!doc) {
    return gFalse;
  }
  if (!(d = doc->findDest(dest))) {
    return gFalse;
  }
  displayDest(d, zoom, rotate, gTrue);
  delete d;
  return gTrue;
}

GBool PDFCore::goForward() {
  int pg;

  if (historyFLen == 0) {
    return gFalse;
  }
  if (++historyCur == pdfHistorySize) {
    historyCur = 0;
  }
  --historyFLen;
  ++historyBLen;
  if (!doc || history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
    if (loadFile(history[historyCur].fileName) != errNone) {
      return gFalse;
    }
  }
  pg = history[historyCur].page;
  update(pg, scrollX, continuousMode ? -1 : scrollY,
	 zoom, rotate, gFalse, gFalse);
  return gTrue;
}

GBool PDFCore::goBackward() {
  int pg;

  if (historyBLen <= 1) {
    return gFalse;
  }
  if (--historyCur < 0) {
    historyCur = pdfHistorySize - 1;
  }
  --historyBLen;
  ++historyFLen;
  if (!doc || history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
    if (loadFile(history[historyCur].fileName) != errNone) {
      return gFalse;
    }
  }
  pg = history[historyCur].page;
  update(pg, scrollX, continuousMode ? -1 : scrollY,
	 zoom, rotate, gFalse, gFalse);
  return gTrue;
}

void PDFCore::scrollLeft(int nCols) {
  scrollTo(scrollX - nCols, scrollY);
}

void PDFCore::scrollRight(int nCols) {
  scrollTo(scrollX + nCols, scrollY);
}

void PDFCore::scrollUp(int nLines) {
  scrollTo(scrollX, scrollY - nLines);
}

void PDFCore::scrollUpPrevPage(int nLines) {
  if (!continuousMode && scrollY == 0) {
    gotoPrevPage(1, gFalse, gTrue);
  } else {
    scrollTo(scrollX, scrollY - nLines);
  }
}

void PDFCore::scrollDown(int nLines) {
  scrollTo(scrollX, scrollY + nLines);
}

void PDFCore::scrollDownNextPage(int nLines) {
  if (!continuousMode &&
      scrollY >= ((PDFCorePage *)pages->get(0))->h - drawAreaHeight) {
    gotoNextPage(1, gTrue);
  } else {
    scrollTo(scrollX, scrollY + nLines);
  }
}

void PDFCore::scrollPageUp() {
  if (!continuousMode && scrollY == 0) {
    gotoPrevPage(1, gFalse, gTrue);
  } else {
    scrollTo(scrollX, scrollY - drawAreaHeight);
  }
}

void PDFCore::scrollPageDown() {
  if (!continuousMode &&
      scrollY >= ((PDFCorePage *)pages->get(0))->h - drawAreaHeight) {
    gotoNextPage(1, gTrue);
  } else {
    scrollTo(scrollX, scrollY + drawAreaHeight);
  }
}

void PDFCore::scrollTo(int x, int y) {
  update(topPage, x, y < 0 ? 0 : y, zoom, rotate, gFalse, gFalse);
}

void PDFCore::scrollToLeftEdge() {
  update(topPage, 0, scrollY, zoom, rotate, gFalse, gFalse);
}

void PDFCore::scrollToRightEdge() {
  PDFCorePage *page;

  page = (PDFCorePage *)pages->get(0);
  update(topPage, page->w - drawAreaWidth, scrollY,
	 zoom, rotate, gFalse, gFalse);
}

void PDFCore::scrollToTopEdge() {
  int y;

  y = continuousMode ? pageY[topPage - 1] : 0;
  update(topPage, scrollX, y, zoom, rotate, gFalse, gFalse);
}

void PDFCore::scrollToBottomEdge() {
  PDFCorePage *page;
  int y, i;

  for (i = pages->getLength() - 1; i > 0; --i) {
    page = (PDFCorePage *)pages->get(i);
    if (page->yDest < drawAreaHeight) {
      break;
    }
  }
  page = (PDFCorePage *)pages->get(i);
  if (continuousMode) {
    y = pageY[page->page - 1] + page->h - drawAreaHeight;
  } else {
    y = page->h - drawAreaHeight;
  }
  update(topPage, scrollX, y, zoom, rotate, gFalse, gFalse);
}

void PDFCore::scrollToTopLeft() {
  int y;

  y = continuousMode ? pageY[topPage - 1] : 0;
  update(topPage, 0, y, zoom, rotate, gFalse, gFalse);
}

void PDFCore::scrollToBottomRight() {
  PDFCorePage *page;
  int x, y, i;

  for (i = pages->getLength() - 1; i > 0; --i) {
    page = (PDFCorePage *)pages->get(i);
    if (page->yDest < drawAreaHeight) {
      break;
    }
  }
  page = (PDFCorePage *)pages->get(i);
  x = page->w - drawAreaWidth;
  if (continuousMode) {
    y = pageY[page->page - 1] + page->h - drawAreaHeight;
  } else {
    y = page->h - drawAreaHeight;
  }
  update(topPage, x, y, zoom, rotate, gFalse, gFalse);
}

void PDFCore::zoomToRect(int pg, double ulx, double uly,
			 double lrx, double lry) {
  int x0, y0, x1, y1, u, sx, sy;
  double rx, ry, newZoom, t;
  PDFCorePage *p;

  cvtUserToDev(pg, ulx, uly, &x0, &y0);
  cvtUserToDev(pg, lrx, lry, &x1, &y1);
  if (x0 > x1) {
    u = x0; x0 = x1; x1 = u;
  }
  if (y0 > y1) {
    u = y0; y0 = y1; y1 = u;
  }
  rx = (double)drawAreaWidth / (double)(x1 - x0);
  ry = (double)drawAreaHeight / (double)(y1 - y0);
  if (rx < ry) {
    newZoom = rx * (dpi / (0.01 * 72));
    sx = (int)(rx * x0);
    t = (drawAreaHeight * (x1 - x0)) / drawAreaWidth;
    sy = (int)(rx * (y0 + y1 - t) / 2);
    if (continuousMode) {
      if ((p = findPage(pg)) && p->w < maxPageW) {
	sx += (int)(0.5 * rx * (maxPageW - p->w));
      }
      u = (pg - 1) * continuousModePageSpacing;
      sy += (int)(rx * (pageY[pg - 1] - u)) + u;
    }
  } else {
    newZoom = ry * (dpi / (0.01 * 72));
    t = (drawAreaWidth * (y1 - y0)) / drawAreaHeight;
    sx = (int)(ry * (x0 + x1 - t) / 2);
    sy = (int)(ry * y0);
    if (continuousMode) {
      if ((p = findPage(pg)) && p->w < maxPageW) {
	sx += (int)(0.5 * rx * (maxPageW - p->w));
      }
      u = (pg - 1) * continuousModePageSpacing;
      sy += (int)(ry * (pageY[pg - 1] - u)) + u;
    }
  }
  update(pg, sx, sy, newZoom, rotate, gFalse, gFalse);
}

void PDFCore::zoomCentered(double zoomA) {
  int sx, sy, rot, hAdjust, vAdjust, i;
  double dpi1, dpi2, pageW, pageH;
  PDFCorePage *page;

  if (zoomA == zoomPage) {
    if (continuousMode) {
      pageW = (rotate == 90 || rotate == 270) ? maxUnscaledPageH
	                                      : maxUnscaledPageW;
      pageH = (rotate == 90 || rotate == 270) ? maxUnscaledPageW
	                                      : maxUnscaledPageH;
      dpi1 = 72.0 * (double)drawAreaWidth / pageW;
      dpi2 = 72.0 * (double)(drawAreaHeight - continuousModePageSpacing) /
	     pageH;
      if (dpi2 < dpi1) {
	dpi1 = dpi2;
      }
    } else {
      // in single-page mode, sx=sy=0 -- so dpi1 is irrelevant
      dpi1 = dpi;
    }
    sx = 0;

  } else if (zoomA == zoomWidth) {
    if (continuousMode) {
      pageW = (rotate == 90 || rotate == 270) ? maxUnscaledPageH
	                                      : maxUnscaledPageW;
    } else {
      rot = rotate + doc->getPageRotate(topPage);
      if (rot >= 360) {
	rot -= 360;
      } else if (rot < 0) {
	rot += 360;
      }
      pageW = (rot == 90 || rot == 270) ? doc->getPageCropHeight(topPage)
	                                : doc->getPageCropWidth(topPage);
    }
    dpi1 = 72.0 * (double)drawAreaWidth / pageW;
    sx = 0;

  } else if (zoomA <= 0) {
    return;

  } else {
    dpi1 = 72.0 * zoomA / 100.0;
    if ((page = (PDFCorePage *)pages->get(0)) && page->xDest > 0) {
      hAdjust = page->xDest;
    } else {
      hAdjust = 0;
    }
    sx = (int)((scrollX - hAdjust + drawAreaWidth / 2) * (dpi1 / dpi)) -
         drawAreaWidth / 2;
    if (sx < 0) {
      sx = 0;
    }
  }

  if (continuousMode) {
    // we can't just multiply scrollY by dpi1/dpi -- the rounding
    // errors add up (because the pageY values are integers) -- so
    // we compute the pageY values at the new zoom level instead
    sy = 0;
    for (i = 1; i < topPage; ++i) {
      rot = rotate + doc->getPageRotate(i);
      if (rot >= 360) {
	rot -= 360;
      } else if (rot < 0) {
	rot += 360;
      }
      if (rot == 90 || rot == 270) {
	sy += (int)((doc->getPageCropWidth(i) * dpi1) / 72 + 0.5);
      } else {
	sy += (int)((doc->getPageCropHeight(i) * dpi1) / 72 + 0.5);
      }
    }
    vAdjust = (topPage - 1) * continuousModePageSpacing;
    sy = sy + (int)((scrollY - pageY[topPage - 1] + drawAreaHeight / 2)
		    * (dpi1 / dpi))
         + vAdjust - drawAreaHeight / 2;
  } else {
    sy = (int)((scrollY + drawAreaHeight / 2) * (dpi1 / dpi))
         - drawAreaHeight / 2;
  }

  update(topPage, sx, sy, zoomA, rotate, gFalse, gFalse);
}

// Zoom so that the current page(s) fill the window width.  Maintain
// the vertical center.
void PDFCore::zoomToCurrentWidth() {
  double w, maxW, dpi1;
  int sx, sy, vAdjust, rot, i;

  // compute the maximum page width of visible pages
  rot = rotate + doc->getPageRotate(topPage);
  if (rot >= 360) {
    rot -= 360;
  } else if (rot < 0) {
    rot += 360;
  }
  if (rot == 90 || rot == 270) {
    maxW = doc->getPageCropHeight(topPage);
  } else {
    maxW = doc->getPageCropWidth(topPage);
  }
  if (continuousMode) {
    for (i = topPage + 1;
	 i < doc->getNumPages() && pageY[i-1] < scrollY + drawAreaHeight;
	 ++i) {
      rot = rotate + doc->getPageRotate(i);
      if (rot >= 360) {
	rot -= 360;
      } else if (rot < 0) {
	rot += 360;
      }
      if (rot == 90 || rot == 270) {
	w = doc->getPageCropHeight(i);
      } else {
	w = doc->getPageCropWidth(i);
      }
      if (w > maxW) {
	maxW = w;
      }
    }
  }

  // compute the resolution
  dpi1 = (drawAreaWidth / maxW) * 72;

  // compute the horizontal scroll position
  if (continuousMode) {
    sx = ((int)(maxPageW * dpi1 / dpi) - drawAreaWidth) / 2;
  } else {
    sx = 0;
  }

  // compute the vertical scroll position
  if (continuousMode) {
    // we can't just multiply scrollY by dpi1/dpi -- the rounding
    // errors add up (because the pageY values are integers) -- so
    // we compute the pageY values at the new zoom level instead
    sy = 0;
    for (i = 1; i < topPage; ++i) {
      rot = rotate + doc->getPageRotate(i);
      if (rot >= 360) {
	rot -= 360;
      } else if (rot < 0) {
	rot += 360;
      }
      if (rot == 90 || rot == 270) {
	sy += (int)((doc->getPageCropWidth(i) * dpi1) / 72 + 0.5);
      } else {
	sy += (int)((doc->getPageCropHeight(i) * dpi1) / 72 + 0.5);
      }
    }
    vAdjust = (topPage - 1) * continuousModePageSpacing;
    sy = sy + (int)((scrollY - pageY[topPage - 1] + drawAreaHeight / 2)
		    * (dpi1 / dpi))
         + vAdjust - drawAreaHeight / 2;
  } else {
    sy = (int)((scrollY + drawAreaHeight / 2) * (dpi1 / dpi))
         - drawAreaHeight / 2;
  }

  update(topPage, sx, sy, (dpi1 * 100) / 72, rotate, gFalse, gFalse);
}

void PDFCore::setContinuousMode(GBool cm) {
  if (continuousMode != cm) {
    continuousMode = cm;
    update(topPage, scrollX, -1, zoom, rotate, gTrue, gFalse);
  }
}

void PDFCore::setSelection(int newSelectPage,
			   int newSelectULX, int newSelectULY,
			   int newSelectLRX, int newSelectLRY) {
  int x0, y0, x1, y1, py;
  GBool haveSel, newHaveSel;
  GBool needRedraw, needScroll;
  GBool moveLeft, moveRight, moveTop, moveBottom;
  SplashColor xorColor;
  PDFCorePage *page;


  haveSel = selectULX != selectLRX && selectULY != selectLRY;
  newHaveSel = newSelectULX != newSelectLRX && newSelectULY != newSelectLRY;

  // erase old selection on off-screen bitmap
  needRedraw = gFalse;
  if (haveSel) {
    xorColor[0] = xorColor[1] = xorColor[2] = 0xff;
    xorRectangle(selectPage, selectULX, selectULY, selectLRX, selectLRY,
		 new SplashSolidColor(xorColor));
    needRedraw = gTrue;
  }

  // draw new selection on off-screen bitmap
  if (newHaveSel) {
    xorColor[0] = xorColor[1] = xorColor[2] = 0xff;
    xorRectangle(newSelectPage, newSelectULX, newSelectULY,
		 newSelectLRX, newSelectLRY,
		 new SplashSolidColor(xorColor));
    needRedraw = gTrue;
  }

  // check which edges moved
  if (!haveSel || newSelectPage != selectPage) {
    moveLeft = moveTop = moveRight = moveBottom = gTrue;
  } else {
    moveLeft = newSelectULX != selectULX;
    moveTop = newSelectULY != selectULY;
    moveRight = newSelectLRX != selectLRX;
    moveBottom = newSelectLRY != selectLRY;
  }

  // redraw currently visible part of bitmap
  if (needRedraw) {
    if (!haveSel) {
      page = findPage(newSelectPage);
      x0 = newSelectULX;
      y0 = newSelectULY;
      x1 = newSelectLRX;
      y1 = newSelectLRY;
      redrawWindow(page->xDest + x0, page->yDest + y0,
		   x1 - x0 + 1, y1 - y0 + 1, gFalse);
    } else if (!newHaveSel) {
      if ((page = findPage(selectPage))) {
	x0 = selectULX;
	y0 = selectULY;
	x1 = selectLRX;
	y1 = selectLRY;
	redrawWindow(page->xDest + x0, page->yDest + y0,
		     x1 - x0 + 1, y1 - y0 + 1, gFalse);
      }
    } else {
      page = findPage(newSelectPage);
      if (moveLeft) {
	x0 = newSelectULX < selectULX ? newSelectULX : selectULX;
	y0 = newSelectULY < selectULY ? newSelectULY : selectULY;
	x1 = newSelectULX > selectULX ? newSelectULX : selectULX;
	y1 = newSelectLRY > selectLRY ? newSelectLRY : selectLRY;
	redrawWindow(page->xDest + x0, page->yDest + y0,
		     x1 - x0 + 1, y1 - y0 + 1, gFalse);
      }
      if (moveRight) {
	x0 = newSelectLRX < selectLRX ? newSelectLRX : selectLRX;
	y0 = newSelectULY < selectULY ? newSelectULY : selectULY;
	x1 = newSelectLRX > selectLRX ? newSelectLRX : selectLRX;
	y1 = newSelectLRY > selectLRY ? newSelectLRY : selectLRY;
	redrawWindow(page->xDest + x0, page->yDest + y0,
		     x1 - x0 + 1, y1 - y0 + 1, gFalse);
      }
      if (moveTop) {
	x0 = newSelectULX < selectULX ? newSelectULX : selectULX;
	y0 = newSelectULY < selectULY ? newSelectULY : selectULY;
	x1 = newSelectLRX > selectLRX ? newSelectLRX : selectLRX;
	y1 = newSelectULY > selectULY ? newSelectULY : selectULY;
	redrawWindow(page->xDest + x0, page->yDest + y0,
		     x1 - x0 + 1, y1 - y0 + 1, gFalse);
      }
      if (moveBottom) {
	x0 = newSelectULX < selectULX ? newSelectULX : selectULX;
	y0 = newSelectLRY < selectLRY ? newSelectLRY : selectLRY;
	x1 = newSelectLRX > selectLRX ? newSelectLRX : selectLRX;
	y1 = newSelectLRY > selectLRY ? newSelectLRY : selectLRY;
	redrawWindow(page->xDest + x0, page->yDest + y0,
		     x1 - x0 + 1, y1 - y0 + 1, gFalse);
      }
    }
  }

  // switch to new selection coords
  selectPage = newSelectPage;
  selectULX = newSelectULX;
  selectULY = newSelectULY;
  selectLRX = newSelectLRX;
  selectLRY = newSelectLRY;

  // scroll if necessary
  if (newHaveSel) {
    page = findPage(selectPage);
    needScroll = gFalse;
    x0 = scrollX;
    y0 = scrollY;
    if (moveLeft && page->xDest + selectULX < 0) {
      x0 += page->xDest + selectULX;
      needScroll = gTrue;
    } else if (moveRight && page->xDest + selectLRX >= drawAreaWidth) {
      x0 += page->xDest + selectLRX - drawAreaWidth;
      needScroll = gTrue;
    } else if (moveLeft && page->xDest + selectULX >= drawAreaWidth) {
      x0 += page->xDest + selectULX - drawAreaWidth;
      needScroll = gTrue;
    } else if (moveRight && page->xDest + selectLRX < 0) {
      x0 += page->xDest + selectLRX;
      needScroll = gTrue;
    }
    py = continuousMode ? pageY[selectPage - 1] : 0;
    if (moveTop && py + selectULY < y0) {
      y0 = py + selectULY;
      needScroll = gTrue;
    } else if (moveBottom && py + selectLRY >= y0 + drawAreaHeight) {
      y0 = py + selectLRY - drawAreaHeight;
      needScroll = gTrue;
    } else if (moveTop && py + selectULY >= y0 + drawAreaHeight) {
      y0 = py + selectULY - drawAreaHeight;
      needScroll = gTrue;
    } else if (moveBottom && py + selectLRY < y0) {
      y0 = py + selectLRY;
      needScroll = gTrue;
    }
    if (needScroll) {
      scrollTo(x0, y0);
    }
  }
}

void PDFCore::moveSelection(int pg, int x, int y) {
  int newSelectULX, newSelectULY, newSelectLRX, newSelectLRY;

  // don't allow selections to span multiple pages
  if (pg != selectPage) {
    return;
  }

  // move appropriate edges of selection
  if (lastDragLeft) {
    if (x < selectLRX) {
      newSelectULX = x;
      newSelectLRX = selectLRX;
    } else {
      newSelectULX = selectLRX;
      newSelectLRX = x;
      lastDragLeft = gFalse;
    }
  } else {
    if (x > selectULX) {
      newSelectULX = selectULX;
      newSelectLRX = x;
    } else {
      newSelectULX = x;
      newSelectLRX = selectULX;
      lastDragLeft = gTrue;
    }
  }
  if (lastDragTop) {
    if (y < selectLRY) {
      newSelectULY = y;
      newSelectLRY = selectLRY;
    } else {
      newSelectULY = selectLRY;
      newSelectLRY = y;
      lastDragTop = gFalse;
    }
  } else {
    if (y > selectULY) {
      newSelectULY = selectULY;
      newSelectLRY = y;
    } else {
      newSelectULY = y;
      newSelectLRY = selectULY;
      lastDragTop = gTrue;
    }
  }

  // redraw the selection
  setSelection(selectPage, newSelectULX, newSelectULY,
	       newSelectLRX, newSelectLRY);
}

void PDFCore::xorRectangle(int pg, int x0, int y0, int x1, int y1,
			   SplashPattern *pattern, PDFCoreTile *oneTile) {
  Splash *splash;
  SplashPath *path;
  PDFCorePage *page;
  PDFCoreTile *tile;
  SplashCoord xx0, yy0, xx1, yy1;
  int xi, yi, wi, hi;
  int i;

  if ((page = findPage(pg))) {
    for (i = 0; i < page->tiles->getLength(); ++i) {
      tile = (PDFCoreTile *)page->tiles->get(i);
      if (!oneTile || tile == oneTile) {
	splash = new Splash(tile->bitmap, gFalse);
	splash->setFillPattern(pattern->copy());
	xx0 = (SplashCoord)(x0 - tile->xMin);
	yy0 = (SplashCoord)(y0 - tile->yMin);
	xx1 = (SplashCoord)(x1 - tile->xMin);
	yy1 = (SplashCoord)(y1 - tile->yMin);
	path = new SplashPath();
	path->moveTo(xx0, yy0);
	path->lineTo(xx1, yy0);
	path->lineTo(xx1, yy1);
	path->lineTo(xx0, yy1);
	path->close();
	splash->xorFill(path, gTrue);
	delete path;
	delete splash;
	xi = x0 - tile->xMin;
	wi = x1 - x0;
	if (xi < 0) {
	  wi += xi;
	  xi = 0;
	}
	if (xi + wi > tile->bitmap->getWidth()) {
	  wi = tile->bitmap->getWidth() - xi;
	}
	yi = y0 - tile->yMin;
	hi = y1 - y0;
	if (yi < 0) {
	  hi += yi;
	  yi = 0;
	}
	if (yi + hi > tile->bitmap->getHeight()) {
	  hi = tile->bitmap->getHeight() - yi;
	}
	updateTileData(tile, xi, yi, wi, hi, gTrue);
      }
    }
  }
  delete pattern;
}

GBool PDFCore::getSelection(int *pg, double *ulx, double *uly,
			    double *lrx, double *lry) {
  if (selectULX == selectLRX || selectULY == selectLRY) {
    return gFalse;
  }
  *pg = selectPage;
  cvtDevToUser(selectPage, selectULX, selectULY, ulx, uly);
  cvtDevToUser(selectPage, selectLRX, selectLRY, lrx, lry);
  return gTrue;
}

GString *PDFCore::extractText(int pg, double xMin, double yMin,
			      double xMax, double yMax) {
  PDFCorePage *page;
  TextOutputDev *textOut;
  int x0, y0, x1, y1, t;
  GString *s;

  if (!doc->okToCopy()) {
    return NULL;
  }
  if ((page = findPage(pg))) {
    cvtUserToDev(pg, xMin, yMin, &x0, &y0);
    cvtUserToDev(pg, xMax, yMax, &x1, &y1);
    if (x0 > x1) {
      t = x0; x0 = x1; x1 = t;
    }
    if (y0 > y1) {
      t = y0; y0 = y1; y1 = t;
    }
    s = page->text->getText(x0, y0, x1, y1);
  } else {
    textOut = new TextOutputDev(NULL, gTrue, gFalse, gFalse);
    if (textOut->isOk()) {
      doc->displayPage(textOut, pg, dpi, dpi, rotate, gFalse, gTrue, gFalse);
      textOut->cvtUserToDev(xMin, yMin, &x0, &y0);
      textOut->cvtUserToDev(xMax, yMax, &x1, &y1);
      if (x0 > x1) {
	t = x0; x0 = x1; x1 = t;
      }
      if (y0 > y1) {
	t = y0; y0 = y1; y1 = t;
      }
      s = textOut->getText(x0, y0, x1, y1);
    } else {
      s = new GString();
    }
    delete textOut;
  }
  return s;
}

GBool PDFCore::find(char *s, GBool caseSensitive, GBool next, GBool backward,
		    GBool onePageOnly) {
  Unicode *u;
  int len, i;
  GBool ret;

  // convert to Unicode
  len = strlen(s);
  u = (Unicode *)gmallocn(len, sizeof(Unicode));
  for (i = 0; i < len; ++i) {
    u[i] = (Unicode)(s[i] & 0xff);
  }

  ret = findU(u, len, caseSensitive, next, backward, onePageOnly);

  gfree(u);
  return ret;
}

GBool PDFCore::findU(Unicode *u, int len, GBool caseSensitive,
		     GBool next, GBool backward, GBool onePageOnly) {
  TextOutputDev *textOut;
  double xMin, yMin, xMax, yMax;
  PDFCorePage *page;
  PDFCoreTile *tile;
  int pg;
  GBool startAtTop, startAtLast, stopAtLast;

  // check for zero-length string
  if (len == 0) {
    return gFalse;
  }

  setBusyCursor(gTrue);

  // search current page starting at previous result, current
  // selection, or top/bottom of page
  startAtTop = startAtLast = gFalse;
  xMin = yMin = xMax = yMax = 0;
  pg = topPage;
  if (next) {
    startAtLast = gTrue;
  } else if (selectULX != selectLRX && selectULY != selectLRY) {
    pg = selectPage;
    if (backward) {
      xMin = selectULX - 1;
      yMin = selectULY - 1;
    } else {
      xMin = selectULX + 1;
      yMin = selectULY + 1;
    }
  } else {
    startAtTop = gTrue;
  }
  if (!(page = findPage(pg))) {
    displayPage(pg, zoom, rotate, gTrue, gFalse);
    page = findPage(pg);
  }
  if (page->text->findText(u, len, startAtTop, gTrue, startAtLast, gFalse,
			   caseSensitive, backward,
			   &xMin, &yMin, &xMax, &yMax)) {
    goto found;
  }

  if (!onePageOnly) {

    // search following/previous pages
    textOut = new TextOutputDev(NULL, gTrue, gFalse, gFalse);
    if (!textOut->isOk()) {
      delete textOut;
      goto notFound;
    }
    for (pg = backward ? pg - 1 : pg + 1;
	 backward ? pg >= 1 : pg <= doc->getNumPages();
	 pg += backward ? -1 : 1) {
      doc->displayPage(textOut, pg, 72, 72, 0, gFalse, gTrue, gFalse);
      if (textOut->findText(u, len, gTrue, gTrue, gFalse, gFalse,
			    caseSensitive, backward,
			    &xMin, &yMin, &xMax, &yMax)) {
	delete textOut;
	goto foundPage;
      }
    }

    // search previous/following pages
    for (pg = backward ? doc->getNumPages() : 1;
	 backward ? pg > topPage : pg < topPage;
	 pg += backward ? -1 : 1) {
      doc->displayPage(textOut, pg, 72, 72, 0, gFalse, gTrue, gFalse);
      if (textOut->findText(u, len, gTrue, gTrue, gFalse, gFalse,
			    caseSensitive, backward,
			    &xMin, &yMin, &xMax, &yMax)) {
	delete textOut;
	goto foundPage;
      }
    }
    delete textOut;

  }

  // search current page ending at previous result, current selection,
  // or bottom/top of page
  if (!startAtTop) {
    xMin = yMin = xMax = yMax = 0;
    if (next) {
      stopAtLast = gTrue;
    } else {
      stopAtLast = gFalse;
      xMax = selectLRX;
      yMax = selectLRY;
    }
    if (page->text->findText(u, len, gTrue, gFalse, gFalse, stopAtLast,
			     caseSensitive, backward,
			     &xMin, &yMin, &xMax, &yMax)) {
      goto found;
    }
  }

  // not found
 notFound:
  setBusyCursor(gFalse);
  return gFalse;

  // found on a different page
 foundPage:
  update(pg, scrollX, continuousMode ? -1 : 0, zoom, rotate, gFalse, gTrue);
  page = findPage(pg);
  if (!page->text->findText(u, len, gTrue, gTrue, gFalse, gFalse,
			    caseSensitive, backward,
			    &xMin, &yMin, &xMax, &yMax)) {
    // this can happen if coalescing is bad
    goto notFound;
  }

  // found: change the selection
 found:
  tile = (PDFCoreTile *)page->tiles->get(0);
  setSelection(pg, (int)floor(xMin), (int)floor(yMin),
	       (int)ceil(xMax), (int)ceil(yMax));

  setBusyCursor(gFalse);
  return gTrue;
}


GBool PDFCore::cvtWindowToUser(int xw, int yw,
			       int *pg, double *xu, double *yu) {
  PDFCorePage *page;
  PDFCoreTile *tile;
  int i;

  for (i = 0; i < pages->getLength(); ++i) {
    page = (PDFCorePage *)pages->get(i);
    if (xw >= page->xDest && xw < page->xDest + page->w &&
	yw >= page->yDest && yw < page->yDest + page->h) {
      tile = (PDFCoreTile *)page->tiles->get(0);
      *pg = page->page;
      xw -= tile->xDest;
      yw -= tile->yDest;
      *xu = tile->ictm[0] * xw + tile->ictm[2] * yw + tile->ictm[4];
      *yu = tile->ictm[1] * xw + tile->ictm[3] * yw + tile->ictm[5];
      return gTrue;
    }
  }
  *pg = 0;
  *xu = *yu = 0;
  return gFalse;
}

GBool PDFCore::cvtWindowToDev(int xw, int yw, int *pg, int *xd, int *yd) {
  PDFCorePage *page;
  int i;

  for (i = 0; i < pages->getLength(); ++i) {
    page = (PDFCorePage *)pages->get(i);
    if (xw >= page->xDest && xw < page->xDest + page->w &&
	yw >= page->yDest && yw < page->yDest + page->h) {
      *pg = page->page;
      *xd = xw - page->xDest;
      *yd = yw - page->yDest;
      return gTrue;
    }
  }
  *pg = 0;
  *xd = *yd = 0;
  return gFalse;
}

void PDFCore::cvtUserToWindow(int pg, double xu, double yu, int *xw, int *yw) {
  PDFCorePage *page;
  PDFCoreTile *tile;

  if ((page = findPage(pg)) &&
      page->tiles->getLength() > 0) {
    tile = (PDFCoreTile *)page->tiles->get(0);
  } else if (curTile && curPage->page == pg) {
    tile = curTile;
  } else {
    tile = NULL;
  }
  if (tile) {
    *xw = tile->xDest + (int)(tile->ctm[0] * xu + tile->ctm[2] * yu +
			      tile->ctm[4] + 0.5);
    *yw = tile->yDest + (int)(tile->ctm[1] * xu + tile->ctm[3] * yu +
			      tile->ctm[5] + 0.5);
  } else {
    // this should never happen
    *xw = *yw = 0;
  }
}

void PDFCore::cvtUserToDev(int pg, double xu, double yu, int *xd, int *yd) {
  PDFCorePage *page;
  PDFCoreTile *tile;
  double ctm[6];

  if ((page = findPage(pg)) &&
      page->tiles->getLength() > 0) {
    tile = (PDFCoreTile *)page->tiles->get(0);
  } else if (curTile && curPage->page == pg) {
    tile = curTile;
  } else {
    tile = NULL;
  }
  if (tile) {
    *xd = (int)(tile->xMin + tile->ctm[0] * xu +
		tile->ctm[2] * yu + tile->ctm[4] + 0.5);
    *yd = (int)(tile->yMin + tile->ctm[1] * xu +
		tile->ctm[3] * yu + tile->ctm[5] + 0.5);
  } else {
    doc->getCatalog()->getPage(pg)->getDefaultCTM(ctm, dpi, dpi, rotate,
						  gFalse, out->upsideDown());
    *xd = (int)(ctm[0] * xu + ctm[2] * yu + ctm[4] + 0.5);
    *yd = (int)(ctm[1] * xu + ctm[3] * yu + ctm[5] + 0.5);
  }
}

void PDFCore::cvtDevToWindow(int pg, int xd, int yd, int *xw, int *yw) {
  PDFCorePage *page;

  if ((page = findPage(pg))) {
    *xw = page->xDest + xd;
    *yw = page->yDest + yd;
  } else {
    // this should never happen
    *xw = *yw = 0;
  }
}

void PDFCore::cvtDevToUser(int pg, int xd, int yd, double *xu, double *yu) {
  PDFCorePage *page;
  PDFCoreTile *tile;

  if ((page = findPage(pg)) &&
      page->tiles->getLength() > 0) {
    tile = (PDFCoreTile *)page->tiles->get(0);
  } else if (curTile && curPage->page == pg) {
    tile = curTile;
  } else {
    tile = NULL;
  }
  if (tile) {
    xd -= tile->xMin;
    yd -= tile->yMin;
    *xu = tile->ictm[0] * xd + tile->ictm[2] * yd + tile->ictm[4];
    *yu = tile->ictm[1] * xd + tile->ictm[3] * yd + tile->ictm[5];
  } else {
    // this should never happen
    *xu = *yu = 0;
  }
}

void PDFCore::setReverseVideo(GBool reverseVideoA) {
  out->setReverseVideo(reverseVideoA);
  update(topPage, scrollX, scrollY, zoom, rotate, gTrue, gFalse);
}

LinkAction *PDFCore::findLink(int pg, double x, double y) {
  PDFCorePage *page;

  if ((page = findPage(pg))) {
    return page->links ? page->links->find(x, y) : (LinkAction *)NULL;
  }
  return NULL;
}

PDFCorePage *PDFCore::findPage(int pg) {
  PDFCorePage *page;
  int i;

  for (i = 0; i < pages->getLength(); ++i) {
    page = (PDFCorePage *)pages->get(i);
    if (page->page == pg) {
      return page;
    }
  }
  return NULL;
}

void PDFCore::redrawCbk(void *data, int x0, int y0, int x1, int y1,
			GBool composited) {
  PDFCore *core = (PDFCore *)data;

  core->curTile->bitmap = core->out->getBitmap();

  // the default CTM is set by the Gfx constructor; tile->ctm is
  // needed by the coordinate conversion functions (which may be
  // called during redraw)
  memcpy(core->curTile->ctm, core->out->getDefCTM(), 6 * sizeof(double));
  memcpy(core->curTile->ictm, core->out->getDefICTM(), 6 * sizeof(double));

  // the bitmap created by Gfx and SplashOutputDev can be a slightly
  // different size due to rounding errors
  if (x1 >= core->curTile->xMax) {
    x1 = core->curTile->xMax - 1;
  }
  if (y1 >= core->curTile->yMax) {
    y1 = core->curTile->yMax - 1;
  }

  core->clippedRedrawRect(core->curTile, x0, y0,
			  core->curTile->xDest + x0, core->curTile->yDest + y0,
			  x1 - x0 + 1, y1 - y0 + 1,
			  0, 0, core->drawAreaWidth, core->drawAreaHeight,
			  gTrue, composited);
}

void PDFCore::redrawWindow(int x, int y, int width, int height,
			   GBool needUpdate) {
  PDFCorePage *page;
  PDFCoreTile *tile;
  int xDest, yDest, w, i, j;

  if (pages->getLength() == 0) {
    redrawRect(NULL, 0, 0, x, y, width, height, gTrue);
    return;
  }

  for (i = 0; i < pages->getLength(); ++i) {
    page = (PDFCorePage *)pages->get(i);
    for (j = 0; j < page->tiles->getLength(); ++j) {
      tile = (PDFCoreTile *)page->tiles->get(j);
      if (tile->edges & pdfCoreTileTopEdge) {
	if (tile->edges & pdfCoreTileLeftEdge) {
	  xDest = 0;
	} else {
	  xDest = tile->xDest;
	}
	if (tile->edges & pdfCoreTileRightEdge) {
	  w = drawAreaWidth - xDest;
	} else {
	  w = tile->xDest + (tile->xMax - tile->xMin) - xDest;
	}
	clippedRedrawRect(NULL, 0, 0,
			  xDest, 0, w, tile->yDest,
			  x, y, width, height, gFalse);
      }
      if (tile->edges & pdfCoreTileBottomEdge) {
	if (tile->edges & pdfCoreTileLeftEdge) {
	  xDest = 0;
	} else {
	  xDest = tile->xDest;
	}
	if (tile->edges & pdfCoreTileRightEdge) {
	  w = drawAreaWidth - xDest;
	} else {
	  w = tile->xDest + (tile->xMax - tile->xMin) - xDest;
	}
	yDest = tile->yDest + (tile->yMax - tile->yMin);
	clippedRedrawRect(NULL, 0, 0,
			  xDest, yDest, w, drawAreaHeight - yDest,
			  x, y, width, height, gFalse);
      } else if ((tile->edges & pdfCoreTileBottomSpace) &&
		 i+1 < pages->getLength()) {
	if (tile->edges & pdfCoreTileLeftEdge) {
	  xDest = 0;
	} else {
	  xDest = tile->xDest;
	}
	if (tile->edges & pdfCoreTileRightEdge) {
	  w = drawAreaWidth - xDest;
	} else {
	  w = tile->xDest + (tile->xMax - tile->xMin) - xDest;
	}
	yDest = tile->yDest + (tile->yMax - tile->yMin);
	clippedRedrawRect(NULL, 0, 0,
			  xDest, yDest,
			  w, ((PDFCorePage *)pages->get(i+1))->yDest - yDest,
			  x, y, width, height, gFalse);
      }
      if (tile->edges & pdfCoreTileLeftEdge) {
	clippedRedrawRect(NULL, 0, 0,
			  0, tile->yDest,
			  tile->xDest, tile->yMax - tile->yMin,
			  x, y, width, height, gFalse);
      }
      if (tile->edges & pdfCoreTileRightEdge) {
	xDest = tile->xDest + (tile->xMax - tile->xMin);
	clippedRedrawRect(NULL, 0, 0,
			  xDest, tile->yDest,
			  drawAreaWidth - xDest, tile->yMax - tile->yMin,
			  x, y, width, height, gFalse);
      }
      clippedRedrawRect(tile, 0, 0, tile->xDest, tile->yDest,
			tile->bitmap->getWidth(), tile->bitmap->getHeight(),
			x, y, width, height, needUpdate);
    }
  }
}

PDFCoreTile *PDFCore::newTile(int xDestA, int yDestA) {
  return new PDFCoreTile(xDestA, yDestA);
}

void PDFCore::updateTileData(PDFCoreTile *tileA, int xSrc, int ySrc,
			     int width, int height, GBool composited) {
}

void PDFCore::clippedRedrawRect(PDFCoreTile *tile, int xSrc, int ySrc,
				int xDest, int yDest, int width, int height,
				int xClip, int yClip, int wClip, int hClip,
				GBool needUpdate, GBool composited) {
  if (tile && needUpdate) {
    updateTileData(tile, xSrc, ySrc, width, height, composited);
  }
  if (xDest < xClip) {
    xSrc += xClip - xDest;
    width -= xClip - xDest;
    xDest = xClip;
  }
  if (xDest + width > xClip + wClip) {
    width = xClip + wClip - xDest;
  }
  if (yDest < yClip) {
    ySrc += yClip - yDest;
    height -= yClip - yDest;
    yDest = yClip;
  }
  if (yDest + height > yClip + hClip) {
    height = yClip + hClip - yDest;
  }
  if (width > 0 && height > 0) {
    redrawRect(tile, xSrc, ySrc, xDest, yDest, width, height, composited);
  }
}
//========================================================================
//
// PDFCore.h
//
// Copyright 2004 Glyph & Cog, LLC
//
//========================================================================

#ifndef PDFCORE_H
#define PDFCORE_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include <stdlib.h>
#include "SplashTypes.h"
#include "CharTypes.h"

class GString;
class GList;
class SplashBitmap;
class SplashPattern;
class BaseStream;
class PDFDoc;
class Links;
class LinkDest;
class LinkAction;
class TextPage;
class HighlightFile;
class CoreOutputDev;
class PDFCore;

//------------------------------------------------------------------------
// zoom factor
//------------------------------------------------------------------------

#define zoomPage  -1
#define zoomWidth -2
#define defZoom   125

//------------------------------------------------------------------------


//------------------------------------------------------------------------

// Number of pixels of matte color between pages in continuous mode.
#define continuousModePageSpacing 3

//------------------------------------------------------------------------
// PDFCorePage
//------------------------------------------------------------------------

class PDFCorePage {
public:

  PDFCorePage(int pageA, int wA, int hA, int tileWA, int tileHA);
  ~PDFCorePage();

  int page;
  GList *tiles;			// cached tiles [PDFCoreTile]
  int xDest, yDest;		// position of upper-left corner
				//   in the drawing area
  int w, h;			// size of whole page bitmap
  int tileW, tileH;		// size of tiles
  Links *links;			// hyperlinks for this page
  TextPage *text;		// extracted text
};

//------------------------------------------------------------------------
// PDFCoreTile
//------------------------------------------------------------------------

class PDFCoreTile {
public:

  PDFCoreTile(int xDestA, int yDestA);
  virtual ~PDFCoreTile();

  int xMin, yMin, xMax, yMax;
  int xDest, yDest;
  Guint edges;
  SplashBitmap *bitmap;
  double ctm[6];		// coordinate transform matrix:
				//   default user space -> device space
  double ictm[6];		// inverse CTM
};

#define pdfCoreTileTopEdge      0x01
#define pdfCoreTileBottomEdge   0x02
#define pdfCoreTileLeftEdge     0x04
#define pdfCoreTileRightEdge    0x08
#define pdfCoreTileTopSpace     0x10
#define pdfCoreTileBottomSpace  0x20

//------------------------------------------------------------------------
// PDFHistory
//------------------------------------------------------------------------

struct PDFHistory {
  GString *fileName;
  int page;
};

#define pdfHistorySize 50


//------------------------------------------------------------------------
// PDFCore
//------------------------------------------------------------------------

class PDFCore {
public:

  PDFCore(SplashColorMode colorModeA, int bitmapRowPadA,
	  GBool reverseVideoA, SplashColorPtr paperColorA,
	  GBool incrementalUpdate);
  virtual ~PDFCore();

  //----- loadFile / displayPage / displayDest

  // Load a new file.  Returns pdfOk or error code.
  virtual int loadFile(GString *fileName, GString *ownerPassword = NULL,
		       GString *userPassword = NULL);

#ifdef WIN32
  // Load a new file.  Returns pdfOk or error code.
  virtual int loadFile(wchar_t *fileName, int fileNameLen,
		       GString *ownerPassword = NULL,
		       GString *userPassword = NULL);
#endif

  // Load a new file, via a Stream instead of a file name.  Returns
  // pdfOk or error code.
  virtual int loadFile(BaseStream *stream, GString *ownerPassword = NULL,
		       GString *userPassword = NULL);

  // Load an already-created PDFDoc object.
  virtual void loadDoc(PDFDoc *docA);

  // Clear out the current document, if any.
  virtual void clear();

  // Same as clear(), but returns the PDFDoc object instead of
  // deleting it.
  virtual PDFDoc *takeDoc(GBool redraw);

  // Display (or redisplay) the specified page.  If <scrollToTop> is
  // set, the window is vertically scrolled to the top; otherwise, no
  // scrolling is done.  If <addToHist> is set, this page change is
  // added to the history list.
  virtual void displayPage(int topPageA, double zoomA, int rotateA,
			   GBool scrollToTop, GBool addToHist);

  // Display a link destination.
  virtual void displayDest(LinkDest *dest, double zoomA, int rotateA,
			   GBool addToHist);

  // Update the display, given the specified parameters.
  virtual void update(int topPageA, int scrollXA, int scrollYA,
		      double zoomA, int rotateA, GBool force, GBool addToHist);

  //----- page/position changes

  virtual GBool gotoNextPage(int inc, GBool top);
  virtual GBool gotoPrevPage(int dec, GBool top, GBool bottom);
  virtual GBool gotoNamedDestination(GString *dest);
  virtual GBool goForward();
  virtual GBool goBackward();
  virtual void scrollLeft(int nCols = 16);
  virtual void scrollRight(int nCols = 16);
  virtual void scrollUp(int nLines = 16);
  virtual void scrollUpPrevPage(int nLines = 16);
  virtual void scrollDown(int nLines = 16);
  virtual void scrollDownNextPage(int nLines = 16);
  virtual void scrollPageUp();
  virtual void scrollPageDown();
  virtual void scrollTo(int x, int y);
  virtual void scrollToLeftEdge();
  virtual void scrollToRightEdge();
  virtual void scrollToTopEdge();
  virtual void scrollToBottomEdge();
  virtual void scrollToTopLeft();
  virtual void scrollToBottomRight();
  virtual void zoomToRect(int pg, double ulx, double uly,
			  double lrx, double lry);
  virtual void zoomCentered(double zoomA);
  virtual void zoomToCurrentWidth();
  virtual void setContinuousMode(GBool cm);

  //----- selection

  // Current selected region.
  void setSelection(int newSelectPage,
		    int newSelectULX, int newSelectULY,
		    int newSelectLRX, int newSelectLRY);
  void moveSelection(int pg, int x, int y);
  GBool getSelection(int *pg, double *ulx, double *uly,
		     double *lrx, double *lry);

  // Text extraction.
  GString *extractText(int pg, double xMin, double yMin,
		       double xMax, double yMax);

  //----- find

  virtual GBool find(char *s, GBool caseSensitive, GBool next, GBool backward,
		     GBool onePageOnly);
  virtual GBool findU(Unicode *u, int len, GBool caseSensitive,
		      GBool next, GBool backward, GBool onePageOnly);


  //----- coordinate conversion

  // user space: per-pace, as defined by PDF file; unit = point
  // device space: (0,0) is upper-left corner of a page; unit = pixel
  // window space: (0,0) is upper-left corner of drawing area; unit = pixel

  GBool cvtWindowToUser(int xw, int yw, int *pg, double *xu, double *yu);
  GBool cvtWindowToDev(int xw, int yw, int *pg, int *xd, int *yd);
  void cvtUserToWindow(int pg, double xy, double yu, int *xw, int *yw);
  void cvtUserToDev(int pg, double xu, double yu, int *xd, int *yd);
  void cvtDevToWindow(int pg, int xd, int yd, int *xw, int *yw);
  void cvtDevToUser(int pg, int xd, int yd, double *xu, double *yu);

  //----- misc access

  PDFDoc *getDoc() { return doc; }
  int getPageNum() { return topPage; }
  double getZoom() { return zoom; }
  double getZoomDPI() { return dpi; }
  int getRotate() { return rotate; }
  GBool getContinuousMode() { return continuousMode; }
  virtual void setReverseVideo(GBool reverseVideoA);
  GBool canGoBack() { return historyBLen > 1; }
  GBool canGoForward() { return historyFLen > 0; }
  int getScrollX() { return scrollX; }
  int getScrollY() { return scrollY; }
  int getDrawAreaWidth() { return drawAreaWidth; }
  int getDrawAreaHeight() { return drawAreaHeight; }
  virtual void setBusyCursor(GBool busy) = 0;
  LinkAction *findLink(int pg, double x, double y);

protected:

  int loadFile2(PDFDoc *newDoc);
  void addPage(int pg, int rot);
  void needTile(PDFCorePage *page, int x, int y);
  void xorRectangle(int pg, int x0, int y0, int x1, int y1,
		    SplashPattern *pattern, PDFCoreTile *oneTile = NULL);
  int loadHighlightFile(HighlightFile *hf, SplashColorPtr color,
			SplashColorPtr selectColor, GBool selectable);
  PDFCorePage *findPage(int pg);
  static void redrawCbk(void *data, int x0, int y0, int x1, int y1,
			GBool composited);
  void redrawWindow(int x, int y, int width, int height,
		    GBool needUpdate);
  virtual PDFCoreTile *newTile(int xDestA, int yDestA);
  virtual void updateTileData(PDFCoreTile *tileA, int xSrc, int ySrc,
			      int width, int height, GBool composited);
  virtual void redrawRect(PDFCoreTile *tileA, int xSrc, int ySrc,
			  int xDest, int yDest, int width, int height,
			  GBool composited) = 0;
  void clippedRedrawRect(PDFCoreTile *tile, int xSrc, int ySrc,
			 int xDest, int yDest, int width, int height,
			 int xClip, int yClip, int wClip, int hClip,
			 GBool needUpdate, GBool composited = gTrue);
  virtual void updateScrollbars() = 0;
  virtual GBool checkForNewFile() { return gFalse; }

  PDFDoc *doc;			// current PDF file
  GBool continuousMode;		// false for single-page mode, true for
				//   continuous mode
  int drawAreaWidth,		// size of the PDF display area
      drawAreaHeight;
  double maxUnscaledPageW,	// maximum unscaled page size
         maxUnscaledPageH;
  int maxPageW;			// maximum page width (only used in
				//   continuous mode)
  int totalDocH;		// total document height (only used in
				//   continuous mode)
  int *pageY;			// top coordinates for each page (only used
				//   in continuous mode)
  int topPage;			// page at top of window
  int scrollX, scrollY;		// offset from top left corner of topPage
				//   to top left corner of window
  double zoom;			// current zoom level, in percent of 72 dpi
  double dpi;			// current zoom level, in DPI
  int rotate;			// current page rotation

  int selectPage;		// page number of current selection
  int selectULX,		// coordinates of current selection,
      selectULY,		//   in device space -- (ULX==LRX || ULY==LRY)
      selectLRX,		//   means there is no selection
      selectLRY;
  GBool dragging;		// set while selection is being dragged
  GBool lastDragLeft;		// last dragged selection edge was left/right
  GBool lastDragTop;		// last dragged selection edge was top/bottom

  PDFHistory			// page history queue
    history[pdfHistorySize];
  int historyCur;               // currently displayed page
  int historyBLen;              // number of valid entries backward from
                                //   current entry
  int historyFLen;              // number of valid entries forward from
                                //   current entry


  GList *pages;			// cached pages [PDFCorePage]
  PDFCoreTile *curTile;		// tile currently being rasterized
  PDFCorePage *curPage;		// page to which curTile belongs

  SplashColor paperColor;
  CoreOutputDev *out;

  friend class PDFCoreTile;
};

#endif
//========================================================================
//
// PDFDoc.cc
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#ifdef WIN32
#  include <windows.h>
#endif
#include "GString.h"
#include "config.h"
#include "GlobalParams.h"
#include "Page.h"
#include "Catalog.h"
#include "Stream.h"
#include "XRef.h"
#include "Link.h"
#include "OutputDev.h"
#include "Error.h"
#include "ErrorCodes.h"
#include "Lexer.h"
#include "Parser.h"
#include "SecurityHandler.h"
#ifndef DISABLE_OUTLINE
#include "Outline.h"
#endif
#include "PDFDoc.h"

//------------------------------------------------------------------------

#define headerSearchSize 1024	// read this many bytes at beginning of
				//   file to look for '%PDF'

//------------------------------------------------------------------------
// PDFDoc
//------------------------------------------------------------------------

PDFDoc::PDFDoc(GString *fileNameA, GString *ownerPassword,
	       GString *userPassword, void *guiDataA) {
  Object obj;
  GString *fileName1, *fileName2;

  ok = gFalse;
  errCode = errNone;

  guiData = guiDataA;

  file = NULL;
  str = NULL;
  xref = NULL;
  catalog = NULL;
#ifndef DISABLE_OUTLINE
  outline = NULL;
#endif

  fileName = fileNameA;
  fileName1 = fileName;


  // try to open file
  fileName2 = NULL;
#ifdef VMS
  if (!(file = fopen(fileName1->getCString(), "rb", "ctx=stm"))) {
    error(-1, "Couldn't open file '%s'", fileName1->getCString());
    errCode = errOpenFile;
    return;
  }
#else
  if (!(file = fopen(fileName1->getCString(), "rb"))) {
    fileName2 = fileName->copy();
    fileName2->lowerCase();
    if (!(file = fopen(fileName2->getCString(), "rb"))) {
      fileName2->upperCase();
      if (!(file = fopen(fileName2->getCString(), "rb"))) {
	error(-1, "Couldn't open file '%s'", fileName->getCString());
	delete fileName2;
	errCode = errOpenFile;
	return;
      }
    }
    delete fileName2;
  }
#endif

  // create stream
  obj.initNull();
  str = new FileStream(file, 0, gFalse, 0, &obj);

  ok = setup(ownerPassword, userPassword);
}

#ifdef WIN32
PDFDoc::PDFDoc(wchar_t *fileNameA, int fileNameLen, GString *ownerPassword,
	       GString *userPassword, void *guiDataA) {
  OSVERSIONINFO version;
  wchar_t fileName2[_MAX_PATH + 1];
  Object obj;
  int i;

  ok = gFalse;
  errCode = errNone;

  guiData = guiDataA;

  file = NULL;
  str = NULL;
  xref = NULL;
  catalog = NULL;
#ifndef DISABLE_OUTLINE
  outline = NULL;
#endif

  //~ file name should be stored in Unicode (?)
  fileName = new GString();
  for (i = 0; i < fileNameLen; ++i) {
    fileName->append((char)fileNameA[i]);
  }

  // zero-terminate the file name string
  for (i = 0; i < fileNameLen && i < _MAX_PATH; ++i) {
    fileName2[i] = fileNameA[i];
  }
  fileName2[i] = 0;

  // try to open file
  // NB: _wfopen is only available in NT
  version.dwOSVersionInfoSize = sizeof(version);
  GetVersionEx(&version);
  if (version.dwPlatformId == VER_PLATFORM_WIN32_NT) {
    file = _wfopen(fileName2, L"rb");
  } else {
    file = fopen(fileName->getCString(), "rb");
  }
  if (!file) {
    error(-1, "Couldn't open file '%s'", fileName->getCString());
    errCode = errOpenFile;
    return;
  }

  // create stream
  obj.initNull();
  str = new FileStream(file, 0, gFalse, 0, &obj);

  ok = setup(ownerPassword, userPassword);
}
#endif

PDFDoc::PDFDoc(BaseStream *strA, GString *ownerPassword,
	       GString *userPassword, void *guiDataA) {
  ok = gFalse;
  errCode = errNone;
  guiData = guiDataA;
  if (strA->getFileName()) {
    fileName = strA->getFileName()->copy();
  } else {
    fileName = NULL;
  }
  file = NULL;
  str = strA;
  xref = NULL;
  catalog = NULL;
#ifndef DISABLE_OUTLINE
  outline = NULL;
#endif
  ok = setup(ownerPassword, userPassword);
}

GBool PDFDoc::setup(GString *ownerPassword, GString *userPassword) {
  str->reset();

  // check header
  checkHeader();

  // read xref table
  xref = new XRef(str);
  if (!xref->isOk()) {
    error(-1, "Couldn't read xref table");
    errCode = xref->getErrorCode();
    return gFalse;
  }

  // check for encryption
  if (!checkEncryption(ownerPassword, userPassword)) {
    errCode = errEncrypted;
    return gFalse;
  }

  // read catalog
  catalog = new Catalog(xref);
  if (!catalog->isOk()) {
    error(-1, "Couldn't read page catalog");
    errCode = errBadCatalog;
    return gFalse;
  }

#ifndef DISABLE_OUTLINE
  // read outline
  outline = new Outline(catalog->getOutline(), xref);
#endif

  // done
  return gTrue;
}

PDFDoc::~PDFDoc() {
#ifndef DISABLE_OUTLINE
  if (outline) {
    delete outline;
  }
#endif
  if (catalog) {
    delete catalog;
  }
  if (xref) {
    delete xref;
  }
  if (str) {
    delete str;
  }
  if (file) {
    fclose(file);
  }
  if (fileName) {
    delete fileName;
  }
}

// Check for a PDF header on this stream.  Skip past some garbage
// if necessary.
void PDFDoc::checkHeader() {
  char hdrBuf[headerSearchSize+1];
  char *p;
  int i;

  pdfVersion = 0;
  for (i = 0; i < headerSearchSize; ++i) {
    hdrBuf[i] = str->getChar();
  }
  hdrBuf[headerSearchSize] = '\0';
  for (i = 0; i < headerSearchSize - 5; ++i) {
    if (!strncmp(&hdrBuf[i], "%PDF-", 5)) {
      break;
    }
  }
  if (i >= headerSearchSize - 5) {
    error(-1, "May not be a PDF file (continuing anyway)");
    return;
  }
  str->moveStart(i);
  if (!(p = strtok(&hdrBuf[i+5], " \t\n\r"))) {
    error(-1, "May not be a PDF file (continuing anyway)");
    return;
  }
  pdfVersion = atof(p);
  if (!(hdrBuf[i+5] >= '0' && hdrBuf[i+5] <= '9') ||
      pdfVersion > supportedPDFVersionNum + 0.0001) {
    error(-1, "PDF version %s -- xpdf supports version %s"
	  " (continuing anyway)", p, supportedPDFVersionStr);
  }
}

GBool PDFDoc::checkEncryption(GString *ownerPassword, GString *userPassword) {
  Object encrypt;
  GBool encrypted;
  SecurityHandler *secHdlr;
  GBool ret;

  xref->getTrailerDict()->dictLookup("Encrypt", &encrypt);
  if ((encrypted = encrypt.isDict())) {
    if ((secHdlr = SecurityHandler::make(this, &encrypt))) {
      if (secHdlr->checkEncryption(ownerPassword, userPassword)) {
	// authorization succeeded
       	xref->setEncryption(secHdlr->getPermissionFlags(),
			    secHdlr->getOwnerPasswordOk(),
			    secHdlr->getFileKey(),
			    secHdlr->getFileKeyLength(),
			    secHdlr->getEncVersion(),
			    secHdlr->getEncAlgorithm());
	ret = gTrue;
      } else {
	// authorization failed
	ret = gFalse;
      }
      delete secHdlr;
    } else {
      // couldn't find the matching security handler
      ret = gFalse;
    }
  } else {
    // document is not encrypted
    ret = gTrue;
  }
  encrypt.free();
  return ret;
}

void PDFDoc::displayPage(OutputDev *out, int page,
			 double hDPI, double vDPI, int rotate,
			 GBool useMediaBox, GBool crop, GBool printing,
			 GBool (*abortCheckCbk)(void *data),
			 void *abortCheckCbkData) {
  if (globalParams->getPrintCommands()) {
    printf("***** page %d *****\n", page);
  }
    
    
  catalog->getPage(page)->display(out, hDPI, vDPI,
				  rotate, useMediaBox, crop, getLinks(page), printing, catalog,
				  abortCheckCbk, abortCheckCbkData);
}

void PDFDoc::displayPages(OutputDev *out, int firstPage, int lastPage,
			  double hDPI, double vDPI, int rotate,
			  GBool useMediaBox, GBool crop, GBool printing,
			  GBool (*abortCheckCbk)(void *data),
			  void *abortCheckCbkData) {
  int page;

  for (page = firstPage; page <= lastPage; ++page) {
    displayPage(out, page, hDPI, vDPI, rotate, useMediaBox, crop, printing,
		abortCheckCbk, abortCheckCbkData);
  }
}

void PDFDoc::displayPageSlice(OutputDev *out, int page,
			      double hDPI, double vDPI, int rotate,
			      GBool useMediaBox, GBool crop, GBool printing,
			      int sliceX, int sliceY, int sliceW, int sliceH,
			      GBool (*abortCheckCbk)(void *data),
			      void *abortCheckCbkData) {
  catalog->getPage(page)->displaySlice(out, hDPI, vDPI,
				       rotate, useMediaBox, crop,
				       sliceX, sliceY, sliceW, sliceH,
				       getLinks(page), printing, catalog,
				       abortCheckCbk, abortCheckCbkData);
}

Links *PDFDoc::getLinks(int page) {
  return catalog->getPage(page)->getLinks(catalog);
}

void PDFDoc::processLinks(OutputDev *out, int page) {
  catalog->getPage(page)->processLinks(out, catalog);
}

GBool PDFDoc::isLinearized() {
  Parser *parser;
  Object obj1, obj2, obj3, obj4, obj5;
  GBool lin;

  lin = gFalse;
  obj1.initNull();
  parser = new Parser(xref,
	     new Lexer(xref,
	       str->makeSubStream(str->getStart(), gFalse, 0, &obj1)),
	     gTrue);
  parser->getObj(&obj1);
  parser->getObj(&obj2);
  parser->getObj(&obj3);
  parser->getObj(&obj4);
  if (obj1.isInt() && obj2.isInt() && obj3.isCmd("obj") &&
      obj4.isDict()) {
    obj4.dictLookup("Linearized", &obj5);
    if (obj5.isNum() && obj5.getNum() > 0) {
      lin = gTrue;
    }
    obj5.free();
  }
  obj4.free();
  obj3.free();
  obj2.free();
  obj1.free();
  delete parser;
  return lin;
}

GBool PDFDoc::saveAs(GString *name) {
  FILE *f;
  int c;

  if (!(f = fopen(name->getCString(), "wb"))) {
    error(-1, "Couldn't open file '%s'", name->getCString());
    return gFalse;
  }
  str->reset();
  while ((c = str->getChar()) != EOF) {
    fputc(c, f);
  }
  str->close();
  fclose(f);
  return gTrue;
}
//========================================================================
//
// PDFDoc.h
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef PDFDOC_H
#define PDFDOC_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include <stdio.h>
#include "XRef.h"
#include "Catalog.h"
#include "Page.h"

class GString;
class BaseStream;
class OutputDev;
class Links;
class LinkAction;
class LinkDest;
class Outline;

//------------------------------------------------------------------------
// PDFDoc
//------------------------------------------------------------------------

class PDFDoc {
public:

  PDFDoc(GString *fileNameA, GString *ownerPassword = NULL,
	 GString *userPassword = NULL, void *guiDataA = NULL);
#ifdef WIN32
  PDFDoc(wchar_t *fileNameA, int fileNameLen, GString *ownerPassword = NULL,
	 GString *userPassword = NULL, void *guiDataA = NULL);
#endif
  PDFDoc(BaseStream *strA, GString *ownerPassword = NULL,
	 GString *userPassword = NULL, void *guiDataA = NULL);
  ~PDFDoc();

  // Was PDF document successfully opened?
  GBool isOk() { return ok; }

  // Get the error code (if isOk() returns false).
  int getErrorCode() { return errCode; }

  // Get file name.
  GString *getFileName() { return fileName; }

  // Get the xref table.
  XRef *getXRef() { return xref; }

  // Get catalog.
  Catalog *getCatalog() { return catalog; }

  // Get base stream.
  BaseStream *getBaseStream() { return str; }

  // Get page parameters.
  double getPageMediaWidth(int page)
    { return catalog->getPage(page)->getMediaWidth(); }
  double getPageMediaHeight(int page)
    { return catalog->getPage(page)->getMediaHeight(); }
  double getPageCropWidth(int page)
    { return catalog->getPage(page)->getCropWidth(); }
  double getPageCropHeight(int page)
    { return catalog->getPage(page)->getCropHeight(); }
  int getPageRotate(int page)
    { return catalog->getPage(page)->getRotate(); }

  // Get number of pages.
  int getNumPages() { return catalog->getNumPages(); }

  // Return the contents of the metadata stream, or NULL if there is
  // no metadata.
  GString *readMetadata() { return catalog->readMetadata(); }

  // Return the structure tree root object.
  Object *getStructTreeRoot() { return catalog->getStructTreeRoot(); }

  // Display a page.
  void displayPage(OutputDev *out, int page,
		   double hDPI, double vDPI, int rotate,
		   GBool useMediaBox, GBool crop, GBool printing,
		   GBool (*abortCheckCbk)(void *data) = NULL,
		   void *abortCheckCbkData = NULL);

  // Display a range of pages.
  void displayPages(OutputDev *out, int firstPage, int lastPage,
		    double hDPI, double vDPI, int rotate,
		    GBool useMediaBox, GBool crop, GBool printing,
		    GBool (*abortCheckCbk)(void *data) = NULL,
		    void *abortCheckCbkData = NULL);

  // Display part of a page.
  void displayPageSlice(OutputDev *out, int page,
			double hDPI, double vDPI, int rotate,
			GBool useMediaBox, GBool crop, GBool printing,
			int sliceX, int sliceY, int sliceW, int sliceH,
			GBool (*abortCheckCbk)(void *data) = NULL,
			void *abortCheckCbkData = NULL);

  // Find a page, given its object ID.  Returns page number, or 0 if
  // not found.
  int findPage(int num, int gen) { return catalog->findPage(num, gen); }

  // Returns the links for the current page, transferring ownership to
  // the caller.
  Links *getLinks(int page);

  // Find a named destination.  Returns the link destination, or
  // NULL if <name> is not a destination.
  LinkDest *findDest(GString *name)
    { return catalog->findDest(name); }

  // Process the links for a page.
  void processLinks(OutputDev *out, int page);

#ifndef DISABLE_OUTLINE
  // Return the outline object.
  Outline *getOutline() { return outline; }
#endif

  // Is the file encrypted?
  GBool isEncrypted() { return xref->isEncrypted(); }

  // Check various permissions.
  GBool okToPrint(GBool ignoreOwnerPW = gFalse)
    { return xref->okToPrint(ignoreOwnerPW); }
  GBool okToChange(GBool ignoreOwnerPW = gFalse)
    { return xref->okToChange(ignoreOwnerPW); }
  GBool okToCopy(GBool ignoreOwnerPW = gFalse)
    { return xref->okToCopy(ignoreOwnerPW); }
  GBool okToAddNotes(GBool ignoreOwnerPW = gFalse)
    { return xref->okToAddNotes(ignoreOwnerPW); }

  // Is this document linearized?
  GBool isLinearized();

  // Return the document's Info dictionary (if any).
  Object *getDocInfo(Object *obj) { return xref->getDocInfo(obj); }
  Object *getDocInfoNF(Object *obj) { return xref->getDocInfoNF(obj); }

  // Return the PDF version specified by the file.
  double getPDFVersion() { return pdfVersion; }

  // Save this file with another name.
  GBool saveAs(GString *name);

  // Return a pointer to the GUI (XPDFCore or WinPDFCore object).
  void *getGUIData() { return guiData; }


private:

  GBool setup(GString *ownerPassword, GString *userPassword);
  void checkHeader();
  GBool checkEncryption(GString *ownerPassword, GString *userPassword);

  GString *fileName;
  FILE *file;
  BaseStream *str;
  void *guiData;
  double pdfVersion;
  XRef *xref;
  Catalog *catalog;
#ifndef DISABLE_OUTLINE
  Outline *outline;
#endif


  GBool ok;
  int errCode;
};

#endif
//========================================================================
//
// PDFDocEncoding.h
//
// Copyright 2002-2003 Glyph & Cog, LLC
//
//========================================================================

#include "PDFDocEncoding.h"

Unicode pdfDocEncoding[256] = {
  0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 00
  0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
  0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 10
  0x02d8, 0x02c7, 0x02c6, 0x02d9, 0x02dd, 0x02db, 0x02da, 0x02dc,
  0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, // 20
  0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
  0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, // 30
  0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
  0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, // 40
  0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
  0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, // 50
  0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
  0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, // 60
  0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
  0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, // 70
  0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x0000,
  0x2022, 0x2020, 0x2021, 0x2026, 0x2014, 0x2013, 0x0192, 0x2044, // 80
  0x2039, 0x203a, 0x2212, 0x2030, 0x201e, 0x201c, 0x201d, 0x2018,
  0x2019, 0x201a, 0x2122, 0xfb01, 0xfb02, 0x0141, 0x0152, 0x0160, // 90
  0x0178, 0x017d, 0x0131, 0x0142, 0x0153, 0x0161, 0x017e, 0x0000,
  0x20ac, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, // a0
  0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x0000, 0x00ae, 0x00af,
  0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, // b0
  0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,
  0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, // c0
  0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,
  0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, // d0
  0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df,
  0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, // e0
  0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,
  0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, // f0
  0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff
};
//========================================================================
//
// PDFDocEncoding.h
//
// Copyright 2002-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef PDFDOCENCODING_H
#define PDFDOCENCODING_H

#include "CharTypes.h"

extern Unicode pdfDocEncoding[256];

#endif
//========================================================================
//
// PSOutputDev.cc
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stdio.h>
#include <stddef.h>
#include <stdarg.h>
#include <signal.h>
#include <math.h>
#include "GString.h"
#include "GList.h"
#include "config.h"
#include "GlobalParams.h"
#include "Object.h"
#include "Error.h"
#include "Function.h"
#include "Gfx.h"
#include "GfxState.h"
#include "GfxFont.h"
#include "UnicodeMap.h"
#include "FoFiType1C.h"
#include "FoFiTrueType.h"
#include "Catalog.h"
#include "Page.h"
#include "Stream.h"
#include "Annot.h"
#include "XRef.h"
#include "PreScanOutputDev.h"
#if HAVE_SPLASH
#  include "Splash.h"
#  include "SplashBitmap.h"
#  include "SplashOutputDev.h"
#endif
#include "PSOutputDev.h"

#ifdef MACOS
// needed for setting type/creator of MacOS files
#include "ICSupport.h"
#endif

// the MSVC math.h doesn't define this
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

//------------------------------------------------------------------------

// Resolution at which pages with transparency will be rasterized.
#define splashDPI 300

//------------------------------------------------------------------------
// PostScript prolog and setup
//------------------------------------------------------------------------

// The '~' escapes mark prolog code that is emitted only in certain
// levels:
//
//   ~[123][sn]
//      ^   ^----- s=psLevel*Sep, n=psLevel*
//      +----- 1=psLevel1*, 2=psLevel2*, 3=psLevel3*

static char *prolog[] = {
  "/xpdf 75 dict def xpdf begin",
  "% PDF special state",
  "/pdfDictSize 15 def",
  "~1sn",
  "/pdfStates 64 array def",
  "  0 1 63 {",
  "    pdfStates exch pdfDictSize dict",
  "    dup /pdfStateIdx 3 index put",
  "    put",
  "  } for",
  "~123sn",
  "/pdfSetup {",
  "  3 1 roll 2 array astore",
  "  /setpagedevice where {",
  "    pop 3 dict begin",
  "      /PageSize exch def",
  "      /ImagingBBox null def",
  "      /Policies 1 dict dup begin /PageSize 3 def end def",
  "      { /Duplex true def } if",
  "    currentdict end setpagedevice",
  "  } {",
  "    pop pop",
  "  } ifelse",
  "} def",
  "~1sn",
  "/pdfOpNames [",
  "  /pdfFill /pdfStroke /pdfLastFill /pdfLastStroke",
  "  /pdfTextMat /pdfFontSize /pdfCharSpacing /pdfTextRender",
  "  /pdfTextRise /pdfWordSpacing /pdfHorizScaling /pdfTextClipPath",
  "] def",
  "~123sn",
  "/pdfStartPage {",
  "~1sn",
  "  pdfStates 0 get begin",
  "~23sn",
  "  pdfDictSize dict begin",
  "~23n",
  "  /pdfFillCS [] def",
  "  /pdfFillXform {} def",
  "  /pdfStrokeCS [] def",
  "  /pdfStrokeXform {} def",
  "~1n",
  "  /pdfFill 0 def",
  "  /pdfStroke 0 def",
  "~1s",
  "  /pdfFill [0 0 0 1] def",
  "  /pdfStroke [0 0 0 1] def",
  "~23sn",
  "  /pdfFill [0] def",
  "  /pdfStroke [0] def",
  "  /pdfFillOP false def",
  "  /pdfStrokeOP false def",
  "~123sn",
  "  /pdfLastFill false def",
  "  /pdfLastStroke false def",
  "  /pdfTextMat [1 0 0 1 0 0] def",
  "  /pdfFontSize 0 def",
  "  /pdfCharSpacing 0 def",
  "  /pdfTextRender 0 def",
  "  /pdfTextRise 0 def",
  "  /pdfWordSpacing 0 def",
  "  /pdfHorizScaling 1 def",
  "  /pdfTextClipPath [] def",
  "} def",
  "/pdfEndPage { end } def",
  "~23s",
  "% separation convention operators",
  "/findcmykcustomcolor where {",
  "  pop",
  "}{",
  "  /findcmykcustomcolor { 5 array astore } def",
  "} ifelse",
  "/setcustomcolor where {",
  "  pop",
  "}{",
  "  /setcustomcolor {",
  "    exch",
  "    [ exch /Separation exch dup 4 get exch /DeviceCMYK exch",
  "      0 4 getinterval cvx",
  "      [ exch /dup load exch { mul exch dup } /forall load",
  "        /pop load dup ] cvx",
  "    ] setcolorspace setcolor",
  "  } def",
  "} ifelse",
  "/customcolorimage where {",
  "  pop",
  "}{",
  "  /customcolorimage {",
  "    gsave",
  "    [ exch /Separation exch dup 4 get exch /DeviceCMYK exch",
  "      0 4 getinterval",
  "      [ exch /dup load exch { mul exch dup } /forall load",
  "        /pop load dup ] cvx",
  "    ] setcolorspace",
  "    10 dict begin",
  "      /ImageType 1 def",
  "      /DataSource exch def",
  "      /ImageMatrix exch def",
  "      /BitsPerComponent exch def",
  "      /Height exch def",
  "      /Width exch def",
  "      /Decode [1 0] def",
  "    currentdict end",
  "    image",
  "    grestore",
  "  } def",
  "} ifelse",
  "~123sn",
  "% PDF color state",
  "~1n",
  "/g { dup /pdfFill exch def setgray",
  "     /pdfLastFill true def /pdfLastStroke false def } def",
  "/G { dup /pdfStroke exch def setgray",
  "     /pdfLastStroke true def /pdfLastFill false def } def",
  "/fCol {",
  "  pdfLastFill not {",
  "    pdfFill setgray",
  "    /pdfLastFill true def /pdfLastStroke false def",
  "  } if",
  "} def",
  "/sCol {",
  "  pdfLastStroke not {",
  "    pdfStroke setgray",
  "    /pdfLastStroke true def /pdfLastFill false def",
  "  } if",
  "} def",
  "~1s",
  "/k { 4 copy 4 array astore /pdfFill exch def setcmykcolor",
  "     /pdfLastFill true def /pdfLastStroke false def } def",
  "/K { 4 copy 4 array astore /pdfStroke exch def setcmykcolor",
  "     /pdfLastStroke true def /pdfLastFill false def } def",
  "/fCol {",
  "  pdfLastFill not {",
  "    pdfFill aload pop setcmykcolor",
  "    /pdfLastFill true def /pdfLastStroke false def",
  "  } if",
  "} def",
  "/sCol {",
  "  pdfLastStroke not {",
  "    pdfStroke aload pop setcmykcolor",
  "    /pdfLastStroke true def /pdfLastFill false def",
  "  } if",
  "} def",
  "~23n",
  "/cs { /pdfFillXform exch def dup /pdfFillCS exch def",
  "      setcolorspace } def",
  "/CS { /pdfStrokeXform exch def dup /pdfStrokeCS exch def",
  "      setcolorspace } def",
  "/sc { pdfLastFill not { pdfFillCS setcolorspace } if",
  "      dup /pdfFill exch def aload pop pdfFillXform setcolor",
  "     /pdfLastFill true def /pdfLastStroke false def } def",
  "/SC { pdfLastStroke not { pdfStrokeCS setcolorspace } if",
  "      dup /pdfStroke exch def aload pop pdfStrokeXform setcolor",
  "     /pdfLastStroke true def /pdfLastFill false def } def",
  "/op { /pdfFillOP exch def",
  "      pdfLastFill { pdfFillOP setoverprint } if } def",
  "/OP { /pdfStrokeOP exch def",
  "      pdfLastStroke { pdfStrokeOP setoverprint } if } def",
  "/fCol {",
  "  pdfLastFill not {",
  "    pdfFillCS setcolorspace",
  "    pdfFill aload pop pdfFillXform setcolor",
  "    pdfFillOP setoverprint",
  "    /pdfLastFill true def /pdfLastStroke false def",
  "  } if",
  "} def",
  "/sCol {",
  "  pdfLastStroke not {",
  "    pdfStrokeCS setcolorspace",
  "    pdfStroke aload pop pdfStrokeXform setcolor",
  "    pdfStrokeOP setoverprint",
  "    /pdfLastStroke true def /pdfLastFill false def",
  "  } if",
  "} def",
  "~23s",
  "/k { 4 copy 4 array astore /pdfFill exch def setcmykcolor",
  "     /pdfLastFill true def /pdfLastStroke false def } def",
  "/K { 4 copy 4 array astore /pdfStroke exch def setcmykcolor",
  "     /pdfLastStroke true def /pdfLastFill false def } def",
  "/ck { 6 copy 6 array astore /pdfFill exch def",
  "      findcmykcustomcolor exch setcustomcolor",
  "      /pdfLastFill true def /pdfLastStroke false def } def",
  "/CK { 6 copy 6 array astore /pdfStroke exch def",
  "      findcmykcustomcolor exch setcustomcolor",
  "      /pdfLastStroke true def /pdfLastFill false def } def",
  "/op { /pdfFillOP exch def",
  "      pdfLastFill { pdfFillOP setoverprint } if } def",
  "/OP { /pdfStrokeOP exch def",
  "      pdfLastStroke { pdfStrokeOP setoverprint } if } def",
  "/fCol {",
  "  pdfLastFill not {",
  "    pdfFill aload length 4 eq {",
  "      setcmykcolor",
  "    }{",
  "      findcmykcustomcolor exch setcustomcolor",
  "    } ifelse",
  "    pdfFillOP setoverprint",
  "    /pdfLastFill true def /pdfLastStroke false def",
  "  } if",
  "} def",
  "/sCol {",
  "  pdfLastStroke not {",
  "    pdfStroke aload length 4 eq {",
  "      setcmykcolor",
  "    }{",
  "      findcmykcustomcolor exch setcustomcolor",
  "    } ifelse",
  "    pdfStrokeOP setoverprint",
  "    /pdfLastStroke true def /pdfLastFill false def",
  "  } if",
  "} def",
  "~123sn",
  "% build a font",
  "/pdfMakeFont {",
  "  4 3 roll findfont",
  "  4 2 roll matrix scale makefont",
  "  dup length dict begin",
  "    { 1 index /FID ne { def } { pop pop } ifelse } forall",
  "    /Encoding exch def",
  "    currentdict",
  "  end",
  "  definefont pop",
  "} def",
  "/pdfMakeFont16 {",
  "  exch findfont",
  "  dup length dict begin",
  "    { 1 index /FID ne { def } { pop pop } ifelse } forall",
  "    /WMode exch def",
  "    currentdict",
  "  end",
  "  definefont pop",
  "} def",
  "~3sn",
  "/pdfMakeFont16L3 {",
  "  1 index /CIDFont resourcestatus {",
  "    pop pop 1 index /CIDFont findresource /CIDFontType known",
  "  } {",
  "    false",
  "  } ifelse",
  "  {",
  "    0 eq { /Identity-H } { /Identity-V } ifelse",
  "    exch 1 array astore composefont pop",
  "  } {",
  "    pdfMakeFont16",
  "  } ifelse",
  "} def",
  "~123sn",
  "% graphics state operators",
  "~1sn",
  "/q {",
  "  gsave",
  "  pdfOpNames length 1 sub -1 0 { pdfOpNames exch get load } for",
  "  pdfStates pdfStateIdx 1 add get begin",
  "  pdfOpNames { exch def } forall",
  "} def",
  "/Q { end grestore } def",
  "~23sn",
  "/q { gsave pdfDictSize dict begin } def",
  "/Q {",
  "  end grestore",
  "  /pdfLastFill where {",
  "    pop",
  "    pdfLastFill {",
  "      pdfFillOP setoverprint",
  "    } {",
  "      pdfStrokeOP setoverprint",
  "    } ifelse",
  "  } if",
  "} def",
  "~123sn",
  "/cm { concat } def",
  "/d { setdash } def",
  "/i { setflat } def",
  "/j { setlinejoin } def",
  "/J { setlinecap } def",
  "/M { setmiterlimit } def",
  "/w { setlinewidth } def",
  "% path segment operators",
  "/m { moveto } def",
  "/l { lineto } def",
  "/c { curveto } def",
  "/re { 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto",
  "      neg 0 rlineto closepath } def",
  "/h { closepath } def",
  "% path painting operators",
  "/S { sCol stroke } def",
  "/Sf { fCol stroke } def",
  "/f { fCol fill } def",
  "/f* { fCol eofill } def",
  "% clipping operators",
  "/W { clip newpath } def",
  "/W* { eoclip newpath } def",
  "/Ws { strokepath clip newpath } def",
  "% text state operators",
  "/Tc { /pdfCharSpacing exch def } def",
  "/Tf { dup /pdfFontSize exch def",
  "      dup pdfHorizScaling mul exch matrix scale",
  "      pdfTextMat matrix concatmatrix dup 4 0 put dup 5 0 put",
  "      exch findfont exch makefont setfont } def",
  "/Tr { /pdfTextRender exch def } def",
  "/Ts { /pdfTextRise exch def } def",
  "/Tw { /pdfWordSpacing exch def } def",
  "/Tz { /pdfHorizScaling exch def } def",
  "% text positioning operators",
  "/Td { pdfTextMat transform moveto } def",
  "/Tm { /pdfTextMat exch def } def",
  "% text string operators",
  "/cshow where {",
  "  pop",
  "  /cshow2 {",
  "    dup {",
  "      pop pop",
  "      1 string dup 0 3 index put 3 index exec",
  "    } exch cshow",
  "    pop pop",
  "  } def",
  "}{",
  "  /cshow2 {",
  "    currentfont /FontType get 0 eq {",
  "      0 2 2 index length 1 sub {",
  "        2 copy get exch 1 add 2 index exch get",
  "        2 copy exch 256 mul add",
  "        2 string dup 0 6 5 roll put dup 1 5 4 roll put",
  "        3 index exec",
  "      } for",
  "    } {",
  "      dup {",
  "        1 string dup 0 3 index put 3 index exec",
  "      } forall",
  "    } ifelse",
  "    pop pop",
  "  } def",
  "} ifelse",
  "/awcp {", // awidthcharpath
  "  exch {",
  "    false charpath",
  "    5 index 5 index rmoveto",
  "    6 index eq { 7 index 7 index rmoveto } if",
  "  } exch cshow2",
  "  6 {pop} repeat",
  "} def",
  "/Tj {",
  "  fCol",  // because stringwidth has to draw Type 3 chars
  "  1 index stringwidth pdfTextMat idtransform pop",
  "  sub 1 index length dup 0 ne { div } { pop pop 0 } ifelse",
  "  pdfWordSpacing pdfHorizScaling mul 0 pdfTextMat dtransform 32",
  "  4 3 roll pdfCharSpacing pdfHorizScaling mul add 0",
  "  pdfTextMat dtransform",
  "  6 5 roll Tj1",
  "} def",
  "/Tj16 {",
  "  fCol",  // because stringwidth has to draw Type 3 chars
  "  2 index stringwidth pdfTextMat idtransform pop",
  "  sub exch div",
  "  pdfWordSpacing pdfHorizScaling mul 0 pdfTextMat dtransform 32",
  "  4 3 roll pdfCharSpacing pdfHorizScaling mul add 0",
  "  pdfTextMat dtransform",
  "  6 5 roll Tj1",
  "} def",
  "/Tj16V {",
  "  fCol",  // because stringwidth has to draw Type 3 chars
  "  2 index stringwidth pdfTextMat idtransform exch pop",
  "  sub exch div",
  "  0 pdfWordSpacing pdfTextMat dtransform 32",
  "  4 3 roll pdfCharSpacing add 0 exch",
  "  pdfTextMat dtransform",
  "  6 5 roll Tj1",
  "} def",
  "/Tj1 {",
  "  0 pdfTextRise pdfTextMat dtransform rmoveto",
  "  currentpoint 8 2 roll",
  "  pdfTextRender 1 and 0 eq {",
  "    6 copy awidthshow",
  "  } if",
  "  pdfTextRender 3 and dup 1 eq exch 2 eq or {",
  "    7 index 7 index moveto",
  "    6 copy",
  "    currentfont /FontType get 3 eq { fCol } { sCol } ifelse",
  "    false awcp currentpoint stroke moveto",
  "  } if",
  "  pdfTextRender 4 and 0 ne {",
  "    8 6 roll moveto",
  "    false awcp",
  "    /pdfTextClipPath [ pdfTextClipPath aload pop",
  "      {/moveto cvx}",
  "      {/lineto cvx}",
  "      {/curveto cvx}",
  "      {/closepath cvx}",
  "    pathforall ] def",
  "    currentpoint newpath moveto",
  "  } {",
  "    8 {pop} repeat",
  "  } ifelse",
  "  0 pdfTextRise neg pdfTextMat dtransform rmoveto",
  "} def",
  "/TJm { pdfFontSize 0.001 mul mul neg 0",
  "       pdfTextMat dtransform rmoveto } def",
  "/TJmV { pdfFontSize 0.001 mul mul neg 0 exch",
  "        pdfTextMat dtransform rmoveto } def",
  "/Tclip { pdfTextClipPath cvx exec clip newpath",
  "         /pdfTextClipPath [] def } def",
  "~1ns",
  "% Level 1 image operators",
  "~1n",
  "/pdfIm1 {",
  "  /pdfImBuf1 4 index string def",
  "  { currentfile pdfImBuf1 readhexstring pop } image",
  "} def",
  "~1s",
  "/pdfIm1Sep {",
  "  /pdfImBuf1 4 index string def",
  "  /pdfImBuf2 4 index string def",
  "  /pdfImBuf3 4 index string def",
  "  /pdfImBuf4 4 index string def",
  "  { currentfile pdfImBuf1 readhexstring pop }",
  "  { currentfile pdfImBuf2 readhexstring pop }",
  "  { currentfile pdfImBuf3 readhexstring pop }",
  "  { currentfile pdfImBuf4 readhexstring pop }",
  "  true 4 colorimage",
  "} def",
  "~1ns",
  "/pdfImM1 {",
  "  fCol /pdfImBuf1 4 index 7 add 8 idiv string def",
  "  { currentfile pdfImBuf1 readhexstring pop } imagemask",
  "} def",
  "/pdfImM1a {",
  "  { 2 copy get exch 1 add exch } imagemask",
  "  pop pop",
  "} def",
  "~23sn",
  "% Level 2 image operators",
  "/pdfImBuf 100 string def",
  "/pdfIm {",
  "  image",
  "  { currentfile pdfImBuf readline",
  "    not { pop exit } if",
  "    (%-EOD-) eq { exit } if } loop",
  "} def",
  "~23s",
  "/pdfImSep {",
  "  findcmykcustomcolor exch",
  "  dup /Width get /pdfImBuf1 exch string def",
  "  dup /Decode get aload pop 1 index sub /pdfImDecodeRange exch def",
  "  /pdfImDecodeLow exch def",
  "  begin Width Height BitsPerComponent ImageMatrix DataSource end",
  "  /pdfImData exch def",
  "  { pdfImData pdfImBuf1 readstring pop",
  "    0 1 2 index length 1 sub {",
  "      1 index exch 2 copy get",
  "      pdfImDecodeRange mul 255 div pdfImDecodeLow add round cvi",
  "      255 exch sub put",
  "    } for }",
  "  6 5 roll customcolorimage",
  "  { currentfile pdfImBuf readline",
  "    not { pop exit } if",
  "    (%-EOD-) eq { exit } if } loop",
  "} def",
  "~23sn",
  "/pdfImM {",
  "  fCol imagemask",
  "  { currentfile pdfImBuf readline",
  "    not { pop exit } if",
  "    (%-EOD-) eq { exit } if } loop",
  "} def",
  "/pr { 2 index 2 index 3 2 roll putinterval 4 add } def",
  "/pdfImClip {",
  "  gsave",
  "  0 2 4 index length 1 sub {",
  "    dup 4 index exch 2 copy",
  "    get 5 index div put",
  "    1 add 3 index exch 2 copy",
  "    get 3 index div put",
  "  } for",
  "  pop pop rectclip",
  "} def",
  "/pdfImClipEnd { grestore } def",
  "~23sn",
  "% shading operators",
  "/colordelta {",
  "  false 0 1 3 index length 1 sub {",
  "    dup 4 index exch get 3 index 3 2 roll get sub abs 0.004 gt {",
  "      pop true",
  "    } if",
  "  } for",
  "  exch pop exch pop",
  "} def",
  "/funcCol { func n array astore } def",
  "/funcSH {",
  "  dup 0 eq {",
  "    true",
  "  } {",
  "    dup 6 eq {",
  "      false",
  "    } {",
  "      4 index 4 index funcCol dup",
  "      6 index 4 index funcCol dup",
  "      3 1 roll colordelta 3 1 roll",
  "      5 index 5 index funcCol dup",
  "      3 1 roll colordelta 3 1 roll",
  "      6 index 8 index funcCol dup",
  "      3 1 roll colordelta 3 1 roll",
  "      colordelta or or or",
  "    } ifelse",
  "  } ifelse",
  "  {",
  "    1 add",
  "    4 index 3 index add 0.5 mul exch 4 index 3 index add 0.5 mul exch",
  "    6 index 6 index 4 index 4 index 4 index funcSH",
  "    2 index 6 index 6 index 4 index 4 index funcSH",
  "    6 index 2 index 4 index 6 index 4 index funcSH",
  "    5 3 roll 3 2 roll funcSH pop pop",
  "  } {",
  "    pop 3 index 2 index add 0.5 mul 3 index  2 index add 0.5 mul",
  "~23n",
  "    funcCol sc",
  "~23s",
  "    funcCol aload pop k",
  "~23sn",
  "    dup 4 index exch mat transform m",
  "    3 index 3 index mat transform l",
  "    1 index 3 index mat transform l",
  "    mat transform l pop pop h f*",
  "  } ifelse",
  "} def",
  "/axialCol {",
  "  dup 0 lt {",
  "    pop t0",
  "  } {",
  "    dup 1 gt {",
  "      pop t1",
  "    } {",
  "      dt mul t0 add",
  "    } ifelse",
  "  } ifelse",
  "  func n array astore",
  "} def",
  "/axialSH {",
  "  dup 0 eq {",
  "    true",
  "  } {",
  "    dup 8 eq {",
  "      false",
  "    } {",
  "      2 index axialCol 2 index axialCol colordelta",
  "    } ifelse",
  "  } ifelse",
  "  {",
  "    1 add 3 1 roll 2 copy add 0.5 mul",
  "    dup 4 3 roll exch 4 index axialSH",
  "    exch 3 2 roll axialSH",
  "  } {",
  "    pop 2 copy add 0.5 mul",
  "~23n",
  "    axialCol sc",
  "~23s",
  "    axialCol aload pop k",
  "~23sn",
  "    exch dup dx mul x0 add exch dy mul y0 add",
  "    3 2 roll dup dx mul x0 add exch dy mul y0 add",
  "    dx abs dy abs ge {",
  "      2 copy yMin sub dy mul dx div add yMin m",
  "      yMax sub dy mul dx div add yMax l",
  "      2 copy yMax sub dy mul dx div add yMax l",
  "      yMin sub dy mul dx div add yMin l",
  "      h f*",
  "    } {",
  "      exch 2 copy xMin sub dx mul dy div add xMin exch m",
  "      xMax sub dx mul dy div add xMax exch l",
  "      exch 2 copy xMax sub dx mul dy div add xMax exch l",
  "      xMin sub dx mul dy div add xMin exch l",
  "      h f*",
  "    } ifelse",
  "  } ifelse",
  "} def",
  "/radialCol {",
  "  dup t0 lt {",
  "    pop t0",
  "  } {",
  "    dup t1 gt {",
  "      pop t1",
  "    } if",
  "  } ifelse",
  "  func n array astore",
  "} def",
  "/radialSH {",
  "  dup 0 eq {",
  "    true",
  "  } {",
  "    dup 8 eq {",
  "      false",
  "    } {",
  "      2 index dt mul t0 add radialCol",
  "      2 index dt mul t0 add radialCol colordelta",
  "    } ifelse",
  "  } ifelse",
  "  {",
  "    1 add 3 1 roll 2 copy add 0.5 mul",
  "    dup 4 3 roll exch 4 index radialSH",
  "    exch 3 2 roll radialSH",
  "  } {",
  "    pop 2 copy add 0.5 mul dt mul t0 add",
  "~23n",
  "    radialCol sc",
  "~23s",
  "    radialCol aload pop k",
  "~23sn",
  "    encl {",
  "      exch dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
  "      0 360 arc h",
  "      dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
  "      360 0 arcn h f",
  "    } {",
  "      2 copy",
  "      dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
  "      a1 a2 arcn",
  "      dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
  "      a2 a1 arcn h",
  "      dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
  "      a1 a2 arc",
  "      dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
  "      a2 a1 arc h f",
  "    } ifelse",
  "  } ifelse",
  "} def",
  "~123sn",
  "end",
  NULL
};

static char *cmapProlog[] = {
  "/CIDInit /ProcSet findresource begin",
  "10 dict begin",
  "  begincmap",
  "  /CMapType 1 def",
  "  /CMapName /Identity-H def",
  "  /CIDSystemInfo 3 dict dup begin",
  "    /Registry (Adobe) def",
  "    /Ordering (Identity) def",
  "    /Supplement 0 def",
  "  end def",
  "  1 begincodespacerange",
  "    <0000> <ffff>",
  "  endcodespacerange",
  "  0 usefont",
  "  1 begincidrange",
  "    <0000> <ffff> 0",
  "  endcidrange",
  "  endcmap",
  "  currentdict CMapName exch /CMap defineresource pop",
  "end",
  "10 dict begin",
  "  begincmap",
  "  /CMapType 1 def",
  "  /CMapName /Identity-V def",
  "  /CIDSystemInfo 3 dict dup begin",
  "    /Registry (Adobe) def",
  "    /Ordering (Identity) def",
  "    /Supplement 0 def",
  "  end def",
  "  /WMode 1 def",
  "  1 begincodespacerange",
  "    <0000> <ffff>",
  "  endcodespacerange",
  "  0 usefont",
  "  1 begincidrange",
  "    <0000> <ffff> 0",
  "  endcidrange",
  "  endcmap",
  "  currentdict CMapName exch /CMap defineresource pop",
  "end",
  "end",
  NULL
};

//------------------------------------------------------------------------
// Fonts
//------------------------------------------------------------------------

struct PSSubstFont {
  char *psName;			// PostScript name
  double mWidth;		// width of 'm' character
};

static char *psFonts[] = {
  "Courier",
  "Courier-Bold",
  "Courier-Oblique",
  "Courier-BoldOblique",
  "Helvetica",
  "Helvetica-Bold",
  "Helvetica-Oblique",
  "Helvetica-BoldOblique",
  "Symbol",
  "Times-Roman",
  "Times-Bold",
  "Times-Italic",
  "Times-BoldItalic",
  "ZapfDingbats",
  NULL
};

static PSSubstFont psSubstFonts[] = {
  {"Helvetica",             0.833},
  {"Helvetica-Oblique",     0.833},
  {"Helvetica-Bold",        0.889},
  {"Helvetica-BoldOblique", 0.889},
  {"Times-Roman",           0.788},
  {"Times-Italic",          0.722},
  {"Times-Bold",            0.833},
  {"Times-BoldItalic",      0.778},
  {"Courier",               0.600},
  {"Courier-Oblique",       0.600},
  {"Courier-Bold",          0.600},
  {"Courier-BoldOblique",   0.600}
};

// Info for 8-bit fonts
struct PSFont8Info {
  Ref fontID;
  Gushort *codeToGID;		// code-to-GID mapping for TrueType fonts
};

// Encoding info for substitute 16-bit font
struct PSFont16Enc {
  Ref fontID;
  GString *enc;
};

//------------------------------------------------------------------------
// process colors
//------------------------------------------------------------------------

#define psProcessCyan     1
#define psProcessMagenta  2
#define psProcessYellow   4
#define psProcessBlack    8
#define psProcessCMYK    15

//------------------------------------------------------------------------
// PSOutCustomColor
//------------------------------------------------------------------------

class PSOutCustomColor {
public:

  PSOutCustomColor(double cA, double mA,
		   double yA, double kA, GString *nameA);
  ~PSOutCustomColor();

  double c, m, y, k;
  GString *name;
  PSOutCustomColor *next;
};

PSOutCustomColor::PSOutCustomColor(double cA, double mA,
				   double yA, double kA, GString *nameA) {
  c = cA;
  m = mA;
  y = yA;
  k = kA;
  name = nameA;
  next = NULL;
}

PSOutCustomColor::~PSOutCustomColor() {
  delete name;
}

//------------------------------------------------------------------------

struct PSOutImgClipRect {
  int x0, x1, y0, y1;
};

//------------------------------------------------------------------------
// DeviceNRecoder
//------------------------------------------------------------------------

class DeviceNRecoder: public FilterStream {
public:

  DeviceNRecoder(Stream *strA, int widthA, int heightA,
		 GfxImageColorMap *colorMapA);
  virtual ~DeviceNRecoder();
  virtual StreamKind getKind() { return strWeird; }
  virtual void reset();
  virtual int getChar()
    { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx++]; }
  virtual int lookChar()
    { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx]; }
  virtual GString *getPSFilter(int psLevel, char *indent) { return NULL; }
  virtual GBool isBinary(GBool last = gTrue) { return gTrue; }
  virtual GBool isEncoder() { return gTrue; }

private:

  GBool fillBuf();

  int width, height;
  GfxImageColorMap *colorMap;
  Function *func;
  ImageStream *imgStr;
  int buf[gfxColorMaxComps];
  int pixelIdx;
  int bufIdx;
  int bufSize;
};

DeviceNRecoder::DeviceNRecoder(Stream *strA, int widthA, int heightA,
			       GfxImageColorMap *colorMapA):
    FilterStream(strA) {
  width = widthA;
  height = heightA;
  colorMap = colorMapA;
  imgStr = NULL;
  pixelIdx = 0;
  bufIdx = gfxColorMaxComps;
  bufSize = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())->
              getAlt()->getNComps();
  func = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())->
           getTintTransformFunc();
}

DeviceNRecoder::~DeviceNRecoder() {
  if (imgStr) {
    delete imgStr;
  }
}

void DeviceNRecoder::reset() {
  imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
			   colorMap->getBits());
  imgStr->reset();
}

GBool DeviceNRecoder::fillBuf() {
  Guchar pixBuf[gfxColorMaxComps];
  GfxColor color;
  double x[gfxColorMaxComps], y[gfxColorMaxComps];
  int i;

  if (pixelIdx >= width * height) {
    return gFalse;
  }
  imgStr->getPixel(pixBuf);
  colorMap->getColor(pixBuf, &color);
  for (i = 0;
       i < ((GfxDeviceNColorSpace *)colorMap->getColorSpace())->getNComps();
       ++i) {
    x[i] = colToDbl(color.c[i]);
  }
  func->transform(x, y);
  for (i = 0; i < bufSize; ++i) {
    buf[i] = (int)(y[i] * 255 + 0.5);
  }
  bufIdx = 0;
  ++pixelIdx;
  return gTrue;
}

//------------------------------------------------------------------------
// PSOutputDev
//------------------------------------------------------------------------

extern "C" {
typedef void (*SignalFunc)(int);
}

static void outputToFile(void *stream, char *data, int len) {
  fwrite(data, 1, len, (FILE *)stream);
}

PSOutputDev::PSOutputDev(char *fileName, XRef *xrefA, Catalog *catalog,
			 int firstPage, int lastPage, PSOutMode modeA,
			 int imgLLXA, int imgLLYA, int imgURXA, int imgURYA,
			 GBool manualCtrlA) {
  FILE *f;
  PSFileType fileTypeA;

  underlayCbk = NULL;
  underlayCbkData = NULL;
  overlayCbk = NULL;
  overlayCbkData = NULL;

  fontIDs = NULL;
  fontFileIDs = NULL;
  fontFileNames = NULL;
  font8Info = NULL;
  font16Enc = NULL;
  imgIDs = NULL;
  formIDs = NULL;
  xobjStack = NULL;
  embFontList = NULL;
  customColors = NULL;
  haveTextClip = gFalse;
  t3String = NULL;

  // open file or pipe
  if (!strcmp(fileName, "-")) {
    fileTypeA = psStdout;
    f = stdout;
  } else if (fileName[0] == '|') {
    fileTypeA = psPipe;
#ifdef HAVE_POPEN
#ifndef WIN32
    signal(SIGPIPE, (SignalFunc)SIG_IGN);
#endif
    if (!(f = popen(fileName + 1, "w"))) {
      error(-1, "Couldn't run print command '%s'", fileName);
      ok = gFalse;
      return;
    }
#else
    error(-1, "Print commands are not supported ('%s')", fileName);
    ok = gFalse;
    return;
#endif
  } else {
    fileTypeA = psFile;
    if (!(f = fopen(fileName, "w"))) {
      error(-1, "Couldn't open PostScript file '%s'", fileName);
      ok = gFalse;
      return;
    }
  }

  init(outputToFile, f, fileTypeA,
       xrefA, catalog, firstPage, lastPage, modeA,
       imgLLXA, imgLLYA, imgURXA, imgURYA, manualCtrlA);
}

PSOutputDev::PSOutputDev(PSOutputFunc outputFuncA, void *outputStreamA,
			 XRef *xrefA, Catalog *catalog,
			 int firstPage, int lastPage, PSOutMode modeA,
			 int imgLLXA, int imgLLYA, int imgURXA, int imgURYA,
			 GBool manualCtrlA) {
  underlayCbk = NULL;
  underlayCbkData = NULL;
  overlayCbk = NULL;
  overlayCbkData = NULL;

  fontIDs = NULL;
  fontFileIDs = NULL;
  fontFileNames = NULL;
  font8Info = NULL;
  font16Enc = NULL;
  imgIDs = NULL;
  formIDs = NULL;
  xobjStack = NULL;
  embFontList = NULL;
  customColors = NULL;
  haveTextClip = gFalse;
  t3String = NULL;

  init(outputFuncA, outputStreamA, psGeneric,
       xrefA, catalog, firstPage, lastPage, modeA,
       imgLLXA, imgLLYA, imgURXA, imgURYA, manualCtrlA);
}

void PSOutputDev::init(PSOutputFunc outputFuncA, void *outputStreamA,
		       PSFileType fileTypeA, XRef *xrefA, Catalog *catalog,
		       int firstPage, int lastPage, PSOutMode modeA,
		       int imgLLXA, int imgLLYA, int imgURXA, int imgURYA,
		       GBool manualCtrlA) {
  Page *page;
  PDFRectangle *box;

  // initialize
  ok = gTrue;
  outputFunc = outputFuncA;
  outputStream = outputStreamA;
  fileType = fileTypeA;
  xref = xrefA;
  level = globalParams->getPSLevel();
  mode = modeA;
  paperWidth = globalParams->getPSPaperWidth();
  paperHeight = globalParams->getPSPaperHeight();
  imgLLX = imgLLXA;
  imgLLY = imgLLYA;
  imgURX = imgURXA;
  imgURY = imgURYA;
  if (imgLLX == 0 && imgURX == 0 && imgLLY == 0 && imgURY == 0) {
    globalParams->getPSImageableArea(&imgLLX, &imgLLY, &imgURX, &imgURY);
  }
  if (paperWidth < 0 || paperHeight < 0) {
    // this check is needed in case the document has zero pages
    if (firstPage > 0 && firstPage <= catalog->getNumPages()) {
      page = catalog->getPage(firstPage);
      paperWidth = (int)ceil(page->getMediaWidth());
      paperHeight = (int)ceil(page->getMediaHeight());
    } else {
      paperWidth = 1;
      paperHeight = 1;
    }
    imgLLX = imgLLY = 0;
    imgURX = paperWidth;
    imgURY = paperHeight;
  }
  preload = globalParams->getPSPreload();
  manualCtrl = manualCtrlA;
  if (mode == psModeForm) {
    lastPage = firstPage;
  }
  processColors = 0;
  inType3Char = gFalse;

#if OPI_SUPPORT
  // initialize OPI nesting levels
  opi13Nest = 0;
  opi20Nest = 0;
#endif

  tx0 = ty0 = -1;
  xScale0 = yScale0 = 0;
  rotate0 = -1;
  clipLLX0 = clipLLY0 = 0;
  clipURX0 = clipURY0 = -1;

  // initialize fontIDs, fontFileIDs, and fontFileNames lists
  fontIDSize = 64;
  fontIDLen = 0;
  fontIDs = (Ref *)gmallocn(fontIDSize, sizeof(Ref));
  fontFileIDSize = 64;
  fontFileIDLen = 0;
  fontFileIDs = (Ref *)gmallocn(fontFileIDSize, sizeof(Ref));
  fontFileNameSize = 64;
  fontFileNameLen = 0;
  fontFileNames = (GString **)gmallocn(fontFileNameSize, sizeof(GString *));
  nextTrueTypeNum = 0;
  font8InfoLen = 0;
  font8InfoSize = 0;
  font16EncLen = 0;
  font16EncSize = 0;
  imgIDLen = 0;
  imgIDSize = 0;
  formIDLen = 0;
  formIDSize = 0;

  xobjStack = new GList();
  numSaves = 0;
  numTilingPatterns = 0;
  nextFunc = 0;

  // initialize embedded font resource comment list
  embFontList = new GString();

  if (!manualCtrl) {
    // this check is needed in case the document has zero pages
    if (firstPage > 0 && firstPage <= catalog->getNumPages()) {
      writeHeader(firstPage, lastPage,
		  catalog->getPage(firstPage)->getMediaBox(),
		  catalog->getPage(firstPage)->getCropBox(),
		  catalog->getPage(firstPage)->getRotate());
    } else {
      box = new PDFRectangle(0, 0, 1, 1);
      writeHeader(firstPage, lastPage, box, box, 0);
      delete box;
    }
    if (mode != psModeForm) {
      writePS("%%BeginProlog\n");
    }
    writeXpdfProcset();
    if (mode != psModeForm) {
      writePS("%%EndProlog\n");
      writePS("%%BeginSetup\n");
    }
    writeDocSetup(catalog, firstPage, lastPage);
    if (mode != psModeForm) {
      writePS("%%EndSetup\n");
    }
  }

  // initialize sequential page number
  seqPage = 1;
}

PSOutputDev::~PSOutputDev() {
  PSOutCustomColor *cc;
  int i;

  if (ok) {
    if (!manualCtrl) {
      writePS("%%Trailer\n");
      writeTrailer();
      if (mode != psModeForm) {
	writePS("%%EOF\n");
      }
    }
    if (fileType == psFile) {
#ifdef MACOS
      ICS_MapRefNumAndAssign((short)((FILE *)outputStream)->handle);
#endif
      fclose((FILE *)outputStream);
    }
#ifdef HAVE_POPEN
    else if (fileType == psPipe) {
      pclose((FILE *)outputStream);
#ifndef WIN32
      signal(SIGPIPE, (SignalFunc)SIG_DFL);
#endif
    }
#endif
  }
  if (embFontList) {
    delete embFontList;
  }
  if (fontIDs) {
    gfree(fontIDs);
  }
  if (fontFileIDs) {
    gfree(fontFileIDs);
  }
  if (fontFileNames) {
    for (i = 0; i < fontFileNameLen; ++i) {
      delete fontFileNames[i];
    }
    gfree(fontFileNames);
  }
  if (font8Info) {
    for (i = 0; i < font8InfoLen; ++i) {
      gfree(font8Info[i].codeToGID);
    }
    gfree(font8Info);
  }
  if (font16Enc) {
    for (i = 0; i < font16EncLen; ++i) {
      delete font16Enc[i].enc;
    }
    gfree(font16Enc);
  }
  gfree(imgIDs);
  gfree(formIDs);
  if (xobjStack) {
    delete xobjStack;
  }
  while (customColors) {
    cc = customColors;
    customColors = cc->next;
    delete cc;
  }
}

void PSOutputDev::writeHeader(int firstPage, int lastPage,
			      PDFRectangle *mediaBox, PDFRectangle *cropBox,
			      int pageRotate) {
  Object info, obj1;
  double x1, y1, x2, y2;

  switch (mode) {
  case psModePS:
    writePS("%!PS-Adobe-3.0\n");
    break;
  case psModeEPS:
    writePS("%!PS-Adobe-3.0 EPSF-3.0\n");
    break;
  case psModeForm:
    writePS("%!PS-Adobe-3.0 Resource-Form\n");
    break;
  }

  writePSFmt("% Produced by xpdf/pdftops {0:s}\n", xpdfVersion);
  xref->getDocInfo(&info);
  if (info.isDict() && info.dictLookup("Creator", &obj1)->isString()) {
    writePS("%%Creator: ");
    writePSTextLine(obj1.getString());
  }
  obj1.free();
  if (info.isDict() && info.dictLookup("Title", &obj1)->isString()) {
    writePS("%%Title: ");
    writePSTextLine(obj1.getString());
  }
  obj1.free();
  info.free();
  writePSFmt("%%LanguageLevel: {0:d}\n",
	     (level == psLevel1 || level == psLevel1Sep) ? 1 :
	     (level == psLevel2 || level == psLevel2Sep) ? 2 : 3);
  if (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) {
    writePS("%%DocumentProcessColors: (atend)\n");
    writePS("%%DocumentCustomColors: (atend)\n");
  }
  writePS("%%DocumentSuppliedResources: (atend)\n");

  switch (mode) {
  case psModePS:
    writePSFmt("%%DocumentMedia: plain {0:d} {1:d} 0 () ()\n",
	       paperWidth, paperHeight);
    writePSFmt("%%BoundingBox: 0 0 {0:d} {1:d}\n", paperWidth, paperHeight);
    writePSFmt("%%Pages: {0:d}\n", lastPage - firstPage + 1);
    writePS("%%EndComments\n");
    writePS("%%BeginDefaults\n");
    writePS("%%PageMedia: plain\n");
    writePS("%%EndDefaults\n");
    break;
  case psModeEPS:
    epsX1 = cropBox->x1;
    epsY1 = cropBox->y1;
    epsX2 = cropBox->x2;
    epsY2 = cropBox->y2;
    if (pageRotate == 0 || pageRotate == 180) {
      x1 = epsX1;
      y1 = epsY1;
      x2 = epsX2;
      y2 = epsY2;
    } else { // pageRotate == 90 || pageRotate == 270
      x1 = 0;
      y1 = 0;
      x2 = epsY2 - epsY1;
      y2 = epsX2 - epsX1;
    }
    writePSFmt("%%BoundingBox: {0:d} {1:d} {2:d} {3:d}\n",
	       (int)floor(x1), (int)floor(y1), (int)ceil(x2), (int)ceil(y2));
    if (floor(x1) != ceil(x1) || floor(y1) != ceil(y1) ||
	floor(x2) != ceil(x2) || floor(y2) != ceil(y2)) {
      writePSFmt("%%HiResBoundingBox: {0:.4g} {1:.4g} {2:.4g} {3:.4g}\n",
		 x1, y1, x2, y2);
    }
    writePS("%%EndComments\n");
    break;
  case psModeForm:
    writePS("%%EndComments\n");
    writePS("32 dict dup begin\n");
    writePSFmt("/BBox [{0:d} {1:d} {2:d} {3:d}] def\n",
	       (int)floor(mediaBox->x1), (int)floor(mediaBox->y1),
	       (int)ceil(mediaBox->x2), (int)ceil(mediaBox->y2));
    writePS("/FormType 1 def\n");
    writePS("/Matrix [1 0 0 1 0 0] def\n");
    break;
  }
}

void PSOutputDev::writeXpdfProcset() {
  GBool lev1, lev2, lev3, sep, nonSep;
  char **p;
  char *q;

  writePSFmt("%%BeginResource: procset xpdf {0:s} 0\n", xpdfVersion);
  writePSFmt("%%Copyright: {0:s}\n", xpdfCopyright);
  lev1 = lev2 = lev3 = sep = nonSep = gTrue;
  for (p = prolog; *p; ++p) {
    if ((*p)[0] == '~') {
      lev1 = lev2 = lev3 = sep = nonSep = gFalse;
      for (q = *p + 1; *q; ++q) {
	switch (*q) {
	case '1': lev1 = gTrue; break;
	case '2': lev2 = gTrue; break;
	case '3': lev3 = gTrue; break;
	case 's': sep = gTrue; break;
	case 'n': nonSep = gTrue; break;
	}
      }
    } else if ((level == psLevel1 && lev1 && nonSep) ||
	       (level == psLevel1Sep && lev1 && sep) ||
	       (level == psLevel2 && lev2 && nonSep) ||
	       (level == psLevel2Sep && lev2 && sep) ||
	       (level == psLevel3 && lev3 && nonSep) ||
	       (level == psLevel3Sep && lev3 && sep)) {
      writePSFmt("{0:s}\n", *p);
    }
  }
  writePS("%%EndResource\n");

  if (level >= psLevel3) {
    for (p = cmapProlog; *p; ++p) {
      writePSFmt("{0:s}\n", *p);
    }
  }
}

void PSOutputDev::writeDocSetup(Catalog *catalog,
				int firstPage, int lastPage) {
  Page *page;
  Dict *resDict;
  Annots *annots;
  Object obj1, obj2;
  int pg, i;

  if (mode == psModeForm) {
    // swap the form and xpdf dicts
    writePS("xpdf end begin dup begin\n");
  } else {
    writePS("xpdf begin\n");
  }
  for (pg = firstPage; pg <= lastPage; ++pg) {
    page = catalog->getPage(pg);
    if ((resDict = page->getResourceDict())) {
      setupResources(resDict);
    }
    annots = new Annots(xref, catalog, page->getAnnots(&obj1));
    obj1.free();
    for (i = 0; i < annots->getNumAnnots(); ++i) {
      if (annots->getAnnot(i)->getAppearance(&obj1)->isStream()) {
	obj1.streamGetDict()->lookup("Resources", &obj2);
	if (obj2.isDict()) {
	  setupResources(obj2.getDict());
	}
	obj2.free();
      }
      obj1.free();
    }
    delete annots;
  }
  if (mode != psModeForm) {
    if (mode != psModeEPS && !manualCtrl) {
      writePSFmt("{0:d} {1:d} {2:s} pdfSetup\n",
		 paperWidth, paperHeight,
		 globalParams->getPSDuplex() ? "true" : "false");
    }
#if OPI_SUPPORT
    if (globalParams->getPSOPI()) {
      writePS("/opiMatrix matrix currentmatrix def\n");
    }
#endif
  }
}

void PSOutputDev::writePageTrailer() {
  if (mode != psModeForm) {
    writePS("pdfEndPage\n");
  }
}

void PSOutputDev::writeTrailer() {
  PSOutCustomColor *cc;

  if (mode == psModeForm) {
    writePS("/Foo exch /Form defineresource pop\n");
  } else {
    writePS("end\n");
    writePS("%%DocumentSuppliedResources:\n");
    writePS(embFontList->getCString());
    if (level == psLevel1Sep || level == psLevel2Sep ||
	level == psLevel3Sep) {
      writePS("%%DocumentProcessColors:");
      if (processColors & psProcessCyan) {
	writePS(" Cyan");
	 }
      if (processColors & psProcessMagenta) {
	writePS(" Magenta");
      }
      if (processColors & psProcessYellow) {
	writePS(" Yellow");
      }
      if (processColors & psProcessBlack) {
	writePS(" Black");
      }
      writePS("\n");
      writePS("%%DocumentCustomColors:");
      for (cc = customColors; cc; cc = cc->next) {
	writePSFmt(" ({0:s})", cc->name->getCString());
      }
      writePS("\n");
      writePS("%%CMYKCustomColor:\n");
      for (cc = customColors; cc; cc = cc->next) {
	writePSFmt("%%+ {0:.4g} {1:.4g} {2:.4g} {3:.4g} ({4:t})\n",
		   cc->c, cc->m, cc->y, cc->k, cc->name);
      }
    }
  }
}

void PSOutputDev::setupResources(Dict *resDict) {
  Object xObjDict, xObjRef, xObj, patDict, patRef, pat, resObj;
  Ref ref0, ref1;
  GBool skip;
  int i, j;

  setupFonts(resDict);
  setupImages(resDict);
  setupForms(resDict);

  //----- recursively scan XObjects
  resDict->lookup("XObject", &xObjDict);
  if (xObjDict.isDict()) {
    for (i = 0; i < xObjDict.dictGetLength(); ++i) {

      // avoid infinite recursion on XObjects
      skip = gFalse;
      if ((xObjDict.dictGetValNF(i, &xObjRef)->isRef())) {
	ref0 = xObjRef.getRef();
	for (j = 0; j < xobjStack->getLength(); ++j) {
	  ref1 = *(Ref *)xobjStack->get(j);
	  if (ref1.num == ref0.num && ref1.gen == ref0.gen) {
	    skip = gTrue;
	    break;
	  }
	}
	if (!skip) {
	  xobjStack->append(&ref0);
	}
      }
      if (!skip) {

	// process the XObject's resource dictionary
	xObjDict.dictGetVal(i, &xObj);
	if (xObj.isStream()) {
	  xObj.streamGetDict()->lookup("Resources", &resObj);
	  if (resObj.isDict()) {
	    setupResources(resObj.getDict());
	  }
	  resObj.free();
	}
	xObj.free();
      }

      if (xObjRef.isRef() && !skip) {
	xobjStack->del(xobjStack->getLength() - 1);
      }
      xObjRef.free();
    }
  }
  xObjDict.free();

  //----- recursively scan Patterns
  resDict->lookup("Pattern", &patDict);
  if (patDict.isDict()) {
    inType3Char = gTrue;
    for (i = 0; i < patDict.dictGetLength(); ++i) {

      // avoid infinite recursion on Patterns
      skip = gFalse;
      if ((patDict.dictGetValNF(i, &patRef)->isRef())) {
	ref0 = patRef.getRef();
	for (j = 0; j < xobjStack->getLength(); ++j) {
	  ref1 = *(Ref *)xobjStack->get(j);
	  if (ref1.num == ref0.num && ref1.gen == ref0.gen) {
	    skip = gTrue;
	    break;
	  }
	}
	if (!skip) {
	  xobjStack->append(&ref0);
	}
      }
      if (!skip) {

	// process the Pattern's resource dictionary
	patDict.dictGetVal(i, &pat);
	if (pat.isStream()) {
	  pat.streamGetDict()->lookup("Resources", &resObj);
	  if (resObj.isDict()) {
	    setupResources(resObj.getDict());
	  }
	  resObj.free();
	}
	pat.free();
      }

      if (patRef.isRef() && !skip) {
	xobjStack->del(xobjStack->getLength() - 1);
      }
      patRef.free();
    }
    inType3Char = gFalse;
  }
  patDict.free();
}

void PSOutputDev::setupFonts(Dict *resDict) {
  Object obj1, obj2;
  Ref r;
  GfxFontDict *gfxFontDict;
  GfxFont *font;
  int i;

  gfxFontDict = NULL;
  resDict->lookupNF("Font", &obj1);
  if (obj1.isRef()) {
    obj1.fetch(xref, &obj2);
    if (obj2.isDict()) {
      r = obj1.getRef();
      gfxFontDict = new GfxFontDict(xref, &r, obj2.getDict());
    }
    obj2.free();
  } else if (obj1.isDict()) {
    gfxFontDict = new GfxFontDict(xref, NULL, obj1.getDict());
  }
  if (gfxFontDict) {
    for (i = 0; i < gfxFontDict->getNumFonts(); ++i) {
      if ((font = gfxFontDict->getFont(i))) {
	setupFont(font, resDict);
      }
    }
    delete gfxFontDict;
  }
  obj1.free();
}

void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) {
  Ref fontFileID;
  GString *name;
  PSFontParam *fontParam;
  GString *psName;
  char buf[16];
  GBool subst;
  UnicodeMap *uMap;
  char *charName;
  double xs, ys;
  int code;
  double w1, w2;
  double *fm;
  int i, j;

  // check if font is already set up
  for (i = 0; i < fontIDLen; ++i) {
    if (fontIDs[i].num == font->getID()->num &&
	fontIDs[i].gen == font->getID()->gen) {
      return;
    }
  }

  // add entry to fontIDs list
  if (fontIDLen >= fontIDSize) {
    fontIDSize += 64;
    fontIDs = (Ref *)greallocn(fontIDs, fontIDSize, sizeof(Ref));
  }
  fontIDs[fontIDLen++] = *font->getID();

  xs = ys = 1;
  subst = gFalse;

  // check for resident 8-bit font
  if (font->getName() &&
      (fontParam = globalParams->getPSFont(font->getName()))) {
    psName = new GString(fontParam->psFontName->getCString());

  // check for embedded Type 1 font
  } else if (globalParams->getPSEmbedType1() &&
	     font->getType() == fontType1 &&
	     font->getEmbeddedFontID(&fontFileID)) {
    psName = filterPSName(font->getEmbeddedFontName());
    setupEmbeddedType1Font(&fontFileID, psName);

  // check for embedded Type 1C font
  } else if (globalParams->getPSEmbedType1() &&
	     font->getType() == fontType1C &&
	     font->getEmbeddedFontID(&fontFileID)) {
    // use the PDF font name because the embedded font name might
    // not include the subset prefix
    psName = filterPSName(font->getOrigName());
    setupEmbeddedType1CFont(font, &fontFileID, psName);

  // check for embedded OpenType - Type 1C font
  } else if (globalParams->getPSEmbedType1() &&
	     font->getType() == fontType1COT &&
	     font->getEmbeddedFontID(&fontFileID)) {
    // use the PDF font name because the embedded font name might
    // not include the subset prefix
    psName = filterPSName(font->getOrigName());
    setupEmbeddedOpenTypeT1CFont(font, &fontFileID, psName);

  // check for external Type 1 font file
  } else if (globalParams->getPSEmbedType1() &&
	     font->getType() == fontType1 &&
	     font->getExtFontFile()) {
    // this assumes that the PS font name matches the PDF font name
    psName = font->getName()->copy();
    setupExternalType1Font(font->getExtFontFile(), psName);

  // check for embedded TrueType font
  } else if (globalParams->getPSEmbedTrueType() &&
	     (font->getType() == fontTrueType ||
	      font->getType() == fontTrueTypeOT) &&
	     font->getEmbeddedFontID(&fontFileID)) {
    psName = filterPSName(font->getEmbeddedFontName());
    setupEmbeddedTrueTypeFont(font, &fontFileID, psName);

  // check for external TrueType font file
  } else if (globalParams->getPSEmbedTrueType() &&
	     font->getType() == fontTrueType &&
	     font->getExtFontFile()) {
    psName = filterPSName(font->getName());
    setupExternalTrueTypeFont(font, psName);

  // check for embedded CID PostScript font
  } else if (globalParams->getPSEmbedCIDPostScript() &&
	     font->getType() == fontCIDType0C &&
	     font->getEmbeddedFontID(&fontFileID)) {
    psName = filterPSName(font->getEmbeddedFontName());
    setupEmbeddedCIDType0Font(font, &fontFileID, psName);

  // check for embedded CID TrueType font
  } else if (globalParams->getPSEmbedCIDTrueType() &&
	     (font->getType() == fontCIDType2 ||
	      font->getType() == fontCIDType2OT) &&
	     font->getEmbeddedFontID(&fontFileID)) {
    psName = filterPSName(font->getEmbeddedFontName());
    //~ should check to see if font actually uses vertical mode
    setupEmbeddedCIDTrueTypeFont(font, &fontFileID, psName, gTrue);

  // check for embedded OpenType - CID CFF font
  } else if (globalParams->getPSEmbedCIDPostScript() &&
	     font->getType() == fontCIDType0COT &&
	     font->getEmbeddedFontID(&fontFileID)) {
    psName = filterPSName(font->getEmbeddedFontName());
    setupEmbeddedOpenTypeCFFFont(font, &fontFileID, psName);

  // check for Type 3 font
  } else if (font->getType() == fontType3) {
    psName = GString::format("T3_{0:d}_{1:d}",
			     font->getID()->num, font->getID()->gen);
    setupType3Font(font, psName, parentResDict);

  // do 8-bit font substitution
  } else if (!font->isCIDFont()) {
    subst = gTrue;
    name = font->getName();
    psName = NULL;
    if (name) {
      for (i = 0; psFonts[i]; ++i) {
	if (name->cmp(psFonts[i]) == 0) {
	  psName = new GString(psFonts[i]);
	  break;
	}
      }
    }
    if (!psName) {
      if (font->isFixedWidth()) {
	i = 8;
      } else if (font->isSerif()) {
	i = 4;
      } else {
	i = 0;
      }
      if (font->isBold()) {
	i += 2;
      }
      if (font->isItalic()) {
	i += 1;
      }
      psName = new GString(psSubstFonts[i].psName);
      for (code = 0; code < 256; ++code) {
	if ((charName = ((Gfx8BitFont *)font)->getCharName(code)) &&
	    charName[0] == 'm' && charName[1] == '\0') {
	  break;
	}
      }
      if (code < 256) {
	w1 = ((Gfx8BitFont *)font)->getWidth(code);
      } else {
	w1 = 0;
      }
      w2 = psSubstFonts[i].mWidth;
      xs = w1 / w2;
      if (xs < 0.1) {
	xs = 1;
      }
      if (font->getType() == fontType3) {
	// This is a hack which makes it possible to substitute for some
	// Type 3 fonts.  The problem is that it's impossible to know what
	// the base coordinate system used in the font is without actually
	// rendering the font.
	ys = xs;
	fm = font->getFontMatrix();
	if (fm[0] != 0) {
	  ys *= fm[3] / fm[0];
	}
      } else {
	ys = 1;
      }
    }

  // do 16-bit font substitution
  } else if ((fontParam = globalParams->
	        getPSFont16(font->getName(),
			    ((GfxCIDFont *)font)->getCollection(),
			    font->getWMode()))) {
    subst = gTrue;
    psName = fontParam->psFontName->copy();
    if (font16EncLen >= font16EncSize) {
      font16EncSize += 16;
      font16Enc = (PSFont16Enc *)greallocn(font16Enc,
					   font16EncSize, sizeof(PSFont16Enc));
    }
    font16Enc[font16EncLen].fontID = *font->getID();
    font16Enc[font16EncLen].enc = fontParam->encoding->copy();
    if ((uMap = globalParams->getUnicodeMap(font16Enc[font16EncLen].enc))) {
      uMap->decRefCnt();
      ++font16EncLen;
    } else {
      error(-1, "Couldn't find Unicode map for 16-bit font encoding '%s'",
	    font16Enc[font16EncLen].enc->getCString());
    }

  // give up - can't do anything with this font
  } else {
    error(-1, "Couldn't find a font to substitute for '%s' ('%s' character collection)",
	  font->getName() ? font->getName()->getCString() : "(unnamed)",
	  ((GfxCIDFont *)font)->getCollection()
	    ? ((GfxCIDFont *)font)->getCollection()->getCString()
	    : "(unknown)");
    return;
  }

  // generate PostScript code to set up the font
  if (font->isCIDFont()) {
    if (level == psLevel3 || level == psLevel3Sep) {
      writePSFmt("/F{0:d}_{1:d} /{2:t} {3:d} pdfMakeFont16L3\n",
		 font->getID()->num, font->getID()->gen, psName,
		 font->getWMode());
    } else {
      writePSFmt("/F{0:d}_{1:d} /{2:t} {3:d} pdfMakeFont16\n",
		 font->getID()->num, font->getID()->gen, psName,
		 font->getWMode());
    }
  } else {
    writePSFmt("/F{0:d}_{1:d} /{2:t} {3:.4g} {4:.4g}\n",
	       font->getID()->num, font->getID()->gen, psName, xs, ys);
    for (i = 0; i < 256; i += 8) {
      writePS((char *)((i == 0) ? "[ " : "  "));
      for (j = 0; j < 8; ++j) {
	if (font->getType() == fontTrueType &&
	    !subst &&
	    !((Gfx8BitFont *)font)->getHasEncoding()) {
	  sprintf(buf, "c%02x", i+j);
	  charName = buf;
	} else {
	  charName = ((Gfx8BitFont *)font)->getCharName(i+j);
	  // this is a kludge for broken PDF files that encode char 32
	  // as .notdef
	  if (i+j == 32 && charName && !strcmp(charName, ".notdef")) {
	    charName = "space";
	  }
	}
	writePS("/");
	writePSName(charName ? charName : (char *)".notdef");
	// the empty name is legal in PDF and PostScript, but PostScript
	// uses a double-slash (//...) for "immediately evaluated names",
	// so we need to add a space character here
	if (charName && !charName[0]) {
	  writePS(" ");
	}
      }
      writePS((i == 256-8) ? (char *)"]\n" : (char *)"\n");
    }
    writePS("pdfMakeFont\n");
  }

  delete psName;
}

void PSOutputDev::setupEmbeddedType1Font(Ref *id, GString *psName) {
  static char hexChar[17] = "0123456789abcdef";
  Object refObj, strObj, obj1, obj2, obj3;
  Dict *dict;
  int length1, length2, length3;
  int c;
  int start[4];
  GBool binMode;
  int i;

  // check if font is already embedded
  for (i = 0; i < fontFileIDLen; ++i) {
    if (fontFileIDs[i].num == id->num &&
	fontFileIDs[i].gen == id->gen)
      return;
  }

  // add entry to fontFileIDs list
  if (fontFileIDLen >= fontFileIDSize) {
    fontFileIDSize += 64;
    fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref));
  }
  fontFileIDs[fontFileIDLen++] = *id;

  // get the font stream and info
  refObj.initRef(id->num, id->gen);
  refObj.fetch(xref, &strObj);
  refObj.free();
  if (!strObj.isStream()) {
    error(-1, "Embedded font file object is not a stream");
    goto err1;
  }
  if (!(dict = strObj.streamGetDict())) {
    error(-1, "Embedded font stream is missing its dictionary");
    goto err1;
  }
  dict->lookup("Length1", &obj1);
  dict->lookup("Length2", &obj2);
  dict->lookup("Length3", &obj3);
  if (!obj1.isInt() || !obj2.isInt() || !obj3.isInt()) {
    error(-1, "Missing length fields in embedded font stream dictionary");
    obj1.free();
    obj2.free();
    obj3.free();
    goto err1;
  }
  length1 = obj1.getInt();
  length2 = obj2.getInt();
  length3 = obj3.getInt();
  obj1.free();
  obj2.free();
  obj3.free();

  // beginning comment
  writePSFmt("%%BeginResource: font {0:t}\n", psName);
  embFontList->append("%%+ font ");
  embFontList->append(psName->getCString());
  embFontList->append("\n");

  // copy ASCII portion of font
  strObj.streamReset();
  for (i = 0; i < length1 && (c = strObj.streamGetChar()) != EOF; ++i) {
    writePSChar(c);
  }

  // figure out if encrypted portion is binary or ASCII
  binMode = gFalse;
  for (i = 0; i < 4; ++i) {
    start[i] = strObj.streamGetChar();
    if (start[i] == EOF) {
      error(-1, "Unexpected end of file in embedded font stream");
      goto err1;
    }
    if (!((start[i] >= '0' && start[i] <= '9') ||
	  (start[i] >= 'A' && start[i] <= 'F') ||
	  (start[i] >= 'a' && start[i] <= 'f')))
      binMode = gTrue;
  }

  // convert binary data to ASCII
  if (binMode) {
    for (i = 0; i < 4; ++i) {
      writePSChar(hexChar[(start[i] >> 4) & 0x0f]);
      writePSChar(hexChar[start[i] & 0x0f]);
    }
#if 0 // this causes trouble for various PostScript printers
    // if Length2 is incorrect (too small), font data gets chopped, so
    // we take a few extra characters from the trailer just in case
    length2 += length3 >= 8 ? 8 : length3;
#endif
    while (i < length2) {
      if ((c = strObj.streamGetChar()) == EOF) {
	break;
      }
      writePSChar(hexChar[(c >> 4) & 0x0f]);
      writePSChar(hexChar[c & 0x0f]);
      if (++i % 32 == 0) {
	writePSChar('\n');
      }
    }
    if (i % 32 > 0) {
      writePSChar('\n');
    }

  // already in ASCII format -- just copy it
  } else {
    for (i = 0; i < 4; ++i) {
      writePSChar(start[i]);
    }
    for (i = 4; i < length2; ++i) {
      if ((c = strObj.streamGetChar()) == EOF) {
	break;
      }
      writePSChar(c);
    }
  }

  // write padding and "cleartomark"
  for (i = 0; i < 8; ++i) {
    writePS("00000000000000000000000000000000"
	    "00000000000000000000000000000000\n");
  }
  writePS("cleartomark\n");

  // ending comment
  writePS("%%EndResource\n");

 err1:
  strObj.streamClose();
  strObj.free();
}

//~ This doesn't handle .pfb files or binary eexec data (which only
//~ happens in pfb files?).
void PSOutputDev::setupExternalType1Font(GString *fileName, GString *psName) {
  FILE *fontFile;
  int c;
  int i;

  // check if font is already embedded
  for (i = 0; i < fontFileNameLen; ++i) {
    if (!fontFileNames[i]->cmp(fileName)) {
      return;
    }
  }

  // add entry to fontFileNames list
  if (fontFileNameLen >= fontFileNameSize) {
    fontFileNameSize += 64;
    fontFileNames = (GString **)greallocn(fontFileNames,
					  fontFileNameSize, sizeof(GString *));
  }
  fontFileNames[fontFileNameLen++] = fileName->copy();

  // beginning comment
  writePSFmt("%%BeginResource: font {0:t}\n", psName);
  embFontList->append("%%+ font ");
  embFontList->append(psName->getCString());
  embFontList->append("\n");

  // copy the font file
  if (!(fontFile = fopen(fileName->getCString(), "rb"))) {
    error(-1, "Couldn't open external font file");
    return;
  }
  while ((c = fgetc(fontFile)) != EOF) {
    writePSChar(c);
  }
  fclose(fontFile);

  // ending comment
  writePS("%%EndResource\n");
}

void PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id,
					  GString *psName) {
  char *fontBuf;
  int fontLen;
  FoFiType1C *ffT1C;
  int i;

  // check if font is already embedded
  for (i = 0; i < fontFileIDLen; ++i) {
    if (fontFileIDs[i].num == id->num &&
	fontFileIDs[i].gen == id->gen)
      return;
  }

  // add entry to fontFileIDs list
  if (fontFileIDLen >= fontFileIDSize) {
    fontFileIDSize += 64;
    fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref));
  }
  fontFileIDs[fontFileIDLen++] = *id;

  // beginning comment
  writePSFmt("%%BeginResource: font {0:t}\n", psName);
  embFontList->append("%%+ font ");
  embFontList->append(psName->getCString());
  embFontList->append("\n");

  // convert it to a Type 1 font
  fontBuf = font->readEmbFontFile(xref, &fontLen);
  if ((ffT1C = FoFiType1C::make(fontBuf, fontLen))) {
    ffT1C->convertToType1(psName->getCString(), NULL, gTrue,
			  outputFunc, outputStream);
    delete ffT1C;
  }
  gfree(fontBuf);

  // ending comment
  writePS("%%EndResource\n");
}

void PSOutputDev::setupEmbeddedOpenTypeT1CFont(GfxFont *font, Ref *id,
					       GString *psName) {
  char *fontBuf;
  int fontLen;
  FoFiTrueType *ffTT;
  int i;

  // check if font is already embedded
  for (i = 0; i < fontFileIDLen; ++i) {
    if (fontFileIDs[i].num == id->num &&
	fontFileIDs[i].gen == id->gen)
      return;
  }

  // add entry to fontFileIDs list
  if (fontFileIDLen >= fontFileIDSize) {
    fontFileIDSize += 64;
    fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref));
  }
  fontFileIDs[fontFileIDLen++] = *id;

  // beginning comment
  writePSFmt("%%BeginResource: font {0:t}\n", psName);
  embFontList->append("%%+ font ");
  embFontList->append(psName->getCString());
  embFontList->append("\n");

  // convert it to a Type 1 font
  fontBuf = font->readEmbFontFile(xref, &fontLen);
  if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) {
    if (ffTT->isOpenTypeCFF()) {
      ffTT->convertToType1(psName->getCString(), NULL, gTrue,
			   outputFunc, outputStream);
    }
    delete ffTT;
  }
  gfree(fontBuf);

  // ending comment
  writePS("%%EndResource\n");
}

void PSOutputDev::setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id,
					    GString *psName) {
  char *fontBuf;
  int fontLen;
  FoFiTrueType *ffTT;
  Gushort *codeToGID;
  int i;

  // check if font is already embedded
  for (i = 0; i < fontFileIDLen; ++i) {
    if (fontFileIDs[i].num == id->num &&
	fontFileIDs[i].gen == id->gen) {
      psName->appendf("_{0:d}", nextTrueTypeNum++);
      break;
    }
  }

  // add entry to fontFileIDs list
  if (i == fontFileIDLen) {
    if (fontFileIDLen >= fontFileIDSize) {
      fontFileIDSize += 64;
      fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref));
    }
    fontFileIDs[fontFileIDLen++] = *id;
  }

  // beginning comment
  writePSFmt("%%BeginResource: font {0:t}\n", psName);
  embFontList->append("%%+ font ");
  embFontList->append(psName->getCString());
  embFontList->append("\n");

  // convert it to a Type 42 font
  fontBuf = font->readEmbFontFile(xref, &fontLen);
  if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) {
    codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT);
    ffTT->convertToType42(psName->getCString(),
			  ((Gfx8BitFont *)font)->getHasEncoding()
			    ? ((Gfx8BitFont *)font)->getEncoding()
			    : (char **)NULL,
			  codeToGID, outputFunc, outputStream);
    if (codeToGID) {
      if (font8InfoLen >= font8InfoSize) {
	font8InfoSize += 16;
	font8Info = (PSFont8Info *)greallocn(font8Info,
					     font8InfoSize,
					     sizeof(PSFont8Info));
      }
      font8Info[font8InfoLen].fontID = *font->getID();
      font8Info[font8InfoLen].codeToGID = codeToGID;
      ++font8InfoLen;
    }
    delete ffTT;
  }
  gfree(fontBuf);

  // ending comment
  writePS("%%EndResource\n");
}

void PSOutputDev::setupExternalTrueTypeFont(GfxFont *font, GString *psName) {
  GString *fileName;
  char *fontBuf;
  int fontLen;
  FoFiTrueType *ffTT;
  Gushort *codeToGID;
  int i;

  // check if font is already embedded
  fileName = font->getExtFontFile();
  for (i = 0; i < fontFileNameLen; ++i) {
    if (!fontFileNames[i]->cmp(fileName)) {
      psName->appendf("_{0:d}", nextTrueTypeNum++);
      break;
    }
  }

  // add entry to fontFileNames list
  if (i == fontFileNameLen) {
    if (fontFileNameLen >= fontFileNameSize) {
      fontFileNameSize += 64;
      fontFileNames =
	(GString **)greallocn(fontFileNames,
			      fontFileNameSize, sizeof(GString *));
    }
    fontFileNames[fontFileNameLen++] = fileName->copy();
  }

  // beginning comment
  writePSFmt("%%BeginResource: font {0:t}\n", psName);
  embFontList->append("%%+ font ");
  embFontList->append(psName->getCString());
  embFontList->append("\n");

  // convert it to a Type 42 font
  fontBuf = font->readExtFontFile(&fontLen);
  if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) {
    codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT);
    ffTT->convertToType42(psName->getCString(),
			  ((Gfx8BitFont *)font)->getHasEncoding()
			    ? ((Gfx8BitFont *)font)->getEncoding()
			    : (char **)NULL,
			  codeToGID, outputFunc, outputStream);
    if (codeToGID) {
      if (font8InfoLen >= font8InfoSize) {
	font8InfoSize += 16;
	font8Info = (PSFont8Info *)greallocn(font8Info,
					     font8InfoSize,
					     sizeof(PSFont8Info));
      }
      font8Info[font8InfoLen].fontID = *font->getID();
      font8Info[font8InfoLen].codeToGID = codeToGID;
      ++font8InfoLen;
    }
    delete ffTT;
  }
  gfree(fontBuf);

  // ending comment
  writePS("%%EndResource\n");
}

void PSOutputDev::setupEmbeddedCIDType0Font(GfxFont *font, Ref *id,
					    GString *psName) {
  char *fontBuf;
  int fontLen;
  FoFiType1C *ffT1C;
  int i;

  // check if font is already embedded
  for (i = 0; i < fontFileIDLen; ++i) {
    if (fontFileIDs[i].num == id->num &&
	fontFileIDs[i].gen == id->gen)
      return;
  }

  // add entry to fontFileIDs list
  if (fontFileIDLen >= fontFileIDSize) {
    fontFileIDSize += 64;
    fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref));
  }
  fontFileIDs[fontFileIDLen++] = *id;

  // beginning comment
  writePSFmt("%%BeginResource: font {0:t}\n", psName);
  embFontList->append("%%+ font ");
  embFontList->append(psName->getCString());
  embFontList->append("\n");

  // convert it to a Type 0 font
  fontBuf = font->readEmbFontFile(xref, &fontLen);
  if ((ffT1C = FoFiType1C::make(fontBuf, fontLen))) {
    if (globalParams->getPSLevel() >= psLevel3) {
      // Level 3: use a CID font
      ffT1C->convertToCIDType0(psName->getCString(), outputFunc, outputStream);
    } else {
      // otherwise: use a non-CID composite font
      ffT1C->convertToType0(psName->getCString(), outputFunc, outputStream);
    }
    delete ffT1C;
  }
  gfree(fontBuf);

  // ending comment
  writePS("%%EndResource\n");
}

void PSOutputDev::setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id,
					       GString *psName,
					       GBool needVerticalMetrics) {
  char *fontBuf;
  int fontLen;
  FoFiTrueType *ffTT;
  int i;

  // check if font is already embedded
  for (i = 0; i < fontFileIDLen; ++i) {
    if (fontFileIDs[i].num == id->num &&
	fontFileIDs[i].gen == id->gen) {
      psName->appendf("_{0:d}", nextTrueTypeNum++);
      break;
    }
  }

  // add entry to fontFileIDs list
  if (fontFileIDLen >= fontFileIDSize) {
    fontFileIDSize += 64;
    fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref));
  }
  fontFileIDs[fontFileIDLen++] = *id;

  // beginning comment
  writePSFmt("%%BeginResource: font {0:t}\n", psName);
  embFontList->append("%%+ font ");
  embFontList->append(psName->getCString());
  embFontList->append("\n");

  // convert it to a Type 0 font
  fontBuf = font->readEmbFontFile(xref, &fontLen);
  if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) {
    if (globalParams->getPSLevel() >= psLevel3) {
      // Level 3: use a CID font
      ffTT->convertToCIDType2(psName->getCString(),
			      ((GfxCIDFont *)font)->getCIDToGID(),
			      ((GfxCIDFont *)font)->getCIDToGIDLen(),
			      needVerticalMetrics,
			      outputFunc, outputStream);
    } else {
      // otherwise: use a non-CID composite font
      ffTT->convertToType0(psName->getCString(),
			   ((GfxCIDFont *)font)->getCIDToGID(),
			   ((GfxCIDFont *)font)->getCIDToGIDLen(),
			   needVerticalMetrics,
			   outputFunc, outputStream);
    }
    delete ffTT;
  }
  gfree(fontBuf);

  // ending comment
  writePS("%%EndResource\n");
}

void PSOutputDev::setupEmbeddedOpenTypeCFFFont(GfxFont *font, Ref *id,
					       GString *psName) {
  char *fontBuf;
  int fontLen;
  FoFiTrueType *ffTT;
  int i;

  // check if font is already embedded
  for (i = 0; i < fontFileIDLen; ++i) {
    if (fontFileIDs[i].num == id->num &&
	fontFileIDs[i].gen == id->gen)
      return;
  }

  // add entry to fontFileIDs list
  if (fontFileIDLen >= fontFileIDSize) {
    fontFileIDSize += 64;
    fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref));
  }
  fontFileIDs[fontFileIDLen++] = *id;

  // beginning comment
  writePSFmt("%%BeginResource: font {0:t}\n", psName);
  embFontList->append("%%+ font ");
  embFontList->append(psName->getCString());
  embFontList->append("\n");

  // convert it to a Type 0 font
  fontBuf = font->readEmbFontFile(xref, &fontLen);
  if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) {
    if (ffTT->isOpenTypeCFF()) {
      if (globalParams->getPSLevel() >= psLevel3) {
	// Level 3: use a CID font
	ffTT->convertToCIDType0(psName->getCString(),
				outputFunc, outputStream);
      } else {
	// otherwise: use a non-CID composite font
	ffTT->convertToType0(psName->getCString(), outputFunc, outputStream);
      }
    }
    delete ffTT;
  }
  gfree(fontBuf);

  // ending comment
  writePS("%%EndResource\n");
}

void PSOutputDev::setupType3Font(GfxFont *font, GString *psName,
				 Dict *parentResDict) {
  Dict *resDict;
  Dict *charProcs;
  Object charProc;
  Gfx *gfx;
  PDFRectangle box;
  double *m;
  GString *buf;
  int i;

  // set up resources used by font
  if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
    inType3Char = gTrue;
    setupResources(resDict);
    inType3Char = gFalse;
  } else {
    resDict = parentResDict;
  }

  // beginning comment
  writePSFmt("%%BeginResource: font {0:t}\n", psName);
  embFontList->append("%%+ font ");
  embFontList->append(psName->getCString());
  embFontList->append("\n");

  // font dictionary
  writePS("8 dict begin\n");
  writePS("/FontType 3 def\n");
  m = font->getFontMatrix();
  writePSFmt("/FontMatrix [{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g}] def\n",
	     m[0], m[1], m[2], m[3], m[4], m[5]);
  m = font->getFontBBox();
  writePSFmt("/FontBBox [{0:.4g} {1:.4g} {2:.4g} {3:.4g}] def\n",
	     m[0], m[1], m[2], m[3]);
  writePS("/Encoding 256 array def\n");
  writePS("  0 1 255 { Encoding exch /.notdef put } for\n");
  writePS("/BuildGlyph {\n");
  writePS("  exch /CharProcs get exch\n");
  writePS("  2 copy known not { pop /.notdef } if\n");
  writePS("  get exec\n");
  writePS("} bind def\n");
  writePS("/BuildChar {\n");
  writePS("  1 index /Encoding get exch get\n");
  writePS("  1 index /BuildGlyph get exec\n");
  writePS("} bind def\n");
  if ((charProcs = ((Gfx8BitFont *)font)->getCharProcs())) {
    writePSFmt("/CharProcs {0:d} dict def\n", charProcs->getLength());
    writePS("CharProcs begin\n");
    box.x1 = m[0];
    box.y1 = m[1];
    box.x2 = m[2];
    box.y2 = m[3];
    gfx = new Gfx(xref, this, resDict, &box, NULL);
    inType3Char = gTrue;
    for (i = 0; i < charProcs->getLength(); ++i) {
      t3Cacheable = gFalse;
      t3NeedsRestore = gFalse;
      writePS("/");
      writePSName(charProcs->getKey(i));
      writePS(" {\n");
      gfx->display(charProcs->getVal(i, &charProc));
      charProc.free();
      if (t3String) {
	if (t3Cacheable) {
	  buf = GString::format("{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g} setcachedevice\n",
				t3WX, t3WY, t3LLX, t3LLY, t3URX, t3URY);
	} else {
	  buf = GString::format("{0:.4g} {1:.4g} setcharwidth\n", t3WX, t3WY);
	}
	(*outputFunc)(outputStream, buf->getCString(), buf->getLength());
	delete buf;
	(*outputFunc)(outputStream, t3String->getCString(),
		      t3String->getLength());
	delete t3String;
	t3String = NULL;
      }
      if (t3NeedsRestore) {
	(*outputFunc)(outputStream, "Q\n", 2);
      }
      writePS("} def\n");
    }
    inType3Char = gFalse;
    delete gfx;
    writePS("end\n");
  }
  writePS("currentdict end\n");
  writePSFmt("/{0:t} exch definefont pop\n", psName);

  // ending comment
  writePS("%%EndResource\n");
}

void PSOutputDev::setupImages(Dict *resDict) {
  Object xObjDict, xObj, xObjRef, subtypeObj;
  int i;

  if (!(mode == psModeForm || inType3Char || preload)) {
    return;
  }

  resDict->lookup("XObject", &xObjDict);
  if (xObjDict.isDict()) {
    for (i = 0; i < xObjDict.dictGetLength(); ++i) {
      xObjDict.dictGetValNF(i, &xObjRef);
      xObjDict.dictGetVal(i, &xObj);
      if (xObj.isStream()) {
	xObj.streamGetDict()->lookup("Subtype", &subtypeObj);
	if (subtypeObj.isName("Image")) {
	  if (xObjRef.isRef()) {
	    setupImage(xObjRef.getRef(), xObj.getStream());
	  } else {
	    error(-1, "Image in resource dict is not an indirect reference");
	  }
	}
	subtypeObj.free();
      }
      xObj.free();
      xObjRef.free();
    }
  }
  xObjDict.free();
}

void PSOutputDev::setupImage(Ref id, Stream *str) {
  GBool useRLE, useCompressed, useASCIIHex;
  GString *s;
  int c;
  int size, line, col, i;

  // check if image is already setup
  for (i = 0; i < imgIDLen; ++i) {
    if (imgIDs[i].num == id.num && imgIDs[i].gen == id.gen) {
      return;
    }
  }

  // add entry to imgIDs list
  if (imgIDLen >= imgIDSize) {
    if (imgIDSize == 0) {
      imgIDSize = 64;
    } else {
      imgIDSize *= 2;
    }
    imgIDs = (Ref *)greallocn(imgIDs, imgIDSize, sizeof(Ref));
  }
  imgIDs[imgIDLen++] = id;

  // filters
  //~ this does not correctly handle the DeviceN color space
  //~   -- need to use DeviceNRecoder
  if (level < psLevel2) {
    useRLE = gFalse;
    useCompressed = gFalse;
    useASCIIHex = gTrue;
  } else {
    s = str->getPSFilter(level < psLevel3 ? 2 : 3, "");
    if (s) {
      useRLE = gFalse;
      useCompressed = gTrue;
      delete s;
    } else {
      useRLE = gTrue;
      useCompressed = gFalse;
    }
    useASCIIHex = level == psLevel1 || level == psLevel1Sep ||
                  globalParams->getPSASCIIHex();
  }
  if (useCompressed) {
    str = str->getUndecodedStream();
  }
  if (useRLE) {
    str = new RunLengthEncoder(str);
  }
  if (useASCIIHex) {
    str = new ASCIIHexEncoder(str);
  } else {
    str = new ASCII85Encoder(str);
  }

  // compute image data size
  str->reset();
  col = size = 0;
  do {
    do {
      c = str->getChar();
    } while (c == '\n' || c == '\r');
    if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
      break;
    }
    if (c == 'z') {
      ++col;
    } else {
      ++col;
      for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) {
	do {
	  c = str->getChar();
	} while (c == '\n' || c == '\r');
	if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
	  break;
	}
	++col;
      }
    }
    if (col > 225) {
      ++size;
      col = 0;
    }
  } while (c != (useASCIIHex ? '>' : '~') && c != EOF);
  // add one entry for the final line of data; add another entry
  // because the RunLengthDecode filter may read past the end
  ++size;
  if (useRLE) {
    ++size;
  }
  writePSFmt("{0:d} array dup /ImData_{1:d}_{2:d} exch def\n",
	     size, id.num, id.gen);
  str->close();

  // write the data into the array
  str->reset();
  line = col = 0;
  writePS((char *)(useASCIIHex ? "dup 0 <" : "dup 0 <~"));
  do {
    do {
      c = str->getChar();
    } while (c == '\n' || c == '\r');
    if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
      break;
    }
    if (c == 'z') {
      writePSChar(c);
      ++col;
    } else {
      writePSChar(c);
      ++col;
      for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) {
	do {
	  c = str->getChar();
	} while (c == '\n' || c == '\r');
	if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
	  break;
	}
	writePSChar(c);
	++col;
      }
    }
    // each line is: "dup nnnnn <~...data...~> put<eol>"
    // so max data length = 255 - 20 = 235
    // chunks are 1 or 4 bytes each, so we have to stop at 232
    // but make it 225 just to be safe
    if (col > 225) {
      writePS((char *)(useASCIIHex ? "> put\n" : "~> put\n"));
      ++line;
      writePSFmt((char *)(useASCIIHex ? "dup {0:d} <" : "dup {0:d} <~"), line);
      col = 0;
    }
  } while (c != (useASCIIHex ? '>' : '~') && c != EOF);
  writePS((char *)(useASCIIHex ? "> put\n" : "~> put\n"));
  if (useRLE) {
    ++line;
    writePSFmt("{0:d} <> put\n", line);
  } else {
    writePS("pop\n");
  }
  str->close();

  delete str;
}

void PSOutputDev::setupForms(Dict *resDict) {
  Object xObjDict, xObj, xObjRef, subtypeObj;
  int i;

  if (!preload) {
    return;
  }

  resDict->lookup("XObject", &xObjDict);
  if (xObjDict.isDict()) {
    for (i = 0; i < xObjDict.dictGetLength(); ++i) {
      xObjDict.dictGetValNF(i, &xObjRef);
      xObjDict.dictGetVal(i, &xObj);
      if (xObj.isStream()) {
	xObj.streamGetDict()->lookup("Subtype", &subtypeObj);
	if (subtypeObj.isName("Form")) {
	  if (xObjRef.isRef()) {
	    setupForm(xObjRef.getRef(), &xObj);
	  } else {
	    error(-1, "Form in resource dict is not an indirect reference");
	  }
	}
	subtypeObj.free();
      }
      xObj.free();
      xObjRef.free();
    }
  }
  xObjDict.free();
}

void PSOutputDev::setupForm(Ref id, Object *strObj) {
  Dict *dict, *resDict;
  Object matrixObj, bboxObj, resObj, obj1;
  double m[6], bbox[4];
  PDFRectangle box;
  Gfx *gfx;
  int i;

  // check if form is already defined
  for (i = 0; i < formIDLen; ++i) {
    if (formIDs[i].num == id.num && formIDs[i].gen == id.gen) {
      return;
    }
  }

  // add entry to formIDs list
  if (formIDLen >= formIDSize) {
    if (formIDSize == 0) {
      formIDSize = 64;
    } else {
      formIDSize *= 2;
    }
    formIDs = (Ref *)greallocn(formIDs, formIDSize, sizeof(Ref));
  }
  formIDs[formIDLen++] = id;

  dict = strObj->streamGetDict();

  // get bounding box
  dict->lookup("BBox", &bboxObj);
  if (!bboxObj.isArray()) {
    bboxObj.free();
    error(-1, "Bad form bounding box");
    return;
  }
  for (i = 0; i < 4; ++i) {
    bboxObj.arrayGet(i, &obj1);
    bbox[i] = obj1.getNum();
    obj1.free();
  }
  bboxObj.free();

  // get matrix
  dict->lookup("Matrix", &matrixObj);
  if (matrixObj.isArray()) {
    for (i = 0; i < 6; ++i) {
      matrixObj.arrayGet(i, &obj1);
      m[i] = obj1.getNum();
      obj1.free();
    }
  } else {
    m[0] = 1; m[1] = 0;
    m[2] = 0; m[3] = 1;
    m[4] = 0; m[5] = 0;
  }
  matrixObj.free();

  // get resources
  dict->lookup("Resources", &resObj);
  resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;

  writePSFmt("/f_{0:d}_{1:d} {{\n", id.num, id.gen);
  writePS("q\n");
  writePSFmt("[{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g}] cm\n",
	     m[0], m[1], m[2], m[3], m[4], m[5]);

  box.x1 = bbox[0];
  box.y1 = bbox[1];
  box.x2 = bbox[2];
  box.y2 = bbox[3];
  gfx = new Gfx(xref, this, resDict, &box, &box);
  gfx->display(strObj);
  delete gfx;

  writePS("Q\n");
  writePS("} def\n");

  resObj.free();
}

GBool PSOutputDev::checkPageSlice(Page *page, double hDPI, double vDPI,
				  int rotateA, GBool useMediaBox, GBool crop,
				  int sliceX, int sliceY,
				  int sliceW, int sliceH,
				  GBool printing, Catalog *catalog,
				  GBool (*abortCheckCbk)(void *data),
				  void *abortCheckCbkData) {
#if HAVE_SPLASH
  PreScanOutputDev *scan;
  GBool rasterize;
  SplashOutputDev *splashOut;
  SplashColor paperColor;
  PDFRectangle box;
  GfxState *state;
  SplashBitmap *bitmap;
  Stream *str0, *str;
  Object obj;
  Guchar *p;
  Guchar col[4];
  double m0, m1, m2, m3, m4, m5;
  int c, w, h, x, y, comp, i;

  scan = new PreScanOutputDev();
  page->displaySlice(scan, 72, 72, rotateA, useMediaBox, crop,
		     sliceX, sliceY, sliceW, sliceH,
		     NULL, printing, catalog, abortCheckCbk, abortCheckCbkData);
  rasterize = scan->usesTransparency();
  delete scan;

  if (!rasterize) {
    return gTrue;
  }

  // rasterize the page
  if (level == psLevel1) {
    paperColor[0] = 0xff;
    splashOut = new SplashOutputDev(splashModeMono8, 1, gFalse,
				    paperColor, gTrue, gFalse);
#if SPLASH_CMYK
  } else if (level == psLevel1Sep) {
    paperColor[0] = paperColor[1] = paperColor[2] = paperColor[3] = 0;
    splashOut = new SplashOutputDev(splashModeCMYK8, 1, gFalse,
				    paperColor, gTrue, gFalse);
#endif
  } else {
    paperColor[0] = paperColor[1] = paperColor[2] = 0xff;
    splashOut = new SplashOutputDev(splashModeRGB8, 1, gFalse,
				    paperColor, gTrue, gFalse);
  }
  splashOut->startDoc(xref);
  page->displaySlice(splashOut, splashDPI, splashDPI, rotateA,
		     useMediaBox, crop,
		     sliceX, sliceY, sliceW, sliceH,
		     NULL, printing, catalog, abortCheckCbk, abortCheckCbkData);

  // start the PS page
  page->makeBox(splashDPI, splashDPI, rotateA, useMediaBox, gFalse,
		sliceX, sliceY, sliceW, sliceH, &box, &crop);
  rotateA += page->getRotate();
  if (rotateA >= 360) {
    rotateA -= 360;
  } else if (rotateA < 0) {
    rotateA += 360;
  }
  state = new GfxState(splashDPI, splashDPI, &box, rotateA, gFalse);
  startPage(page->getNum(), state);
  delete state;
  switch (rotateA) {
  case 0:
  default:  // this should never happen
    m0 = box.x2 - box.x1;
    m1 = 0;
    m2 = 0;
    m3 = box.y2 - box.y1;
    m4 = box.x1;
    m5 = box.y1;
    break;
  case 90:
    m0 = 0;
    m1 = box.y2 - box.y1;
    m2 = -(box.x2 - box.x1);
    m3 = 0;
    m4 = box.x2;
    m5 = box.y1;
    break;
  case 180:
    m0 = -(box.x2 - box.x1);
    m1 = 0;
    m2 = 0;
    m3 = -(box.y2 - box.y1);
    m4 = box.x2;
    m5 = box.y2;
    break;
  case 270:
    m0 = 0;
    m1 = -(box.y2 - box.y1);
    m2 = box.x2 - box.x1;
    m3 = 0;
    m4 = box.x1;
    m5 = box.y2;
    break;
  }

  //~ need to add the process colors

  // draw the rasterized image
  bitmap = splashOut->getBitmap();
  w = bitmap->getWidth();
  h = bitmap->getHeight();
  writePS("gsave\n");
  writePSFmt("[{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g}] concat\n",
	     m0, m1, m2, m3, m4, m5);
  switch (level) {
  case psLevel1:
    writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1\n",
	       w, h, w, -h, h);
    p = bitmap->getDataPtr();
    i = 0;
    for (y = 0; y < h; ++y) {
      for (x = 0; x < w; ++x) {
	writePSFmt("{0:02x}", *p++);
	if (++i == 32) {
	  writePSChar('\n');
	  i = 0;
	}
      }
    }
    if (i != 0) {
      writePSChar('\n');
    }
    break;
  case psLevel1Sep:
    writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1Sep\n",
	       w, h, w, -h, h);
    p = bitmap->getDataPtr();
    i = 0;
    col[0] = col[1] = col[2] = col[3] = 0;
    for (y = 0; y < h; ++y) {
      for (comp = 0; comp < 4; ++comp) {
	for (x = 0; x < w; ++x) {
	  writePSFmt("{0:02x}", p[4*x + comp]);
	  col[comp] |= p[4*x + comp];
	  if (++i == 32) {
	    writePSChar('\n');
	    i = 0;
	  }
	}
      }
      p += bitmap->getRowSize();
    }
    if (i != 0) {
      writePSChar('\n');
    }
    if (col[0]) {
      processColors |= psProcessCyan;
    }
    if (col[1]) {
      processColors |= psProcessMagenta;
    }
    if (col[2]) {
      processColors |= psProcessYellow;
    }
    if (col[3]) {
      processColors |= psProcessBlack;
    }
    break;
  case psLevel2:
  case psLevel2Sep:
  case psLevel3:
  case psLevel3Sep:
    writePS("/DeviceRGB setcolorspace\n");
    writePS("<<\n  /ImageType 1\n");
    writePSFmt("  /Width {0:d}\n", bitmap->getWidth());
    writePSFmt("  /Height {0:d}\n", bitmap->getHeight());
    writePSFmt("  /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n", w, -h, h);
    writePS("  /BitsPerComponent 8\n");
    writePS("  /Decode [0 1 0 1 0 1]\n");
    writePS("  /DataSource currentfile\n");
    if (globalParams->getPSASCIIHex()) {
      writePS("    /ASCIIHexDecode filter\n");
    } else {
      writePS("    /ASCII85Decode filter\n");
    }
    writePS("    /RunLengthDecode filter\n");
    writePS(">>\n");
    writePS("image\n");
    obj.initNull();
    str0 = new MemStream((char *)bitmap->getDataPtr(), 0, w * h * 3, &obj);
    str = new RunLengthEncoder(str0);
    if (globalParams->getPSASCIIHex()) {
      str = new ASCIIHexEncoder(str);
    } else {
      str = new ASCII85Encoder(str);
    }
    str->reset();
    while ((c = str->getChar()) != EOF) {
      writePSChar(c);
    }
    str->close();
    delete str;
    delete str0;
    processColors |= psProcessCMYK;
    break;
  }
  delete splashOut;
  writePS("grestore\n");

  // finish the PS page
  endPage();

  return gFalse;
#else
  return gTrue;
#endif
}

void PSOutputDev::startPage(int pageNum, GfxState *state) {
  int x1, y1, x2, y2, width, height;
  int imgWidth, imgHeight, imgWidth2, imgHeight2;
  GBool landscape;


  if (mode == psModePS) {
    writePSFmt("%%Page: {0:d} {1:d}\n", pageNum, seqPage);
    writePS("%%BeginPageSetup\n");
  }

  // underlays
  if (underlayCbk) {
    (*underlayCbk)(this, underlayCbkData);
  }
  if (overlayCbk) {
    saveState(NULL);
  }

  switch (mode) {

  case psModePS:
    // rotate, translate, and scale page
    imgWidth = imgURX - imgLLX;
    imgHeight = imgURY - imgLLY;
    x1 = (int)floor(state->getX1());
    y1 = (int)floor(state->getY1());
    x2 = (int)ceil(state->getX2());
    y2 = (int)ceil(state->getY2());
    width = x2 - x1;
    height = y2 - y1;
    tx = ty = 0;
    // rotation and portrait/landscape mode
    if (rotate0 >= 0) {
      rotate = (360 - rotate0) % 360;
      landscape = gFalse;
    } else {
      rotate = (360 - state->getRotate()) % 360;
      if (rotate == 0 || rotate == 180) {
	if (width > height && width > imgWidth) {
	  rotate += 90;
	  landscape = gTrue;
	} else {
	  landscape = gFalse;
	}
      } else { // rotate == 90 || rotate == 270
	if (height > width && height > imgWidth) {
	  rotate = 270 - rotate;
	  landscape = gTrue;
	} else {
	  landscape = gFalse;
	}
      }
    }
    writePSFmt("%%PageOrientation: {0:s}\n",
	       landscape ? "Landscape" : "Portrait");
    writePS("pdfStartPage\n");
    if (rotate == 0) {
      imgWidth2 = imgWidth;
      imgHeight2 = imgHeight;
    } else if (rotate == 90) {
      writePS("90 rotate\n");
      ty = -imgWidth;
      imgWidth2 = imgHeight;
      imgHeight2 = imgWidth;
    } else if (rotate == 180) {
      writePS("180 rotate\n");
      imgWidth2 = imgWidth;
      imgHeight2 = imgHeight;
      tx = -imgWidth;
      ty = -imgHeight;
    } else { // rotate == 270
      writePS("270 rotate\n");
      tx = -imgHeight;
      imgWidth2 = imgHeight;
      imgHeight2 = imgWidth;
    }
    // shrink or expand
    if (xScale0 > 0 && yScale0 > 0) {
      xScale = xScale0;
      yScale = yScale0;
    } else if ((globalParams->getPSShrinkLarger() &&
	 (width > imgWidth2 || height > imgHeight2)) ||
	(globalParams->getPSExpandSmaller() &&
	 (width < imgWidth2 && height < imgHeight2))) {
      xScale = (double)imgWidth2 / (double)width;
      yScale = (double)imgHeight2 / (double)height;
      if (yScale < xScale) {
	xScale = yScale;
      } else {
	yScale = xScale;
      }
    } else {
      xScale = yScale = 1;
    }
    // deal with odd bounding boxes or clipping
    if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) {
      tx -= xScale * clipLLX0;
      ty -= yScale * clipLLY0;
    } else {
      tx -= xScale * x1;
      ty -= yScale * y1;
    }
    // center
    if (tx0 >= 0 && ty0 >= 0) {
      tx += rotate == 0 ? tx0 : ty0;
      ty += rotate == 0 ? ty0 : -tx0;
    } else if (globalParams->getPSCenter()) {
      if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) {
	tx += (imgWidth2 - xScale * (clipURX0 - clipLLX0)) / 2;
	ty += (imgHeight2 - yScale * (clipURY0 - clipLLY0)) / 2;
      } else {
	tx += (imgWidth2 - xScale * width) / 2;
	ty += (imgHeight2 - yScale * height) / 2;
      }
    }
    tx += rotate == 0 ? imgLLX : imgLLY;
    ty += rotate == 0 ? imgLLY : -imgLLX;
    if (tx != 0 || ty != 0) {
      writePSFmt("{0:.4g} {1:.4g} translate\n", tx, ty);
    }
    if (xScale != 1 || yScale != 1) {
      writePSFmt("{0:.4f} {0:.4f} scale\n", xScale);
    }
    if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) {
      writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} re W\n",
		 clipLLX0, clipLLY0, clipURX0 - clipLLX0, clipURY0 - clipLLY0);
    } else {
      writePSFmt("{0:d} {1:d} {2:d} {3:d} re W\n", x1, y1, x2 - x1, y2 - y1);
    }

    writePS("%%EndPageSetup\n");
    ++seqPage;
    break;

  case psModeEPS:
    writePS("pdfStartPage\n");
    tx = ty = 0;
    rotate = (360 - state->getRotate()) % 360;
    if (rotate == 0) {
    } else if (rotate == 90) {
      writePS("90 rotate\n");
      tx = -epsX1;
      ty = -epsY2;
    } else if (rotate == 180) {
      writePS("180 rotate\n");
      tx = -(epsX1 + epsX2);
      ty = -(epsY1 + epsY2);
    } else { // rotate == 270
      writePS("270 rotate\n");
      tx = -epsX2;
      ty = -epsY1;
    }
    if (tx != 0 || ty != 0) {
      writePSFmt("{0:.4g} {1:.4g} translate\n", tx, ty);
    }
    xScale = yScale = 1;
    break;

  case psModeForm:
    writePS("/PaintProc {\n");
    writePS("begin xpdf begin\n");
    writePS("pdfStartPage\n");
    tx = ty = 0;
    xScale = yScale = 1;
    rotate = 0;
    break;
  }
}

void PSOutputDev::endPage() {
  if (overlayCbk) {
    restoreState(NULL);
    (*overlayCbk)(this, overlayCbkData);
  }


  if (mode == psModeForm) {
    writePS("pdfEndPage\n");
    writePS("end end\n");
    writePS("} def\n");
    writePS("end end\n");
  } else {
    if (!manualCtrl) {
      writePS("showpage\n");
    }
    writePS("%%PageTrailer\n");
    writePageTrailer();
  }
}

void PSOutputDev::saveState(GfxState *state) {
  writePS("q\n");
  ++numSaves;
}

void PSOutputDev::restoreState(GfxState *state) {
  writePS("Q\n");
  --numSaves;
}

void PSOutputDev::updateCTM(GfxState *state, double m11, double m12,
			    double m21, double m22, double m31, double m32) {
  writePSFmt("[{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g}] cm\n",
	     m11, m12, m21, m22, m31, m32);
}

void PSOutputDev::updateLineDash(GfxState *state) {
  double *dash;
  double start;
  int length, i;

  state->getLineDash(&dash, &length, &start);
  writePS("[");
  for (i = 0; i < length; ++i) {
    writePSFmt("{0:.4g}{1:w}",
	       dash[i] < 0 ? 0 : dash[i],
	       (i == length-1) ? 0 : 1);
  }
  writePSFmt("] {0:.4g} d\n", start);
}

void PSOutputDev::updateFlatness(GfxState *state) {
  writePSFmt("{0:d} i\n", state->getFlatness());
}

void PSOutputDev::updateLineJoin(GfxState *state) {
  writePSFmt("{0:d} j\n", state->getLineJoin());
}

void PSOutputDev::updateLineCap(GfxState *state) {
  writePSFmt("{0:d} J\n", state->getLineCap());
}

void PSOutputDev::updateMiterLimit(GfxState *state) {
  writePSFmt("{0:.4g} M\n", state->getMiterLimit());
}

void PSOutputDev::updateLineWidth(GfxState *state) {
  writePSFmt("{0:.4g} w\n", state->getLineWidth());
}

void PSOutputDev::updateFillColorSpace(GfxState *state) {
  switch (level) {
  case psLevel1:
  case psLevel1Sep:
    break;
  case psLevel2:
  case psLevel3:
    if (state->getFillColorSpace()->getMode() != csPattern) {
      dumpColorSpaceL2(state->getFillColorSpace(), gTrue, gFalse, gFalse);
      writePS(" cs\n");
    }
    break;
  case psLevel2Sep:
  case psLevel3Sep:
    break;
  }
}

void PSOutputDev::updateStrokeColorSpace(GfxState *state) {
  switch (level) {
  case psLevel1:
  case psLevel1Sep:
    break;
  case psLevel2:
  case psLevel3:
    if (state->getStrokeColorSpace()->getMode() != csPattern) {
      dumpColorSpaceL2(state->getStrokeColorSpace(), gTrue, gFalse, gFalse);
      writePS(" CS\n");
    }
    break;
  case psLevel2Sep:
  case psLevel3Sep:
    break;
  }
}

void PSOutputDev::updateFillColor(GfxState *state) {
  GfxColor color;
  GfxColor *colorPtr;
  GfxGray gray;
  GfxCMYK cmyk;
  GfxSeparationColorSpace *sepCS;
  double c, m, y, k;
  int i;

  switch (level) {
  case psLevel1:
    state->getFillGray(&gray);
    writePSFmt("{0:.4g} g\n", colToDbl(gray));
    break;
  case psLevel1Sep:
    state->getFillCMYK(&cmyk);
    c = colToDbl(cmyk.c);
    m = colToDbl(cmyk.m);
    y = colToDbl(cmyk.y);
    k = colToDbl(cmyk.k);
    writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} k\n", c, m, y, k);
    addProcessColor(c, m, y, k);
    break;
  case psLevel2:
  case psLevel3:
    if (state->getFillColorSpace()->getMode() != csPattern) {
      colorPtr = state->getFillColor();
      writePS("[");
      for (i = 0; i < state->getFillColorSpace()->getNComps(); ++i) {
	if (i > 0) {
	  writePS(" ");
	}
	writePSFmt("{0:.4g}", colToDbl(colorPtr->c[i]));
      }
      writePS("] sc\n");
    }
    break;
  case psLevel2Sep:
  case psLevel3Sep:
    if (state->getFillColorSpace()->getMode() == csSeparation) {
      sepCS = (GfxSeparationColorSpace *)state->getFillColorSpace();
      color.c[0] = gfxColorComp1;
      sepCS->getCMYK(&color, &cmyk);
      writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} ({5:t}) ck\n",
		 colToDbl(state->getFillColor()->c[0]),
		 colToDbl(cmyk.c), colToDbl(cmyk.m),
		 colToDbl(cmyk.y), colToDbl(cmyk.k),
		 sepCS->getName());
      addCustomColor(sepCS);
    } else {
      state->getFillCMYK(&cmyk);
      c = colToDbl(cmyk.c);
      m = colToDbl(cmyk.m);
      y = colToDbl(cmyk.y);
      k = colToDbl(cmyk.k);
      writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} k\n", c, m, y, k);
      addProcessColor(c, m, y, k);
    }
    break;
  }
  t3Cacheable = gFalse;
}

void PSOutputDev::updateStrokeColor(GfxState *state) {
  GfxColor color;
  GfxColor *colorPtr;
  GfxGray gray;
  GfxCMYK cmyk;
  GfxSeparationColorSpace *sepCS;
  double c, m, y, k;
  int i;

  switch (level) {
  case psLevel1:
    state->getStrokeGray(&gray);
    writePSFmt("{0:.4g} G\n", colToDbl(gray));
    break;
  case psLevel1Sep:
    state->getStrokeCMYK(&cmyk);
    c = colToDbl(cmyk.c);
    m = colToDbl(cmyk.m);
    y = colToDbl(cmyk.y);
    k = colToDbl(cmyk.k);
    writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} K\n", c, m, y, k);
    addProcessColor(c, m, y, k);
    break;
  case psLevel2:
  case psLevel3:
    if (state->getStrokeColorSpace()->getMode() != csPattern) {
      colorPtr = state->getStrokeColor();
      writePS("[");
      for (i = 0; i < state->getStrokeColorSpace()->getNComps(); ++i) {
	if (i > 0) {
	  writePS(" ");
	}
	writePSFmt("{0:.4g}", colToDbl(colorPtr->c[i]));
      }
      writePS("] SC\n");
    }
    break;
  case psLevel2Sep:
  case psLevel3Sep:
    if (state->getStrokeColorSpace()->getMode() == csSeparation) {
      sepCS = (GfxSeparationColorSpace *)state->getStrokeColorSpace();
      color.c[0] = gfxColorComp1;
      sepCS->getCMYK(&color, &cmyk);
      writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} ({5:t}) CK\n",
		 colToDbl(state->getStrokeColor()->c[0]),
		 colToDbl(cmyk.c), colToDbl(cmyk.m),
		 colToDbl(cmyk.y), colToDbl(cmyk.k),
		 sepCS->getName());
      addCustomColor(sepCS);
    } else {
      state->getStrokeCMYK(&cmyk);
      c = colToDbl(cmyk.c);
      m = colToDbl(cmyk.m);
      y = colToDbl(cmyk.y);
      k = colToDbl(cmyk.k);
      writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} K\n", c, m, y, k);
      addProcessColor(c, m, y, k);
    }
    break;
  }
  t3Cacheable = gFalse;
}

void PSOutputDev::addProcessColor(double c, double m, double y, double k) {
  if (c > 0) {
    processColors |= psProcessCyan;
  }
  if (m > 0) {
    processColors |= psProcessMagenta;
  }
  if (y > 0) {
    processColors |= psProcessYellow;
  }
  if (k > 0) {
    processColors |= psProcessBlack;
  }
}

void PSOutputDev::addCustomColor(GfxSeparationColorSpace *sepCS) {
  PSOutCustomColor *cc;
  GfxColor color;
  GfxCMYK cmyk;

  for (cc = customColors; cc; cc = cc->next) {
    if (!cc->name->cmp(sepCS->getName())) {
      return;
    }
  }
  color.c[0] = gfxColorComp1;
  sepCS->getCMYK(&color, &cmyk);
  cc = new PSOutCustomColor(colToDbl(cmyk.c), colToDbl(cmyk.m),
			    colToDbl(cmyk.y), colToDbl(cmyk.k),
			    sepCS->getName()->copy());
  cc->next = customColors;
  customColors = cc;
}

void PSOutputDev::updateFillOverprint(GfxState *state) {
  if (level >= psLevel2) {
    writePSFmt("{0:s} op\n", state->getFillOverprint() ? "true" : "false");
  }
}

void PSOutputDev::updateStrokeOverprint(GfxState *state) {
  if (level >= psLevel2) {
    writePSFmt("{0:s} OP\n", state->getStrokeOverprint() ? "true" : "false");
  }
}

void PSOutputDev::updateTransfer(GfxState *state) {
  Function **funcs;
  int i;

  funcs = state->getTransfer();
  if (funcs[0] && funcs[1] && funcs[2] && funcs[3]) {
    if (level >= psLevel2) {
      for (i = 0; i < 4; ++i) {
	cvtFunction(funcs[i]);
      }
      writePS("setcolortransfer\n");
    } else {
      cvtFunction(funcs[3]);
      writePS("settransfer\n");
    }
  } else if (funcs[0]) {
    cvtFunction(funcs[0]);
    writePS("settransfer\n");
  } else {
    writePS("{} settransfer\n");
  }
}

void PSOutputDev::updateFont(GfxState *state) {
  if (state->getFont()) {
    writePSFmt("/F{0:d}_{1:d} {2:.4g} Tf\n",
	       state->getFont()->getID()->num, state->getFont()->getID()->gen,
	       fabs(state->getFontSize()) < 0.00001 ? 0.00001
	                                            : state->getFontSize());
  }
}

void PSOutputDev::updateTextMat(GfxState *state) {
  double *mat;

  mat = state->getTextMat();
  if (fabs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.00001) {
    // avoid a singular (or close-to-singular) matrix
    writePSFmt("[0.00001 0 0 0.00001 {0:.4g} {1:.4g}] Tm\n", mat[4], mat[5]);
  } else {
    writePSFmt("[{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g}] Tm\n",
	       mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
  }
}

void PSOutputDev::updateCharSpace(GfxState *state) {
  writePSFmt("{0:.4g} Tc\n", state->getCharSpace());
}

void PSOutputDev::updateRender(GfxState *state) {
  int rm;

  rm = state->getRender();
  writePSFmt("{0:d} Tr\n", rm);
  rm &= 3;
  if (rm != 0 && rm != 3) {
    t3Cacheable = gFalse;
  }
}

void PSOutputDev::updateRise(GfxState *state) {
  writePSFmt("{0:.4g} Ts\n", state->getRise());
}

void PSOutputDev::updateWordSpace(GfxState *state) {
  writePSFmt("{0:.4g} Tw\n", state->getWordSpace());
}

void PSOutputDev::updateHorizScaling(GfxState *state) {
  double h;

  h = state->getHorizScaling();
  if (fabs(h) < 0.01) {
    h = 0.01;
  }
  writePSFmt("{0:.4g} Tz\n", h);
}

void PSOutputDev::updateTextPos(GfxState *state) {
  writePSFmt("{0:.4g} {1:.4g} Td\n", state->getLineX(), state->getLineY());
}

void PSOutputDev::updateTextShift(GfxState *state, double shift) {
  if (state->getFont()->getWMode()) {
    writePSFmt("{0:.4g} TJmV\n", shift);
  } else {
    writePSFmt("{0:.4g} TJm\n", shift);
  }
}

void PSOutputDev::stroke(GfxState *state) {
  doPath(state->getPath());
  if (t3String) {
    // if we're construct a cacheable Type 3 glyph, we need to do
    // everything in the fill color
    writePS("Sf\n");
  } else {
    writePS("S\n");
  }
}

void PSOutputDev::fill(GfxState *state) {
  doPath(state->getPath());
  writePS("f\n");
}

void PSOutputDev::eoFill(GfxState *state) {
  doPath(state->getPath());
  writePS("f*\n");
}

void PSOutputDev::tilingPatternFill(GfxState *state, Object *str,
				    int paintType, Dict *resDict,
				    double *mat, double *bbox,
				    int x0, int y0, int x1, int y1,
				    double xStep, double yStep) {
  PDFRectangle box;
  Gfx *gfx;

  // define a Type 3 font
  writePS("8 dict begin\n");
  writePS("/FontType 3 def\n");
  writePS("/FontMatrix [1 0 0 1 0 0] def\n");
  writePSFmt("/FontBBox [{0:.4g} {1:.4g} {2:.4g} {3:.4g}] def\n",
	     bbox[0], bbox[1], bbox[2], bbox[3]);
  writePS("/Encoding 256 array def\n");
  writePS("  0 1 255 { Encoding exch /.notdef put } for\n");
  writePS("  Encoding 120 /x put\n");
  writePS("/BuildGlyph {\n");
  writePS("  exch /CharProcs get exch\n");
  writePS("  2 copy known not { pop /.notdef } if\n");
  writePS("  get exec\n");
  writePS("} bind def\n");
  writePS("/BuildChar {\n");
  writePS("  1 index /Encoding get exch get\n");
  writePS("  1 index /BuildGlyph get exec\n");
  writePS("} bind def\n");
  writePS("/CharProcs 1 dict def\n");
  writePS("CharProcs begin\n");
  box.x1 = bbox[0];
  box.y1 = bbox[1];
  box.x2 = bbox[2];
  box.y2 = bbox[3];
  gfx = new Gfx(xref, this, resDict, &box, NULL);
  writePS("/x {\n");
  if (paintType == 2) {
    writePSFmt("{0:.4g} 0 {1:.4g} {2:.4g} {3:.4g} {4:.4g} setcachedevice\n",
	       xStep, bbox[0], bbox[1], bbox[2], bbox[3]);
  } else {
    if (x1 - 1 <= x0) {
      writePS("1 0 setcharwidth\n");
    } else {
      writePSFmt("{0:.4g} 0 setcharwidth\n", xStep);
    }
  }
  inType3Char = gTrue;
  ++numTilingPatterns;
  gfx->display(str);
  --numTilingPatterns;
  inType3Char = gFalse;
  writePS("} def\n");
  delete gfx;
  writePS("end\n");
  writePS("currentdict end\n");
  writePSFmt("/xpdfTile{0:d} exch definefont pop\n", numTilingPatterns);

  // draw the tiles
  writePSFmt("/xpdfTile{0:d} findfont setfont\n", numTilingPatterns);
  writePSFmt("gsave [{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g}] concat\n",
	     mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
  writePSFmt("{0:d} 1 {1:d} {{ {2:.4g} exch {3:.4g} mul m {4:d} 1 {5:d} {{ pop (x) show }} for }} for\n",
	     y0, y1 - 1, x0 * xStep, yStep, x0, x1 - 1);
  writePS("grestore\n");
}

GBool PSOutputDev::functionShadedFill(GfxState *state,
				      GfxFunctionShading *shading) {
  double x0, y0, x1, y1;
  double *mat;
  int i;

  if (level == psLevel2Sep || level == psLevel3Sep) {
    if (shading->getColorSpace()->getMode() != csDeviceCMYK) {
      return gFalse;
    }
    processColors |= psProcessCMYK;
  }

  shading->getDomain(&x0, &y0, &x1, &y1);
  mat = shading->getMatrix();
  writePSFmt("/mat [{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g}] def\n",
	     mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
  writePSFmt("/n {0:d} def\n", shading->getColorSpace()->getNComps());
  if (shading->getNFuncs() == 1) {
    writePS("/func ");
    cvtFunction(shading->getFunc(0));
    writePS("def\n");
  } else {
    writePS("/func {\n");
    for (i = 0; i < shading->getNFuncs(); ++i) {
      if (i < shading->getNFuncs() - 1) {
	writePS("2 copy\n");
      }
      cvtFunction(shading->getFunc(i));
      writePS("exec\n");
      if (i < shading->getNFuncs() - 1) {
	writePS("3 1 roll\n");
      }
    }
    writePS("} def\n");
  }
  writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} 0 funcSH\n", x0, y0, x1, y1);

  return gTrue;
}

GBool PSOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading) {
  double xMin, yMin, xMax, yMax;
  double x0, y0, x1, y1, dx, dy, mul;
  double tMin, tMax, t, t0, t1;
  int i;

  if (level == psLevel2Sep || level == psLevel3Sep) {
    if (shading->getColorSpace()->getMode() != csDeviceCMYK) {
      return gFalse;
    }
    processColors |= psProcessCMYK;
  }

  // get the clip region bbox
  state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);

  // compute min and max t values, based on the four corners of the
  // clip region bbox
  shading->getCoords(&x0, &y0, &x1, &y1);
  dx = x1 - x0;
  dy = y1 - y0;
  if (fabs(dx) < 0.01 && fabs(dy) < 0.01) {
    return gTrue;
  } else {
    mul = 1 / (dx * dx + dy * dy);
    tMin = tMax = ((xMin - x0) * dx + (yMin - y0) * dy) * mul;
    t = ((xMin - x0) * dx + (yMax - y0) * dy) * mul;
    if (t < tMin) {
      tMin = t;
    } else if (t > tMax) {
      tMax = t;
    }
    t = ((xMax - x0) * dx + (yMin - y0) * dy) * mul;
    if (t < tMin) {
      tMin = t;
    } else if (t > tMax) {
      tMax = t;
    }
    t = ((xMax - x0) * dx + (yMax - y0) * dy) * mul;
    if (t < tMin) {
      tMin = t;
    } else if (t > tMax) {
      tMax = t;
    }
    if (tMin < 0 && !shading->getExtend0()) {
      tMin = 0;
    }
    if (tMax > 1 && !shading->getExtend1()) {
      tMax = 1;
    }
  }

  // get the function domain
  t0 = shading->getDomain0();
  t1 = shading->getDomain1();

  // generate the PS code
  writePSFmt("/t0 {0:.4g} def\n", t0);
  writePSFmt("/t1 {0:.4g} def\n", t1);
  writePSFmt("/dt {0:.4g} def\n", t1 - t0);
  writePSFmt("/x0 {0:.4g} def\n", x0);
  writePSFmt("/y0 {0:.4g} def\n", y0);
  writePSFmt("/dx {0:.4g} def\n", x1 - x0);
  writePSFmt("/x1 {0:.4g} def\n", x1);
  writePSFmt("/y1 {0:.4g} def\n", y1);
  writePSFmt("/dy {0:.4g} def\n", y1 - y0);
  writePSFmt("/xMin {0:.4g} def\n", xMin);
  writePSFmt("/yMin {0:.4g} def\n", yMin);
  writePSFmt("/xMax {0:.4g} def\n", xMax);
  writePSFmt("/yMax {0:.4g} def\n", yMax);
  writePSFmt("/n {0:d} def\n", shading->getColorSpace()->getNComps());
  if (shading->getNFuncs() == 1) {
    writePS("/func ");
    cvtFunction(shading->getFunc(0));
    writePS("def\n");
  } else {
    writePS("/func {\n");
    for (i = 0; i < shading->getNFuncs(); ++i) {
      if (i < shading->getNFuncs() - 1) {
	writePS("dup\n");
      }
      cvtFunction(shading->getFunc(i));
      writePS("exec\n");
      if (i < shading->getNFuncs() - 1) {
	writePS("exch\n");
      }
    }
    writePS("} def\n");
  }
  writePSFmt("{0:.4g} {1:.4g} 0 axialSH\n", tMin, tMax);

  return gTrue;
}

GBool PSOutputDev::radialShadedFill(GfxState *state,
				    GfxRadialShading *shading) {
  double xMin, yMin, xMax, yMax;
  double x0, y0, r0, x1, y1, r1, t0, t1;
  double xa, ya, ra;
  double sz, xz, yz, sMin, sMax, sa, ta;
  double theta, alpha, a1, a2;
  GBool enclosed;
  int i;

  if (level == psLevel2Sep || level == psLevel3Sep) {
    if (shading->getColorSpace()->getMode() != csDeviceCMYK) {
      return gFalse;
    }
    processColors |= psProcessCMYK;
  }

  // get the shading info
  shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1);
  t0 = shading->getDomain0();
  t1 = shading->getDomain1();

  // Compute the point at which r(s) = 0; check for the enclosed
  // circles case; and compute the angles for the tangent lines.
  if (r0 == r1) {
    enclosed = x0 == x1 && y0 == y1;
    theta = 0;
    sz = 0; // make gcc happy
  } else {
    sz = -r0 / (r1 - r0);
    xz = x0 + sz * (x1 - x0);
    yz = y0 + sz * (y1 - y0);
    enclosed = (xz - x0) * (xz - x0) + (yz - y0) * (yz - y0) <= r0 * r0;
    theta = asin(r0 / sqrt((x0 - xz) * (x0 - xz) + (y0 - yz) * (y0 - yz)));
    if (r0 > r1) {
      theta = -theta;
    }
  }
  if (enclosed) {
    a1 = 0;
    a2 = 360;
  } else {
    alpha = atan2(y1 - y0, x1 - x0);
    a1 = (180 / M_PI) * (alpha + theta) + 90;
    a2 = (180 / M_PI) * (alpha - theta) - 90;
    while (a2 < a1) {
      a2 += 360;
    }
  }

  // compute the (possibly extended) s range
  state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
  if (enclosed) {
    sMin = 0;
    sMax = 1;
  } else {
    sMin = 1;
    sMax = 0;
    // solve for x(s) + r(s) = xMin
    if ((x1 + r1) - (x0 + r0) != 0) {
      sa = (xMin - (x0 + r0)) / ((x1 + r1) - (x0 + r0));
      if (sa < sMin) {
	sMin = sa;
      } else if (sa > sMax) {
	sMax = sa;
      }
    }
    // solve for x(s) - r(s) = xMax
    if ((x1 - r1) - (x0 - r0) != 0) {
      sa = (xMax - (x0 - r0)) / ((x1 - r1) - (x0 - r0));
      if (sa < sMin) {
	sMin = sa;
      } else if (sa > sMax) {
	sMax = sa;
      }
    }
    // solve for y(s) + r(s) = yMin
    if ((y1 + r1) - (y0 + r0) != 0) {
      sa = (yMin - (y0 + r0)) / ((y1 + r1) - (y0 + r0));
      if (sa < sMin) {
	sMin = sa;
      } else if (sa > sMax) {
	sMax = sa;
      }
    }
    // solve for y(s) - r(s) = yMax
    if ((y1 - r1) - (y0 - r0) != 0) {
      sa = (yMax - (y0 - r0)) / ((y1 - r1) - (y0 - r0));
      if (sa < sMin) {
	sMin = sa;
      } else if (sa > sMax) {
	sMax = sa;
      }
    }
    // check against sz
    if (r0 < r1) {
      if (sMin < sz) {
	sMin = sz;
      }
    } else if (r0 > r1) {
      if (sMax > sz) {
	sMax = sz;
      }
    }
    // check the 'extend' flags
    if (!shading->getExtend0() && sMin < 0) {
      sMin = 0;
    }
    if (!shading->getExtend1() && sMax > 1) {
      sMax = 1;
    }
  }

  // generate the PS code
  writePSFmt("/x0 {0:.4g} def\n", x0);
  writePSFmt("/x1 {0:.4g} def\n", x1);
  writePSFmt("/dx {0:.4g} def\n", x1 - x0);
  writePSFmt("/y0 {0:.4g} def\n", y0);
  writePSFmt("/y1 {0:.4g} def\n", y1);
  writePSFmt("/dy {0:.4g} def\n", y1 - y0);
  writePSFmt("/r0 {0:.4g} def\n", r0);
  writePSFmt("/r1 {0:.4g} def\n", r1);
  writePSFmt("/dr {0:.4g} def\n", r1 - r0);
  writePSFmt("/t0 {0:.4g} def\n", t0);
  writePSFmt("/t1 {0:.4g} def\n", t1);
  writePSFmt("/dt {0:.4g} def\n", t1 - t0);
  writePSFmt("/n {0:d} def\n", shading->getColorSpace()->getNComps());
  writePSFmt("/encl {0:s} def\n", enclosed ? "true" : "false");
  writePSFmt("/a1 {0:.4g} def\n", a1);
  writePSFmt("/a2 {0:.4g} def\n", a2);
  if (shading->getNFuncs() == 1) {
    writePS("/func ");
    cvtFunction(shading->getFunc(0));
    writePS("def\n");
  } else {
    writePS("/func {\n");
    for (i = 0; i < shading->getNFuncs(); ++i) {
      if (i < shading->getNFuncs() - 1) {
	writePS("dup\n");
      }
      cvtFunction(shading->getFunc(i));
      writePS("exec\n");
      if (i < shading->getNFuncs() - 1) {
	writePS("exch\n");
      }
    }
    writePS("} def\n");
  }
  writePSFmt("{0:.4g} {1:.4g} 0 radialSH\n", sMin, sMax);

  // extend the 'enclosed' case
  if (enclosed) {
    // extend the smaller circle
    if ((shading->getExtend0() && r0 <= r1) ||
	(shading->getExtend1() && r1 < r0)) {
      if (r0 <= r1) {
	ta = t0;
	ra = r0;
	xa = x0;
	ya = y0;
      } else {
	ta = t1;
	ra = r1;
	xa = x1;
	ya = y1;
      }
      if (level == psLevel2Sep || level == psLevel3Sep) {
	writePSFmt("{0:.4g} radialCol aload pop k\n", ta);
      } else {
	writePSFmt("{0:.4g} radialCol sc\n", ta);
      }
      writePSFmt("{0:.4g} {1:.4g} {2:.4g} 0 360 arc h f*\n", xa, ya, ra);
    }

    // extend the larger circle
    if ((shading->getExtend0() && r0 > r1) ||
	(shading->getExtend1() && r1 >= r0)) {
      if (r0 > r1) {
	ta = t0;
	ra = r0;
	xa = x0;
	ya = y0;
      } else {
	ta = t1;
	ra = r1;
	xa = x1;
	ya = y1;
      }
      if (level == psLevel2Sep || level == psLevel3Sep) {
	writePSFmt("{0:.4g} radialCol aload pop k\n", ta);
      } else {
	writePSFmt("{0:.4g} radialCol sc\n", ta);
      }
      writePSFmt("{0:.4g} {1:.4g} {2:.4g} 0 360 arc h\n", xa, ya, ra);
      writePSFmt("{0:.4g} {1:.4g} m {2:.4g} {3:.4g} l {4:.4g} {5:.4g} l {6:.4g} {7:.4g} l h f*\n",
		 xMin, yMin, xMin, yMax, xMax, yMax, xMax, yMin);
    }
  }

  return gTrue;
}

void PSOutputDev::clip(GfxState *state) {
  doPath(state->getPath());
  writePS("W\n");
}

void PSOutputDev::eoClip(GfxState *state) {
  doPath(state->getPath());
  writePS("W*\n");
}

void PSOutputDev::clipToStrokePath(GfxState *state) {
  doPath(state->getPath());
  writePS("Ws\n");
}

void PSOutputDev::doPath(GfxPath *path) {
  GfxSubpath *subpath;
  double x0, y0, x1, y1, x2, y2, x3, y3, x4, y4;
  int n, m, i, j;

  n = path->getNumSubpaths();

  if (n == 1 && path->getSubpath(0)->getNumPoints() == 5) {
    subpath = path->getSubpath(0);
    x0 = subpath->getX(0);
    y0 = subpath->getY(0);
    x4 = subpath->getX(4);
    y4 = subpath->getY(4);
    if (x4 == x0 && y4 == y0) {
      x1 = subpath->getX(1);
      y1 = subpath->getY(1);
      x2 = subpath->getX(2);
      y2 = subpath->getY(2);
      x3 = subpath->getX(3);
      y3 = subpath->getY(3);
      if (x0 == x1 && x2 == x3 && y0 == y3 && y1 == y2) {
	writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} re\n",
		   x0 < x2 ? x0 : x2, y0 < y1 ? y0 : y1,
		   fabs(x2 - x0), fabs(y1 - y0));
	return;
      } else if (x0 == x3 && x1 == x2 && y0 == y1 && y2 == y3) {
	writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} re\n",
		   x0 < x1 ? x0 : x1, y0 < y2 ? y0 : y2,
		   fabs(x1 - x0), fabs(y2 - y0));
	return;
      }
    }
  }

  for (i = 0; i < n; ++i) {
    subpath = path->getSubpath(i);
    m = subpath->getNumPoints();
    writePSFmt("{0:.4g} {1:.4g} m\n", subpath->getX(0), subpath->getY(0));
    j = 1;
    while (j < m) {
      if (subpath->getCurve(j)) {
	writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g} c\n",
		   subpath->getX(j), subpath->getY(j),
		   subpath->getX(j+1), subpath->getY(j+1),
		   subpath->getX(j+2), subpath->getY(j+2));
	j += 3;
      } else {
	writePSFmt("{0:.4g} {1:.4g} l\n", subpath->getX(j), subpath->getY(j));
	++j;
      }
    }
    if (subpath->isClosed()) {
      writePS("h\n");
    }
  }
}

void PSOutputDev::drawString(GfxState *state, GString *s) {
  GfxFont *font;
  int wMode;
  Gushort *codeToGID;
  GString *s2;
  double dx, dy, dx2, dy2, originX, originY;
  char *p;
  UnicodeMap *uMap;
  CharCode code;
  Unicode u[8];
  char buf[8];
  int len, nChars, uLen, n, m, i, j;

  // check for invisible text -- this is used by Acrobat Capture
  if (state->getRender() == 3) {
    return;
  }

  // ignore empty strings
  if (s->getLength() == 0) {
    return;
  }

  // get the font
  if (!(font = state->getFont())) {
    return;
  }
  wMode = font->getWMode();

  // check for a subtitute 16-bit font
  uMap = NULL;
  codeToGID = NULL;
  if (font->isCIDFont()) {
    for (i = 0; i < font16EncLen; ++i) {
      if (font->getID()->num == font16Enc[i].fontID.num &&
	  font->getID()->gen == font16Enc[i].fontID.gen) {
	uMap = globalParams->getUnicodeMap(font16Enc[i].enc);
	break;
      }
    }

  // check for a code-to-GID map
  } else {
    for (i = 0; i < font8InfoLen; ++i) {
      if (font->getID()->num == font8Info[i].fontID.num &&
	  font->getID()->gen == font8Info[i].fontID.gen) {
	codeToGID = font8Info[i].codeToGID;
	break;
      }
    }
  }

  // compute width of chars in string, ignoring char spacing and word
  // spacing -- the Tj operator will adjust for the metrics of the
  // font that's actually used
  dx = dy = 0;
  nChars = 0;
  p = s->getCString();
  len = s->getLength();
  s2 = new GString();
  while (len > 0) {
    n = font->getNextChar(p, len, &code,
			  u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
			  &dx2, &dy2, &originX, &originY);
    if (font->isCIDFont()) {
      if (uMap) {
	for (i = 0; i < uLen; ++i) {
	  m = uMap->mapUnicode(u[i], buf, (int)sizeof(buf));
	  for (j = 0; j < m; ++j) {
	    s2->append(buf[j]);
	  }
	}
	//~ this really needs to get the number of chars in the target
	//~ encoding - which may be more than the number of Unicode
	//~ chars
	nChars += uLen;
      } else {
	s2->append((char)((code >> 8) & 0xff));
	s2->append((char)(code & 0xff));
	++nChars;
      }
    } else {
      if (!codeToGID || codeToGID[code]) {
	s2->append((char)code);
      }
    }
    dx += dx2;
    dy += dy2;
    p += n;
    len -= n;
  }
  dx *= state->getFontSize() * state->getHorizScaling();
  dy *= state->getFontSize();
  if (uMap) {
    uMap->decRefCnt();
  }

  if (s2->getLength() > 0) {
    writePSString(s2);
    if (font->isCIDFont()) {
      if (wMode) {
	writePSFmt(" {0:d} {1:.4g} Tj16V\n", nChars, dy);
      } else {
	writePSFmt(" {0:d} {1:.4g} Tj16\n", nChars, dx);
      }
    } else {
      writePSFmt(" {0:.4g} Tj\n", dx);
    }
  }
  delete s2;

  if (state->getRender() & 4) {
    haveTextClip = gTrue;
  }
}

void PSOutputDev::endTextObject(GfxState *state) {
  if (haveTextClip) {
    writePS("Tclip\n");
    haveTextClip = gFalse;
  }
}

void PSOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
				int width, int height, GBool invert,
				GBool inlineImg) {
  int len;

  len = height * ((width + 7) / 8);
  switch (level) {
  case psLevel1:
  case psLevel1Sep:
    doImageL1(ref, NULL, invert, inlineImg, str, width, height, len);
    break;
  case psLevel2:
  case psLevel2Sep:
    doImageL2(ref, NULL, invert, inlineImg, str, width, height, len,
	      NULL, NULL, 0, 0, gFalse);
    break;
  case psLevel3:
  case psLevel3Sep:
    doImageL3(ref, NULL, invert, inlineImg, str, width, height, len,
	      NULL, NULL, 0, 0, gFalse);
    break;
  }
}

void PSOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
			    int width, int height, GfxImageColorMap *colorMap,
			    int *maskColors, GBool inlineImg) {
  int len;

  len = height * ((width * colorMap->getNumPixelComps() *
		   colorMap->getBits() + 7) / 8);
  switch (level) {
  case psLevel1:
    doImageL1(ref, colorMap, gFalse, inlineImg, str, width, height, len);
    break;
  case psLevel1Sep:
    //~ handle indexed, separation, ... color spaces
    doImageL1Sep(colorMap, gFalse, inlineImg, str, width, height, len);
    break;
  case psLevel2:
  case psLevel2Sep:
    doImageL2(ref, colorMap, gFalse, inlineImg, str,
	      width, height, len, maskColors, NULL, 0, 0, gFalse);
    break;
  case psLevel3:
  case psLevel3Sep:
    doImageL3(ref, colorMap, gFalse, inlineImg, str,
	      width, height, len, maskColors, NULL, 0, 0, gFalse);
    break;
  }
  t3Cacheable = gFalse;
}

void PSOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
				  int width, int height,
				  GfxImageColorMap *colorMap,
				  Stream *maskStr,
				  int maskWidth, int maskHeight,
				  GBool maskInvert) {
  int len;

  len = height * ((width * colorMap->getNumPixelComps() *
		   colorMap->getBits() + 7) / 8);
  switch (level) {
  case psLevel1:
    doImageL1(ref, colorMap, gFalse, gFalse, str, width, height, len);
    break;
  case psLevel1Sep:
    //~ handle indexed, separation, ... color spaces
    doImageL1Sep(colorMap, gFalse, gFalse, str, width, height, len);
    break;
  case psLevel2:
  case psLevel2Sep:
    doImageL2(ref, colorMap, gFalse, gFalse, str, width, height, len,
	      NULL, maskStr, maskWidth, maskHeight, maskInvert);
    break;
  case psLevel3:
  case psLevel3Sep:
    doImageL3(ref, colorMap, gFalse, gFalse, str, width, height, len,
	      NULL, maskStr, maskWidth, maskHeight, maskInvert);
    break;
  }
  t3Cacheable = gFalse;
}

void PSOutputDev::doImageL1(Object *ref, GfxImageColorMap *colorMap,
			    GBool invert, GBool inlineImg,
			    Stream *str, int width, int height, int len) {
  ImageStream *imgStr;
  Guchar pixBuf[gfxColorMaxComps];
  GfxGray gray;
  int col, x, y, c, i;

  if ((inType3Char || preload) && !colorMap) {
    if (inlineImg) {
      // create an array
      str = new FixedLengthEncoder(str, len);
      str = new ASCIIHexEncoder(str);
      str->reset();
      col = 0;
      writePS("[<");
      do {
	do {
	  c = str->getChar();
	} while (c == '\n' || c == '\r');
	if (c == '>' || c == EOF) {
	  break;
	}
	writePSChar(c);
	++col;
	// each line is: "<...data...><eol>"
	// so max data length = 255 - 4 = 251
	// but make it 240 just to be safe
	// chunks are 2 bytes each, so we need to stop on an even col number
	if (col == 240) {
	  writePS(">\n<");
	  col = 0;
	}
      } while (c != '>' && c != EOF);
      writePS(">]\n");
      writePS("0\n");
      str->close();
      delete str;
    } else {
      // set up to use the array already created by setupImages()
      writePSFmt("ImData_{0:d}_{1:d} 0\n", ref->getRefNum(), ref->getRefGen());
    }
  }

  // image/imagemask command
  if ((inType3Char || preload) && !colorMap) {
    writePSFmt("{0:d} {1:d} {2:s} [{3:d} 0 0 {4:d} 0 {5:d}] pdfImM1a\n",
	       width, height, invert ? "true" : "false",
	       width, -height, height);
  } else if (colorMap) {
    writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1\n",
	       width, height,
	       width, -height, height);
  } else {
    writePSFmt("{0:d} {1:d} {2:s} [{3:d} 0 0 {4:d} 0 {5:d}] pdfImM1\n",
	       width, height, invert ? "true" : "false",
	       width, -height, height);
  }

  // image data
  if (!((inType3Char || preload) && !colorMap)) {

    if (colorMap) {

      // set up to process the data stream
      imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
			       colorMap->getBits());
      imgStr->reset();

      // process the data stream
      i = 0;
      for (y = 0; y < height; ++y) {

	// write the line
	for (x = 0; x < width; ++x) {
	  imgStr->getPixel(pixBuf);
	  colorMap->getGray(pixBuf, &gray);
	  writePSFmt("{0:02x}", colToByte(gray));
	  if (++i == 32) {
	    writePSChar('\n');
	    i = 0;
	  }
	}
      }
      if (i != 0) {
	writePSChar('\n');
      }
      str->close();
      delete imgStr;

    // imagemask
    } else {
      str->reset();
      i = 0;
      for (y = 0; y < height; ++y) {
	for (x = 0; x < width; x += 8) {
	  writePSFmt("{0:02x}", str->getChar() & 0xff);
	  if (++i == 32) {
	    writePSChar('\n');
	    i = 0;
	  }
	}
      }
      if (i != 0) {
	writePSChar('\n');
      }
      str->close();
    }
  }
}

void PSOutputDev::doImageL1Sep(GfxImageColorMap *colorMap,
			       GBool invert, GBool inlineImg,
			       Stream *str, int width, int height, int len) {
  ImageStream *imgStr;
  Guchar *lineBuf;
  Guchar pixBuf[gfxColorMaxComps];
  GfxCMYK cmyk;
  int x, y, i, comp;

  // width, height, matrix, bits per component
  writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1Sep\n",
	     width, height,
	     width, -height, height);

  // allocate a line buffer
  lineBuf = (Guchar *)gmalloc(4 * width);

  // set up to process the data stream
  imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
			   colorMap->getBits());
  imgStr->reset();

  // process the data stream
  i = 0;
  for (y = 0; y < height; ++y) {

    // read the line
    for (x = 0; x < width; ++x) {
      imgStr->getPixel(pixBuf);
      colorMap->getCMYK(pixBuf, &cmyk);
      lineBuf[4*x+0] = colToByte(cmyk.c);
      lineBuf[4*x+1] = colToByte(cmyk.m);
      lineBuf[4*x+2] = colToByte(cmyk.y);
      lineBuf[4*x+3] = colToByte(cmyk.k);
      addProcessColor(colToDbl(cmyk.c), colToDbl(cmyk.m),
		      colToDbl(cmyk.y), colToDbl(cmyk.k));
    }

    // write one line of each color component
    for (comp = 0; comp < 4; ++comp) {
      for (x = 0; x < width; ++x) {
	writePSFmt("{0:02x}", lineBuf[4*x + comp]);
	if (++i == 32) {
	  writePSChar('\n');
	  i = 0;
	}
      }
    }
  }

  if (i != 0) {
    writePSChar('\n');
  }

  str->close();
  delete imgStr;
  gfree(lineBuf);
}

void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
			    GBool invert, GBool inlineImg,
			    Stream *str, int width, int height, int len,
			    int *maskColors, Stream *maskStr,
			    int maskWidth, int maskHeight, GBool maskInvert) {
  Stream *str2;
  ImageStream *imgStr;
  Guchar *line;
  PSOutImgClipRect *rects0, *rects1, *rectsTmp, *rectsOut;
  int rects0Len, rects1Len, rectsSize, rectsOutLen, rectsOutSize;
  GBool emitRect, addRect, extendRect;
  GString *s;
  int n, numComps;
  GBool useRLE, useASCII, useASCIIHex, useCompressed;
  GfxSeparationColorSpace *sepCS;
  GfxColor color;
  GfxCMYK cmyk;
  int c;
  int col, i, j, x0, x1, y, maskXor;

  // color key masking
  if (maskColors && colorMap && !inlineImg) {
    // can't read the stream twice for inline images -- but masking
    // isn't allowed with inline images anyway
    numComps = colorMap->getNumPixelComps();
    imgStr = new ImageStream(str, width, numComps, colorMap->getBits());
    imgStr->reset();
    rects0Len = rects1Len = rectsOutLen = 0;
    rectsSize = rectsOutSize = 64;
    rects0 = (PSOutImgClipRect *)gmallocn(rectsSize, sizeof(PSOutImgClipRect));
    rects1 = (PSOutImgClipRect *)gmallocn(rectsSize, sizeof(PSOutImgClipRect));
    rectsOut = (PSOutImgClipRect *)gmallocn(rectsOutSize,
					    sizeof(PSOutImgClipRect));
    for (y = 0; y < height; ++y) {
      if (!(line = imgStr->getLine())) {
	break;
      }
      i = 0;
      rects1Len = 0;
      for (x0 = 0; x0 < width; ++x0) {
	for (j = 0; j < numComps; ++j) {
	  if (line[x0*numComps+j] < maskColors[2*j] ||
	      line[x0*numComps+j] > maskColors[2*j+1]) {
	    break;
	  }
	}
	if (j < numComps) {
	  break;
	}
      }
      for (x1 = x0; x1 < width; ++x1) {
	for (j = 0; j < numComps; ++j) {
	  if (line[x1*numComps+j] < maskColors[2*j] ||
	      line[x1*numComps+j] > maskColors[2*j+1]) {
	    break;
	  }
	}
	if (j == numComps) {
	  break;
	}
      }
      while (x0 < width || i < rects0Len) {
	emitRect = addRect = extendRect = gFalse;
	if (x0 >= width) {
	  emitRect = gTrue;
	} else if (i >= rects0Len) {
	  addRect = gTrue;
	} else if (rects0[i].x0 < x0) {
	  emitRect = gTrue;
	} else if (x0 < rects0[i].x0) {
	  addRect = gTrue;
	} else if (rects0[i].x1 == x1) {
	  extendRect = gTrue;
	} else {
	  emitRect = addRect = gTrue;
	}
	if (emitRect) {
	  if (rectsOutLen == rectsOutSize) {
	    rectsOutSize *= 2;
	    rectsOut = (PSOutImgClipRect *)greallocn(rectsOut, rectsOutSize,
						     sizeof(PSOutImgClipRect));
	  }
	  rectsOut[rectsOutLen].x0 = rects0[i].x0;
	  rectsOut[rectsOutLen].x1 = rects0[i].x1;
	  rectsOut[rectsOutLen].y0 = height - y - 1;
	  rectsOut[rectsOutLen].y1 = height - rects0[i].y0 - 1;
	  ++rectsOutLen;
	  ++i;
	}
	if (addRect || extendRect) {
	  if (rects1Len == rectsSize) {
	    rectsSize *= 2;
	    rects0 = (PSOutImgClipRect *)greallocn(rects0, rectsSize,
						   sizeof(PSOutImgClipRect));
	    rects1 = (PSOutImgClipRect *)greallocn(rects1, rectsSize,
						   sizeof(PSOutImgClipRect));
	  }
	  rects1[rects1Len].x0 = x0;
	  rects1[rects1Len].x1 = x1;
	  if (addRect) {
	    rects1[rects1Len].y0 = y;
	  }
	  if (extendRect) {
	    rects1[rects1Len].y0 = rects0[i].y0;
	    ++i;
	  }
	  ++rects1Len;
	  for (x0 = x1; x0 < width; ++x0) {
	    for (j = 0; j < numComps; ++j) {
	      if (line[x0*numComps+j] < maskColors[2*j] ||
		  line[x0*numComps+j] > maskColors[2*j+1]) {
		break;
	      }
	    }
	    if (j < numComps) {
	      break;
	    }
	  }
	  for (x1 = x0; x1 < width; ++x1) {
	    for (j = 0; j < numComps; ++j) {
	      if (line[x1*numComps+j] < maskColors[2*j] ||
		  line[x1*numComps+j] > maskColors[2*j+1]) {
		break;
	      }
	    }
	    if (j == numComps) {
	      break;
	    }
	  }
	}
      }
      rectsTmp = rects0;
      rects0 = rects1;
      rects1 = rectsTmp;
      i = rects0Len;
      rects0Len = rects1Len;
      rects1Len = i;
    }
    for (i = 0; i < rects0Len; ++i) {
      if (rectsOutLen == rectsOutSize) {
	rectsOutSize *= 2;
	rectsOut = (PSOutImgClipRect *)greallocn(rectsOut, rectsOutSize,
						 sizeof(PSOutImgClipRect));
      }
      rectsOut[rectsOutLen].x0 = rects0[i].x0;
      rectsOut[rectsOutLen].x1 = rects0[i].x1;
      rectsOut[rectsOutLen].y0 = height - y - 1;
      rectsOut[rectsOutLen].y1 = height - rects0[i].y0 - 1;
      ++rectsOutLen;
    }
    writePSFmt("{0:d} array 0\n", rectsOutLen * 4);
    for (i = 0; i < rectsOutLen; ++i) {
      writePSFmt("[{0:d} {1:d} {2:d} {3:d}] pr\n",
		 rectsOut[i].x0, rectsOut[i].y0,
		 rectsOut[i].x1 - rectsOut[i].x0,
		 rectsOut[i].y1 - rectsOut[i].y0);
    }
    writePSFmt("pop {0:d} {1:d} pdfImClip\n", width, height);
    gfree(rectsOut);
    gfree(rects0);
    gfree(rects1);
    delete imgStr;
    str->close();

  // explicit masking
  } else if (maskStr) {
    imgStr = new ImageStream(maskStr, maskWidth, 1, 1);
    imgStr->reset();
    rects0Len = rects1Len = rectsOutLen = 0;
    rectsSize = rectsOutSize = 64;
    rects0 = (PSOutImgClipRect *)gmallocn(rectsSize, sizeof(PSOutImgClipRect));
    rects1 = (PSOutImgClipRect *)gmallocn(rectsSize, sizeof(PSOutImgClipRect));
    rectsOut = (PSOutImgClipRect *)gmallocn(rectsOutSize,
					    sizeof(PSOutImgClipRect));
    maskXor = maskInvert ? 1 : 0;
    for (y = 0; y < maskHeight; ++y) {
      if (!(line = imgStr->getLine())) {
	break;
      }
      i = 0;
      rects1Len = 0;
      for (x0 = 0; x0 < maskWidth && (line[x0] ^ maskXor); ++x0) ;
      for (x1 = x0; x1 < maskWidth && !(line[x1] ^ maskXor); ++x1) ;
      while (x0 < maskWidth || i < rects0Len) {
	emitRect = addRect = extendRect = gFalse;
	if (x0 >= maskWidth) {
	  emitRect = gTrue;
	} else if (i >= rects0Len) {
	  addRect = gTrue;
	} else if (rects0[i].x0 < x0) {
	  emitRect = gTrue;
	} else if (x0 < rects0[i].x0) {
	  addRect = gTrue;
	} else if (rects0[i].x1 == x1) {
	  extendRect = gTrue;
	} else {
	  emitRect = addRect = gTrue;
	}
	if (emitRect) {
	  if (rectsOutLen == rectsOutSize) {
	    rectsOutSize *= 2;
	    rectsOut = (PSOutImgClipRect *)greallocn(rectsOut, rectsOutSize,
						     sizeof(PSOutImgClipRect));
	  }
	  rectsOut[rectsOutLen].x0 = rects0[i].x0;
	  rectsOut[rectsOutLen].x1 = rects0[i].x1;
	  rectsOut[rectsOutLen].y0 = maskHeight - y - 1;
	  rectsOut[rectsOutLen].y1 = maskHeight - rects0[i].y0 - 1;
	  ++rectsOutLen;
	  ++i;
	}
	if (addRect || extendRect) {
	  if (rects1Len == rectsSize) {
	    rectsSize *= 2;
	    rects0 = (PSOutImgClipRect *)greallocn(rects0, rectsSize,
						   sizeof(PSOutImgClipRect));
	    rects1 = (PSOutImgClipRect *)greallocn(rects1, rectsSize,
						   sizeof(PSOutImgClipRect));
	  }
	  rects1[rects1Len].x0 = x0;
	  rects1[rects1Len].x1 = x1;
	  if (addRect) {
	    rects1[rects1Len].y0 = y;
	  }
	  if (extendRect) {
	    rects1[rects1Len].y0 = rects0[i].y0;
	    ++i;
	  }
	  ++rects1Len;
	  for (x0 = x1; x0 < maskWidth && (line[x0] ^ maskXor); ++x0) ;
	  for (x1 = x0; x1 < maskWidth && !(line[x1] ^ maskXor); ++x1) ;
	}
      }
      rectsTmp = rects0;
      rects0 = rects1;
      rects1 = rectsTmp;
      i = rects0Len;
      rects0Len = rects1Len;
      rects1Len = i;
    }
    for (i = 0; i < rects0Len; ++i) {
      if (rectsOutLen == rectsOutSize) {
	rectsOutSize *= 2;
	rectsOut = (PSOutImgClipRect *)greallocn(rectsOut, rectsOutSize,
						 sizeof(PSOutImgClipRect));
      }
      rectsOut[rectsOutLen].x0 = rects0[i].x0;
      rectsOut[rectsOutLen].x1 = rects0[i].x1;
      rectsOut[rectsOutLen].y0 = maskHeight - y - 1;
      rectsOut[rectsOutLen].y1 = maskHeight - rects0[i].y0 - 1;
      ++rectsOutLen;
    }
    writePSFmt("{0:d} array 0\n", rectsOutLen * 4);
    for (i = 0; i < rectsOutLen; ++i) {
      writePSFmt("[{0:d} {1:d} {2:d} {3:d}] pr\n",
		 rectsOut[i].x0, rectsOut[i].y0,
		 rectsOut[i].x1 - rectsOut[i].x0,
		 rectsOut[i].y1 - rectsOut[i].y0);
    }
    writePSFmt("pop {0:d} {1:d} pdfImClip\n", maskWidth, maskHeight);
    gfree(rectsOut);
    gfree(rects0);
    gfree(rects1);
    delete imgStr;
    maskStr->close();
  }

  // color space
  if (colorMap) {
    dumpColorSpaceL2(colorMap->getColorSpace(), gFalse, gTrue, gFalse);
    writePS(" setcolorspace\n");
  }

  useASCIIHex = globalParams->getPSASCIIHex();

  // set up the image data
  if (mode == psModeForm || inType3Char || preload) {
    if (inlineImg) {
      // create an array
      str2 = new FixedLengthEncoder(str, len);
      str2 = new RunLengthEncoder(str2);
      if (useASCIIHex) {
	str2 = new ASCIIHexEncoder(str2);
      } else {
	str2 = new ASCII85Encoder(str2);
      }
      str2->reset();
      col = 0;
      writePS((char *)(useASCIIHex ? "[<" : "[<~"));
      do {
	do {
	  c = str2->getChar();
	} while (c == '\n' || c == '\r');
	if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
	  break;
	}
	if (c == 'z') {
	  writePSChar(c);
	  ++col;
	} else {
	  writePSChar(c);
	  ++col;
	  for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) {
	    do {
	      c = str2->getChar();
	    } while (c == '\n' || c == '\r');
	    if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
	      break;
	    }
	    writePSChar(c);
	    ++col;
	  }
	}
	// each line is: "<~...data...~><eol>"
	// so max data length = 255 - 6 = 249
	// chunks are 1 or 5 bytes each, so we have to stop at 245
	// but make it 240 just to be safe
	if (col > 240) {
	  writePS((char *)(useASCIIHex ? ">\n<" : "~>\n<~"));
	  col = 0;
	}
      } while (c != (useASCIIHex ? '>' : '~') && c != EOF);
      writePS((char *)(useASCIIHex ? ">\n" : "~>\n"));
      // add an extra entry because the RunLengthDecode filter may
      // read past the end
      writePS("<>]\n");
      writePS("0\n");
      str2->close();
      delete str2;
    } else {
      // set up to use the array already created by setupImages()
      writePSFmt("ImData_{0:d}_{1:d} 0\n", ref->getRefNum(), ref->getRefGen());
    }
  }

  // image dictionary
  writePS("<<\n  /ImageType 1\n");

  // width, height, matrix, bits per component
  writePSFmt("  /Width {0:d}\n", width);
  writePSFmt("  /Height {0:d}\n", height);
  writePSFmt("  /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n",
	     width, -height, height);
  if (colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) {
    writePS("  /BitsPerComponent 8\n");
  } else {
    writePSFmt("  /BitsPerComponent {0:d}\n",
	       colorMap ? colorMap->getBits() : 1);
  }

  // decode 
  if (colorMap) {
    writePS("  /Decode [");
    if ((level == psLevel2Sep || level == psLevel3Sep) &&
	colorMap->getColorSpace()->getMode() == csSeparation) {
      // this matches up with the code in the pdfImSep operator
      n = (1 << colorMap->getBits()) - 1;
      writePSFmt("{0:.4g} {1:.4g}", colorMap->getDecodeLow(0) * n,
		 colorMap->getDecodeHigh(0) * n);
    } else if (colorMap->getColorSpace()->getMode() == csDeviceN) {
      numComps = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())->
	           getAlt()->getNComps();
      for (i = 0; i < numComps; ++i) {
	if (i > 0) {
	  writePS(" ");
	}
	writePS("0 1");
      }
    } else {
      numComps = colorMap->getNumPixelComps();
      for (i = 0; i < numComps; ++i) {
	if (i > 0) {
	  writePS(" ");
	}
	writePSFmt("{0:.4g} {1:.4g}",
		   colorMap->getDecodeLow(i), colorMap->getDecodeHigh(i));
      }
    }
    writePS("]\n");
  } else {
    writePSFmt("  /Decode [{0:d} {1:d}]\n", invert ? 1 : 0, invert ? 0 : 1);
  }

  // data source
  if (mode == psModeForm || inType3Char || preload) {
    writePS("  /DataSource { 2 copy get exch 1 add exch }\n");
  } else {
    writePS("  /DataSource currentfile\n");
  }

  // filters
  s = str->getPSFilter(level < psLevel2 ? 1 : level < psLevel3 ? 2 : 3,
		       "    ");
  if ((colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) ||
      inlineImg || !s) {
    useRLE = gTrue;
    useASCII = !(mode == psModeForm || inType3Char || preload);
    useCompressed = gFalse;
  } else {
    useRLE = gFalse;
    useASCII = str->isBinary() &&
               !(mode == psModeForm || inType3Char || preload);
    useCompressed = gTrue;
  }
  if (useASCII) {
    writePSFmt("    /ASCII{0:s}Decode filter\n",
	       useASCIIHex ? "Hex" : "85");
  }
  if (useRLE) {
    writePS("    /RunLengthDecode filter\n");
  }
  if (useCompressed) {
    writePS(s->getCString());
  }
  if (s) {
    delete s;
  }

  if (mode == psModeForm || inType3Char || preload) {

    // end of image dictionary
    writePSFmt(">>\n{0:s}\n", colorMap ? "image" : "imagemask");

    // get rid of the array and index
    writePS("pop pop\n");

  } else {

    // cut off inline image streams at appropriate length
    if (inlineImg) {
      str = new FixedLengthEncoder(str, len);
    } else if (useCompressed) {
      str = str->getUndecodedStream();
    }

    // recode DeviceN data
    if (colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) {
      str = new DeviceNRecoder(str, width, height, colorMap);
    }

    // add RunLengthEncode and ASCIIHex/85 encode filters
    if (useRLE) {
      str = new RunLengthEncoder(str);
    }
    if (useASCII) {
      if (useASCIIHex) {
	str = new ASCIIHexEncoder(str);
      } else {
	str = new ASCII85Encoder(str);
      }
    }

    // end of image dictionary
    writePS(">>\n");
#if OPI_SUPPORT
    if (opi13Nest) {
      if (inlineImg) {
	// this can't happen -- OPI dictionaries are in XObjects
	error(-1, "Internal: OPI in inline image");
	n = 0;
      } else {
	// need to read the stream to count characters -- the length
	// is data-dependent (because of ASCII and RLE filters)
	str->reset();
	n = 0;
	while ((c = str->getChar()) != EOF) {
	  ++n;
	}
	str->close();
      }
      // +6/7 for "pdfIm\n" / "pdfImM\n"
      // +8 for newline + trailer
      n += colorMap ? 14 : 15;
      writePSFmt("%%BeginData: {0:d} Hex Bytes\n", n);
    }
#endif
    if ((level == psLevel2Sep || level == psLevel3Sep) && colorMap &&
	colorMap->getColorSpace()->getMode() == csSeparation) {
      color.c[0] = gfxColorComp1;
      sepCS = (GfxSeparationColorSpace *)colorMap->getColorSpace();
      sepCS->getCMYK(&color, &cmyk);
      writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} ({4:t}) pdfImSep\n",
		 colToDbl(cmyk.c), colToDbl(cmyk.m),
		 colToDbl(cmyk.y), colToDbl(cmyk.k),
		 sepCS->getName());
    } else {
      writePSFmt("{0:s}\n", colorMap ? "pdfIm" : "pdfImM");
    }

    // copy the stream data
    str->reset();
    while ((c = str->getChar()) != EOF) {
      writePSChar(c);
    }
    str->close();

    // add newline and trailer to the end
    writePSChar('\n');
    writePS("%-EOD-\n");
#if OPI_SUPPORT
    if (opi13Nest) {
      writePS("%%EndData\n");
    }
#endif

    // delete encoders
    if (useRLE || useASCII || inlineImg) {
      delete str;
    }
  }

  if ((maskColors && colorMap && !inlineImg) || maskStr) {
    writePS("pdfImClipEnd\n");
  }
}

//~ this doesn't currently support OPI
//~ this doesn't currently support OPI
void PSOutputDev::doImageL3(Object *ref, GfxImageColorMap *colorMap,
			    GBool invert, GBool inlineImg,
			    Stream *str, int width, int height, int len,
			    int *maskColors, Stream *maskStr,
			    int maskWidth, int maskHeight, GBool maskInvert) {
  Stream *str2;
  GString *s;
  int n, numComps;
  GBool useRLE, useASCII, useASCIIHex, useCompressed;
  GBool maskUseRLE, maskUseASCII, maskUseCompressed;
  GfxSeparationColorSpace *sepCS;
  GfxColor color;
  GfxCMYK cmyk;
  int c;
  int col, i;

  useASCIIHex = globalParams->getPSASCIIHex();
  useRLE = useASCII = useCompressed = gFalse; // make gcc happy
  maskUseRLE = maskUseASCII = maskUseCompressed = gFalse; // make gcc happy

  // color space
  if (colorMap) {
    dumpColorSpaceL2(colorMap->getColorSpace(), gFalse, gTrue, gFalse);
    writePS(" setcolorspace\n");
  }

  // set up the image data
  if (mode == psModeForm || inType3Char || preload) {
    if (inlineImg) {
      // create an array
      str2 = new FixedLengthEncoder(str, len);
      str2 = new RunLengthEncoder(str2);
      if (useASCIIHex) {
	str2 = new ASCIIHexEncoder(str2);
      } else {
	str2 = new ASCII85Encoder(str2);
      }
      str2->reset();
      col = 0;
      writePS((char *)(useASCIIHex ? "[<" : "[<~"));
      do {
	do {
	  c = str2->getChar();
	} while (c == '\n' || c == '\r');
	if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
	  break;
	}
	if (c == 'z') {
	  writePSChar(c);
	  ++col;
	} else {
	  writePSChar(c);
	  ++col;
	  for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) {
	    do {
	      c = str2->getChar();
	    } while (c == '\n' || c == '\r');
	    if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
	      break;
	    }
	    writePSChar(c);
	    ++col;
	  }
	}
	// each line is: "<~...data...~><eol>"
	// so max data length = 255 - 6 = 249
	// chunks are 1 or 5 bytes each, so we have to stop at 245
	// but make it 240 just to be safe
	if (col > 240) {
	  writePS((char *)(useASCIIHex ? ">\n<" : "~>\n<~"));
	  col = 0;
	}
      } while (c != (useASCIIHex ? '>' : '~') && c != EOF);
      writePS((char *)(useASCIIHex ? ">\n" : "~>\n"));
      // add an extra entry because the RunLengthDecode filter may
      // read past the end
      writePS("<>]\n");
      writePS("0\n");
      str2->close();
      delete str2;
    } else {
      // set up to use the array already created by setupImages()
      writePSFmt("ImData_{0:d}_{1:d} 0\n", ref->getRefNum(), ref->getRefGen());
    }
  }

  // explicit masking
  if (maskStr) {
    writePS("<<\n  /ImageType 3\n");
    writePS("  /InterleaveType 3\n");
    writePS("  /DataDict\n");
  }

  // image (data) dictionary
  writePSFmt("<<\n  /ImageType {0:d}\n", (maskColors && colorMap) ? 4 : 1);

  // color key masking
  if (maskColors && colorMap) {
    writePS("  /MaskColor [\n");
    numComps = colorMap->getNumPixelComps();
    for (i = 0; i < 2 * numComps; i += 2) {
      writePSFmt("    {0:d} {1:d}\n", maskColors[i], maskColors[i+1]);
    }
    writePS("  ]\n");
  }

  // width, height, matrix, bits per component
  writePSFmt("  /Width {0:d}\n", width);
  writePSFmt("  /Height {0:d}\n", height);
  writePSFmt("  /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n",
	     width, -height, height);
  if (colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) {
    writePS("  /BitsPerComponent 8\n");
  } else {
    writePSFmt("  /BitsPerComponent {0:d}\n",
	       colorMap ? colorMap->getBits() : 1);
  }

  // decode 
  if (colorMap) {
    writePS("  /Decode [");
    if ((level == psLevel2Sep || level == psLevel3Sep) &&
	colorMap->getColorSpace()->getMode() == csSeparation) {
      // this matches up with the code in the pdfImSep operator
      n = (1 << colorMap->getBits()) - 1;
      writePSFmt("{0:.4g} {1:.4g}", colorMap->getDecodeLow(0) * n,
		 colorMap->getDecodeHigh(0) * n);
    } else if (colorMap->getColorSpace()->getMode() == csDeviceN) {
      numComps = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())->
	           getAlt()->getNComps();
      for (i = 0; i < numComps; ++i) {
	if (i > 0) {
	  writePS(" ");
	}
	writePS("0 1");
      }
    } else {
      numComps = colorMap->getNumPixelComps();
      for (i = 0; i < numComps; ++i) {
	if (i > 0) {
	  writePS(" ");
	}
	writePSFmt("{0:.4g} {1:.4g}", colorMap->getDecodeLow(i),
		   colorMap->getDecodeHigh(i));
      }
    }
    writePS("]\n");
  } else {
    writePSFmt("  /Decode [{0:d} {1:d}]\n", invert ? 1 : 0, invert ? 0 : 1);
  }

  // data source
  if (mode == psModeForm || inType3Char || preload) {
    writePS("  /DataSource { 2 copy get exch 1 add exch }\n");
  } else {
    writePS("  /DataSource currentfile\n");
  }

  // filters
  s = str->getPSFilter(level < psLevel2 ? 1 : level < psLevel3 ? 2 : 3,
		       "    ");
  if ((colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) ||
      inlineImg || !s) {
    useRLE = gTrue;
    useASCII = !(mode == psModeForm || inType3Char || preload);
    useCompressed = gFalse;
  } else {
    useRLE = gFalse;
    useASCII = str->isBinary() &&
               !(mode == psModeForm || inType3Char || preload);
    useCompressed = gTrue;
  }
  if (useASCII) {
    writePSFmt("    /ASCII{0:s}Decode filter\n",
	       useASCIIHex ? "Hex" : "85");
  }
  if (useRLE) {
    writePS("    /RunLengthDecode filter\n");
  }
  if (useCompressed) {
    writePS(s->getCString());
  }
  if (s) {
    delete s;
  }

  // end of image (data) dictionary
  writePS(">>\n");

  // explicit masking
  if (maskStr) {
    writePS("  /MaskDict\n");
    writePS("<<\n");
    writePS("  /ImageType 1\n");
    writePSFmt("  /Width {0:d}\n", maskWidth);
    writePSFmt("  /Height {0:d}\n", maskHeight);
    writePSFmt("  /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n",
	       maskWidth, -maskHeight, maskHeight);
    writePS("  /BitsPerComponent 1\n");
    writePSFmt("  /Decode [{0:d} {1:d}]\n",
	       maskInvert ? 1 : 0, maskInvert ? 0 : 1);

    // mask data source
    writePS("  /DataSource currentfile\n");
    s = maskStr->getPSFilter(3, "    ");
    if (!s) {
      maskUseRLE = gTrue;
      maskUseASCII = gTrue;
      maskUseCompressed = gFalse;
    } else {
      maskUseRLE = gFalse;
      maskUseASCII = maskStr->isBinary();
      maskUseCompressed = gTrue;
    }
    if (maskUseASCII) {
      writePSFmt("    /ASCII{0:s}Decode filter\n",
		 useASCIIHex ? "Hex" : "85");
    }
    if (maskUseRLE) {
      writePS("    /RunLengthDecode filter\n");
    }
    if (maskUseCompressed) {
      writePS(s->getCString());
    }
    if (s) {
      delete s;
    }

    writePS(">>\n");
    writePS(">>\n");
  }

  if (mode == psModeForm || inType3Char || preload) {

    // image command
    writePSFmt("{0:s}\n", colorMap ? "image" : "imagemask");

  } else {

    if ((level == psLevel2Sep || level == psLevel3Sep) && colorMap &&
	colorMap->getColorSpace()->getMode() == csSeparation) {
      color.c[0] = gfxColorComp1;
      sepCS = (GfxSeparationColorSpace *)colorMap->getColorSpace();
      sepCS->getCMYK(&color, &cmyk);
      writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} ({4:t}) pdfImSep\n",
		 colToDbl(cmyk.c), colToDbl(cmyk.m),
		 colToDbl(cmyk.y), colToDbl(cmyk.k),
		 sepCS->getName());
    } else {
      writePSFmt("{0:s}\n", colorMap ? "pdfIm" : "pdfImM");
    }

  }

  // explicit masking
  if (maskStr) {

    if (maskUseCompressed) {
      maskStr = maskStr->getUndecodedStream();
    }

    // add RunLengthEncode and ASCIIHex/85 encode filters
    if (maskUseRLE) {
      maskStr = new RunLengthEncoder(maskStr);
    }
    if (maskUseASCII) {
      if (useASCIIHex) {
	maskStr = new ASCIIHexEncoder(maskStr);
      } else {
	maskStr = new ASCII85Encoder(maskStr);
      }
    }

    // copy the stream data
    maskStr->reset();
    while ((c = maskStr->getChar()) != EOF) {
      writePSChar(c);
    }
    maskStr->close();
    writePSChar('\n');

    // delete encoders
    if (maskUseRLE || maskUseASCII) {
      delete maskStr;
    }
  }

  // get rid of the array and index
  if (mode == psModeForm || inType3Char || preload) {
    writePS("pop pop\n");

  // image data
  } else {

    // cut off inline image streams at appropriate length
    if (inlineImg) {
      str = new FixedLengthEncoder(str, len);
    } else if (useCompressed) {
      str = str->getUndecodedStream();
    }

    // recode DeviceN data
    if (colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) {
      str = new DeviceNRecoder(str, width, height, colorMap);
    }

    // add RunLengthEncode and ASCIIHex/85 encode filters
    if (useRLE) {
      str = new RunLengthEncoder(str);
    }
    if (useASCII) {
      if (useASCIIHex) {
	str = new ASCIIHexEncoder(str);
      } else {
	str = new ASCII85Encoder(str);
      }
    }

    // copy the stream data
    str->reset();
    while ((c = str->getChar()) != EOF) {
      writePSChar(c);
    }
    str->close();

    // add newline and trailer to the end
    writePSChar('\n');
    writePS("%-EOD-\n");

    // delete encoders
    if (useRLE || useASCII || inlineImg) {
      delete str;
    }
  }
}

void PSOutputDev::dumpColorSpaceL2(GfxColorSpace *colorSpace,
				   GBool genXform, GBool updateColors,
				   GBool map01) {
  GfxCalGrayColorSpace *calGrayCS;
  GfxCalRGBColorSpace *calRGBCS;
  GfxLabColorSpace *labCS;
  GfxIndexedColorSpace *indexedCS;
  GfxSeparationColorSpace *separationCS;
  GfxDeviceNColorSpace *deviceNCS;
  GfxColorSpace *baseCS;
  Guchar *lookup, *p;
  double x[gfxColorMaxComps], y[gfxColorMaxComps];
  double low[gfxColorMaxComps], range[gfxColorMaxComps];
  GfxColor color;
  GfxCMYK cmyk;
  Function *func;
  int n, numComps, numAltComps;
  int byte;
  int i, j, k;

  switch (colorSpace->getMode()) {

  case csDeviceGray:
    writePS("/DeviceGray");
    if (genXform) {
      writePS(" {}");
    }
    if (updateColors) {
      processColors |= psProcessBlack;
    }
    break;

  case csCalGray:
    calGrayCS = (GfxCalGrayColorSpace *)colorSpace;
    writePS("[/CIEBasedA <<\n");
    writePSFmt(" /DecodeA {{{0:.4g} exp}} bind\n", calGrayCS->getGamma());
    writePSFmt(" /MatrixA [{0:.4g} {1:.4g} {2:.4g}]\n",
	       calGrayCS->getWhiteX(), calGrayCS->getWhiteY(),
	       calGrayCS->getWhiteZ());
    writePSFmt(" /WhitePoint [{0:.4g} {1:.4g} {2:.4g}]\n",
	       calGrayCS->getWhiteX(), calGrayCS->getWhiteY(),
	       calGrayCS->getWhiteZ());
    writePSFmt(" /BlackPoint [{0:.4g} {1:.4g} {2:.4g}]\n",
	       calGrayCS->getBlackX(), calGrayCS->getBlackY(),
	       calGrayCS->getBlackZ());
    writePS(">>]");
    if (genXform) {
      writePS(" {}");
    }
    if (updateColors) {
      processColors |= psProcessBlack;
    }
    break;

  case csDeviceRGB:
    writePS("/DeviceRGB");
    if (genXform) {
      writePS(" {}");
    }
    if (updateColors) {
      processColors |= psProcessCMYK;
    }
    break;

  case csCalRGB:
    calRGBCS = (GfxCalRGBColorSpace *)colorSpace;
    writePS("[/CIEBasedABC <<\n");
    writePSFmt(" /DecodeABC [{{{0:.4g} exp}} bind {{{1:.4g} exp}} bind {{{2:.4g} exp}} bind]\n",
	       calRGBCS->getGammaR(), calRGBCS->getGammaG(),
	       calRGBCS->getGammaB());
    writePSFmt(" /MatrixABC [{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g} {6:.4g} {7:.4g} {8:.4g}]\n",
	       calRGBCS->getMatrix()[0], calRGBCS->getMatrix()[1],
	       calRGBCS->getMatrix()[2], calRGBCS->getMatrix()[3],
	       calRGBCS->getMatrix()[4], calRGBCS->getMatrix()[5],
	       calRGBCS->getMatrix()[6], calRGBCS->getMatrix()[7],
	       calRGBCS->getMatrix()[8]);
    writePSFmt(" /WhitePoint [{0:.4g} {1:.4g} {2:.4g}]\n",
	       calRGBCS->getWhiteX(), calRGBCS->getWhiteY(),
	       calRGBCS->getWhiteZ());
    writePSFmt(" /BlackPoint [{0:.4g} {1:.4g} {2:.4g}]\n",
	       calRGBCS->getBlackX(), calRGBCS->getBlackY(),
	       calRGBCS->getBlackZ());
    writePS(">>]");
    if (genXform) {
      writePS(" {}");
    }
    if (updateColors) {
      processColors |= psProcessCMYK;
    }
    break;

  case csDeviceCMYK:
    writePS("/DeviceCMYK");
    if (genXform) {
      writePS(" {}");
    }
    if (updateColors) {
      processColors |= psProcessCMYK;
    }
    break;

  case csLab:
    labCS = (GfxLabColorSpace *)colorSpace;
    writePS("[/CIEBasedABC <<\n");
    if (map01) {
      writePS(" /RangeABC [0 1 0 1 0 1]\n");
      writePSFmt(" /DecodeABC [{{100 mul 16 add 116 div}} bind {{{0:.4g} mul {1:.4g} add}} bind {{{2:.4g} mul {3:.4g} add}} bind]\n",
		 (labCS->getAMax() - labCS->getAMin()) / 500.0,
		 labCS->getAMin() / 500.0,
		 (labCS->getBMax() - labCS->getBMin()) / 200.0,
		 labCS->getBMin() / 200.0);
    } else {
      writePSFmt(" /RangeABC [0 100 {0:.4g} {1:.4g} {2:.4g} {3:.4g}]\n",
		 labCS->getAMin(), labCS->getAMax(),
		 labCS->getBMin(), labCS->getBMax());
      writePS(" /DecodeABC [{16 add 116 div} bind {500 div} bind {200 div} bind]\n");
    }
    writePS(" /MatrixABC [1 1 1 1 0 0 0 0 -1]\n");
    writePS(" /DecodeLMN\n");
    writePS("   [{dup 6 29 div ge {dup dup mul mul}\n");
    writePSFmt("     {{4 29 div sub 108 841 div mul }} ifelse {0:.4g} mul}} bind\n",
	       labCS->getWhiteX());
    writePS("    {dup 6 29 div ge {dup dup mul mul}\n");
    writePSFmt("     {{4 29 div sub 108 841 div mul }} ifelse {0:.4g} mul}} bind\n",
	       labCS->getWhiteY());
    writePS("    {dup 6 29 div ge {dup dup mul mul}\n");
    writePSFmt("     {{4 29 div sub 108 841 div mul }} ifelse {0:.4g} mul}} bind]\n",
	       labCS->getWhiteZ());
    writePSFmt(" /WhitePoint [{0:.4g} {1:.4g} {2:.4g}]\n",
	       labCS->getWhiteX(), labCS->getWhiteY(), labCS->getWhiteZ());
    writePSFmt(" /BlackPoint [{0:.4g} {1:.4g} {2:.4g}]\n",
	       labCS->getBlackX(), labCS->getBlackY(), labCS->getBlackZ());
    writePS(">>]");
    if (genXform) {
      writePS(" {}");
    }
    if (updateColors) {
      processColors |= psProcessCMYK;
    }
    break;

  case csICCBased:
    // there is no transform function to the alternate color space, so
    // we can use it directly
    dumpColorSpaceL2(((GfxICCBasedColorSpace *)colorSpace)->getAlt(),
		     genXform, updateColors, gFalse);
    break;

  case csIndexed:
    indexedCS = (GfxIndexedColorSpace *)colorSpace;
    baseCS = indexedCS->getBase();
    writePS("[/Indexed ");
    dumpColorSpaceL2(baseCS, gFalse, gFalse, gTrue);
    n = indexedCS->getIndexHigh();
    numComps = baseCS->getNComps();
    lookup = indexedCS->getLookup();
    writePSFmt(" {0:d} <\n", n);
    if (baseCS->getMode() == csDeviceN) {
      func = ((GfxDeviceNColorSpace *)baseCS)->getTintTransformFunc();
      baseCS->getDefaultRanges(low, range, indexedCS->getIndexHigh());
      if (((GfxDeviceNColorSpace *)baseCS)->getAlt()->getMode() == csLab) {
	labCS = (GfxLabColorSpace *)((GfxDeviceNColorSpace *)baseCS)->getAlt();
      } else {
	labCS = NULL;
      }
      numAltComps = ((GfxDeviceNColorSpace *)baseCS)->getAlt()->getNComps();
      p = lookup;
      for (i = 0; i <= n; i += 8) {
	writePS("  ");
	for (j = i; j < i+8 && j <= n; ++j) {
	  for (k = 0; k < numComps; ++k) {
	    x[k] = low[k] + (*p++ / 255.0) * range[k];
	  }
	  func->transform(x, y);
	  if (labCS) {
	    y[0] /= 100.0;
	    y[1] = (y[1] - labCS->getAMin()) /
	           (labCS->getAMax() - labCS->getAMin());
	    y[2] = (y[2] - labCS->getBMin()) /
	           (labCS->getBMax() - labCS->getBMin());
	  }
	  for (k = 0; k < numAltComps; ++k) {
	    byte = (int)(y[k] * 255 + 0.5);
	    if (byte < 0) {
	      byte = 0;
	    } else if (byte > 255) {
	      byte = 255;
	    }
	    writePSFmt("{0:02x}", byte);
	  }
	  if (updateColors) {
	    color.c[0] = dblToCol(j);
	    indexedCS->getCMYK(&color, &cmyk);
	    addProcessColor(colToDbl(cmyk.c), colToDbl(cmyk.m),
			    colToDbl(cmyk.y), colToDbl(cmyk.k));
	  }
	}
	writePS("\n");
      }
    } else {
      for (i = 0; i <= n; i += 8) {
	writePS("  ");
	for (j = i; j < i+8 && j <= n; ++j) {
	  for (k = 0; k < numComps; ++k) {
	    writePSFmt("{0:02x}", lookup[j * numComps + k]);
	  }
	  if (updateColors) {
	    color.c[0] = dblToCol(j);
	    indexedCS->getCMYK(&color, &cmyk);
	    addProcessColor(colToDbl(cmyk.c), colToDbl(cmyk.m),
			    colToDbl(cmyk.y), colToDbl(cmyk.k));
	  }
	}
	writePS("\n");
      }
    }
    writePS(">]");
    if (genXform) {
      writePS(" {}");
    }
    break;

  case csSeparation:
    separationCS = (GfxSeparationColorSpace *)colorSpace;
    writePS("[/Separation ");
    writePSString(separationCS->getName());
    writePS(" ");
    dumpColorSpaceL2(separationCS->getAlt(), gFalse, gFalse, gFalse);
    writePS("\n");
    cvtFunction(separationCS->getFunc());
    writePS("]");
    if (genXform) {
      writePS(" {}");
    }
    if (updateColors) {
      addCustomColor(separationCS);
    }
    break;

  case csDeviceN:
    // DeviceN color spaces are a Level 3 PostScript feature.
    deviceNCS = (GfxDeviceNColorSpace *)colorSpace;
    dumpColorSpaceL2(deviceNCS->getAlt(), gFalse, updateColors, map01);
    if (genXform) {
      writePS(" ");
      cvtFunction(deviceNCS->getTintTransformFunc());
    }
    break;

  case csPattern:
    //~ unimplemented
    break;
  }
}

#if OPI_SUPPORT
void PSOutputDev::opiBegin(GfxState *state, Dict *opiDict) {
  Object dict;

  if (globalParams->getPSOPI()) {
    opiDict->lookup("2.0", &dict);
    if (dict.isDict()) {
      opiBegin20(state, dict.getDict());
      dict.free();
    } else {
      dict.free();
      opiDict->lookup("1.3", &dict);
      if (dict.isDict()) {
	opiBegin13(state, dict.getDict());
      }
      dict.free();
    }
  }
}

void PSOutputDev::opiBegin20(GfxState *state, Dict *dict) {
  Object obj1, obj2, obj3, obj4;
  double width, height, left, right, top, bottom;
  int w, h;
  int i;

  writePS("%%BeginOPI: 2.0\n");
  writePS("%%Distilled\n");

  dict->lookup("F", &obj1);
  if (getFileSpec(&obj1, &obj2)) {
    writePSFmt("%%ImageFileName: {0:t}\n", obj2.getString());
    obj2.free();
  }
  obj1.free();

  dict->lookup("MainImage", &obj1);
  if (obj1.isString()) {
    writePSFmt("%%MainImage: {0:t}\n", obj1.getString());
  }
  obj1.free();

  //~ ignoring 'Tags' entry
  //~ need to use writePSString() and deal with >255-char lines

  dict->lookup("Size", &obj1);
  if (obj1.isArray() && obj1.arrayGetLength() == 2) {
    obj1.arrayGet(0, &obj2);
    width = obj2.getNum();
    obj2.free();
    obj1.arrayGet(1, &obj2);
    height = obj2.getNum();
    obj2.free();
    writePSFmt("%%ImageDimensions: {0:.4g} {1:.4g}\n", width, height);
  }
  obj1.free();

  dict->lookup("CropRect", &obj1);
  if (obj1.isArray() && obj1.arrayGetLength() == 4) {
    obj1.arrayGet(0, &obj2);
    left = obj2.getNum();
    obj2.free();
    obj1.arrayGet(1, &obj2);
    top = obj2.getNum();
    obj2.free();
    obj1.arrayGet(2, &obj2);
    right = obj2.getNum();
    obj2.free();
    obj1.arrayGet(3, &obj2);
    bottom = obj2.getNum();
    obj2.free();
    writePSFmt("%%ImageCropRect: {0:.4g} {1:.4g} {2:.4g} {3:.4g}\n",
	       left, top, right, bottom);
  }
  obj1.free();

  dict->lookup("Overprint", &obj1);
  if (obj1.isBool()) {
    writePSFmt("%%ImageOverprint: {0:s}\n", obj1.getBool() ? "true" : "false");
  }
  obj1.free();

  dict->lookup("Inks", &obj1);
  if (obj1.isName()) {
    writePSFmt("%%ImageInks: {0:s}\n", obj1.getName());
  } else if (obj1.isArray() && obj1.arrayGetLength() >= 1) {
    obj1.arrayGet(0, &obj2);
    if (obj2.isName()) {
      writePSFmt("%%ImageInks: {0:s} {1:d}",
		 obj2.getName(), (obj1.arrayGetLength() - 1) / 2);
      for (i = 1; i+1 < obj1.arrayGetLength(); i += 2) {
	obj1.arrayGet(i, &obj3);
	obj1.arrayGet(i+1, &obj4);
	if (obj3.isString() && obj4.isNum()) {
	  writePS(" ");
	  writePSString(obj3.getString());
	  writePSFmt(" {0:.4g}", obj4.getNum());
	}
	obj3.free();
	obj4.free();
      }
      writePS("\n");
    }
    obj2.free();
  }
  obj1.free();

  writePS("gsave\n");

  writePS("%%BeginIncludedImage\n");

  dict->lookup("IncludedImageDimensions", &obj1);
  if (obj1.isArray() && obj1.arrayGetLength() == 2) {
    obj1.arrayGet(0, &obj2);
    w = obj2.getInt();
    obj2.free();
    obj1.arrayGet(1, &obj2);
    h = obj2.getInt();
    obj2.free();
    writePSFmt("%%IncludedImageDimensions: {0:d} {1:d}\n", w, h);
  }
  obj1.free();

  dict->lookup("IncludedImageQuality", &obj1);
  if (obj1.isNum()) {
    writePSFmt("%%IncludedImageQuality: {0:.4g}\n", obj1.getNum());
  }
  obj1.free();

  ++opi20Nest;
}

void PSOutputDev::opiBegin13(GfxState *state, Dict *dict) {
  Object obj1, obj2;
  int left, right, top, bottom, samples, bits, width, height;
  double c, m, y, k;
  double llx, lly, ulx, uly, urx, ury, lrx, lry;
  double tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry;
  double horiz, vert;
  int i, j;

  writePS("save\n");
  writePS("/opiMatrix2 matrix currentmatrix def\n");
  writePS("opiMatrix setmatrix\n");

  dict->lookup("F", &obj1);
  if (getFileSpec(&obj1, &obj2)) {
    writePSFmt("%ALDImageFileName: {0:t}\n", obj2.getString());
    obj2.free();
  }
  obj1.free();

  dict->lookup("CropRect", &obj1);
  if (obj1.isArray() && obj1.arrayGetLength() == 4) {
    obj1.arrayGet(0, &obj2);
    left = obj2.getInt();
    obj2.free();
    obj1.arrayGet(1, &obj2);
    top = obj2.getInt();
    obj2.free();
    obj1.arrayGet(2, &obj2);
    right = obj2.getInt();
    obj2.free();
    obj1.arrayGet(3, &obj2);
    bottom = obj2.getInt();
    obj2.free();
    writePSFmt("%ALDImageCropRect: {0:d} {1:d} {2:d} {3:d}\n",
	       left, top, right, bottom);
  }
  obj1.free();

  dict->lookup("Color", &obj1);
  if (obj1.isArray() && obj1.arrayGetLength() == 5) {
    obj1.arrayGet(0, &obj2);
    c = obj2.getNum();
    obj2.free();
    obj1.arrayGet(1, &obj2);
    m = obj2.getNum();
    obj2.free();
    obj1.arrayGet(2, &obj2);
    y = obj2.getNum();
    obj2.free();
    obj1.arrayGet(3, &obj2);
    k = obj2.getNum();
    obj2.free();
    obj1.arrayGet(4, &obj2);
    if (obj2.isString()) {
      writePSFmt("%ALDImageColor: {0:.4g} {1:.4g} {2:.4g} {3:.4g} ",
		 c, m, y, k);
      writePSString(obj2.getString());
      writePS("\n");
    }
    obj2.free();
  }
  obj1.free();

  dict->lookup("ColorType", &obj1);
  if (obj1.isName()) {
    writePSFmt("%ALDImageColorType: {0:s}\n", obj1.getName());
  }
  obj1.free();

  //~ ignores 'Comments' entry
  //~ need to handle multiple lines

  dict->lookup("CropFixed", &obj1);
  if (obj1.isArray()) {
    obj1.arrayGet(0, &obj2);
    ulx = obj2.getNum();
    obj2.free();
    obj1.arrayGet(1, &obj2);
    uly = obj2.getNum();
    obj2.free();
    obj1.arrayGet(2, &obj2);
    lrx = obj2.getNum();
    obj2.free();
    obj1.arrayGet(3, &obj2);
    lry = obj2.getNum();
    obj2.free();
    writePSFmt("%ALDImageCropFixed: {0:.4g} {1:.4g} {2:.4g} {3:.4g}\n",
	       ulx, uly, lrx, lry);
  }
  obj1.free();

  dict->lookup("GrayMap", &obj1);
  if (obj1.isArray()) {
    writePS("%ALDImageGrayMap:");
    for (i = 0; i < obj1.arrayGetLength(); i += 16) {
      if (i > 0) {
	writePS("\n%%+");
      }
      for (j = 0; j < 16 && i+j < obj1.arrayGetLength(); ++j) {
	obj1.arrayGet(i+j, &obj2);
	writePSFmt(" {0:d}", obj2.getInt());
	obj2.free();
      }
    }
    writePS("\n");
  }
  obj1.free();

  dict->lookup("ID", &obj1);
  if (obj1.isString()) {
    writePSFmt("%ALDImageID: {0:t}\n", obj1.getString());
  }
  obj1.free();

  dict->lookup("ImageType", &obj1);
  if (obj1.isArray() && obj1.arrayGetLength() == 2) {
    obj1.arrayGet(0, &obj2);
    samples = obj2.getInt();
    obj2.free();
    obj1.arrayGet(1, &obj2);
    bits = obj2.getInt();
    obj2.free();
    writePSFmt("%ALDImageType: {0:d} {1:d}\n", samples, bits);
  }
  obj1.free();

  dict->lookup("Overprint", &obj1);
  if (obj1.isBool()) {
    writePSFmt("%ALDImageOverprint: {0:s}\n",
	       obj1.getBool() ? "true" : "false");
  }
  obj1.free();

  dict->lookup("Position", &obj1);
  if (obj1.isArray() && obj1.arrayGetLength() == 8) {
    obj1.arrayGet(0, &obj2);
    llx = obj2.getNum();
    obj2.free();
    obj1.arrayGet(1, &obj2);
    lly = obj2.getNum();
    obj2.free();
    obj1.arrayGet(2, &obj2);
    ulx = obj2.getNum();
    obj2.free();
    obj1.arrayGet(3, &obj2);
    uly = obj2.getNum();
    obj2.free();
    obj1.arrayGet(4, &obj2);
    urx = obj2.getNum();
    obj2.free();
    obj1.arrayGet(5, &obj2);
    ury = obj2.getNum();
    obj2.free();
    obj1.arrayGet(6, &obj2);
    lrx = obj2.getNum();
    obj2.free();
    obj1.arrayGet(7, &obj2);
    lry = obj2.getNum();
    obj2.free();
    opiTransform(state, llx, lly, &tllx, &tlly);
    opiTransform(state, ulx, uly, &tulx, &tuly);
    opiTransform(state, urx, ury, &turx, &tury);
    opiTransform(state, lrx, lry, &tlrx, &tlry);
    writePSFmt("%ALDImagePosition: {0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g} {6:.4g} {7:.4g}\n",
	       tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry);
    obj2.free();
  }
  obj1.free();

  dict->lookup("Resolution", &obj1);
  if (obj1.isArray() && obj1.arrayGetLength() == 2) {
    obj1.arrayGet(0, &obj2);
    horiz = obj2.getNum();
    obj2.free();
    obj1.arrayGet(1, &obj2);
    vert = obj2.getNum();
    obj2.free();
    writePSFmt("%ALDImageResoution: {0:.4g} {1:.4g}\n", horiz, vert);
    obj2.free();
  }
  obj1.free();

  dict->lookup("Size", &obj1);
  if (obj1.isArray() && obj1.arrayGetLength() == 2) {
    obj1.arrayGet(0, &obj2);
    width = obj2.getInt();
    obj2.free();
    obj1.arrayGet(1, &obj2);
    height = obj2.getInt();
    obj2.free();
    writePSFmt("%ALDImageDimensions: {0:d} {1:d}\n", width, height);
  }
  obj1.free();

  //~ ignoring 'Tags' entry
  //~ need to use writePSString() and deal with >255-char lines

  dict->lookup("Tint", &obj1);
  if (obj1.isNum()) {
    writePSFmt("%ALDImageTint: {0:.4g}\n", obj1.getNum());
  }
  obj1.free();

  dict->lookup("Transparency", &obj1);
  if (obj1.isBool()) {
    writePSFmt("%ALDImageTransparency: {0:s}\n",
	       obj1.getBool() ? "true" : "false");
  }
  obj1.free();

  writePS("%%BeginObject: image\n");
  writePS("opiMatrix2 setmatrix\n");
  ++opi13Nest;
}

// Convert PDF user space coordinates to PostScript default user space
// coordinates.  This has to account for both the PDF CTM and the
// PSOutputDev page-fitting transform.
void PSOutputDev::opiTransform(GfxState *state, double x0, double y0,
			       double *x1, double *y1) {
  double t;

  state->transform(x0, y0, x1, y1);
  *x1 += tx;
  *y1 += ty;
  if (rotate == 90) {
    t = *x1;
    *x1 = -*y1;
    *y1 = t;
  } else if (rotate == 180) {
    *x1 = -*x1;
    *y1 = -*y1;
  } else if (rotate == 270) {
    t = *x1;
    *x1 = *y1;
    *y1 = -t;
  }
  *x1 *= xScale;
  *y1 *= yScale;
}

void PSOutputDev::opiEnd(GfxState *state, Dict *opiDict) {
  Object dict;

  if (globalParams->getPSOPI()) {
    opiDict->lookup("2.0", &dict);
    if (dict.isDict()) {
      writePS("%%EndIncludedImage\n");
      writePS("%%EndOPI\n");
      writePS("grestore\n");
      --opi20Nest;
      dict.free();
    } else {
      dict.free();
      opiDict->lookup("1.3", &dict);
      if (dict.isDict()) {
	writePS("%%EndObject\n");
	writePS("restore\n");
	--opi13Nest;
      }
      dict.free();
    }
  }
}

GBool PSOutputDev::getFileSpec(Object *fileSpec, Object *fileName) {
  if (fileSpec->isString()) {
    fileSpec->copy(fileName);
    return gTrue;
  }
  if (fileSpec->isDict()) {
    fileSpec->dictLookup("DOS", fileName);
    if (fileName->isString()) {
      return gTrue;
    }
    fileName->free();
    fileSpec->dictLookup("Mac", fileName);
    if (fileName->isString()) {
      return gTrue;
    }
    fileName->free();
    fileSpec->dictLookup("Unix", fileName);
    if (fileName->isString()) {
      return gTrue;
    }
    fileName->free();
    fileSpec->dictLookup("F", fileName);
    if (fileName->isString()) {
      return gTrue;
    }
    fileName->free();
  }
  return gFalse;
}
#endif // OPI_SUPPORT

void PSOutputDev::type3D0(GfxState *state, double wx, double wy) {
  writePSFmt("{0:.4g} {1:.4g} setcharwidth\n", wx, wy);
  writePS("q\n");
  t3NeedsRestore = gTrue;
}

void PSOutputDev::type3D1(GfxState *state, double wx, double wy,
			  double llx, double lly, double urx, double ury) {
  t3WX = wx;
  t3WY = wy;
  t3LLX = llx;
  t3LLY = lly;
  t3URX = urx;
  t3URY = ury;
  t3String = new GString();
  writePS("q\n");
  t3Cacheable = gTrue;
  t3NeedsRestore = gTrue;
}

void PSOutputDev::drawForm(Ref id) {
  writePSFmt("f_{0:d}_{1:d}\n", id.num, id.gen);
}

void PSOutputDev::psXObject(Stream *psStream, Stream *level1Stream) {
  Stream *str;
  int c;

  if ((level == psLevel1 || level == psLevel1Sep) && level1Stream) {
    str = level1Stream;
  } else {
    str = psStream;
  }
  str->reset();
  while ((c = str->getChar()) != EOF) {
    writePSChar(c);
  }
  str->close();
}

//~ can nextFunc be reset to 0 -- maybe at the start of each page?
//~   or maybe at the start of each color space / pattern?
void PSOutputDev::cvtFunction(Function *func) {
  SampledFunction *func0;
  ExponentialFunction *func2;
  StitchingFunction *func3;
  PostScriptFunction *func4;
  int thisFunc, m, n, nSamples, i, j, k;

  switch (func->getType()) {

  case -1:			// identity
    writePS("{}\n");
    break;

  case 0:			// sampled
    func0 = (SampledFunction *)func;
    thisFunc = nextFunc++;
    m = func0->getInputSize();
    n = func0->getOutputSize();
    nSamples = n;
    for (i = 0; i < m; ++i) {
      nSamples *= func0->getSampleSize(i);
    }
    writePSFmt("/xpdfSamples{0:d} [\n", thisFunc);
    for (i = 0; i < nSamples; ++i) {
      writePSFmt("{0:.4g}\n", func0->getSamples()[i]);
    }
    writePS("] def\n");
    writePSFmt("{{ {0:d} array {1:d} array {2:d} 2 roll\n", 2*m, m, m+2);
    // [e01] [efrac] x0 x1 ... xm-1
    for (i = m-1; i >= 0; --i) {
      // [e01] [efrac] x0 x1 ... xi
      writePSFmt("{0:.4g} sub {1:.4g} mul {2:.4g} add\n",
	      func0->getDomainMin(i),
	      (func0->getEncodeMax(i) - func0->getEncodeMin(i)) /
	        (func0->getDomainMax(i) - func0->getDomainMin(i)),
	      func0->getEncodeMin(i));
      // [e01] [efrac] x0 x1 ... xi-1 xi'
      writePSFmt("dup 0 lt {{ pop 0 }} {{ dup {0:d} gt {{ pop {1:d} }} if }} ifelse\n",
		 func0->getSampleSize(i) - 1, func0->getSampleSize(i) - 1);
      // [e01] [efrac] x0 x1 ... xi-1 xi'
      writePS("dup floor cvi exch dup ceiling cvi exch 2 index sub\n");
      // [e01] [efrac] x0 x1 ... xi-1 floor(xi') ceiling(xi') xi'-floor(xi')
      writePSFmt("{0:d} index {1:d} 3 2 roll put\n", i+3, i);
      // [e01] [efrac] x0 x1 ... xi-1 floor(xi') ceiling(xi')
      writePSFmt("{0:d} index {1:d} 3 2 roll put\n", i+3, 2*i+1);
      // [e01] [efrac] x0 x1 ... xi-1 floor(xi')
      writePSFmt("{0:d} index {1:d} 3 2 roll put\n", i+2, 2*i);
      // [e01] [efrac] x0 x1 ... xi-1
    }
    // [e01] [efrac]
    for (i = 0; i < n; ++i) {
      // [e01] [efrac] y(0) ... y(i-1)
      for (j = 0; j < (1<<m); ++j) {
	// [e01] [efrac] y(0) ... y(i-1) s(0) s(1) ... s(j-1)
	writePSFmt("xpdfSamples{0:d}\n", thisFunc);
	k = m - 1;
	writePSFmt("{0:d} index {1:d} get\n", i+j+2, 2 * k + ((j >> k) & 1));
	for (k = m - 2; k >= 0; --k) {
	  writePSFmt("{0:d} mul {1:d} index {2:d} get add\n",
		     func0->getSampleSize(k),
		     i + j + 3,
		     2 * k + ((j >> k) & 1));
	}
	if (n > 1) {
	  writePSFmt("{0:d} mul {1:d} add ", n, i);
	}
	writePS("get\n");
      }
      // [e01] [efrac] y(0) ... y(i-1) s(0) s(1) ... s(2^m-1)
      for (j = 0; j < m; ++j) {
	// [e01] [efrac] y(0) ... y(i-1) s(0) s(1) ... s(2^(m-j)-1)
	for (k = 0; k < (1 << (m - j)); k += 2) {
	  // [e01] [efrac] y(0) ... y(i-1) <k/2 s' values> <2^(m-j)-k s values>
	  writePSFmt("{0:d} index {1:d} get dup\n",
		     i + k/2 + (1 << (m-j)) - k, j);
	  writePS("3 2 roll mul exch 1 exch sub 3 2 roll mul add\n");
	  writePSFmt("{0:d} 1 roll\n", k/2 + (1 << m-j) - k - 1);
	}
	// [e01] [efrac] s'(0) s'(1) ... s(2^(m-j-1)-1)
      }
      // [e01] [efrac] y(0) ... y(i-1) s
      writePSFmt("{0:.4g} mul {1:.4g} add\n",
		 func0->getDecodeMax(i) - func0->getDecodeMin(i),
		 func0->getDecodeMin(i));
      writePSFmt("dup {0:.4g} lt {{ pop {1:.4g} }} {{ dup {2:.4g} gt {{ pop {3:.4g} }} if }} ifelse\n",
		 func0->getRangeMin(i), func0->getRangeMin(i),
		 func0->getRangeMax(i), func0->getRangeMax(i));
      // [e01] [efrac] y(0) ... y(i-1) y(i)
    }
    // [e01] [efrac] y(0) ... y(n-1)
    writePSFmt("{0:d} {1:d} roll pop pop }}\n", n+2, n);
    break;

  case 2:			// exponential
    func2 = (ExponentialFunction *)func;
    n = func2->getOutputSize();
    writePSFmt("{{ dup {0:.4g} lt {{ pop {1:.4g} }} {{ dup {2:.4g} gt {{ pop {3:.4g} }} if }} ifelse\n",
	       func2->getDomainMin(0), func2->getDomainMin(0),
	       func2->getDomainMax(0), func2->getDomainMax(0));
    // x
    for (i = 0; i < n; ++i) {
      // x y(0) .. y(i-1)
      writePSFmt("{0:d} index {1:.4g} exp {2:.4g} mul {3:.4g} add\n",
		 i, func2->getE(), func2->getC1()[i] - func2->getC0()[i],
		 func2->getC0()[i]);
      if (func2->getHasRange()) {
	writePSFmt("dup {0:.4g} lt {{ pop {1:.4g} }} {{ dup {2:.4g} gt {{ pop {3:.4g} }} if }} ifelse\n",
		   func2->getRangeMin(i), func2->getRangeMin(i),
		   func2->getRangeMax(i), func2->getRangeMax(i));
      }
    }
    // x y(0) .. y(n-1)
    writePSFmt("{0:d} {1:d} roll pop }}\n", n+1, n);
    break;

  case 3:			// stitching
    func3 = (StitchingFunction *)func;
    thisFunc = nextFunc++;
    for (i = 0; i < func3->getNumFuncs(); ++i) {
      cvtFunction(func3->getFunc(i));
      writePSFmt("/xpdfFunc{0:d}_{1:d} exch def\n", thisFunc, i);
    }
    writePSFmt("{{ dup {0:.4g} lt {{ pop {1:.4g} }} {{ dup {2:.4g} gt {{ pop {3:.4g} }} if }} ifelse\n",
	       func3->getDomainMin(0), func3->getDomainMin(0),
	       func3->getDomainMax(0), func3->getDomainMax(0));
    for (i = 0; i < func3->getNumFuncs() - 1; ++i) {
      writePSFmt("dup {0:.4g} lt {{ {1:.4g} sub {2:.4g} mul {3:.4g} add xpdfFunc{4:d}_{5:d} }} {{\n",
		 func3->getBounds()[i+1],
		 func3->getBounds()[i],
		 func3->getScale()[i],
		 func3->getEncode()[2*i],
		 thisFunc, i);
    }
    writePSFmt("{0:.4g} sub {1:.4g} mul {2:.4g} add xpdfFunc{3:d}_{4:d}\n",
	       func3->getBounds()[i],
	       func3->getScale()[i],
	       func3->getEncode()[2*i],
	       thisFunc, i);
    for (i = 0; i < func3->getNumFuncs() - 1; ++i) {
      writePS("} ifelse\n");
    }
    writePS("}\n");
    break;

  case 4:			// PostScript
    func4 = (PostScriptFunction *)func;
    writePS(func4->getCodeString()->getCString());
    writePS("\n");
    break;
  }
}

void PSOutputDev::writePSChar(char c) {
  if (t3String) {
    t3String->append(c);
  } else {
    (*outputFunc)(outputStream, &c, 1);
  }
}

void PSOutputDev::writePS(char *s) {
  if (t3String) {
    t3String->append(s);
  } else {
    (*outputFunc)(outputStream, s, strlen(s));
  }
}

void PSOutputDev::writePSFmt(const char *fmt, ...) {
  va_list args;
  GString *buf;

  va_start(args, fmt);
  if (t3String) {
    t3String->appendfv((char *)fmt, args);
  } else {
    buf = GString::formatv((char *)fmt, args);
    (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
    delete buf;
  }
  va_end(args);
}

void PSOutputDev::writePSString(GString *s) {
  Guchar *p;
  int n, line;
  char buf[8];

  writePSChar('(');
  line = 1;
  for (p = (Guchar *)s->getCString(), n = s->getLength(); n; ++p, --n) {
    if (line >= 64) {
      writePSChar('\\');
      writePSChar('\n');
      line = 0;
    }
    if (*p == '(' || *p == ')' || *p == '\\') {
      writePSChar('\\');
      writePSChar((char)*p);
      line += 2;
    } else if (*p < 0x20 || *p >= 0x80) {
      sprintf(buf, "\\%03o", *p);
      writePS(buf);
      line += 4;
    } else {
      writePSChar((char)*p);
      ++line;
    }
  }
  writePSChar(')');
}

void PSOutputDev::writePSName(char *s) {
  char *p;
  char c;

  p = s;
  while ((c = *p++)) {
    if (c <= (char)0x20 || c >= (char)0x7f ||
	c == '(' || c == ')' || c == '<' || c == '>' ||
	c == '[' || c == ']' || c == '{' || c == '}' ||
	c == '/' || c == '%') {
      writePSFmt("#{0:02x}", c & 0xff);
    } else {
      writePSChar(c);
    }
  }
}

GString *PSOutputDev::filterPSName(GString *name) {
  GString *name2;
  char buf[8];
  int i;
  char c;

  name2 = new GString();

  // ghostscript chokes on names that begin with out-of-limits
  // numbers, e.g., 1e4foo is handled correctly (as a name), but
  // 1e999foo generates a limitcheck error
  c = name->getChar(0);
  if (c >= '0' && c <= '9') {
    name2->append('f');
  }

  for (i = 0; i < name->getLength(); ++i) {
    c = name->getChar(i);
    if (c <= (char)0x20 || c >= (char)0x7f ||
	c == '(' || c == ')' || c == '<' || c == '>' ||
	c == '[' || c == ']' || c == '{' || c == '}' ||
	c == '/' || c == '%') {
      sprintf(buf, "#%02x", c & 0xff);
      name2->append(buf);
    } else {
      name2->append(c);
    }
  }
  return name2;
}

// Write a DSC-compliant <textline>.
void PSOutputDev::writePSTextLine(GString *s) {
  int i, j, step;
  int c;

  // - DSC comments must be printable ASCII; control chars and
  //   backslashes have to be escaped (we do cheap Unicode-to-ASCII
  //   conversion by simply ignoring the high byte)
  // - lines are limited to 255 chars (we limit to 200 here to allow
  //   for the keyword, which was emitted by the caller)
  // - lines that start with a left paren are treated as <text>
  //   instead of <textline>, so we escape a leading paren
  if (s->getLength() >= 2 &&
      (s->getChar(0) & 0xff) == 0xfe &&
      (s->getChar(1) & 0xff) == 0xff) {
    i = 3;
    step = 2;
  } else {
    i = 0;
    step = 1;
  }
  for (j = 0; i < s->getLength() && j < 200; i += step) {
    c = s->getChar(i) & 0xff;
    if (c == '\\') {
      writePS("\\\\");
      j += 2;
    } else if (c < 0x20 || c > 0x7e || (j == 0 && c == '(')) {
      writePSFmt("\\{0:03o}", c);
      j += 4;
    } else {
      writePSChar(c);
      ++j;
    }
  }
  writePS("\n");
}
//========================================================================
//
// PSOutputDev.h
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef PSOUTPUTDEV_H
#define PSOUTPUTDEV_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include <stddef.h>
#include "config.h"
#include "Object.h"
#include "GlobalParams.h"
#include "OutputDev.h"

class Function;
class GfxPath;
class GfxFont;
class GfxColorSpace;
class GfxSeparationColorSpace;
class PDFRectangle;
struct PSFont8Info;
struct PSFont16Enc;
class PSOutCustomColor;

//------------------------------------------------------------------------
// PSOutputDev
//------------------------------------------------------------------------

enum PSOutMode {
  psModePS,
  psModeEPS,
  psModeForm
};

enum PSFileType {
  psFile,			// write to file
  psPipe,			// write to pipe
  psStdout,			// write to stdout
  psGeneric			// write to a generic stream
};

typedef void (*PSOutputFunc)(void *stream, char *data, int len);

class PSOutputDev: public OutputDev {
public:

  // Open a PostScript output file, and write the prolog.
  PSOutputDev(char *fileName, XRef *xrefA, Catalog *catalog,
	      int firstPage, int lastPage, PSOutMode modeA,
	      int imgLLXA = 0, int imgLLYA = 0,
	      int imgURXA = 0, int imgURYA = 0,
	      GBool manualCtrlA = gFalse);

  // Open a PSOutputDev that will write to a generic stream.
  PSOutputDev(PSOutputFunc outputFuncA, void *outputStreamA,
	      XRef *xrefA, Catalog *catalog,
	      int firstPage, int lastPage, PSOutMode modeA,
	      int imgLLXA = 0, int imgLLYA = 0,
	      int imgURXA = 0, int imgURYA = 0,
	      GBool manualCtrlA = gFalse);

  // Destructor -- writes the trailer and closes the file.
  virtual ~PSOutputDev();

  // Check if file was successfully created.
  virtual GBool isOk() { return ok; }

  //---- get info about output device

  // Does this device use upside-down coordinates?
  // (Upside-down means (0,0) is the top left corner of the page.)
  virtual GBool upsideDown() { return gFalse; }

  // Does this device use drawChar() or drawString()?
  virtual GBool useDrawChar() { return gFalse; }

  // Does this device use tilingPatternFill()?  If this returns false,
  // tiling pattern fills will be reduced to a series of other drawing
  // operations.
  virtual GBool useTilingPatternFill() { return gTrue; }

  // Does this device use functionShadedFill(), axialShadedFill(), and
  // radialShadedFill()?  If this returns false, these shaded fills
  // will be reduced to a series of other drawing operations.
  virtual GBool useShadedFills()
    { return level >= psLevel2; }

  // Does this device use drawForm()?  If this returns false,
  // form-type XObjects will be interpreted (i.e., unrolled).
  virtual GBool useDrawForm() { return preload; }

  // Does this device use beginType3Char/endType3Char?  Otherwise,
  // text in Type 3 fonts will be drawn with drawChar/drawString.
  virtual GBool interpretType3Chars() { return gFalse; }

  //----- header/trailer (used only if manualCtrl is true)

  // Write the document-level header.
  void writeHeader(int firstPage, int lastPage,
		   PDFRectangle *mediaBox, PDFRectangle *cropBox,
		   int pageRotate);

  // Write the Xpdf procset.
  void writeXpdfProcset();

  // Write the document-level setup.
  void writeDocSetup(Catalog *catalog, int firstPage, int lastPage);

  // Write the trailer for the current page.
  void writePageTrailer();

  // Write the document trailer.
  void writeTrailer();

  //----- initialization and control

  // Check to see if a page slice should be displayed.  If this
  // returns false, the page display is aborted.  Typically, an
  // OutputDev will use some alternate means to display the page
  // before returning false.
  virtual GBool checkPageSlice(Page *page, double hDPI, double vDPI,
			       int rotate, GBool useMediaBox, GBool crop,
			       int sliceX, int sliceY, int sliceW, int sliceH,
			       GBool printing, Catalog *catalog,
			       GBool (*abortCheckCbk)(void *data) = NULL,
			       void *abortCheckCbkData = NULL);

  // Start a page.
  virtual void startPage(int pageNum, GfxState *state);

  // End a page.
  virtual void endPage();

  //----- save/restore graphics state
  virtual void saveState(GfxState *state);
  virtual void restoreState(GfxState *state);

  //----- update graphics state
  virtual void updateCTM(GfxState *state, double m11, double m12,
			 double m21, double m22, double m31, double m32);
  virtual void updateLineDash(GfxState *state);
  virtual void updateFlatness(GfxState *state);
  virtual void updateLineJoin(GfxState *state);
  virtual void updateLineCap(GfxState *state);
  virtual void updateMiterLimit(GfxState *state);
  virtual void updateLineWidth(GfxState *state);
  virtual void updateFillColorSpace(GfxState *state);
  virtual void updateStrokeColorSpace(GfxState *state);
  virtual void updateFillColor(GfxState *state);
  virtual void updateStrokeColor(GfxState *state);
  virtual void updateFillOverprint(GfxState *state);
  virtual void updateStrokeOverprint(GfxState *state);
  virtual void updateTransfer(GfxState *state);

  //----- update text state
  virtual void updateFont(GfxState *state);
  virtual void updateTextMat(GfxState *state);
  virtual void updateCharSpace(GfxState *state);
  virtual void updateRender(GfxState *state);
  virtual void updateRise(GfxState *state);
  virtual void updateWordSpace(GfxState *state);
  virtual void updateHorizScaling(GfxState *state);
  virtual void updateTextPos(GfxState *state);
  virtual void updateTextShift(GfxState *state, double shift);

  //----- path painting
  virtual void stroke(GfxState *state);
  virtual void fill(GfxState *state);
  virtual void eoFill(GfxState *state);
  virtual void tilingPatternFill(GfxState *state, Object *str,
				 int paintType, Dict *resDict,
				 double *mat, double *bbox,
				 int x0, int y0, int x1, int y1,
				 double xStep, double yStep);
  virtual GBool functionShadedFill(GfxState *state,
				   GfxFunctionShading *shading);
  virtual GBool axialShadedFill(GfxState *state, GfxAxialShading *shading);
  virtual GBool radialShadedFill(GfxState *state, GfxRadialShading *shading);

  //----- path clipping
  virtual void clip(GfxState *state);
  virtual void eoClip(GfxState *state);
  virtual void clipToStrokePath(GfxState *state);

  //----- text drawing
  virtual void drawString(GfxState *state, GString *s);
  virtual void endTextObject(GfxState *state);

  //----- image drawing
  virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
			     int width, int height, GBool invert,
			     GBool inlineImg);
  virtual void drawImage(GfxState *state, Object *ref, Stream *str,
			 int width, int height, GfxImageColorMap *colorMap,
			 int *maskColors, GBool inlineImg);
  virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str,
			       int width, int height,
			       GfxImageColorMap *colorMap,
			       Stream *maskStr, int maskWidth, int maskHeight,
			       GBool maskInvert);

#if OPI_SUPPORT
  //----- OPI functions
  virtual void opiBegin(GfxState *state, Dict *opiDict);
  virtual void opiEnd(GfxState *state, Dict *opiDict);
#endif

  //----- Type 3 font operators
  virtual void type3D0(GfxState *state, double wx, double wy);
  virtual void type3D1(GfxState *state, double wx, double wy,
		       double llx, double lly, double urx, double ury);

  //----- form XObjects
  virtual void drawForm(Ref ref);

  //----- PostScript XObjects
  virtual void psXObject(Stream *psStream, Stream *level1Stream);

  //----- miscellaneous
  void setOffset(double x, double y)
    { tx0 = x; ty0 = y; }
  void setScale(double x, double y)
    { xScale0 = x; yScale0 = y; }
  void setRotate(int rotateA)
    { rotate0 = rotateA; }
  void setClip(double llx, double lly, double urx, double ury)
    { clipLLX0 = llx; clipLLY0 = lly; clipURX0 = urx; clipURY0 = ury; }
  void setUnderlayCbk(void (*cbk)(PSOutputDev *psOut, void *data),
		      void *data)
    { underlayCbk = cbk; underlayCbkData = data; }
  void setOverlayCbk(void (*cbk)(PSOutputDev *psOut, void *data),
		     void *data)
    { overlayCbk = cbk; overlayCbkData = data; }

private:

  void init(PSOutputFunc outputFuncA, void *outputStreamA,
	    PSFileType fileTypeA, XRef *xrefA, Catalog *catalog,
	    int firstPage, int lastPage, PSOutMode modeA,
	    int imgLLXA, int imgLLYA, int imgURXA, int imgURYA,
	    GBool manualCtrlA);
  void setupResources(Dict *resDict);
  void setupFonts(Dict *resDict);
  void setupFont(GfxFont *font, Dict *parentResDict);
  void setupEmbeddedType1Font(Ref *id, GString *psName);
  void setupExternalType1Font(GString *fileName, GString *psName);
  void setupEmbeddedType1CFont(GfxFont *font, Ref *id, GString *psName);
  void setupEmbeddedOpenTypeT1CFont(GfxFont *font, Ref *id, GString *psName);
  void setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id, GString *psName);
  void setupExternalTrueTypeFont(GfxFont *font, GString *psName);
  void setupEmbeddedCIDType0Font(GfxFont *font, Ref *id, GString *psName);
  void setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id, GString *psName,
				    GBool needVerticalMetrics);
  void setupEmbeddedOpenTypeCFFFont(GfxFont *font, Ref *id, GString *psName);
  void setupType3Font(GfxFont *font, GString *psName, Dict *parentResDict);
  void setupImages(Dict *resDict);
  void setupImage(Ref id, Stream *str);
  void setupForms(Dict *resDict);
  void setupForm(Ref id, Object *strObj);
  void addProcessColor(double c, double m, double y, double k);
  void addCustomColor(GfxSeparationColorSpace *sepCS);
  void doPath(GfxPath *path);
  void doImageL1(Object *ref, GfxImageColorMap *colorMap,
		 GBool invert, GBool inlineImg,
		 Stream *str, int width, int height, int len);
  void doImageL1Sep(GfxImageColorMap *colorMap,
		    GBool invert, GBool inlineImg,
		    Stream *str, int width, int height, int len);
  void doImageL2(Object *ref, GfxImageColorMap *colorMap,
		 GBool invert, GBool inlineImg,
		 Stream *str, int width, int height, int len,
		 int *maskColors, Stream *maskStr,
		 int maskWidth, int maskHeight, GBool maskInvert);
  void doImageL3(Object *ref, GfxImageColorMap *colorMap,
		 GBool invert, GBool inlineImg,
		 Stream *str, int width, int height, int len,
		 int *maskColors, Stream *maskStr,
		 int maskWidth, int maskHeight, GBool maskInvert);
  void dumpColorSpaceL2(GfxColorSpace *colorSpace,
			GBool genXform, GBool updateColors,
			GBool map01);
#if OPI_SUPPORT
  void opiBegin20(GfxState *state, Dict *dict);
  void opiBegin13(GfxState *state, Dict *dict);
  void opiTransform(GfxState *state, double x0, double y0,
		    double *x1, double *y1);
  GBool getFileSpec(Object *fileSpec, Object *fileName);
#endif
  void cvtFunction(Function *func);
  void writePSChar(char c);
  void writePS(char *s);
  void writePSFmt(const char *fmt, ...);
  void writePSString(GString *s);
  void writePSName(char *s);
  GString *filterPSName(GString *name);
  void writePSTextLine(GString *s);

  PSLevel level;		// PostScript level (1, 2, separation)
  PSOutMode mode;		// PostScript mode (PS, EPS, form)
  int paperWidth;		// width of paper, in pts
  int paperHeight;		// height of paper, in pts
  int imgLLX, imgLLY,		// imageable area, in pts
      imgURX, imgURY;
  GBool preload;		// load all images into memory, and
				//   predefine forms

  PSOutputFunc outputFunc;
  void *outputStream;
  PSFileType fileType;		// file / pipe / stdout
  GBool manualCtrl;
  int seqPage;			// current sequential page number
  void (*underlayCbk)(PSOutputDev *psOut, void *data);
  void *underlayCbkData;
  void (*overlayCbk)(PSOutputDev *psOut, void *data);
  void *overlayCbkData;

  XRef *xref;			// the xref table for this PDF file

  Ref *fontIDs;			// list of object IDs of all used fonts
  int fontIDLen;		// number of entries in fontIDs array
  int fontIDSize;		// size of fontIDs array
  Ref *fontFileIDs;		// list of object IDs of all embedded fonts
  int fontFileIDLen;		// number of entries in fontFileIDs array
  int fontFileIDSize;		// size of fontFileIDs array
  GString **fontFileNames;	// list of names of all embedded external fonts
  int fontFileNameLen;		// number of entries in fontFileNames array
  int fontFileNameSize;		// size of fontFileNames array
  int nextTrueTypeNum;		// next unique number to append to a TrueType
				//   font name
  PSFont8Info *font8Info;	// info for 8-bit fonts
  int font8InfoLen;		// number of entries in font8Info array
  int font8InfoSize;		// size of font8Info array
  PSFont16Enc *font16Enc;	// encodings for substitute 16-bit fonts
  int font16EncLen;		// number of entries in font16Enc array
  int font16EncSize;		// size of font16Enc array
  Ref *imgIDs;			// list of image IDs for in-memory images
  int imgIDLen;			// number of entries in imgIDs array
  int imgIDSize;		// size of imgIDs array
  Ref *formIDs;			// list of IDs for predefined forms
  int formIDLen;		// number of entries in formIDs array
  int formIDSize;		// size of formIDs array
  GList *xobjStack;		// stack of XObject dicts currently being
				//   processed
  int numSaves;			// current number of gsaves
  int numTilingPatterns;	// current number of nested tiling patterns
  int nextFunc;			// next unique number to use for a function

  double tx0, ty0;		// global translation
  double xScale0, yScale0;	// global scaling
  int rotate0;			// rotation angle (0, 90, 180, 270)
  double clipLLX0, clipLLY0,
         clipURX0, clipURY0;
  double tx, ty;		// global translation for current page
  double xScale, yScale;	// global scaling for current page
  int rotate;			// rotation angle for current page
  double epsX1, epsY1,		// EPS bounding box (unrotated)
         epsX2, epsY2;

  GString *embFontList;		// resource comments for embedded fonts

  int processColors;		// used process colors
  PSOutCustomColor		// used custom colors
    *customColors;

  GBool haveTextClip;		// set if text has been drawn with a
				//   clipping render mode

  GBool inType3Char;		// inside a Type 3 CharProc
  GString *t3String;		// Type 3 content string
  double t3WX, t3WY,		// Type 3 character parameters
         t3LLX, t3LLY, t3URX, t3URY;
  GBool t3Cacheable;		// cleared if char is not cacheable
  GBool t3NeedsRestore;		// set if a 'q' operator was issued

#if OPI_SUPPORT
  int opi13Nest;		// nesting level of OPI 1.3 objects
  int opi20Nest;		// nesting level of OPI 2.0 objects
#endif

  GBool ok;			// set up ok?


  friend class WinPDFPrinter;
};

#endif
//========================================================================
//
// PSTokenizer.cc
//
// Copyright 2002-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stdio.h>
#include <stdlib.h>
#include "PSTokenizer.h"

//------------------------------------------------------------------------

// A '1' in this array means the character is white space.  A '1' or
// '2' means the character ends a name or command.
static char specialChars[256] = {
  1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0,   // 0x
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // 1x
  1, 0, 0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2,   // 2x
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0,   // 3x
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // 4x
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0,   // 5x
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // 6x
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0,   // 7x
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // 8x
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // 9x
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // ax
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // bx
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // cx
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // dx
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // ex
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0    // fx
};

//------------------------------------------------------------------------

PSTokenizer::PSTokenizer(int (*getCharFuncA)(void *), void *dataA) {
  getCharFunc = getCharFuncA;
  data = dataA;
  charBuf = -1;
}

PSTokenizer::~PSTokenizer() {
}

GBool PSTokenizer::getToken(char *buf, int size, int *length) {
  GBool comment, backslash;
  int c;
  int i;

  // skip whitespace and comments
  comment = gFalse;
  while (1) {
    if ((c = getChar()) == EOF) {
      buf[0] = '\0';
      *length = 0;
      return gFalse;
    }
    if (comment) {
      if (c == '\x0a' || c == '\x0d') {
	comment = gFalse;
      }
    } else if (c == '%') {
      comment = gTrue;
    } else if (specialChars[c] != 1) {
      break;
    }
  }

  // read a token
  i = 0;
  buf[i++] = c;
  if (c == '(') {
    backslash = gFalse;
    while ((c = lookChar()) != EOF) {
      if (i < size - 1) {
	buf[i++] = c;
      }
      getChar();
      if (c == '\\') {
	backslash = gTrue;
      } else if (!backslash && c == ')') {
	break;
      } else {
	backslash = gFalse;
      }
    }
  } else if (c == '<') {
    while ((c = lookChar()) != EOF) {
      getChar();
      if (i < size - 1 && specialChars[c] != 1) {
	buf[i++] = c;
      }
      if (c == '>') {
	break;
      }
    }
  } else if (c != '[' && c != ']') {
    while ((c = lookChar()) != EOF && !specialChars[c]) {
      getChar();
      if (i < size - 1) {
	buf[i++] = c;
      }
    }
  }
  buf[i] = '\0';
  *length = i;

  return gTrue;
}

int PSTokenizer::lookChar() {
  if (charBuf < 0) {
    charBuf = (*getCharFunc)(data);
  }
  return charBuf;
}

int PSTokenizer::getChar() {
  int c;

  if (charBuf < 0) {
    charBuf = (*getCharFunc)(data);
  }
  c = charBuf;
  charBuf = -1;
  return c;
}
//========================================================================
//
// PSTokenizer.h
//
// Copyright 2002-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef PSTOKENIZER_H
#define PSTOKENIZER_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "gtypes.h"

//------------------------------------------------------------------------

class PSTokenizer {
public:

  PSTokenizer(int (*getCharFuncA)(void *), void *dataA);
  ~PSTokenizer();

  // Get the next PostScript token.  Returns false at end-of-stream.
  GBool getToken(char *buf, int size, int *length);

private:

  int lookChar();
  int getChar();

  int (*getCharFunc)(void *);
  void *data;
  int charBuf;
};

#endif
//========================================================================
//
// Page.cc
//
// Copyright 1996-2007 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stddef.h>
#include "GlobalParams.h"
#include "Object.h"
#include "Array.h"
#include "Dict.h"
#include "XRef.h"
#include "Link.h"
#include "OutputDev.h"
#ifndef PDF_PARSER_ONLY
#include "Gfx.h"
#include "GfxState.h"
#include "Annot.h"
#endif
#include "Error.h"
#include "Catalog.h"
#include "Page.h"

//------------------------------------------------------------------------
// PDFRectangle
//------------------------------------------------------------------------

void PDFRectangle::clipTo(PDFRectangle *rect) {
  if (x1 < rect->x1) {
    x1 = rect->x1;
  } else if (x1 > rect->x2) {
    x1 = rect->x2;
  }
  if (x2 < rect->x1) {
    x2 = rect->x1;
  } else if (x2 > rect->x2) {
    x2 = rect->x2;
  }
  if (y1 < rect->y1) {
    y1 = rect->y1;
  } else if (y1 > rect->y2) {
    y1 = rect->y2;
  }
  if (y2 < rect->y1) {
    y2 = rect->y1;
  } else if (y2 > rect->y2) {
    y2 = rect->y2;
  }
}

//------------------------------------------------------------------------
// PageAttrs
//------------------------------------------------------------------------

PageAttrs::PageAttrs(PageAttrs *attrs, Dict *dict) {
  Object obj1;

  // get old/default values
  if (attrs) {
    mediaBox = attrs->mediaBox;
    cropBox = attrs->cropBox;
    haveCropBox = attrs->haveCropBox;
    rotate = attrs->rotate;
    attrs->resources.copy(&resources);
  } else {
    // set default MediaBox to 8.5" x 11" -- this shouldn't be necessary
    // but some (non-compliant) PDF files don't specify a MediaBox
    mediaBox.x1 = 0;
    mediaBox.y1 = 0;
    mediaBox.x2 = 612;
    mediaBox.y2 = 792;
    cropBox.x1 = cropBox.y1 = cropBox.x2 = cropBox.y2 = 0;
    haveCropBox = gFalse;
    rotate = 0;
    resources.initNull();
  }

  // media box
  readBox(dict, "MediaBox", &mediaBox);

  // crop box
  if (readBox(dict, "CropBox", &cropBox)) {
    haveCropBox = gTrue;
  }
  if (!haveCropBox) {
    cropBox = mediaBox;
  }

  // other boxes
  bleedBox = cropBox;
  readBox(dict, "BleedBox", &bleedBox);
  trimBox = cropBox;
  readBox(dict, "TrimBox", &trimBox);
  artBox = cropBox;
  readBox(dict, "ArtBox", &artBox);

  // clip all other boxes to the media box
  cropBox.clipTo(&mediaBox);
  bleedBox.clipTo(&mediaBox);
  trimBox.clipTo(&mediaBox);
  artBox.clipTo(&mediaBox);

  // rotate
  dict->lookup("Rotate", &obj1);
  if (obj1.isInt()) {
    rotate = obj1.getInt();
  }
  obj1.free();
  while (rotate < 0) {
    rotate += 360;
  }
  while (rotate >= 360) {
    rotate -= 360;
  }

  // misc attributes
  dict->lookup("LastModified", &lastModified);
  dict->lookup("BoxColorInfo", &boxColorInfo);
  dict->lookup("Group", &group);
  dict->lookup("Metadata", &metadata);
  dict->lookup("PieceInfo", &pieceInfo);
  dict->lookup("SeparationInfo", &separationInfo);

  // resource dictionary
  dict->lookup("Resources", &obj1);
  if (obj1.isDict()) {
    resources.free();
    obj1.copy(&resources);
  }
  obj1.free();
}

PageAttrs::~PageAttrs() {
  lastModified.free();
  boxColorInfo.free();
  group.free();
  metadata.free();
  pieceInfo.free();
  separationInfo.free();
  resources.free();
}

GBool PageAttrs::readBox(Dict *dict, char *key, PDFRectangle *box) {
  PDFRectangle tmp;
  double t;
  Object obj1, obj2;
  GBool ok;

  dict->lookup(key, &obj1);
  if (obj1.isArray() && obj1.arrayGetLength() == 4) {
    ok = gTrue;
    obj1.arrayGet(0, &obj2);
    if (obj2.isNum()) {
      tmp.x1 = obj2.getNum();
    } else {
      ok = gFalse;
    }
    obj2.free();
    obj1.arrayGet(1, &obj2);
    if (obj2.isNum()) {
      tmp.y1 = obj2.getNum();
    } else {
      ok = gFalse;
    }
    obj2.free();
    obj1.arrayGet(2, &obj2);
    if (obj2.isNum()) {
      tmp.x2 = obj2.getNum();
    } else {
      ok = gFalse;
    }
    obj2.free();
    obj1.arrayGet(3, &obj2);
    if (obj2.isNum()) {
      tmp.y2 = obj2.getNum();
    } else {
      ok = gFalse;
    }
    obj2.free();
    if (ok) {
      if (tmp.x1 > tmp.x2) {
	t = tmp.x1; tmp.x1 = tmp.x2; tmp.x2 = t;
      }
      if (tmp.y1 > tmp.y2) {
	t = tmp.y1; tmp.y1 = tmp.y2; tmp.y2 = t;
      }
      *box = tmp;
    }
  } else {
    ok = gFalse;
  }
  obj1.free();
  return ok;
}

//------------------------------------------------------------------------
// Page
//------------------------------------------------------------------------

Page::Page(XRef *xrefA, int numA, Dict *pageDict, PageAttrs *attrsA) {
  ok = gTrue;
  xref = xrefA;
  num = numA;

  // get attributes
  attrs = attrsA;

  // annotations
  pageDict->lookupNF("Annots", &annots);
  if (!(annots.isRef() || annots.isArray() || annots.isNull())) {
    error(-1, "Page annotations object (page %d) is wrong type (%s)",
	  num, annots.getTypeName());
    annots.free();
    goto err2;
  }

  // contents
  pageDict->lookupNF("Contents", &contents);
  if (!(contents.isRef() || contents.isArray() ||
	contents.isNull())) {
    error(-1, "Page contents object (page %d) is wrong type (%s)",
	  num, contents.getTypeName());
    contents.free();
    goto err1;
  }

  return;

 err2:
  annots.initNull();
 err1:
  contents.initNull();
  ok = gFalse;
}

Page::~Page() {
  delete attrs;
  annots.free();
  contents.free();
}

Links *Page::getLinks(Catalog *catalog) {
  Links *links;
  Object obj;

  links = new Links(getAnnots(&obj), catalog->getBaseURI());
  obj.free();
  return links;
}

void Page::display(OutputDev *out, double hDPI, double vDPI,
		   int rotate, GBool useMediaBox, GBool crop,
           Links *links, GBool printing, Catalog *catalog,
		   GBool (*abortCheckCbk)(void *data),
		   void *abortCheckCbkData) {

  displaySlice(out, hDPI, vDPI, rotate, useMediaBox, crop,
	       -1, -1, -1, -1, links, printing, catalog,
	       abortCheckCbk, abortCheckCbkData);
}

void Page::displaySlice(OutputDev *out, double hDPI, double vDPI,
			int rotate, GBool useMediaBox, GBool crop,
			int sliceX, int sliceY, int sliceW, int sliceH,
            Links *links, GBool printing, Catalog *catalog,
			GBool (*abortCheckCbk)(void *data),
			void *abortCheckCbkData) {
#ifndef PDF_PARSER_ONLY
  PDFRectangle *mediaBox, *cropBox;
  PDFRectangle box;
  Gfx *gfx;
  Object obj;
  Link *link;
  Annots *annotList;
  Dict *acroForm;
  int i;

  if (!out->checkPageSlice(this, hDPI, vDPI, rotate, useMediaBox, crop,
			   sliceX, sliceY, sliceW, sliceH,
			   printing, catalog,
			   abortCheckCbk, abortCheckCbkData)) {
    return;
  }

  rotate += getRotate();
  if (rotate >= 360) {
    rotate -= 360;
  } else if (rotate < 0) {
    rotate += 360;
  }

  makeBox(hDPI, vDPI, rotate, useMediaBox, out->upsideDown(),
	  sliceX, sliceY, sliceW, sliceH, &box, &crop);
  cropBox = getCropBox();

  if (globalParams->getPrintCommands()) {
    mediaBox = getMediaBox();
    printf("***** MediaBox = ll:%g,%g ur:%g,%g\n",
	   mediaBox->x1, mediaBox->y1, mediaBox->x2, mediaBox->y2);
    printf("***** CropBox = ll:%g,%g ur:%g,%g\n",
	   cropBox->x1, cropBox->y1, cropBox->x2, cropBox->y2);
    printf("***** Rotate = %d\n", attrs->getRotate());
  }

  gfx = new Gfx(xref, out, num, attrs->getResourceDict(),
		hDPI, vDPI, &box, crop ? cropBox : (PDFRectangle *)NULL,
		rotate, abortCheckCbk, abortCheckCbkData);
  contents.fetch(xref, &obj);
  if (!obj.isNull()) {
    gfx->saveState();
    gfx->display(&obj);
    gfx->restoreState();
  }
  obj.free();

  // draw links
  if (links) {
        gfx->saveState();
        for (i = 0; i < links->getNumLinks(); ++i) {
            link = links->getLink(i);
            out->drawLink(link, catalog);
        }
        gfx->restoreState();
        out->dump();
  }    

  // draw annotations
  annotList = new Annots(xref, catalog, getAnnots(&obj));
  obj.free();
  acroForm = catalog->getAcroForm()->isDict() ?
               catalog->getAcroForm()->getDict() : NULL;
  if (acroForm) {
    if (acroForm->lookup("NeedAppearances", &obj)) {
      if (obj.isBool() && obj.getBool()) {
	annotList->generateAppearances(acroForm);
      }
    }
    obj.free();
  }
  if (annotList->getNumAnnots() > 0) {
    if (globalParams->getPrintCommands()) {
      printf("***** Annotations\n");
    }
    for (i = 0; i < annotList->getNumAnnots(); ++i) {
      annotList->getAnnot(i)->draw(gfx, printing);
    }
    out->dump();
  }
  delete annotList;

  delete gfx;
#endif
}

void Page::makeBox(double hDPI, double vDPI, int rotate,
		   GBool useMediaBox, GBool upsideDown,
		   double sliceX, double sliceY, double sliceW, double sliceH,
		   PDFRectangle *box, GBool *crop) {
  PDFRectangle *mediaBox, *cropBox, *baseBox;
  double kx, ky;

  mediaBox = getMediaBox();
  cropBox = getCropBox();
  if (sliceW >= 0 && sliceH >= 0) {
    baseBox = useMediaBox ? mediaBox : cropBox;
    kx = 72.0 / hDPI;
    ky = 72.0 / vDPI;
    if (rotate == 90) {
      if (upsideDown) {
	box->x1 = baseBox->x1 + ky * sliceY;
	box->x2 = baseBox->x1 + ky * (sliceY + sliceH);
      } else {
	box->x1 = baseBox->x2 - ky * (sliceY + sliceH);
	box->x2 = baseBox->x2 - ky * sliceY;
      }
      box->y1 = baseBox->y1 + kx * sliceX;
      box->y2 = baseBox->y1 + kx * (sliceX + sliceW);
    } else if (rotate == 180) {
      box->x1 = baseBox->x2 - kx * (sliceX + sliceW);
      box->x2 = baseBox->x2 - kx * sliceX;
      if (upsideDown) {
	box->y1 = baseBox->y1 + ky * sliceY;
	box->y2 = baseBox->y1 + ky * (sliceY + sliceH);
      } else {
	box->y1 = baseBox->y2 - ky * (sliceY + sliceH);
	box->y2 = baseBox->y2 - ky * sliceY;
      }
    } else if (rotate == 270) {
      if (upsideDown) {
	box->x1 = baseBox->x2 - ky * (sliceY + sliceH);
	box->x2 = baseBox->x2 - ky * sliceY;
      } else {
	box->x1 = baseBox->x1 + ky * sliceY;
	box->x2 = baseBox->x1 + ky * (sliceY + sliceH);
      }
      box->y1 = baseBox->y2 - kx * (sliceX + sliceW);
      box->y2 = baseBox->y2 - kx * sliceX;
    } else {
      box->x1 = baseBox->x1 + kx * sliceX;
      box->x2 = baseBox->x1 + kx * (sliceX + sliceW);
      if (upsideDown) {
	box->y1 = baseBox->y2 - ky * (sliceY + sliceH);
	box->y2 = baseBox->y2 - ky * sliceY;
      } else {
	box->y1 = baseBox->y1 + ky * sliceY;
	box->y2 = baseBox->y1 + ky * (sliceY + sliceH);
      }
    }
  } else if (useMediaBox) {
    *box = *mediaBox;
  } else {
    *box = *cropBox;
    *crop = gFalse;
  }
}

void Page::processLinks(OutputDev *out, Catalog *catalog) {
  Links *links;
  int i;

  links = getLinks(catalog);
  for (i = 0; i < links->getNumLinks(); ++i) {
    out->processLink(links->getLink(i), catalog);
  }
  delete links;
}

void Page::getDefaultCTM(double *ctm, double hDPI, double vDPI,
			 int rotate, GBool useMediaBox, GBool upsideDown) {
  GfxState *state;
  int i;

  rotate += getRotate();
  if (rotate >= 360) {
    rotate -= 360;
  } else if (rotate < 0) {
    rotate += 360;
  }
  state = new GfxState(hDPI, vDPI,
		       useMediaBox ? getMediaBox() : getCropBox(),
		       rotate, upsideDown);
  for (i = 0; i < 6; ++i) {
    ctm[i] = state->getCTM()[i];
  }
  delete state;
}
//========================================================================
//
// Page.h
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef PAGE_H
#define PAGE_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "Object.h"

class Dict;
class XRef;
class OutputDev;
class Links;
class Catalog;

//------------------------------------------------------------------------

class PDFRectangle {
public:
  double x1, y1, x2, y2;

  PDFRectangle() { x1 = y1 = x2 = y2 = 0; }
  PDFRectangle(double x1A, double y1A, double x2A, double y2A)
    { x1 = x1A; y1 = y1A; x2 = x2A; y2 = y2A; }
  GBool isValid() { return x1 != 0 || y1 != 0 || x2 != 0 || y2 != 0; }
  void clipTo(PDFRectangle *rect);
};

//------------------------------------------------------------------------
// PageAttrs
//------------------------------------------------------------------------

class PageAttrs {
public:

  // Construct a new PageAttrs object by merging a dictionary
  // (of type Pages or Page) into another PageAttrs object.  If
  // <attrs> is NULL, uses defaults.
  PageAttrs(PageAttrs *attrs, Dict *dict);

  // Destructor.
  ~PageAttrs();

  // Accessors.
  PDFRectangle *getMediaBox() { return &mediaBox; }
  PDFRectangle *getCropBox() { return &cropBox; }
  GBool isCropped() { return haveCropBox; }
  PDFRectangle *getBleedBox() { return &bleedBox; }
  PDFRectangle *getTrimBox() { return &trimBox; }
  PDFRectangle *getArtBox() { return &artBox; }
  int getRotate() { return rotate; }
  GString *getLastModified()
    { return lastModified.isString()
	? lastModified.getString() : (GString *)NULL; }
  Dict *getBoxColorInfo()
    { return boxColorInfo.isDict() ? boxColorInfo.getDict() : (Dict *)NULL; }
  Dict *getGroup()
    { return group.isDict() ? group.getDict() : (Dict *)NULL; }
  Stream *getMetadata()
    { return metadata.isStream() ? metadata.getStream() : (Stream *)NULL; }
  Dict *getPieceInfo()
    { return pieceInfo.isDict() ? pieceInfo.getDict() : (Dict *)NULL; }
  Dict *getSeparationInfo()
    { return separationInfo.isDict()
	? separationInfo.getDict() : (Dict *)NULL; }
  Dict *getResourceDict()
    { return resources.isDict() ? resources.getDict() : (Dict *)NULL; }

private:

  GBool readBox(Dict *dict, char *key, PDFRectangle *box);

  PDFRectangle mediaBox;
  PDFRectangle cropBox;
  GBool haveCropBox;
  PDFRectangle bleedBox;
  PDFRectangle trimBox;
  PDFRectangle artBox;
  int rotate;
  Object lastModified;
  Object boxColorInfo;
  Object group;
  Object metadata;
  Object pieceInfo;
  Object separationInfo;
  Object resources;
};

//------------------------------------------------------------------------
// Page
//------------------------------------------------------------------------

class Page {
public:

  // Constructor.
  Page(XRef *xrefA, int numA, Dict *pageDict, PageAttrs *attrsA);

  // Destructor.
  ~Page();

  // Is page valid?
  GBool isOk() { return ok; }

  // Get page parameters.
  int getNum() { return num; }
  PDFRectangle *getMediaBox() { return attrs->getMediaBox(); }
  PDFRectangle *getCropBox() { return attrs->getCropBox(); }
  GBool isCropped() { return attrs->isCropped(); }
  double getMediaWidth() 
    { return attrs->getMediaBox()->x2 - attrs->getMediaBox()->x1; }
  double getMediaHeight()
    { return attrs->getMediaBox()->y2 - attrs->getMediaBox()->y1; }
  double getCropWidth() 
    { return attrs->getCropBox()->x2 - attrs->getCropBox()->x1; }
  double getCropHeight()
    { return attrs->getCropBox()->y2 - attrs->getCropBox()->y1; }
  PDFRectangle *getBleedBox() { return attrs->getBleedBox(); }
  PDFRectangle *getTrimBox() { return attrs->getTrimBox(); }
  PDFRectangle *getArtBox() { return attrs->getArtBox(); }
  int getRotate() { return attrs->getRotate(); }
  GString *getLastModified() { return attrs->getLastModified(); }
  Dict *getBoxColorInfo() { return attrs->getBoxColorInfo(); }
  Dict *getGroup() { return attrs->getGroup(); }
  Stream *getMetadata() { return attrs->getMetadata(); }
  Dict *getPieceInfo() { return attrs->getPieceInfo(); }
  Dict *getSeparationInfo() { return attrs->getSeparationInfo(); }

  // Get resource dictionary.
  Dict *getResourceDict() { return attrs->getResourceDict(); }

  // Get annotations array.
  Object *getAnnots(Object *obj) { return annots.fetch(xref, obj); }

  // Return a list of links.
  Links *getLinks(Catalog *catalog);

  // Get contents.
  Object *getContents(Object *obj) { return contents.fetch(xref, obj); }

  // Display a page.
  void display(OutputDev *out, double hDPI, double vDPI,
	       int rotate, GBool useMediaBox, GBool crop,
	       Links *links, GBool printing, Catalog *catalog,
	       GBool (*abortCheckCbk)(void *data) = NULL,
	       void *abortCheckCbkData = NULL);

  // Display part of a page.
  void displaySlice(OutputDev *out, double hDPI, double vDPI,
		    int rotate, GBool useMediaBox, GBool crop,
		    int sliceX, int sliceY, int sliceW, int sliceH,
		    Links *links, GBool printing, Catalog *catalog,
		    GBool (*abortCheckCbk)(void *data) = NULL,
		    void *abortCheckCbkData = NULL);

  void makeBox(double hDPI, double vDPI, int rotate,
	       GBool useMediaBox, GBool upsideDown,
	       double sliceX, double sliceY, double sliceW, double sliceH,
	       PDFRectangle *box, GBool *crop);

  void processLinks(OutputDev *out, Catalog *catalog);

  // Get the page's default CTM.
  void getDefaultCTM(double *ctm, double hDPI, double vDPI,
		     int rotate, GBool useMediaBox, GBool upsideDown);

private:

  XRef *xref;			// the xref table for this PDF file
  int num;			// page number
  PageAttrs *attrs;		// page attributes
  Object annots;		// annotations array
  Object contents;		// page contents
  GBool ok;			// true if page is valid
};

#endif
//========================================================================
//
// Parser.cc
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stddef.h>
#include "Object.h"
#include "Array.h"
#include "Dict.h"
#include "Decrypt.h"
#include "Parser.h"
#include "XRef.h"
#include "Error.h"

Parser::Parser(XRef *xrefA, Lexer *lexerA, GBool allowStreamsA) {
  xref = xrefA;
  lexer = lexerA;
  inlineImg = 0;
  allowStreams = allowStreamsA;
  lexer->getObj(&buf1);
  lexer->getObj(&buf2);
}

Parser::~Parser() {
  buf1.free();
  buf2.free();
  delete lexer;
}

Object *Parser::getObj(Object *obj, Guchar *fileKey,
		       CryptAlgorithm encAlgorithm, int keyLength,
		       int objNum, int objGen) {
  char *key;
  Stream *str;
  Object obj2;
  int num;
  DecryptStream *decrypt;
  GString *s, *s2;
  int c;

  // refill buffer after inline image data
  if (inlineImg == 2) {
    buf1.free();
    buf2.free();
    lexer->getObj(&buf1);
    lexer->getObj(&buf2);
    inlineImg = 0;
  }

  // array
  if (buf1.isCmd("[")) {
    shift();
    obj->initArray(xref);
    while (!buf1.isCmd("]") && !buf1.isEOF())
      obj->arrayAdd(getObj(&obj2, fileKey, encAlgorithm, keyLength,
			   objNum, objGen));
    if (buf1.isEOF())
      error(getPos(), "End of file inside array");
    shift();

  // dictionary or stream
  } else if (buf1.isCmd("<<")) {
    shift();
    obj->initDict(xref);
    while (!buf1.isCmd(">>") && !buf1.isEOF()) {
      if (!buf1.isName()) {
	error(getPos(), "Dictionary key must be a name object");
	shift();
      } else {
	key = copyString(buf1.getName());
	shift();
	if (buf1.isEOF() || buf1.isError()) {
	  gfree(key);
	  break;
	}
	obj->dictAdd(key, getObj(&obj2, fileKey, encAlgorithm, keyLength,
				 objNum, objGen));
      }
    }
    if (buf1.isEOF())
      error(getPos(), "End of file inside dictionary");
    // stream objects are not allowed inside content streams or
    // object streams
    if (allowStreams && buf2.isCmd("stream")) {
      if ((str = makeStream(obj, fileKey, encAlgorithm, keyLength,
			    objNum, objGen))) {
	obj->initStream(str);
      } else {
	obj->free();
	obj->initError();
      }
    } else {
      shift();
    }

  // indirect reference or integer
  } else if (buf1.isInt()) {
    num = buf1.getInt();
    shift();
    if (buf1.isInt() && buf2.isCmd("R")) {
      obj->initRef(num, buf1.getInt());
      shift();
      shift();
    } else {
      obj->initInt(num);
    }

  // string
  } else if (buf1.isString() && fileKey) {
    s = buf1.getString();
    s2 = new GString();
    obj2.initNull();
    decrypt = new DecryptStream(new MemStream(s->getCString(), 0,
					      s->getLength(), &obj2),
				fileKey, encAlgorithm, keyLength,
				objNum, objGen);
    decrypt->reset();
    while ((c = decrypt->getChar()) != EOF) {
      s2->append((char)c);
    }
    delete decrypt;
    obj->initString(s2);
    shift();

  // simple object
  } else {
    buf1.copy(obj);
    shift();
  }

  return obj;
}

Stream *Parser::makeStream(Object *dict, Guchar *fileKey,
			   CryptAlgorithm encAlgorithm, int keyLength,
			   int objNum, int objGen) {
  Object obj;
  BaseStream *baseStr;
  Stream *str;
  Guint pos, endPos, length;

  // get stream start position
  lexer->skipToNextLine();
  pos = lexer->getPos();

  // get length
  dict->dictLookup("Length", &obj);
  if (obj.isInt()) {
    length = (Guint)obj.getInt();
    obj.free();
  } else {
    error(getPos(), "Bad 'Length' attribute in stream");
    obj.free();
    return NULL;
  }

  // check for length in damaged file
  if (xref && xref->getStreamEnd(pos, &endPos)) {
    length = endPos - pos;
  }

  // in badly damaged PDF files, we can run off the end of the input
  // stream immediately after the "stream" token
  if (!lexer->getStream()) {
    return NULL;
  }
  baseStr = lexer->getStream()->getBaseStream();

  // skip over stream data
  lexer->setPos(pos + length);

  // refill token buffers and check for 'endstream'
  shift();  // kill '>>'
  shift();  // kill 'stream'
  if (buf1.isCmd("endstream")) {
    shift();
  } else {
    error(getPos(), "Missing 'endstream'");
    // kludge for broken PDF files: just add 5k to the length, and
    // hope its enough
    length += 5000;
  }

  // make base stream
  str = baseStr->makeSubStream(pos, gTrue, length, dict);

  // handle decryption
  if (fileKey) {
    str = new DecryptStream(str, fileKey, encAlgorithm, keyLength,
			    objNum, objGen);
  }

  // get filters
  str = str->addFilters(dict);

  return str;
}

void Parser::shift() {
  if (inlineImg > 0) {
    if (inlineImg < 2) {
      ++inlineImg;
    } else {
      // in a damaged content stream, if 'ID' shows up in the middle
      // of a dictionary, we need to reset
      inlineImg = 0;
    }
  } else if (buf2.isCmd("ID")) {
    lexer->skipChar();		// skip char after 'ID' command
    inlineImg = 1;
  }
  buf1.free();
  buf1 = buf2;
  if (inlineImg > 0)		// don't buffer inline image data
    buf2.initNull();
  else
    lexer->getObj(&buf2);
}
//========================================================================
//
// Parser.h
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef PARSER_H
#define PARSER_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "Lexer.h"

//------------------------------------------------------------------------
// Parser
//------------------------------------------------------------------------

class Parser {
public:

  // Constructor.
  Parser(XRef *xrefA, Lexer *lexerA, GBool allowStreamsA);

  // Destructor.
  ~Parser();

  // Get the next object from the input stream.
  Object *getObj(Object *obj, Guchar *fileKey = NULL,
		 CryptAlgorithm encAlgorithm = cryptRC4, int keyLength = 0,
		 int objNum = 0, int objGen = 0);

  // Get stream.
  Stream *getStream() { return lexer->getStream(); }

  // Get current position in file.
  int getPos() { return lexer->getPos(); }

private:

  XRef *xref;			// the xref table for this PDF file
  Lexer *lexer;			// input stream
  GBool allowStreams;		// parse stream objects?
  Object buf1, buf2;		// next two tokens
  int inlineImg;		// set when inline image data is encountered

  Stream *makeStream(Object *dict, Guchar *fileKey,
		     CryptAlgorithm encAlgorithm, int keyLength,
		     int objNum, int objGen);
  void shift();
};

#endif

//========================================================================
//
// PreScanOutputDev.cc
//
// Copyright 2005 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <math.h>
#include "GlobalParams.h"
#include "GfxFont.h"
#include "Link.h"
#include "PreScanOutputDev.h"

//------------------------------------------------------------------------
// PreScanOutputDev
//------------------------------------------------------------------------

PreScanOutputDev::PreScanOutputDev() {
  clearStats();
}

PreScanOutputDev::~PreScanOutputDev() {
}

void PreScanOutputDev::startPage(int pageNum, GfxState *state) {
}

void PreScanOutputDev::endPage() {
}

void PreScanOutputDev::stroke(GfxState *state) {
  double *dash;
  int dashLen;
  double dashStart;

  check(state->getStrokeColorSpace(), state->getStrokeColor(),
	state->getStrokeOpacity(), state->getBlendMode());
  state->getLineDash(&dash, &dashLen, &dashStart);
  if (dashLen != 0) {
    gdi = gFalse;
  }
}

void PreScanOutputDev::fill(GfxState *state) {
  check(state->getFillColorSpace(), state->getFillColor(),
	state->getFillOpacity(), state->getBlendMode());
}

void PreScanOutputDev::eoFill(GfxState *state) {
  check(state->getFillColorSpace(), state->getFillColor(),
	state->getFillOpacity(), state->getBlendMode());
}

void PreScanOutputDev::clip(GfxState *state) {
  //~ check for a rectangle "near" the edge of the page;
  //~   else set gdi to false
}

void PreScanOutputDev::eoClip(GfxState *state) {
  //~ see clip()
}

void PreScanOutputDev::beginStringOp(GfxState *state) {
  int render;
  GfxFont *font;
  double m11, m12, m21, m22;
  Ref embRef;
  DisplayFontParam *dfp;
  GBool simpleTTF;

  render = state->getRender();
  if (!(render & 1)) {
    check(state->getFillColorSpace(), state->getFillColor(),
	  state->getFillOpacity(), state->getBlendMode());
  }
  if ((render & 3) == 1 || (render & 3) == 2) {
    check(state->getStrokeColorSpace(), state->getStrokeColor(),
	  state->getStrokeOpacity(), state->getBlendMode());
  }

  font = state->getFont();
  state->getFontTransMat(&m11, &m12, &m21, &m22);
  simpleTTF = fabs(m11 + m22) < 0.01 &&
              m11 > 0 &&
              fabs(m12) < 0.01 &&
              fabs(m21) < 0.01 &&
              fabs(state->getHorizScaling() - 1) < 0.001 &&
              (font->getType() == fontTrueType ||
	       font->getType() == fontTrueTypeOT) &&
              (font->getEmbeddedFontID(&embRef) ||
	       font->getExtFontFile() ||
	       (font->getName() &&
		(dfp = globalParams->getDisplayFont(font->getName())) &&
		dfp->kind == displayFontTT));
  if (simpleTTF) {
    //~ need to create a FoFiTrueType object, and check for a Unicode cmap
  }
  if (state->getRender() != 0 || !simpleTTF) {
    gdi = gFalse;
  }
}

void PreScanOutputDev::endStringOp(GfxState *state) {
}

GBool PreScanOutputDev::beginType3Char(GfxState *state, double x, double y,
				       double dx, double dy,
				       CharCode code, Unicode *u, int uLen) {
  // return false so all Type 3 chars get rendered (no caching)
  return gFalse;
}

void PreScanOutputDev::endType3Char(GfxState *state) {
}

void PreScanOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
				     int width, int height, GBool invert,
				     GBool inlineImg) {
  int i, j;

  check(state->getFillColorSpace(), state->getFillColor(),
	state->getFillOpacity(), state->getBlendMode());
  gdi = gFalse;

  if (inlineImg) {
    str->reset();
    j = height * ((width + 7) / 8);
    for (i = 0; i < j; ++i)
      str->getChar();
    str->close();
  }
}

void PreScanOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
				 int width, int height,
				 GfxImageColorMap *colorMap,
				 int *maskColors, GBool inlineImg) {
  GfxColorSpace *colorSpace;
  int i, j;

  colorSpace = colorMap->getColorSpace();
  if (colorSpace->getMode() == csIndexed) {
    colorSpace = ((GfxIndexedColorSpace *)colorSpace)->getBase();
  }
  if (colorSpace->getMode() != csDeviceGray &&
      colorSpace->getMode() != csCalGray) {
    gray = gFalse;
  }
  mono = gFalse;
  if (state->getBlendMode() != gfxBlendNormal) {
    transparency = gTrue;
  }
  gdi = gFalse;

  if (inlineImg) {
    str->reset();
    j = height * ((width * colorMap->getNumPixelComps() *
		   colorMap->getBits() + 7) / 8);
    for (i = 0; i < j; ++i)
      str->getChar();
    str->close();
  }
}

void PreScanOutputDev::drawMaskedImage(GfxState *state, Object *ref,
				       Stream *str,
				       int width, int height,
				       GfxImageColorMap *colorMap,
				       Stream *maskStr,
				       int maskWidth, int maskHeight,
				       GBool maskInvert) {
  GfxColorSpace *colorSpace;

  colorSpace = colorMap->getColorSpace();
  if (colorSpace->getMode() == csIndexed) {
    colorSpace = ((GfxIndexedColorSpace *)colorSpace)->getBase();
  }
  if (colorSpace->getMode() != csDeviceGray &&
      colorSpace->getMode() != csCalGray) {
    gray = gFalse;
  }
  mono = gFalse;
  if (state->getBlendMode() != gfxBlendNormal) {
    transparency = gTrue;
  }
  gdi = gFalse;
}

void PreScanOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
					   Stream *str,
					   int width, int height,
					   GfxImageColorMap *colorMap,
					   Stream *maskStr,
					   int maskWidth, int maskHeight,
					   GfxImageColorMap *maskColorMap) {
  GfxColorSpace *colorSpace;

  colorSpace = colorMap->getColorSpace();
  if (colorSpace->getMode() == csIndexed) {
    colorSpace = ((GfxIndexedColorSpace *)colorSpace)->getBase();
  }
  if (colorSpace->getMode() != csDeviceGray &&
      colorSpace->getMode() != csCalGray) {
    gray = gFalse;
  }
  mono = gFalse;
  transparency = gTrue;
  gdi = gFalse;
}

void PreScanOutputDev::beginTransparencyGroup(
			   GfxState *state, double *bbox,
			   GfxColorSpace *blendingColorSpace,
			   GBool isolated, GBool knockout,
			   GBool forSoftMask) {
  transparency = gTrue;
  gdi = gFalse;
}

void PreScanOutputDev::check(GfxColorSpace *colorSpace, GfxColor *color,
			     double opacity, GfxBlendMode blendMode) {
  GfxRGB rgb;

  if (colorSpace->getMode() == csPattern) {
    mono = gFalse;
    gray = gFalse;
    gdi = gFalse;
  } else {
    colorSpace->getRGB(color, &rgb);
    if (rgb.r != rgb.g || rgb.g != rgb.b || rgb.b != rgb.r) {
      mono = gFalse;
      gray = gFalse;
    } else if (!((rgb.r == 0 && rgb.g == 0 && rgb.b == 0) ||
		 (rgb.r == gfxColorComp1 &&
		  rgb.g == gfxColorComp1 &&
		  rgb.b == gfxColorComp1))) {
      mono = gFalse;
    }
  }
  if (opacity != 1 || blendMode != gfxBlendNormal) {
    transparency = gTrue;
  }
}

void PreScanOutputDev::clearStats() {
  mono = gTrue;
  gray = gTrue;
  transparency = gFalse;
  gdi = gTrue;
}
//========================================================================
//
// PreScanOutputDev.h
//
// Copyright 2005 Glyph & Cog, LLC
//
//========================================================================

#ifndef PRESCANOUTPUTDEV_H
#define PRESCANOUTPUTDEV_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "gtypes.h"
#include "GfxState.h"
#include "OutputDev.h"

//------------------------------------------------------------------------
// PreScanOutputDev
//------------------------------------------------------------------------

class PreScanOutputDev: public OutputDev {
public:

  // Constructor.
  PreScanOutputDev();

  // Destructor.
  virtual ~PreScanOutputDev();

  //----- get info about output device

  // Does this device use upside-down coordinates?
  // (Upside-down means (0,0) is the top left corner of the page.)
  virtual GBool upsideDown() { return gTrue; }

  // Does this device use drawChar() or drawString()?
  virtual GBool useDrawChar() { return gTrue; }

  // Does this device use beginType3Char/endType3Char?  Otherwise,
  // text in Type 3 fonts will be drawn with drawChar/drawString.
  virtual GBool interpretType3Chars() { return gTrue; }

  //----- initialization and control

  // Start a page.
  virtual void startPage(int pageNum, GfxState *state);

  // End a page.
  virtual void endPage();

  //----- path painting
  virtual void stroke(GfxState *state);
  virtual void fill(GfxState *state);
  virtual void eoFill(GfxState *state);

  //----- path clipping
  virtual void clip(GfxState *state);
  virtual void eoClip(GfxState *state);

  //----- text drawing
  virtual void beginStringOp(GfxState *state);
  virtual void endStringOp(GfxState *state);
  virtual GBool beginType3Char(GfxState *state, double x, double y,
			       double dx, double dy,
			       CharCode code, Unicode *u, int uLen);
  virtual void endType3Char(GfxState *state);

  //----- image drawing
  virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
			     int width, int height, GBool invert,
			     GBool inlineImg);
  virtual void drawImage(GfxState *state, Object *ref, Stream *str,
			 int width, int height, GfxImageColorMap *colorMap,
			 int *maskColors, GBool inlineImg);
  virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str,
			       int width, int height,
			       GfxImageColorMap *colorMap,
			       Stream *maskStr, int maskWidth, int maskHeight,
			       GBool maskInvert);
  virtual void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
				   int width, int height,
				   GfxImageColorMap *colorMap,
				   Stream *maskStr,
				   int maskWidth, int maskHeight,
				   GfxImageColorMap *maskColorMap);

  //----- transparency groups and soft masks
  virtual void beginTransparencyGroup(GfxState *state, double *bbox,
				      GfxColorSpace *blendingColorSpace,
				      GBool isolated, GBool knockout,
				      GBool forSoftMask);

  //----- special access

  // Returns true if the operations performed since the last call to
  // clearStats() are all monochrome (black or white).
  GBool isMonochrome() { return mono; }

  // Returns true if the operations performed since the last call to
  // clearStats() are all gray.
  GBool isGray() { return gray; }

  // Returns true if the operations performed since the last call to
  // clearStats() included any transparency.
  GBool usesTransparency() { return transparency; }

  // Returns true if the operations performed since the last call to
  // clearStats() are all rasterizable by GDI calls in GDIOutputDev.
  GBool isAllGDI() { return gdi; }

  // Clear the stats used by the above functions.
  void clearStats();

private:

  void check(GfxColorSpace *colorSpace, GfxColor *color,
	     double opacity, GfxBlendMode blendMode);

  GBool mono;
  GBool gray;
  GBool transparency;
  GBool gdi;
};

#endif
//========================================================================
//
// SecurityHandler.cc
//
// Copyright 2004 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include "GString.h"
#include "PDFDoc.h"
#include "Decrypt.h"
#include "Error.h"
#include "GlobalParams.h"
#if HAVE_XPDFCORE
#  include "XPDFCore.h"
#elif HAVE_WINPDFCORE
#  include "WinPDFCore.h"
#endif
#ifdef ENABLE_PLUGINS
#  include "XpdfPluginAPI.h"
#endif
#include "SecurityHandler.h"

//------------------------------------------------------------------------
// SecurityHandler
//------------------------------------------------------------------------

SecurityHandler *SecurityHandler::make(PDFDoc *docA, Object *encryptDictA) {
  Object filterObj;
  SecurityHandler *secHdlr;
#ifdef ENABLE_PLUGINS
  XpdfSecurityHandler *xsh;
#endif

  encryptDictA->dictLookup("Filter", &filterObj);
  if (filterObj.isName("Standard")) {
    secHdlr = new StandardSecurityHandler(docA, encryptDictA);
  } else if (filterObj.isName()) {
#ifdef ENABLE_PLUGINS
    if ((xsh = globalParams->getSecurityHandler(filterObj.getName()))) {
      secHdlr = new ExternalSecurityHandler(docA, encryptDictA, xsh);
    } else {
#endif
      error(-1, "Couldn't find the '%s' security handler",
	    filterObj.getName());
      secHdlr = NULL;
#ifdef ENABLE_PLUGINS
    }
#endif
  } else {
    error(-1, "Missing or invalid 'Filter' entry in encryption dictionary");
    secHdlr = NULL;
  }
  filterObj.free();
  return secHdlr;
}

SecurityHandler::SecurityHandler(PDFDoc *docA) {
  doc = docA;
}

SecurityHandler::~SecurityHandler() {
}

GBool SecurityHandler::checkEncryption(GString *ownerPassword,
				       GString *userPassword) {
  void *authData;
  GBool ok;
  int i;

  if (ownerPassword || userPassword) {
    authData = makeAuthData(ownerPassword, userPassword);
  } else {
    authData = NULL;
  }
  ok = authorize(authData);
  if (authData) {
    freeAuthData(authData);
  }
  for (i = 0; !ok && i < 3; ++i) {
    if (!(authData = getAuthData())) {
      break;
    }
    ok = authorize(authData);
    if (authData) {
      freeAuthData(authData);
    }
  }
  if (!ok) {
    error(-1, "Incorrect password");
  }
  return ok;
}

//------------------------------------------------------------------------
// StandardSecurityHandler
//------------------------------------------------------------------------

class StandardAuthData {
public:

  StandardAuthData(GString *ownerPasswordA, GString *userPasswordA) {
    ownerPassword = ownerPasswordA;
    userPassword = userPasswordA;
  }

  ~StandardAuthData() {
    if (ownerPassword) {
      delete ownerPassword;
    }
    if (userPassword) {
      delete userPassword;
    }
  }

  GString *ownerPassword;
  GString *userPassword;
};

StandardSecurityHandler::StandardSecurityHandler(PDFDoc *docA,
						 Object *encryptDictA):
  SecurityHandler(docA)
{
  Object versionObj, revisionObj, lengthObj;
  Object ownerKeyObj, userKeyObj, permObj, fileIDObj;
  Object fileIDObj1;
  Object cryptFiltersObj, streamFilterObj, stringFilterObj;
  Object cryptFilterObj, cfmObj, cfLengthObj;
  Object encryptMetadataObj;

  ok = gFalse;
  fileID = NULL;
  ownerKey = NULL;
  userKey = NULL;

  encryptDictA->dictLookup("V", &versionObj);
  encryptDictA->dictLookup("R", &revisionObj);
  encryptDictA->dictLookup("Length", &lengthObj);
  encryptDictA->dictLookup("O", &ownerKeyObj);
  encryptDictA->dictLookup("U", &userKeyObj);
  encryptDictA->dictLookup("P", &permObj);
  doc->getXRef()->getTrailerDict()->dictLookup("ID", &fileIDObj);
  if (versionObj.isInt() &&
      revisionObj.isInt() &&
      ownerKeyObj.isString() && ownerKeyObj.getString()->getLength() == 32 &&
      userKeyObj.isString() && userKeyObj.getString()->getLength() == 32 &&
      permObj.isInt()) {
    encVersion = versionObj.getInt();
    encRevision = revisionObj.getInt();
    encAlgorithm = cryptRC4;
    // revision 2 forces a 40-bit key - some buggy PDF generators
    // set the Length value incorrectly
    if (encRevision == 2 || !lengthObj.isInt()) {
      fileKeyLength = 5;
    } else {
      fileKeyLength = lengthObj.getInt() / 8;
    }
    encryptMetadata = gTrue;
    //~ this currently only handles a subset of crypt filter functionality
    if (encVersion == 4 && encRevision == 4) {
      encryptDictA->dictLookup("CF", &cryptFiltersObj);
      encryptDictA->dictLookup("StmF", &streamFilterObj);
      encryptDictA->dictLookup("StrF", &stringFilterObj);
      if (cryptFiltersObj.isDict() &&
	  streamFilterObj.isName() &&
	  stringFilterObj.isName() &&
	  !strcmp(streamFilterObj.getName(), stringFilterObj.getName())) {
	if (cryptFiltersObj.dictLookup(streamFilterObj.getName(),
				       &cryptFilterObj)->isDict()) {
	  cryptFilterObj.dictLookup("CFM", &cfmObj);
	  if (cfmObj.isName("V2")) {
	    encVersion = 2;
	    encRevision = 3;
	    if (cryptFilterObj.dictLookup("Length", &cfLengthObj)->isInt()) {
	      //~ according to the spec, this should be cfLengthObj / 8
	      fileKeyLength = cfLengthObj.getInt();
	    }
	    cfLengthObj.free();
	  } else if (cfmObj.isName("AESV2")) {
	    encVersion = 2;
	    encRevision = 3;
	    encAlgorithm = cryptAES;
	    if (cryptFilterObj.dictLookup("Length", &cfLengthObj)->isInt()) {
	      //~ according to the spec, this should be cfLengthObj / 8
	      fileKeyLength = cfLengthObj.getInt();
	    }
	    cfLengthObj.free();
	  }
	  cfmObj.free();
	}
	cryptFilterObj.free();
      }
      stringFilterObj.free();
      streamFilterObj.free();
      cryptFiltersObj.free();
      if (encryptDictA->dictLookup("EncryptMetadata",
				   &encryptMetadataObj)->isBool()) {
	encryptMetadata = encryptMetadataObj.getBool();
      }
      encryptMetadataObj.free();
    }
    permFlags = permObj.getInt();
    ownerKey = ownerKeyObj.getString()->copy();
    userKey = userKeyObj.getString()->copy();
    if (encVersion >= 1 && encVersion <= 2 &&
	encRevision >= 2 && encRevision <= 3) {
      if (fileIDObj.isArray()) {
	if (fileIDObj.arrayGet(0, &fileIDObj1)->isString()) {
	  fileID = fileIDObj1.getString()->copy();
	} else {
	  fileID = new GString();
	}
	fileIDObj1.free();
      } else {
	fileID = new GString();
      }
      ok = gTrue;
    } else {
      error(-1, "Unsupported version/revision (%d/%d) of Standard security handler",
	    encVersion, encRevision);
    }
  } else {
    error(-1, "Weird encryption info");
  }
  if (fileKeyLength > 16) {
    fileKeyLength = 16;
  }
  fileIDObj.free();
  permObj.free();
  userKeyObj.free();
  ownerKeyObj.free();
  lengthObj.free();
  revisionObj.free();
  versionObj.free();
}

StandardSecurityHandler::~StandardSecurityHandler() {
  if (fileID) {
    delete fileID;
  }
  if (ownerKey) {
    delete ownerKey;
  }
  if (userKey) {
    delete userKey;
  }
}

void *StandardSecurityHandler::makeAuthData(GString *ownerPassword,
					    GString *userPassword) {
  return new StandardAuthData(ownerPassword ? ownerPassword->copy()
			                    : (GString *)NULL,
			      userPassword ? userPassword->copy()
			                   : (GString *)NULL);
}

void *StandardSecurityHandler::getAuthData() {
#if HAVE_XPDFCORE
  XPDFCore *core;
  GString *password;

  if (!(core = (XPDFCore *)doc->getGUIData()) ||
      !(password = core->getPassword())) {
    return NULL;
  }
  return new StandardAuthData(password, password->copy());
#elif HAVE_WINPDFCORE
  WinPDFCore *core;
  GString *password;

  if (!(core = (WinPDFCore *)doc->getGUIData()) ||
      !(password = core->getPassword())) {
    return NULL;
  }
  return new StandardAuthData(password, password->copy());
#else
  return NULL;
#endif
}

void StandardSecurityHandler::freeAuthData(void *authData) {
  delete (StandardAuthData *)authData;
}

GBool StandardSecurityHandler::authorize(void *authData) {
  GString *ownerPassword, *userPassword;

  if (!ok) {
    return gFalse;
  }
  if (authData) {
    ownerPassword = ((StandardAuthData *)authData)->ownerPassword;
    userPassword = ((StandardAuthData *)authData)->userPassword;
  } else {
    ownerPassword = NULL;
    userPassword = NULL;
  }
  if (!Decrypt::makeFileKey(encVersion, encRevision, fileKeyLength,
			    ownerKey, userKey, permFlags, fileID,
			    ownerPassword, userPassword, fileKey,
			    encryptMetadata, &ownerPasswordOk)) {
    return gFalse;
  }
  return gTrue;
}

#ifdef ENABLE_PLUGINS

//------------------------------------------------------------------------
// ExternalSecurityHandler
//------------------------------------------------------------------------

ExternalSecurityHandler::ExternalSecurityHandler(PDFDoc *docA,
						 Object *encryptDictA,
						 XpdfSecurityHandler *xshA):
  SecurityHandler(docA)
{
  encryptDictA->copy(&encryptDict);
  xsh = xshA;
  encAlgorithm = cryptRC4; //~ this should be obtained via getKey
  ok = gFalse;

  if (!(*xsh->newDoc)(xsh->handlerData, (XpdfDoc)docA,
		      (XpdfObject)encryptDictA, &docData)) {
    return;
  }

  ok = gTrue;
}

ExternalSecurityHandler::~ExternalSecurityHandler() {
  (*xsh->freeDoc)(xsh->handlerData, docData);
  encryptDict.free();
}

void *ExternalSecurityHandler::makeAuthData(GString *ownerPassword,
					    GString *userPassword) {
  char *opw, *upw;
  void *authData;

  opw = ownerPassword ? ownerPassword->getCString() : (char *)NULL;
  upw = userPassword ? userPassword->getCString() : (char *)NULL;
  if (!(*xsh->makeAuthData)(xsh->handlerData, docData, opw, upw, &authData)) {
    return NULL;
  }
  return authData;
}

void *ExternalSecurityHandler::getAuthData() {
  void *authData;

  if (!(*xsh->getAuthData)(xsh->handlerData, docData, &authData)) {
    return NULL;
  }
  return authData;
}

void ExternalSecurityHandler::freeAuthData(void *authData) {
  (*xsh->freeAuthData)(xsh->handlerData, docData, authData);
}

GBool ExternalSecurityHandler::authorize(void *authData) {
  char *key;
  int length;

  if (!ok) {
    return gFalse;
  }
  permFlags = (*xsh->authorize)(xsh->handlerData, docData, authData);
  if (!(permFlags & xpdfPermissionOpen)) {
    return gFalse;
  }
  if (!(*xsh->getKey)(xsh->handlerData, docData, &key, &length, &encVersion)) {
    return gFalse;
  }
  if ((fileKeyLength = length) > 16) {
    fileKeyLength = 16;
  }
  memcpy(fileKey, key, fileKeyLength);
  (*xsh->freeKey)(xsh->handlerData, docData, key, length);
  return gTrue;
}

#endif // ENABLE_PLUGINS
//========================================================================
//
// SecurityHandler.h
//
// Copyright 2004 Glyph & Cog, LLC
//
//========================================================================

#ifndef SECURITYHANDLER_H
#define SECURITYHANDLER_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "gtypes.h"
#include "Object.h"

class GString;
class PDFDoc;
struct XpdfSecurityHandler;

//------------------------------------------------------------------------
// SecurityHandler
//------------------------------------------------------------------------

class SecurityHandler {
public:

  static SecurityHandler *make(PDFDoc *docA, Object *encryptDictA);

  SecurityHandler(PDFDoc *docA);
  virtual ~SecurityHandler();

  // Check the document's encryption.  If the document is encrypted,
  // this will first try <ownerPassword> and <userPassword> (in
  // "batch" mode), and if those fail, it will attempt to request a
  // password from the user.  This is the high-level function that
  // calls the lower level functions for the specific security handler
  // (requesting a password three times, etc.).  Returns true if the
  // document can be opened (if it's unencrypted, or if a correct
  // password is obtained); false otherwise (encrypted and no correct
  // password).
  GBool checkEncryption(GString *ownerPassword,
			GString *userPassword);

  // Create authorization data for the specified owner and user
  // passwords.  If the security handler doesn't support "batch" mode,
  // this function should return NULL.
  virtual void *makeAuthData(GString *ownerPassword,
			     GString *userPassword) = 0;

  // Construct authorization data, typically by prompting the user for
  // a password.  Returns an authorization data object, or NULL to
  // cancel.
  virtual void *getAuthData() = 0;

  // Free the authorization data returned by makeAuthData or
  // getAuthData.
  virtual void freeAuthData(void *authData) = 0;

  // Attempt to authorize the document, using the supplied
  // authorization data (which may be NULL).  Returns true if
  // successful (i.e., if at least the right to open the document was
  // granted).
  virtual GBool authorize(void *authData) = 0;

  // Return the various authorization parameters.  These are only
  // valid after authorize has returned true.
  virtual int getPermissionFlags() = 0;
  virtual GBool getOwnerPasswordOk() = 0;
  virtual Guchar *getFileKey() = 0;
  virtual int getFileKeyLength() = 0;
  virtual int getEncVersion() = 0;
  virtual CryptAlgorithm getEncAlgorithm() = 0;

protected:

  PDFDoc *doc;
};

//------------------------------------------------------------------------
// StandardSecurityHandler
//------------------------------------------------------------------------

class StandardSecurityHandler: public SecurityHandler {
public:

  StandardSecurityHandler(PDFDoc *docA, Object *encryptDictA);
  virtual ~StandardSecurityHandler();

  virtual void *makeAuthData(GString *ownerPassword,
			     GString *userPassword);
  virtual void *getAuthData();
  virtual void freeAuthData(void *authData);
  virtual GBool authorize(void *authData);
  virtual int getPermissionFlags() { return permFlags; }
  virtual GBool getOwnerPasswordOk() { return ownerPasswordOk; }
  virtual Guchar *getFileKey() { return fileKey; }
  virtual int getFileKeyLength() { return fileKeyLength; }
  virtual int getEncVersion() { return encVersion; }
  virtual CryptAlgorithm getEncAlgorithm() { return encAlgorithm; }

private:

  int permFlags;
  GBool ownerPasswordOk;
  Guchar fileKey[16];
  int fileKeyLength;
  int encVersion;
  int encRevision;
  CryptAlgorithm encAlgorithm;
  GBool encryptMetadata;

  GString *ownerKey, *userKey;
  GString *fileID;
  GBool ok;
};

#ifdef ENABLE_PLUGINS
//------------------------------------------------------------------------
// ExternalSecurityHandler
//------------------------------------------------------------------------

class ExternalSecurityHandler: public SecurityHandler {
public:

  ExternalSecurityHandler(PDFDoc *docA, Object *encryptDictA,
			  XpdfSecurityHandler *xshA);
  virtual ~ExternalSecurityHandler();

  virtual void *makeAuthData(GString *ownerPassword,
			     GString *userPassword);
  virtual void *getAuthData();
  virtual void freeAuthData(void *authData);
  virtual GBool authorize(void *authData);
  virtual int getPermissionFlags() { return permFlags; }
  virtual GBool getOwnerPasswordOk() { return gFalse; }
  virtual Guchar *getFileKey() { return fileKey; }
  virtual int getFileKeyLength() { return fileKeyLength; }
  virtual int getEncVersion() { return encVersion; }
  virtual CryptAlgorithm getEncAlgorithm() { return encAlgorithm; }

private:

  Object encryptDict;
  XpdfSecurityHandler *xsh;
  void *docData;
  int permFlags;
  Guchar fileKey[16];
  int fileKeyLength;
  int encVersion;
  CryptAlgorithm encAlgorithm;
  GBool ok;
};
#endif // ENABLE_PLUGINS

#endif
//========================================================================
//
// SplashOutputDev.cc
//
// Copyright 2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <string.h>
#include <math.h>
#include "gfile.h"
#include "GlobalParams.h"
#include "Error.h"
#include "Object.h"
#include "GfxFont.h"
#include "Link.h"
#include "CharCodeToUnicode.h"
#include "FontEncodingTables.h"
#include "FoFiTrueType.h"
#include "SplashBitmap.h"
#include "SplashGlyphBitmap.h"
#include "SplashPattern.h"
#include "SplashScreen.h"
#include "SplashPath.h"
#include "SplashState.h"
#include "SplashErrorCodes.h"
#include "SplashFontEngine.h"
#include "SplashFont.h"
#include "SplashFontFile.h"
#include "SplashFontFileID.h"
#include "Splash.h"
#include "SplashOutputDev.h"

#ifdef VMS
#if (__VMS_VER < 70000000)
extern "C" int unlink(char *filename);
#endif
#endif

//------------------------------------------------------------------------

// Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result.
static inline Guchar div255(int x) {
  return (Guchar)((x + (x >> 8) + 0x80) >> 8);
}

//------------------------------------------------------------------------
// Blend functions
//------------------------------------------------------------------------

static void splashOutBlendMultiply(SplashColorPtr src, SplashColorPtr dest,
				   SplashColorPtr blend, SplashColorMode cm) {
  int i;

  for (i = 0; i < splashColorModeNComps[cm]; ++i) {
    blend[i] = (dest[i] * src[i]) / 255;
  }
}

static void splashOutBlendScreen(SplashColorPtr src, SplashColorPtr dest,
				 SplashColorPtr blend, SplashColorMode cm) {
  int i;

  for (i = 0; i < splashColorModeNComps[cm]; ++i) {
    blend[i] = dest[i] + src[i] - (dest[i] * src[i]) / 255;
  }
}

static void splashOutBlendOverlay(SplashColorPtr src, SplashColorPtr dest,
				  SplashColorPtr blend, SplashColorMode cm) {
  int i;

  for (i = 0; i < splashColorModeNComps[cm]; ++i) {
    blend[i] = dest[i] < 0x80
                 ? (src[i] * 2 * dest[i]) / 255
                 : 255 - 2 * ((255 - src[i]) * (255 - dest[i])) / 255;
  }
}

static void splashOutBlendDarken(SplashColorPtr src, SplashColorPtr dest,
				 SplashColorPtr blend, SplashColorMode cm) {
  int i;

  for (i = 0; i < splashColorModeNComps[cm]; ++i) {
    blend[i] = dest[i] < src[i] ? dest[i] : src[i];
  }
}

static void splashOutBlendLighten(SplashColorPtr src, SplashColorPtr dest,
				  SplashColorPtr blend, SplashColorMode cm) {
  int i;

  for (i = 0; i < splashColorModeNComps[cm]; ++i) {
    blend[i] = dest[i] > src[i] ? dest[i] : src[i];
  }
}

static void splashOutBlendColorDodge(SplashColorPtr src, SplashColorPtr dest,
				     SplashColorPtr blend,
				     SplashColorMode cm) {
  int i, x;

  for (i = 0; i < splashColorModeNComps[cm]; ++i) {
    if (src[i] == 255) {
      blend[i] = 255;
    } else {
      x = (dest[i] * 255) / (255 - src[i]);
      blend[i] = x <= 255 ? x : 255;
    }
  }
}

static void splashOutBlendColorBurn(SplashColorPtr src, SplashColorPtr dest,
				    SplashColorPtr blend, SplashColorMode cm) {
  int i, x;

  for (i = 0; i < splashColorModeNComps[cm]; ++i) {
    if (src[i] == 0) {
      blend[i] = 0;
    } else {
      x = ((255 - dest[i]) * 255) / src[i];
      blend[i] = x <= 255 ? 255 - x : 0;
    }
  }
}

static void splashOutBlendHardLight(SplashColorPtr src, SplashColorPtr dest,
				    SplashColorPtr blend, SplashColorMode cm) {
  int i;

  for (i = 0; i < splashColorModeNComps[cm]; ++i) {
    blend[i] = src[i] < 0x80
                 ? (dest[i] * 2 * src[i]) / 255
                 : 255 - 2 * ((255 - dest[i]) * (255 - src[i])) / 255;
  }
}

static void splashOutBlendSoftLight(SplashColorPtr src, SplashColorPtr dest,
				    SplashColorPtr blend, SplashColorMode cm) {
  int i, x;

  for (i = 0; i < splashColorModeNComps[cm]; ++i) {
    if (src[i] < 0x80) {
      blend[i] = dest[i] - (255 - 2 * src[i]) * dest[i] * (255 - dest[i]) /
	         (255 * 255);
    } else {
      if (dest[i] < 0x40) {
	x = (((((16 * dest[i] - 12 * 255) * dest[i]) / 255)
	      + 4 * 255) * dest[i]) / 255;
      } else {
	x = (int)sqrt(255.0 * dest[i]);
      }
      blend[i] = dest[i] + (2 * src[i] - 255) * (x - dest[i]) / 255;
    }
  }
}

static void splashOutBlendDifference(SplashColorPtr src, SplashColorPtr dest,
				     SplashColorPtr blend,
				     SplashColorMode cm) {
  int i;

  for (i = 0; i < splashColorModeNComps[cm]; ++i) {
    blend[i] = dest[i] < src[i] ? src[i] - dest[i] : dest[i] - src[i];
  }
}

static void splashOutBlendExclusion(SplashColorPtr src, SplashColorPtr dest,
				    SplashColorPtr blend, SplashColorMode cm) {
  int i;

  for (i = 0; i < splashColorModeNComps[cm]; ++i) {
    blend[i] = dest[i] + src[i] - (2 * dest[i] * src[i]) / 255;
  }
}

static void cvtRGBToHSV(Guchar r, Guchar g, Guchar b, int *h, int *s, int *v) {
  int cmax, cmid, cmin, x;

  if (r >= g) {
    if (g >= b)      { x = 0; cmax = r; cmid = g; cmin = b; }
    else if (b >= r) { x = 4; cmax = b; cmid = r; cmin = g; }
    else             { x = 5; cmax = r; cmid = b; cmin = g; }
  } else {
    if (r >= b)      { x = 1; cmax = g; cmid = r; cmin = b; }
    else if (g >= b) { x = 2; cmax = g; cmid = b; cmin = r; }
    else             { x = 3; cmax = b; cmid = g; cmin = r; }
  }
  if (cmax == cmin) {
    *h = *s = 0;
  } else {
    *h = x * 60;
    if (x & 1) {
      *h += ((cmax - cmid) * 60) / (cmax - cmin);
    } else {
      *h += ((cmid - cmin) * 60) / (cmax - cmin);
    }
    *s = (255 * (cmax - cmin)) / cmax;
  }
  *v = cmax;
}

static void cvtHSVToRGB(int h, int s, int v, Guchar *r, Guchar *g, Guchar *b) {
  int x, f, cmax, cmid, cmin;

  if (s == 0) {
    *r = *g = *b = v;
  } else {
    x = h / 60;
    f = h % 60;
    cmax = v;
    if (x & 1) {
      cmid = div255(v * 255 - ((s * f) / 60));
    } else {
      cmid = div255(v * (255 - ((s * (60 - f)) / 60)));
    }
    cmin = div255(v * (255 - s));
    switch (x) {
    case 0: *r = cmax; *g = cmid; *b = cmin; break;
    case 1: *g = cmax; *r = cmid; *b = cmin; break;
    case 2: *g = cmax; *b = cmid; *r = cmin; break;
    case 3: *b = cmax; *g = cmid; *r = cmin; break;
    case 4: *b = cmax; *r = cmid; *g = cmin; break;
    case 5: *r = cmax; *b = cmid; *g = cmin; break;
    }
  }
}

static void splashOutBlendHue(SplashColorPtr src, SplashColorPtr dest,
			      SplashColorPtr blend, SplashColorMode cm) {
  int hs, ss, vs, hd, sd, vd;
#if SPLASH_CMYK
  Guchar r, g, b;
#endif

  switch (cm) {
  case splashModeMono1:
  case splashModeMono8:
    blend[0] = dest[0];
    break;
  case splashModeRGB8:
  case splashModeBGR8:
    cvtRGBToHSV(src[0], src[1], src[2], &hs, &ss, &vs);
    cvtRGBToHSV(dest[0], dest[1], dest[2], &hd, &sd, &vd);
    cvtHSVToRGB(hs, sd, vd, &blend[0], &blend[1], &blend[2]);
    break;
#if SPLASH_CMYK
  case splashModeCMYK8:
    //~ (0xff - ...) should be clipped
    cvtRGBToHSV(0xff - (src[0] + src[3]),
		0xff - (src[1] + src[3]),
		0xff - (src[2] + src[3]), &hs, &ss, &vs);
    cvtRGBToHSV(0xff - (dest[0] + dest[3]),
		0xff - (dest[1] + dest[3]),
		0xff - (dest[2] + dest[3]), &hd, &sd, &vd);
    cvtHSVToRGB(hs, sd, vd, &r, &g, &b);
    //~ should do black generation
    blend[0] = 0xff - r;
    blend[1] = 0xff - g;
    blend[2] = 0xff - b;
    blend[3] = 0;
    break;
#endif
  }
}

static void splashOutBlendSaturation(SplashColorPtr src, SplashColorPtr dest,
				     SplashColorPtr blend,
				     SplashColorMode cm) {
  int hs, ss, vs, hd, sd, vd;
#if SPLASH_CMYK
  Guchar r, g, b;
#endif

  switch (cm) {
  case splashModeMono1:
  case splashModeMono8:
    blend[0] = dest[0];
    break;
  case splashModeRGB8:
  case splashModeBGR8:
    cvtRGBToHSV(src[0], src[1], src[2], &hs, &ss, &vs);
    cvtRGBToHSV(dest[0], dest[1], dest[2], &hd, &sd, &vd);
    cvtHSVToRGB(hd, ss, vd, &blend[0], &blend[1], &blend[2]);
    break;
#if SPLASH_CMYK
  case splashModeCMYK8:
    //~ (0xff - ...) should be clipped
    cvtRGBToHSV(0xff - (src[0] + src[3]),
		0xff - (src[1] + src[3]),
		0xff - (src[2] + src[3]), &hs, &ss, &vs);
    cvtRGBToHSV(0xff - (dest[0] + dest[3]),
		0xff - (dest[1] + dest[3]),
		0xff - (dest[2] + dest[3]), &hd, &sd, &vd);
    cvtHSVToRGB(hd, ss, vd, &r, &g, &b);
    //~ should do black generation
    blend[0] = 0xff - r;
    blend[1] = 0xff - g;
    blend[2] = 0xff - b;
    blend[3] = 0;
    break;
#endif
  }
}

static void splashOutBlendColor(SplashColorPtr src, SplashColorPtr dest,
				SplashColorPtr blend, SplashColorMode cm) {
  int hs, ss, vs, hd, sd, vd;
#if SPLASH_CMYK
  Guchar r, g, b;
#endif

  switch (cm) {
  case splashModeMono1:
  case splashModeMono8:
    blend[0] = dest[0];
    break;
  case splashModeRGB8:
  case splashModeBGR8:
    cvtRGBToHSV(src[0], src[1], src[2], &hs, &ss, &vs);
    cvtRGBToHSV(dest[0], dest[1], dest[2], &hd, &sd, &vd);
    cvtHSVToRGB(hs, ss, vd, &blend[0], &blend[1], &blend[2]);
    break;
#if SPLASH_CMYK
  case splashModeCMYK8:
    //~ (0xff - ...) should be clipped
    cvtRGBToHSV(0xff - (src[0] + src[3]),
		0xff - (src[1] + src[3]),
		0xff - (src[2] + src[3]), &hs, &ss, &vs);
    cvtRGBToHSV(0xff - (dest[0] + dest[3]),
		0xff - (dest[1] + dest[3]),
		0xff - (dest[2] + dest[3]), &hd, &sd, &vd);
    cvtHSVToRGB(hs, ss, vd, &r, &g, &b);
    //~ should do black generation
    blend[0] = 0xff - r;
    blend[1] = 0xff - g;
    blend[2] = 0xff - b;
    blend[3] = 0;
    break;
#endif
  }
}

static void splashOutBlendLuminosity(SplashColorPtr src, SplashColorPtr dest,
				     SplashColorPtr blend,
				     SplashColorMode cm) {
  int hs, ss, vs, hd, sd, vd;
#if SPLASH_CMYK
  Guchar r, g, b;
#endif

  switch (cm) {
  case splashModeMono1:
  case splashModeMono8:
    blend[0] = dest[0];
    break;
  case splashModeRGB8:
  case splashModeBGR8:
    cvtRGBToHSV(src[0], src[1], src[2], &hs, &ss, &vs);
    cvtRGBToHSV(dest[0], dest[1], dest[2], &hd, &sd, &vd);
    cvtHSVToRGB(hd, sd, vs, &blend[0], &blend[1], &blend[2]);
    break;
#if SPLASH_CMYK
  case splashModeCMYK8:
    //~ (0xff - ...) should be clipped
    cvtRGBToHSV(0xff - (src[0] + src[3]),
		0xff - (src[1] + src[3]),
		0xff - (src[2] + src[3]), &hs, &ss, &vs);
    cvtRGBToHSV(0xff - (dest[0] + dest[3]),
		0xff - (dest[1] + dest[3]),
		0xff - (dest[2] + dest[3]), &hd, &sd, &vd);
    cvtHSVToRGB(hd, sd, vs, &r, &g, &b);
    //~ should do black generation
    blend[0] = 0xff - r;
    blend[1] = 0xff - g;
    blend[2] = 0xff - b;
    blend[3] = 0;
    break;
#endif
  }
}

// NB: This must match the GfxBlendMode enum defined in GfxState.h.
SplashBlendFunc splashOutBlendFuncs[] = {
  NULL,
  &splashOutBlendMultiply,
  &splashOutBlendScreen,
  &splashOutBlendOverlay,
  &splashOutBlendDarken,
  &splashOutBlendLighten,
  &splashOutBlendColorDodge,
  &splashOutBlendColorBurn,
  &splashOutBlendHardLight,
  &splashOutBlendSoftLight,
  &splashOutBlendDifference,
  &splashOutBlendExclusion,
  &splashOutBlendHue,
  &splashOutBlendSaturation,
  &splashOutBlendColor,
  &splashOutBlendLuminosity
};

//------------------------------------------------------------------------
// Font substitutions
//------------------------------------------------------------------------

struct SplashOutFontSubst {
  char *name;
  double mWidth;
};

// index: {symbolic:12, fixed:8, serif:4, sans-serif:0} + bold*2 + italic
static SplashOutFontSubst splashOutSubstFonts[16] = {
  {"Helvetica",             0.833},
  {"Helvetica-Oblique",     0.833},
  {"Helvetica-Bold",        0.889},
  {"Helvetica-BoldOblique", 0.889},
  {"Times-Roman",           0.788},
  {"Times-Italic",          0.722},
  {"Times-Bold",            0.833},
  {"Times-BoldItalic",      0.778},
  {"Courier",               0.600},
  {"Courier-Oblique",       0.600},
  {"Courier-Bold",          0.600},
  {"Courier-BoldOblique",   0.600},
  {"Symbol",                0.576},
  {"Symbol",                0.576},
  {"Symbol",                0.576},
  {"Symbol",                0.576}
};

//------------------------------------------------------------------------
// SplashOutFontFileID
//------------------------------------------------------------------------

class SplashOutFontFileID: public SplashFontFileID {
public:

  SplashOutFontFileID(Ref *rA) { r = *rA; substIdx = -1; }

  ~SplashOutFontFileID() {}

  GBool matches(SplashFontFileID *id) {
    return ((SplashOutFontFileID *)id)->r.num == r.num &&
           ((SplashOutFontFileID *)id)->r.gen == r.gen;
  }

  void setSubstIdx(int substIdxA) { substIdx = substIdxA; }
  int getSubstIdx() { return substIdx; }

private:

  Ref r;
  int substIdx;
};

//------------------------------------------------------------------------
// T3FontCache
//------------------------------------------------------------------------

struct T3FontCacheTag {
  Gushort code;
  Gushort mru;			// valid bit (0x8000) and MRU index
};

class T3FontCache {
public:

  T3FontCache(Ref *fontID, double m11A, double m12A,
	      double m21A, double m22A,
	      int glyphXA, int glyphYA, int glyphWA, int glyphHA,
	      GBool aa, GBool validBBoxA);
  ~T3FontCache();
  GBool matches(Ref *idA, double m11A, double m12A,
		double m21A, double m22A)
    { return fontID.num == idA->num && fontID.gen == idA->gen &&
	     m11 == m11A && m12 == m12A && m21 == m21A && m22 == m22A; }

  Ref fontID;			// PDF font ID
  double m11, m12, m21, m22;	// transform matrix
  int glyphX, glyphY;		// pixel offset of glyph bitmaps
  int glyphW, glyphH;		// size of glyph bitmaps, in pixels
  GBool validBBox;		// false if the bbox was [0 0 0 0]
  int glyphSize;		// size of glyph bitmaps, in bytes
  int cacheSets;		// number of sets in cache
  int cacheAssoc;		// cache associativity (glyphs per set)
  Guchar *cacheData;		// glyph pixmap cache
  T3FontCacheTag *cacheTags;	// cache tags, i.e., char codes
};

T3FontCache::T3FontCache(Ref *fontIDA, double m11A, double m12A,
			 double m21A, double m22A,
			 int glyphXA, int glyphYA, int glyphWA, int glyphHA,
			 GBool validBBoxA, GBool aa) {
  int i;

  fontID = *fontIDA;
  m11 = m11A;
  m12 = m12A;
  m21 = m21A;
  m22 = m22A;
  glyphX = glyphXA;
  glyphY = glyphYA;
  glyphW = glyphWA;
  glyphH = glyphHA;
  validBBox = validBBoxA;
  if (aa) {
    glyphSize = glyphW * glyphH;
  } else {
    glyphSize = ((glyphW + 7) >> 3) * glyphH;
  }
  cacheAssoc = 8;
  if (glyphSize <= 256) {
    cacheSets = 8;
  } else if (glyphSize <= 512) {
    cacheSets = 4;
  } else if (glyphSize <= 1024) {
    cacheSets = 2;
  } else {
    cacheSets = 1;
  }
  cacheData = (Guchar *)gmallocn(cacheSets * cacheAssoc, glyphSize);
  cacheTags = (T3FontCacheTag *)gmallocn(cacheSets * cacheAssoc,
					 sizeof(T3FontCacheTag));
  for (i = 0; i < cacheSets * cacheAssoc; ++i) {
    cacheTags[i].mru = i & (cacheAssoc - 1);
  }
}

T3FontCache::~T3FontCache() {
  gfree(cacheData);
  gfree(cacheTags);
}

struct T3GlyphStack {
  Gushort code;			// character code

  //----- cache info
  T3FontCache *cache;		// font cache for the current font
  T3FontCacheTag *cacheTag;	// pointer to cache tag for the glyph
  Guchar *cacheData;		// pointer to cache data for the glyph

  //----- saved state
  SplashBitmap *origBitmap;
  Splash *origSplash;
  double origCTM4, origCTM5;

  T3GlyphStack *next;		// next object on stack
};

//------------------------------------------------------------------------
// SplashTransparencyGroup
//------------------------------------------------------------------------

struct SplashTransparencyGroup {
  int tx, ty;			// translation coordinates
  SplashBitmap *tBitmap;	// bitmap for transparency group
  GfxColorSpace *blendingColorSpace;
  GBool isolated;

  //----- saved state
  SplashBitmap *origBitmap;
  Splash *origSplash;

  SplashTransparencyGroup *next;
};

//------------------------------------------------------------------------
// SplashOutputDev
//------------------------------------------------------------------------

SplashOutputDev::SplashOutputDev(SplashColorMode colorModeA,
				 int bitmapRowPadA,
				 GBool reverseVideoA,
				 SplashColorPtr paperColorA,
				 GBool bitmapTopDownA,
				 GBool allowAntialiasA) {
  colorMode = colorModeA;
  bitmapRowPad = bitmapRowPadA;
  bitmapTopDown = bitmapTopDownA;
  allowAntialias = allowAntialiasA;
  vectorAntialias = allowAntialias &&
		      globalParams->getVectorAntialias() &&
		      colorMode != splashModeMono1;
  setupScreenParams(72.0, 72.0);
  reverseVideo = reverseVideoA;
  splashColorCopy(paperColor, paperColorA);

  xref = NULL;

  bitmap = new SplashBitmap(1, 1, bitmapRowPad, colorMode,
			    colorMode != splashModeMono1, bitmapTopDown);
  splash = new Splash(bitmap, vectorAntialias, &screenParams);
  splash->clear(paperColor, 0);

  fontEngine = NULL;

  nT3Fonts = 0;
  t3GlyphStack = NULL;

  font = NULL;
  needFontUpdate = gFalse;
  textClipPath = NULL;

  transpGroupStack = NULL;
}

void SplashOutputDev::setupScreenParams(double hDPI, double vDPI) {
  screenParams.size = globalParams->getScreenSize();
  screenParams.dotRadius = globalParams->getScreenDotRadius();
  screenParams.gamma = (SplashCoord)globalParams->getScreenGamma();
  screenParams.blackThreshold =
      (SplashCoord)globalParams->getScreenBlackThreshold();
  screenParams.whiteThreshold =
      (SplashCoord)globalParams->getScreenWhiteThreshold();
  switch (globalParams->getScreenType()) {
  case screenDispersed:
    screenParams.type = splashScreenDispersed;
    if (screenParams.size < 0) {
      screenParams.size = 4;
    }
    break;
  case screenClustered:
    screenParams.type = splashScreenClustered;
    if (screenParams.size < 0) {
      screenParams.size = 10;
    }
    break;
  case screenStochasticClustered:
    screenParams.type = splashScreenStochasticClustered;
    if (screenParams.size < 0) {
      screenParams.size = 100;
    }
    if (screenParams.dotRadius < 0) {
      screenParams.dotRadius = 2;
    }
    break;
  case screenUnset:
  default:
    // use clustered dithering for resolution >= 300 dpi
    // (compare to 299.9 to avoid floating point issues)
    if (hDPI > 299.9 && vDPI > 299.9) {
      screenParams.type = splashScreenStochasticClustered;
      if (screenParams.size < 0) {
	screenParams.size = 100;
      }
      if (screenParams.dotRadius < 0) {
	screenParams.dotRadius = 2;
      }
    } else {
      screenParams.type = splashScreenDispersed;
      if (screenParams.size < 0) {
	screenParams.size = 4;
      }
    }
  }
}

SplashOutputDev::~SplashOutputDev() {
  int i;

  for (i = 0; i < nT3Fonts; ++i) {
    delete t3FontCache[i];
  }
  if (fontEngine) {
    delete fontEngine;
  }
  if (splash) {
    delete splash;
  }
  if (bitmap) {
    delete bitmap;
  }
}

void SplashOutputDev::startDoc(XRef *xrefA) {
  int i;

  xref = xrefA;
  if (fontEngine) {
    delete fontEngine;
  }
  fontEngine = new SplashFontEngine(
#if HAVE_T1LIB_H
				    globalParams->getEnableT1lib(),
#endif
#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
				    globalParams->getEnableFreeType(),
#endif
				    allowAntialias &&
				      globalParams->getAntialias() &&
				      colorMode != splashModeMono1);
  for (i = 0; i < nT3Fonts; ++i) {
    delete t3FontCache[i];
  }
  nT3Fonts = 0;
}

void SplashOutputDev::startPage(int pageNum, GfxState *state) {
  int w, h;
  double *ctm;
  SplashCoord mat[6];
  SplashColor color;

  if (state) {
    setupScreenParams(state->getHDPI(), state->getVDPI());
    w = (int)(state->getPageWidth() + 0.5);
    if (w <= 0) {
      w = 1;
    }
    h = (int)(state->getPageHeight() + 0.5);
    if (h <= 0) {
      h = 1;
    }
  } else {
    w = h = 1;
  }
  if (splash) {
    delete splash;
  }
  if (!bitmap || w != bitmap->getWidth() || h != bitmap->getHeight()) {
    if (bitmap) {
      delete bitmap;
    }
    bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode,
			      colorMode != splashModeMono1, bitmapTopDown);
  }
  splash = new Splash(bitmap, vectorAntialias, &screenParams);
  if (state) {
    ctm = state->getCTM();
    mat[0] = (SplashCoord)ctm[0];
    mat[1] = (SplashCoord)ctm[1];
    mat[2] = (SplashCoord)ctm[2];
    mat[3] = (SplashCoord)ctm[3];
    mat[4] = (SplashCoord)ctm[4];
    mat[5] = (SplashCoord)ctm[5];
    splash->setMatrix(mat);
  }
  switch (colorMode) {
  case splashModeMono1:
  case splashModeMono8:
    color[0] = 0;
    break;
  case splashModeRGB8:
  case splashModeBGR8:
    color[0] = color[1] = color[2] = 0;
    break;
#if SPLASH_CMYK
  case splashModeCMYK8:
    color[0] = color[1] = color[2] = color[3] = 0;
    break;
#endif
  }
  splash->setStrokePattern(new SplashSolidColor(color));
  splash->setFillPattern(new SplashSolidColor(color));
  splash->setLineCap(splashLineCapButt);
  splash->setLineJoin(splashLineJoinMiter);
  splash->setLineDash(NULL, 0, 0);
  splash->setMiterLimit(10);
  splash->setFlatness(1);
  // the SA parameter supposedly defaults to false, but Acrobat
  // apparently hardwires it to true
  splash->setStrokeAdjust(globalParams->getStrokeAdjust());
  splash->clear(paperColor, 0);
}

void SplashOutputDev::endPage() {
  if (colorMode != splashModeMono1) {
    splash->compositeBackground(paperColor);
  }
}

void SplashOutputDev::saveState(GfxState *state) {
  splash->saveState();
}

void SplashOutputDev::restoreState(GfxState *state) {
  splash->restoreState();
  needFontUpdate = gTrue;
}

void SplashOutputDev::updateAll(GfxState *state) {
  updateLineDash(state);
  updateLineJoin(state);
  updateLineCap(state);
  updateLineWidth(state);
  updateFlatness(state);
  updateMiterLimit(state);
  updateStrokeAdjust(state);
  updateFillColor(state);
  updateStrokeColor(state);
  needFontUpdate = gTrue;
}

void SplashOutputDev::updateCTM(GfxState *state, double m11, double m12,
				double m21, double m22,
				double m31, double m32) {
  double *ctm;
  SplashCoord mat[6];

  ctm = state->getCTM();
  mat[0] = (SplashCoord)ctm[0];
  mat[1] = (SplashCoord)ctm[1];
  mat[2] = (SplashCoord)ctm[2];
  mat[3] = (SplashCoord)ctm[3];
  mat[4] = (SplashCoord)ctm[4];
  mat[5] = (SplashCoord)ctm[5];
  splash->setMatrix(mat);
}

void SplashOutputDev::updateLineDash(GfxState *state) {
  double *dashPattern;
  int dashLength;
  double dashStart;
  SplashCoord dash[20];
  int i;

  state->getLineDash(&dashPattern, &dashLength, &dashStart);
  if (dashLength > 20) {
    dashLength = 20;
  }
  for (i = 0; i < dashLength; ++i) {
    dash[i] = (SplashCoord)dashPattern[i];
    if (dash[i] < 0) {
      dash[i] = 0;
    }
  }
  splash->setLineDash(dash, dashLength, (SplashCoord)dashStart);
}

void SplashOutputDev::updateFlatness(GfxState *state) {
  splash->setFlatness(state->getFlatness());
}

void SplashOutputDev::updateLineJoin(GfxState *state) {
  splash->setLineJoin(state->getLineJoin());
}

void SplashOutputDev::updateLineCap(GfxState *state) {
  splash->setLineCap(state->getLineCap());
}

void SplashOutputDev::updateMiterLimit(GfxState *state) {
  splash->setMiterLimit(state->getMiterLimit());
}

void SplashOutputDev::updateLineWidth(GfxState *state) {
  splash->setLineWidth(state->getLineWidth());
}

void SplashOutputDev::updateStrokeAdjust(GfxState *state) {
#if 0 // the SA parameter supposedly defaults to false, but Acrobat
      // apparently hardwires it to true
  splash->setStrokeAdjust(state->getStrokeAdjust());
#endif
}

void SplashOutputDev::updateFillColor(GfxState *state) {
  GfxGray gray;
  GfxRGB rgb;
#if SPLASH_CMYK
  GfxCMYK cmyk;
#endif

  state->getFillGray(&gray);
  state->getFillRGB(&rgb);
#if SPLASH_CMYK
  state->getFillCMYK(&cmyk);
  splash->setFillPattern(getColor(gray, &rgb, &cmyk));
#else
  splash->setFillPattern(getColor(gray, &rgb));
#endif
}

void SplashOutputDev::updateStrokeColor(GfxState *state) {
  GfxGray gray;
  GfxRGB rgb;
#if SPLASH_CMYK
  GfxCMYK cmyk;
#endif

  state->getStrokeGray(&gray);
  state->getStrokeRGB(&rgb);
#if SPLASH_CMYK
  state->getStrokeCMYK(&cmyk);
  splash->setStrokePattern(getColor(gray, &rgb, &cmyk));
#else
  splash->setStrokePattern(getColor(gray, &rgb));
#endif
}

#if SPLASH_CMYK
SplashPattern *SplashOutputDev::getColor(GfxGray gray, GfxRGB *rgb,
					 GfxCMYK *cmyk) {
#else
SplashPattern *SplashOutputDev::getColor(GfxGray gray, GfxRGB *rgb) {
#endif
  SplashPattern *pattern;
  SplashColor color;
  GfxColorComp r, g, b;

  if (reverseVideo) {
    gray = gfxColorComp1 - gray;
    r = gfxColorComp1 - rgb->r;
    g = gfxColorComp1 - rgb->g;
    b = gfxColorComp1 - rgb->b;
  } else {
    r = rgb->r;
    g = rgb->g;
    b = rgb->b;
  }

  pattern = NULL; // make gcc happy
  switch (colorMode) {
  case splashModeMono1:
  case splashModeMono8:
    color[0] = colToByte(gray);
    pattern = new SplashSolidColor(color);
    break;
  case splashModeRGB8:
  case splashModeBGR8:
    color[0] = colToByte(r);
    color[1] = colToByte(g);
    color[2] = colToByte(b);
    pattern = new SplashSolidColor(color);
    break;
#if SPLASH_CMYK
  case splashModeCMYK8:
    color[0] = colToByte(cmyk->c);
    color[1] = colToByte(cmyk->m);
    color[2] = colToByte(cmyk->y);
    color[3] = colToByte(cmyk->k);
    pattern = new SplashSolidColor(color);
    break;
#endif
  }

  return pattern;
}

void SplashOutputDev::updateBlendMode(GfxState *state) {
  splash->setBlendFunc(splashOutBlendFuncs[state->getBlendMode()]);
}

void SplashOutputDev::updateFillOpacity(GfxState *state) {
  splash->setFillAlpha((SplashCoord)state->getFillOpacity());
}

void SplashOutputDev::updateStrokeOpacity(GfxState *state) {
  splash->setStrokeAlpha((SplashCoord)state->getStrokeOpacity());
}

void SplashOutputDev::updateFont(GfxState *state) {
  needFontUpdate = gTrue;
}

void SplashOutputDev::doUpdateFont(GfxState *state) {
  GfxFont *gfxFont;
  GfxFontType fontType;
  SplashOutFontFileID *id;
  SplashFontFile *fontFile;
  FoFiTrueType *ff;
  Ref embRef;
  Object refObj, strObj;
  GString *tmpFileName, *fileName, *substName;
  FILE *tmpFile;
  Gushort *codeToGID;
  DisplayFontParam *dfp;
  CharCodeToUnicode *ctu;
  double *textMat;
  double m11, m12, m21, m22, w1, w2, fontSize;
  SplashCoord mat[4];
  char *name;
  Unicode uBuf[8];
  int c, substIdx, n, code, cmap;

  needFontUpdate = gFalse;
  font = NULL;
  tmpFileName = NULL;
  substIdx = -1;
  dfp = NULL;

  if (!(gfxFont = state->getFont())) {
    goto err1;
  }
  fontType = gfxFont->getType();
  if (fontType == fontType3) {
    goto err1;
  }

  // check the font file cache
  id = new SplashOutFontFileID(gfxFont->getID());
  if ((fontFile = fontEngine->getFontFile(id))) {
    delete id;

  } else {

    // if there is an embedded font, write it to disk
    if (gfxFont->getEmbeddedFontID(&embRef)) {
      if (!openTempFile(&tmpFileName, &tmpFile, "wb", NULL)) {
	error(-1, "Couldn't create temporary font file");
	goto err2;
      }
      refObj.initRef(embRef.num, embRef.gen);
      refObj.fetch(xref, &strObj);
      refObj.free();
      if (!strObj.isStream()) {
	error(-1, "Embedded font object is wrong type");
	strObj.free();
	fclose(tmpFile);
	goto err2;
      }
      strObj.streamReset();
      while ((c = strObj.streamGetChar()) != EOF) {
	fputc(c, tmpFile);
      }
      strObj.streamClose();
      strObj.free();
      fclose(tmpFile);
      fileName = tmpFileName;

    // if there is an external font file, use it
    } else if (!(fileName = gfxFont->getExtFontFile())) {

      // look for a display font mapping or a substitute font
      if (gfxFont->isCIDFont()) {
	if (((GfxCIDFont *)gfxFont)->getCollection()) {
	  dfp = globalParams->
	          getDisplayCIDFont(gfxFont->getName(),
				    ((GfxCIDFont *)gfxFont)->getCollection());
	}
      } else {
	if (gfxFont->getName()) {
	  dfp = globalParams->getDisplayFont(gfxFont->getName());
	}
	if (!dfp) {
	  // 8-bit font substitution
	  if (gfxFont->isFixedWidth()) {
	    substIdx = 8;
	  } else if (gfxFont->isSerif()) {
	    substIdx = 4;
	  } else {
	    substIdx = 0;
	  }
	  if (gfxFont->isBold()) {
	    substIdx += 2;
	  }
	  if (gfxFont->isItalic()) {
	    substIdx += 1;
	  }
	  substName = new GString(splashOutSubstFonts[substIdx].name);
	  dfp = globalParams->getDisplayFont(substName);
	  delete substName;
	  id->setSubstIdx(substIdx);
	}
      }
      if (!dfp) {
	error(-1, "Couldn't find a font for '%s'",
	      gfxFont->getName() ? gfxFont->getName()->getCString()
	                         : "(unnamed)");
	goto err2;
      }
      switch (dfp->kind) {
      case displayFontT1:
	fileName = dfp->t1.fileName;
	fontType = gfxFont->isCIDFont() ? fontCIDType0 : fontType1;
	break;
      case displayFontTT:
	fileName = dfp->tt.fileName;
	fontType = gfxFont->isCIDFont() ? fontCIDType2 : fontTrueType;
	break;
      }
    }

    // load the font file
    switch (fontType) {
    case fontType1:
      if (!(fontFile = fontEngine->loadType1Font(
			   id,
			   fileName->getCString(),
			   fileName == tmpFileName,
			   ((Gfx8BitFont *)gfxFont)->getEncoding()))) {
	error(-1, "Couldn't create a font for '%s'",
	      gfxFont->getName() ? gfxFont->getName()->getCString()
	                         : "(unnamed)");
	goto err2;
      }
      break;
    case fontType1C:
      if (!(fontFile = fontEngine->loadType1CFont(
			   id,
			   fileName->getCString(),
			   fileName == tmpFileName,
			   ((Gfx8BitFont *)gfxFont)->getEncoding()))) {
	error(-1, "Couldn't create a font for '%s'",
	      gfxFont->getName() ? gfxFont->getName()->getCString()
	                         : "(unnamed)");
	goto err2;
      }
      break;
    case fontType1COT:
      if (!(fontFile = fontEngine->loadOpenTypeT1CFont(
			   id,
			   fileName->getCString(),
			   fileName == tmpFileName,
			   ((Gfx8BitFont *)gfxFont)->getEncoding()))) {
	error(-1, "Couldn't create a font for '%s'",
	      gfxFont->getName() ? gfxFont->getName()->getCString()
	                         : "(unnamed)");
	goto err2;
      }
      break;
    case fontTrueType:
    case fontTrueTypeOT:
      if ((ff = FoFiTrueType::load(fileName->getCString()))) {
	codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff);
	n = 256;
	delete ff;
      } else {
	codeToGID = NULL;
	n = 0;
      }
      if (!(fontFile = fontEngine->loadTrueTypeFont(
			   id,
			   fileName->getCString(),
			   fileName == tmpFileName,
			   codeToGID, n))) {
	error(-1, "Couldn't create a font for '%s'",
	      gfxFont->getName() ? gfxFont->getName()->getCString()
	                         : "(unnamed)");
	goto err2;
      }
      break;
    case fontCIDType0:
    case fontCIDType0C:
      if (!(fontFile = fontEngine->loadCIDFont(
			   id,
			   fileName->getCString(),
			   fileName == tmpFileName))) {
	error(-1, "Couldn't create a font for '%s'",
	      gfxFont->getName() ? gfxFont->getName()->getCString()
	                         : "(unnamed)");
	goto err2;
      }
      break;
    case fontCIDType0COT:
      if (!(fontFile = fontEngine->loadOpenTypeCFFFont(
			   id,
			   fileName->getCString(),
			   fileName == tmpFileName))) {
	error(-1, "Couldn't create a font for '%s'",
	      gfxFont->getName() ? gfxFont->getName()->getCString()
	                         : "(unnamed)");
	goto err2;
      }
      break;
    case fontCIDType2:
    case fontCIDType2OT:
      codeToGID = NULL;
      n = 0;
      if (dfp) {
	// create a CID-to-GID mapping, via Unicode
	if ((ctu = ((GfxCIDFont *)gfxFont)->getToUnicode())) {
	  if ((ff = FoFiTrueType::load(fileName->getCString()))) {
	    // look for a Unicode cmap
	    for (cmap = 0; cmap < ff->getNumCmaps(); ++cmap) {
	      if ((ff->getCmapPlatform(cmap) == 3 &&
		   ff->getCmapEncoding(cmap) == 1) ||
		  ff->getCmapPlatform(cmap) == 0) {
		break;
	      }
	    }
	    if (cmap < ff->getNumCmaps()) {
	      // map CID -> Unicode -> GID
	      n = ctu->getLength();
	      codeToGID = (Gushort *)gmallocn(n, sizeof(Gushort));
	      for (code = 0; code < n; ++code) {
		if (ctu->mapToUnicode(code, uBuf, 8) > 0) {
		  codeToGID[code] = ff->mapCodeToGID(cmap, uBuf[0]);
		} else {
		  codeToGID[code] = 0;
		}
	      }
	    }
	    delete ff;
	  }
	  ctu->decRefCnt();
	} else {
	  error(-1, "Couldn't find a mapping to Unicode for font '%s'",
		gfxFont->getName() ? gfxFont->getName()->getCString()
		                   : "(unnamed)");
	}
      } else {
	if (((GfxCIDFont *)gfxFont)->getCIDToGID()) {
	  n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen();
	  codeToGID = (Gushort *)gmallocn(n, sizeof(Gushort));
	  memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(),
		 n * sizeof(Gushort));
	}
      }
      if (!(fontFile = fontEngine->loadTrueTypeFont(
			   id,
			   fileName->getCString(),
			   fileName == tmpFileName,
			   codeToGID, n))) {
	error(-1, "Couldn't create a font for '%s'",
	      gfxFont->getName() ? gfxFont->getName()->getCString()
	                         : "(unnamed)");
	goto err2;
      }
      break;
    default:
      // this shouldn't happen
      goto err2;
    }
  }

  // get the font matrix
  textMat = state->getTextMat();
  fontSize = state->getFontSize();
  m11 = textMat[0] * fontSize * state->getHorizScaling();
  m12 = textMat[1] * fontSize * state->getHorizScaling();
  m21 = textMat[2] * fontSize;
  m22 = textMat[3] * fontSize;

  // for substituted fonts: adjust the font matrix -- compare the
  // width of 'm' in the original font and the substituted font
  substIdx = ((SplashOutFontFileID *)fontFile->getID())->getSubstIdx();
  if (substIdx >= 0) {
    for (code = 0; code < 256; ++code) {
      if ((name = ((Gfx8BitFont *)gfxFont)->getCharName(code)) &&
	  name[0] == 'm' && name[1] == '\0') {
	break;
      }
    }
    if (code < 256) {
      w1 = ((Gfx8BitFont *)gfxFont)->getWidth(code);
      w2 = splashOutSubstFonts[substIdx].mWidth;
      if (!gfxFont->isSymbolic()) {
	// if real font is substantially narrower than substituted
	// font, reduce the font size accordingly
	if (w1 > 0.01 && w1 < 0.9 * w2) {
	  w1 /= w2;
	  m11 *= w1;
	  m21 *= w1;
	}
      }
    }
  }

  // create the scaled font
  mat[0] = m11;  mat[1] = m12;
  mat[2] = m21;  mat[3] = m22;
  font = fontEngine->getFont(fontFile, mat, splash->getMatrix());

  if (tmpFileName) {
    delete tmpFileName;
  }
  return;

 err2:
  delete id;
 err1:
  if (tmpFileName) {
    unlink(tmpFileName->getCString());
    delete tmpFileName;
  }
  return;
}

void SplashOutputDev::stroke(GfxState *state) {
  SplashPath *path;

  if (state->getStrokeColorSpace()->isNonMarking()) {
    return;
  }
  path = convertPath(state, state->getPath());
  splash->stroke(path);
  delete path;
}

void SplashOutputDev::fill(GfxState *state) {
  SplashPath *path;

  if (state->getFillColorSpace()->isNonMarking()) {
    return;
  }
  path = convertPath(state, state->getPath());
  splash->fill(path, gFalse);
  delete path;
}

void SplashOutputDev::eoFill(GfxState *state) {
  SplashPath *path;

  if (state->getFillColorSpace()->isNonMarking()) {
    return;
  }
  path = convertPath(state, state->getPath());
  splash->fill(path, gTrue);
  delete path;
}

void SplashOutputDev::clip(GfxState *state) {
  SplashPath *path;

  path = convertPath(state, state->getPath());
  splash->clipToPath(path, gFalse);
  delete path;
}

void SplashOutputDev::eoClip(GfxState *state) {
  SplashPath *path;

  path = convertPath(state, state->getPath());
  splash->clipToPath(path, gTrue);
  delete path;
}

void SplashOutputDev::clipToStrokePath(GfxState *state) {
  SplashPath *path, *path2;

  path = convertPath(state, state->getPath());
  path2 = splash->makeStrokePath(path);
  delete path;
  splash->clipToPath(path2, gFalse);
  delete path2;
}

SplashPath *SplashOutputDev::convertPath(GfxState *state, GfxPath *path) {
  SplashPath *sPath;
  GfxSubpath *subpath;
  int i, j;

  sPath = new SplashPath();
  for (i = 0; i < path->getNumSubpaths(); ++i) {
    subpath = path->getSubpath(i);
    if (subpath->getNumPoints() > 0) {
      sPath->moveTo((SplashCoord)subpath->getX(0),
		    (SplashCoord)subpath->getY(0));
      j = 1;
      while (j < subpath->getNumPoints()) {
	if (subpath->getCurve(j)) {
	  sPath->curveTo((SplashCoord)subpath->getX(j),
			 (SplashCoord)subpath->getY(j),
			 (SplashCoord)subpath->getX(j+1),
			 (SplashCoord)subpath->getY(j+1),
			 (SplashCoord)subpath->getX(j+2),
			 (SplashCoord)subpath->getY(j+2));
	  j += 3;
	} else {
	  sPath->lineTo((SplashCoord)subpath->getX(j),
			(SplashCoord)subpath->getY(j));
	  ++j;
	}
      }
      if (subpath->isClosed()) {
	sPath->close();
      }
    }
  }
  return sPath;
}

void SplashOutputDev::drawChar(GfxState *state, double x, double y,
			       double dx, double dy,
			       double originX, double originY,
			       CharCode code, int nBytes,
			       Unicode *u, int uLen) {
  SplashPath *path;
  int render;

  // check for invisible text -- this is used by Acrobat Capture
  render = state->getRender();
  if (render == 3) {
    return;
  }

  if (needFontUpdate) {
    doUpdateFont(state);
  }
  if (!font) {
    return;
  }

  x -= originX;
  y -= originY;

  // fill
  if (!(render & 1)) {
    if (!state->getFillColorSpace()->isNonMarking()) {
      splash->fillChar((SplashCoord)x, (SplashCoord)y, code, font);
    }
  }

  // stroke
  if ((render & 3) == 1 || (render & 3) == 2) {
    if (!state->getStrokeColorSpace()->isNonMarking()) {
      if ((path = font->getGlyphPath(code))) {
	path->offset((SplashCoord)x, (SplashCoord)y);
	splash->stroke(path);
	delete path;
      }
    }
  }

  // clip
  if (render & 4) {
    if ((path = font->getGlyphPath(code))) {
      path->offset((SplashCoord)x, (SplashCoord)y);
      if (textClipPath) {
	textClipPath->append(path);
	delete path;
      } else {
	textClipPath = path;
      }
    }
  }
}

GBool SplashOutputDev::beginType3Char(GfxState *state, double x, double y,
				      double dx, double dy,
				      CharCode code, Unicode *u, int uLen) {
  GfxFont *gfxFont;
  Ref *fontID;
  double *ctm, *bbox;
  T3FontCache *t3Font;
  T3GlyphStack *t3gs;
  GBool validBBox;
  double x1, y1, xMin, yMin, xMax, yMax, xt, yt;
  int i, j;

  if (!(gfxFont = state->getFont())) {
    return gFalse;
  }
  fontID = gfxFont->getID();
  ctm = state->getCTM();
  state->transform(0, 0, &xt, &yt);

  // is it the first (MRU) font in the cache?
  if (!(nT3Fonts > 0 &&
	t3FontCache[0]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3]))) {

    // is the font elsewhere in the cache?
    for (i = 1; i < nT3Fonts; ++i) {
      if (t3FontCache[i]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3])) {
	t3Font = t3FontCache[i];
	for (j = i; j > 0; --j) {
	  t3FontCache[j] = t3FontCache[j - 1];
	}
	t3FontCache[0] = t3Font;
	break;
      }
    }
    if (i >= nT3Fonts) {

      // create new entry in the font cache
      if (nT3Fonts == splashOutT3FontCacheSize) {
	delete t3FontCache[nT3Fonts - 1];
	--nT3Fonts;
      }
      for (j = nT3Fonts; j > 0; --j) {
	t3FontCache[j] = t3FontCache[j - 1];
      }
      ++nT3Fonts;
      bbox = gfxFont->getFontBBox();
      if (bbox[0] == 0 && bbox[1] == 0 && bbox[2] == 0 && bbox[3] == 0) {
	// unspecified bounding box -- just take a guess
	xMin = xt - 5;
	xMax = xMin + 30;
	yMax = yt + 15;
	yMin = yMax - 45;
	validBBox = gFalse;
      } else {
	state->transform(bbox[0], bbox[1], &x1, &y1);
	xMin = xMax = x1;
	yMin = yMax = y1;
	state->transform(bbox[0], bbox[3], &x1, &y1);
	if (x1 < xMin) {
	  xMin = x1;
	} else if (x1 > xMax) {
	  xMax = x1;
	}
	if (y1 < yMin) {
	  yMin = y1;
	} else if (y1 > yMax) {
	  yMax = y1;
	}
	state->transform(bbox[2], bbox[1], &x1, &y1);
	if (x1 < xMin) {
	  xMin = x1;
	} else if (x1 > xMax) {
	  xMax = x1;
	}
	if (y1 < yMin) {
	  yMin = y1;
	} else if (y1 > yMax) {
	  yMax = y1;
	}
	state->transform(bbox[2], bbox[3], &x1, &y1);
	if (x1 < xMin) {
	  xMin = x1;
	} else if (x1 > xMax) {
	  xMax = x1;
	}
	if (y1 < yMin) {
	  yMin = y1;
	} else if (y1 > yMax) {
	  yMax = y1;
	}
	validBBox = gTrue;
      }
      t3FontCache[0] = new T3FontCache(fontID, ctm[0], ctm[1], ctm[2], ctm[3],
	                               (int)floor(xMin - xt),
				       (int)floor(yMin - yt),
				       (int)ceil(xMax) - (int)floor(xMin) + 3,
				       (int)ceil(yMax) - (int)floor(yMin) + 3,
				       validBBox,
				       colorMode != splashModeMono1);
    }
  }
  t3Font = t3FontCache[0];

  // is the glyph in the cache?
  i = (code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc;
  for (j = 0; j < t3Font->cacheAssoc; ++j) {
    if ((t3Font->cacheTags[i+j].mru & 0x8000) &&
	t3Font->cacheTags[i+j].code == code) {
      drawType3Glyph(t3Font, &t3Font->cacheTags[i+j],
		     t3Font->cacheData + (i+j) * t3Font->glyphSize);
      return gTrue;
    }
  }

  // push a new Type 3 glyph record
  t3gs = new T3GlyphStack();
  t3gs->next = t3GlyphStack;
  t3GlyphStack = t3gs;
  t3GlyphStack->code = code;
  t3GlyphStack->cache = t3Font;
  t3GlyphStack->cacheTag = NULL;
  t3GlyphStack->cacheData = NULL;

  return gFalse;
}

void SplashOutputDev::endType3Char(GfxState *state) {
  T3GlyphStack *t3gs;
  double *ctm;

  if (t3GlyphStack->cacheTag) {
    memcpy(t3GlyphStack->cacheData, bitmap->getDataPtr(),
	   t3GlyphStack->cache->glyphSize);
    delete bitmap;
    delete splash;
    bitmap = t3GlyphStack->origBitmap;
    splash = t3GlyphStack->origSplash;
    ctm = state->getCTM();
    state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
		  t3GlyphStack->origCTM4, t3GlyphStack->origCTM5);
    updateCTM(state, 0, 0, 0, 0, 0, 0);
    drawType3Glyph(t3GlyphStack->cache,
		   t3GlyphStack->cacheTag, t3GlyphStack->cacheData);
  }
  t3gs = t3GlyphStack;
  t3GlyphStack = t3gs->next;
  delete t3gs;
}

void SplashOutputDev::type3D0(GfxState *state, double wx, double wy) {
}

void SplashOutputDev::type3D1(GfxState *state, double wx, double wy,
			      double llx, double lly, double urx, double ury) {
  double *ctm;
  T3FontCache *t3Font;
  SplashColor color;
  double xt, yt, xMin, xMax, yMin, yMax, x1, y1;
  int i, j;

  t3Font = t3GlyphStack->cache;

  // check for a valid bbox
  state->transform(0, 0, &xt, &yt);
  state->transform(llx, lly, &x1, &y1);
  xMin = xMax = x1;
  yMin = yMax = y1;
  state->transform(llx, ury, &x1, &y1);
  if (x1 < xMin) {
    xMin = x1;
  } else if (x1 > xMax) {
    xMax = x1;
  }
  if (y1 < yMin) {
    yMin = y1;
  } else if (y1 > yMax) {
    yMax = y1;
  }
  state->transform(urx, lly, &x1, &y1);
  if (x1 < xMin) {
    xMin = x1;
  } else if (x1 > xMax) {
    xMax = x1;
  }
  if (y1 < yMin) {
    yMin = y1;
  } else if (y1 > yMax) {
    yMax = y1;
  }
  state->transform(urx, ury, &x1, &y1);
  if (x1 < xMin) {
    xMin = x1;
  } else if (x1 > xMax) {
    xMax = x1;
  }
  if (y1 < yMin) {
    yMin = y1;
  } else if (y1 > yMax) {
    yMax = y1;
  }
  if (xMin - xt < t3Font->glyphX ||
      yMin - yt < t3Font->glyphY ||
      xMax - xt > t3Font->glyphX + t3Font->glyphW ||
      yMax - yt > t3Font->glyphY + t3Font->glyphH) {
    if (t3Font->validBBox) {
      error(-1, "Bad bounding box in Type 3 glyph");
    }
    return;
  }

  // allocate a cache entry
  i = (t3GlyphStack->code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc;
  for (j = 0; j < t3Font->cacheAssoc; ++j) {
    if ((t3Font->cacheTags[i+j].mru & 0x7fff) == t3Font->cacheAssoc - 1) {
      t3Font->cacheTags[i+j].mru = 0x8000;
      t3Font->cacheTags[i+j].code = t3GlyphStack->code;
      t3GlyphStack->cacheTag = &t3Font->cacheTags[i+j];
      t3GlyphStack->cacheData = t3Font->cacheData + (i+j) * t3Font->glyphSize;
    } else {
      ++t3Font->cacheTags[i+j].mru;
    }
  }

  // save state
  t3GlyphStack->origBitmap = bitmap;
  t3GlyphStack->origSplash = splash;
  ctm = state->getCTM();
  t3GlyphStack->origCTM4 = ctm[4];
  t3GlyphStack->origCTM5 = ctm[5];

  // create the temporary bitmap
  if (colorMode == splashModeMono1) {
    bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1,
			      splashModeMono1, gFalse);
    splash = new Splash(bitmap, gFalse,
			t3GlyphStack->origSplash->getScreen());
    color[0] = 0;
    splash->clear(color);
    color[0] = 1;
  } else {
    bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1,
			      splashModeMono8, gFalse);
    splash = new Splash(bitmap, vectorAntialias,
			t3GlyphStack->origSplash->getScreen());
    color[0] = 0x00;
    splash->clear(color);
    color[0] = 0xff;
  }
  splash->setFillPattern(new SplashSolidColor(color));
  splash->setStrokePattern(new SplashSolidColor(color));
  //~ this should copy other state from t3GlyphStack->origSplash?
  state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
		-t3Font->glyphX, -t3Font->glyphY);
  updateCTM(state, 0, 0, 0, 0, 0, 0);
}

void SplashOutputDev::drawType3Glyph(T3FontCache *t3Font,
				     T3FontCacheTag *tag, Guchar *data) {
  SplashGlyphBitmap glyph;

  glyph.x = -t3Font->glyphX;
  glyph.y = -t3Font->glyphY;
  glyph.w = t3Font->glyphW;
  glyph.h = t3Font->glyphH;
  glyph.aa = colorMode != splashModeMono1;
  glyph.data = data;
  glyph.freeData = gFalse;
  splash->fillGlyph(0, 0, &glyph);
}

void SplashOutputDev::endTextObject(GfxState *state) {
  if (textClipPath) {
    splash->clipToPath(textClipPath, gFalse);
    delete textClipPath;
    textClipPath = NULL;
  }
}

struct SplashOutImageMaskData {
  ImageStream *imgStr;
  GBool invert;
  int width, height, y;
};

GBool SplashOutputDev::imageMaskSrc(void *data, SplashColorPtr line) {
  SplashOutImageMaskData *imgMaskData = (SplashOutImageMaskData *)data;
  Guchar *p;
  SplashColorPtr q;
  int x;

  if (imgMaskData->y == imgMaskData->height) {
    return gFalse;
  }
  for (x = 0, p = imgMaskData->imgStr->getLine(), q = line;
       x < imgMaskData->width;
       ++x) {
    *q++ = *p++ ^ imgMaskData->invert;
  }
  ++imgMaskData->y;
  return gTrue;
}

void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
				    int width, int height, GBool invert,
				    GBool inlineImg) {
  double *ctm;
  SplashCoord mat[6];
  SplashOutImageMaskData imgMaskData;

  if (state->getFillColorSpace()->isNonMarking()) {
    return;
  }

  ctm = state->getCTM();
  mat[0] = ctm[0];
  mat[1] = ctm[1];
  mat[2] = -ctm[2];
  mat[3] = -ctm[3];
  mat[4] = ctm[2] + ctm[4];
  mat[5] = ctm[3] + ctm[5];

  imgMaskData.imgStr = new ImageStream(str, width, 1, 1);
  imgMaskData.imgStr->reset();
  imgMaskData.invert = invert ? 0 : 1;
  imgMaskData.width = width;
  imgMaskData.height = height;
  imgMaskData.y = 0;

  splash->fillImageMask(&imageMaskSrc, &imgMaskData, width, height, mat,
			t3GlyphStack != NULL);
  if (inlineImg) {
    while (imgMaskData.y < height) {
      imgMaskData.imgStr->getLine();
      ++imgMaskData.y;
    }
  }

  delete imgMaskData.imgStr;
  str->close();
}

struct SplashOutImageData {
  ImageStream *imgStr;
  GfxImageColorMap *colorMap;
  SplashColorPtr lookup;
  int *maskColors;
  SplashColorMode colorMode;
  int width, height, y;
};

GBool SplashOutputDev::imageSrc(void *data, SplashColorPtr colorLine,
				Guchar *alphaLine) {
  SplashOutImageData *imgData = (SplashOutImageData *)data;
  Guchar *p;
  SplashColorPtr q, col;
  GfxRGB rgb;
  GfxGray gray;
#if SPLASH_CMYK
  GfxCMYK cmyk;
#endif
  int nComps, x;

  if (imgData->y == imgData->height) {
    return gFalse;
  }

  nComps = imgData->colorMap->getNumPixelComps();

  if (imgData->lookup) {
    switch (imgData->colorMode) {
    case splashModeMono1:
    case splashModeMono8:
      for (x = 0, p = imgData->imgStr->getLine(), q = colorLine;
	   x < imgData->width;
	   ++x, ++p) {
	*q++ = imgData->lookup[*p];
      }
      break;
    case splashModeRGB8:
    case splashModeBGR8:
      for (x = 0, p = imgData->imgStr->getLine(), q = colorLine;
	   x < imgData->width;
	   ++x, ++p) {
	col = &imgData->lookup[3 * *p];
	*q++ = col[0];
	*q++ = col[1];
	*q++ = col[2];
      }
      break;
#if SPLASH_CMYK
    case splashModeCMYK8:
      for (x = 0, p = imgData->imgStr->getLine(), q = colorLine;
	   x < imgData->width;
	   ++x, ++p) {
	col = &imgData->lookup[4 * *p];
	*q++ = col[0];
	*q++ = col[1];
	*q++ = col[2];
	*q++ = col[3];
      }
      break;
#endif
    }
  } else {
    switch (imgData->colorMode) {
    case splashModeMono1:
    case splashModeMono8:
      for (x = 0, p = imgData->imgStr->getLine(), q = colorLine;
	   x < imgData->width;
	   ++x, p += nComps) {
	imgData->colorMap->getGray(p, &gray);
	*q++ = colToByte(gray);
      }
      break;
    case splashModeRGB8:
    case splashModeBGR8:
      for (x = 0, p = imgData->imgStr->getLine(), q = colorLine;
	   x < imgData->width;
	   ++x, p += nComps) {
	imgData->colorMap->getRGB(p, &rgb);
	*q++ = colToByte(rgb.r);
	*q++ = colToByte(rgb.g);
	*q++ = colToByte(rgb.b);
      }
      break;
#if SPLASH_CMYK
    case splashModeCMYK8:
      for (x = 0, p = imgData->imgStr->getLine(), q = colorLine;
	   x < imgData->width;
	   ++x, p += nComps) {
	imgData->colorMap->getCMYK(p, &cmyk);
	*q++ = colToByte(cmyk.c);
	*q++ = colToByte(cmyk.m);
	*q++ = colToByte(cmyk.y);
	*q++ = colToByte(cmyk.k);
      }
      break;
#endif
    }
  }

  ++imgData->y;
  return gTrue;
}

GBool SplashOutputDev::alphaImageSrc(void *data, SplashColorPtr colorLine,
				     Guchar *alphaLine) {
  SplashOutImageData *imgData = (SplashOutImageData *)data;
  Guchar *p, *aq;
  SplashColorPtr q, col;
  GfxRGB rgb;
  GfxGray gray;
#if SPLASH_CMYK
  GfxCMYK cmyk;
#endif
  Guchar alpha;
  int nComps, x, i;

  if (imgData->y == imgData->height) {
    return gFalse;
  }

  nComps = imgData->colorMap->getNumPixelComps();

  for (x = 0, p = imgData->imgStr->getLine(), q = colorLine, aq = alphaLine;
       x < imgData->width;
       ++x, p += nComps) {
    alpha = 0;
    for (i = 0; i < nComps; ++i) {
      if (p[i] < imgData->maskColors[2*i] ||
	  p[i] > imgData->maskColors[2*i+1]) {
	alpha = 0xff;
	break;
      }
    }
    if (imgData->lookup) {
      switch (imgData->colorMode) {
      case splashModeMono1:
      case splashModeMono8:
	*q++ = imgData->lookup[*p];
	*aq++ = alpha;
	break;
      case splashModeRGB8:
      case splashModeBGR8:
	col = &imgData->lookup[3 * *p];
	*q++ = col[0];
	*q++ = col[1];
	*q++ = col[2];
	*aq++ = alpha;
	break;
#if SPLASH_CMYK
      case splashModeCMYK8:
	col = &imgData->lookup[4 * *p];
	*q++ = col[0];
	*q++ = col[1];
	*q++ = col[2];
	*q++ = col[3];
	*aq++ = alpha;
	break;
#endif
      }
    } else {
      switch (imgData->colorMode) {
      case splashModeMono1:
      case splashModeMono8:
	imgData->colorMap->getGray(p, &gray);
	*q++ = colToByte(gray);
	*aq++ = alpha;
	break;
      case splashModeRGB8:
      case splashModeBGR8:
	imgData->colorMap->getRGB(p, &rgb);
	*q++ = colToByte(rgb.r);
	*q++ = colToByte(rgb.g);
	*q++ = colToByte(rgb.b);
	*aq++ = alpha;
	break;
#if SPLASH_CMYK
      case splashModeCMYK8:
	imgData->colorMap->getCMYK(p, &cmyk);
	*q++ = colToByte(cmyk.c);
	*q++ = colToByte(cmyk.m);
	*q++ = colToByte(cmyk.y);
	*q++ = colToByte(cmyk.k);
	*aq++ = alpha;
	break;
#endif
      }
    }
  }

  ++imgData->y;
  return gTrue;
}

void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
				int width, int height,
				GfxImageColorMap *colorMap,
				int *maskColors, GBool inlineImg) {
  double *ctm;
  SplashCoord mat[6];
  SplashOutImageData imgData;
  SplashColorMode srcMode;
  SplashImageSource src;
  GfxGray gray;
  GfxRGB rgb;
#if SPLASH_CMYK
  GfxCMYK cmyk;
#endif
  Guchar pix;
  int n, i;

  ctm = state->getCTM();
  mat[0] = ctm[0];
  mat[1] = ctm[1];
  mat[2] = -ctm[2];
  mat[3] = -ctm[3];
  mat[4] = ctm[2] + ctm[4];
  mat[5] = ctm[3] + ctm[5];

  imgData.imgStr = new ImageStream(str, width,
				   colorMap->getNumPixelComps(),
				   colorMap->getBits());
  imgData.imgStr->reset();
  imgData.colorMap = colorMap;
  imgData.maskColors = maskColors;
  imgData.colorMode = colorMode;
  imgData.width = width;
  imgData.height = height;
  imgData.y = 0;

  // special case for one-channel (monochrome/gray/separation) images:
  // build a lookup table here
  imgData.lookup = NULL;
  if (colorMap->getNumPixelComps() == 1) {
    n = 1 << colorMap->getBits();
    switch (colorMode) {
    case splashModeMono1:
    case splashModeMono8:
      imgData.lookup = (SplashColorPtr)gmalloc(n);
      for (i = 0; i < n; ++i) {
	pix = (Guchar)i;
	colorMap->getGray(&pix, &gray);
	imgData.lookup[i] = colToByte(gray);
      }
      break;
    case splashModeRGB8:
    case splashModeBGR8:
      imgData.lookup = (SplashColorPtr)gmalloc(3 * n);
      for (i = 0; i < n; ++i) {
	pix = (Guchar)i;
	colorMap->getRGB(&pix, &rgb);
	imgData.lookup[3*i] = colToByte(rgb.r);
	imgData.lookup[3*i+1] = colToByte(rgb.g);
	imgData.lookup[3*i+2] = colToByte(rgb.b);
      }
      break;
#if SPLASH_CMYK
    case splashModeCMYK8:
      imgData.lookup = (SplashColorPtr)gmalloc(4 * n);
      for (i = 0; i < n; ++i) {
	pix = (Guchar)i;
	colorMap->getCMYK(&pix, &cmyk);
	imgData.lookup[4*i] = colToByte(cmyk.c);
	imgData.lookup[4*i+1] = colToByte(cmyk.m);
	imgData.lookup[4*i+2] = colToByte(cmyk.y);
	imgData.lookup[4*i+3] = colToByte(cmyk.k);
      }
      break;
#endif
      break;
    }
  }

  if (colorMode == splashModeMono1) {
    srcMode = splashModeMono8;
  } else {
    srcMode = colorMode;
  }
  src = maskColors ? &alphaImageSrc : &imageSrc;
  splash->drawImage(src, &imgData, srcMode, maskColors ? gTrue : gFalse,
		    width, height, mat);
  if (inlineImg) {
    while (imgData.y < height) {
      imgData.imgStr->getLine();
      ++imgData.y;
    }
  }

  gfree(imgData.lookup);
  delete imgData.imgStr;
  str->close();
}

struct SplashOutMaskedImageData {
  ImageStream *imgStr;
  GfxImageColorMap *colorMap;
  SplashBitmap *mask;
  SplashColorPtr lookup;
  SplashColorMode colorMode;
  int width, height, y;
};

GBool SplashOutputDev::maskedImageSrc(void *data, SplashColorPtr colorLine,
				      Guchar *alphaLine) {
  SplashOutMaskedImageData *imgData = (SplashOutMaskedImageData *)data;
  Guchar *p, *aq;
  SplashColor maskColor;
  SplashColorPtr q, col;
  GfxRGB rgb;
  GfxGray gray;
#if SPLASH_CMYK
  GfxCMYK cmyk;
#endif
  Guchar alpha;
  int nComps, x;

  if (imgData->y == imgData->height) {
    return gFalse;
  }

  nComps = imgData->colorMap->getNumPixelComps();

  for (x = 0, p = imgData->imgStr->getLine(), q = colorLine, aq = alphaLine;
       x < imgData->width;
       ++x, p += nComps) {
    imgData->mask->getPixel(x, imgData->y, maskColor);
    alpha = maskColor[0] ? 0xff : 0x00;
    if (imgData->lookup) {
      switch (imgData->colorMode) {
      case splashModeMono1:
      case splashModeMono8:
	*q++ = imgData->lookup[*p];
	*aq++ = alpha;
	break;
      case splashModeRGB8:
      case splashModeBGR8:
	col = &imgData->lookup[3 * *p];
	*q++ = col[0];
	*q++ = col[1];
	*q++ = col[2];
	*aq++ = alpha;
	break;
#if SPLASH_CMYK
      case splashModeCMYK8:
	col = &imgData->lookup[4 * *p];
	*q++ = col[0];
	*q++ = col[1];
	*q++ = col[2];
	*q++ = col[3];
	*aq++ = alpha;
	break;
#endif
      }
    } else {
      switch (imgData->colorMode) {
      case splashModeMono1:
      case splashModeMono8:
	imgData->colorMap->getGray(p, &gray);
	*q++ = colToByte(gray);
	*aq++ = alpha;
	break;
      case splashModeRGB8:
      case splashModeBGR8:
	imgData->colorMap->getRGB(p, &rgb);
	*q++ = colToByte(rgb.r);
	*q++ = colToByte(rgb.g);
	*q++ = colToByte(rgb.b);
	*aq++ = alpha;
	break;
#if SPLASH_CMYK
      case splashModeCMYK8:
	imgData->colorMap->getCMYK(p, &cmyk);
	*q++ = colToByte(cmyk.c);
	*q++ = colToByte(cmyk.m);
	*q++ = colToByte(cmyk.y);
	*q++ = colToByte(cmyk.k);
	*aq++ = alpha;
	break;
#endif
      }
    }
  }

  ++imgData->y;
  return gTrue;
}

void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref,
				      Stream *str, int width, int height,
				      GfxImageColorMap *colorMap,
				      Stream *maskStr, int maskWidth,
				      int maskHeight, GBool maskInvert) {
  GfxImageColorMap *maskColorMap;
  Object maskDecode, decodeLow, decodeHigh;
  double *ctm;
  SplashCoord mat[6];
  SplashOutMaskedImageData imgData;
  SplashOutImageMaskData imgMaskData;
  SplashColorMode srcMode;
  SplashBitmap *maskBitmap;
  Splash *maskSplash;
  SplashColor maskColor;
  GfxGray gray;
  GfxRGB rgb;
#if SPLASH_CMYK
  GfxCMYK cmyk;
#endif
  Guchar pix;
  int n, i;

  // If the mask is higher resolution than the image, use
  // drawSoftMaskedImage() instead.
  if (maskWidth > width || maskHeight > height) {
    decodeLow.initInt(maskInvert ? 0 : 1);
    decodeHigh.initInt(maskInvert ? 1 : 0);
    maskDecode.initArray(xref);
    maskDecode.arrayAdd(&decodeLow);
    maskDecode.arrayAdd(&decodeHigh);
    maskColorMap = new GfxImageColorMap(1, &maskDecode,
					new GfxDeviceGrayColorSpace());
    maskDecode.free();
    drawSoftMaskedImage(state, ref, str, width, height, colorMap,
			maskStr, maskWidth, maskHeight, maskColorMap);
    delete maskColorMap;

  } else {

    //----- scale the mask image to the same size as the source image

    mat[0] = (SplashCoord)width;
    mat[1] = 0;
    mat[2] = 0;
    mat[3] = (SplashCoord)height;
    mat[4] = 0;
    mat[5] = 0;
    imgMaskData.imgStr = new ImageStream(maskStr, maskWidth, 1, 1);
    imgMaskData.imgStr->reset();
    imgMaskData.invert = maskInvert ? 0 : 1;
    imgMaskData.width = maskWidth;
    imgMaskData.height = maskHeight;
    imgMaskData.y = 0;
    maskBitmap = new SplashBitmap(width, height, 1, splashModeMono1, gFalse);
    maskSplash = new Splash(maskBitmap, gFalse);
    maskColor[0] = 0;
    maskSplash->clear(maskColor);
    maskColor[0] = 0xff;
    maskSplash->setFillPattern(new SplashSolidColor(maskColor));
    maskSplash->fillImageMask(&imageMaskSrc, &imgMaskData,
			      maskWidth, maskHeight, mat, gFalse);
    delete imgMaskData.imgStr;
    maskStr->close();
    delete maskSplash;

    //----- draw the source image

    ctm = state->getCTM();
    mat[0] = ctm[0];
    mat[1] = ctm[1];
    mat[2] = -ctm[2];
    mat[3] = -ctm[3];
    mat[4] = ctm[2] + ctm[4];
    mat[5] = ctm[3] + ctm[5];

    imgData.imgStr = new ImageStream(str, width,
				     colorMap->getNumPixelComps(),
				     colorMap->getBits());
    imgData.imgStr->reset();
    imgData.colorMap = colorMap;
    imgData.mask = maskBitmap;
    imgData.colorMode = colorMode;
    imgData.width = width;
    imgData.height = height;
    imgData.y = 0;

    // special case for one-channel (monochrome/gray/separation) images:
    // build a lookup table here
    imgData.lookup = NULL;
    if (colorMap->getNumPixelComps() == 1) {
      n = 1 << colorMap->getBits();
      switch (colorMode) {
      case splashModeMono1:
      case splashModeMono8:
	imgData.lookup = (SplashColorPtr)gmalloc(n);
	for (i = 0; i < n; ++i) {
	  pix = (Guchar)i;
	  colorMap->getGray(&pix, &gray);
	  imgData.lookup[i] = colToByte(gray);
	}
	break;
      case splashModeRGB8:
      case splashModeBGR8:
	imgData.lookup = (SplashColorPtr)gmalloc(3 * n);
	for (i = 0; i < n; ++i) {
	  pix = (Guchar)i;
	  colorMap->getRGB(&pix, &rgb);
	  imgData.lookup[3*i] = colToByte(rgb.r);
	  imgData.lookup[3*i+1] = colToByte(rgb.g);
	  imgData.lookup[3*i+2] = colToByte(rgb.b);
	}
	break;
#if SPLASH_CMYK
      case splashModeCMYK8:
	imgData.lookup = (SplashColorPtr)gmalloc(4 * n);
	for (i = 0; i < n; ++i) {
	  pix = (Guchar)i;
	  colorMap->getCMYK(&pix, &cmyk);
	  imgData.lookup[4*i] = colToByte(cmyk.c);
	  imgData.lookup[4*i+1] = colToByte(cmyk.m);
	  imgData.lookup[4*i+2] = colToByte(cmyk.y);
	  imgData.lookup[4*i+3] = colToByte(cmyk.k);
	}
	break;
#endif
      }
    }

    if (colorMode == splashModeMono1) {
      srcMode = splashModeMono8;
    } else {
      srcMode = colorMode;
    }
    splash->drawImage(&maskedImageSrc, &imgData, srcMode, gTrue,
		      width, height, mat);

    delete maskBitmap;
    gfree(imgData.lookup);
    delete imgData.imgStr;
    str->close();
  }
}

void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
					  Stream *str, int width, int height,
					  GfxImageColorMap *colorMap,
					  Stream *maskStr,
					  int maskWidth, int maskHeight,
					  GfxImageColorMap *maskColorMap) {
  double *ctm;
  SplashCoord mat[6];
  SplashOutImageData imgData;
  SplashOutImageData imgMaskData;
  SplashColorMode srcMode;
  SplashBitmap *maskBitmap;
  Splash *maskSplash;
  SplashColor maskColor;
  GfxGray gray;
  GfxRGB rgb;
#if SPLASH_CMYK
  GfxCMYK cmyk;
#endif
  Guchar pix;
  int n, i;

  ctm = state->getCTM();
  mat[0] = ctm[0];
  mat[1] = ctm[1];
  mat[2] = -ctm[2];
  mat[3] = -ctm[3];
  mat[4] = ctm[2] + ctm[4];
  mat[5] = ctm[3] + ctm[5];

  //----- set up the soft mask

  imgMaskData.imgStr = new ImageStream(maskStr, maskWidth,
				       maskColorMap->getNumPixelComps(),
				       maskColorMap->getBits());
  imgMaskData.imgStr->reset();
  imgMaskData.colorMap = maskColorMap;
  imgMaskData.maskColors = NULL;
  imgMaskData.colorMode = splashModeMono8;
  imgMaskData.width = maskWidth;
  imgMaskData.height = maskHeight;
  imgMaskData.y = 0;
  n = 1 << maskColorMap->getBits();
  imgMaskData.lookup = (SplashColorPtr)gmalloc(n);
  for (i = 0; i < n; ++i) {
    pix = (Guchar)i;
    maskColorMap->getGray(&pix, &gray);
    imgMaskData.lookup[i] = colToByte(gray);
  }
  maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(),
				1, splashModeMono8, gFalse);
  maskSplash = new Splash(maskBitmap, vectorAntialias);
  maskColor[0] = 0;
  maskSplash->clear(maskColor);
  maskSplash->drawImage(&imageSrc, &imgMaskData, splashModeMono8, gFalse,
			maskWidth, maskHeight, mat);
  delete imgMaskData.imgStr;
  maskStr->close();
  gfree(imgMaskData.lookup);
  delete maskSplash;
  splash->setSoftMask(maskBitmap);

  //----- draw the source image

  imgData.imgStr = new ImageStream(str, width,
				   colorMap->getNumPixelComps(),
				   colorMap->getBits());
  imgData.imgStr->reset();
  imgData.colorMap = colorMap;
  imgData.maskColors = NULL;
  imgData.colorMode = colorMode;
  imgData.width = width;
  imgData.height = height;
  imgData.y = 0;

  // special case for one-channel (monochrome/gray/separation) images:
  // build a lookup table here
  imgData.lookup = NULL;
  if (colorMap->getNumPixelComps() == 1) {
    n = 1 << colorMap->getBits();
    switch (colorMode) {
    case splashModeMono1:
    case splashModeMono8:
      imgData.lookup = (SplashColorPtr)gmalloc(n);
      for (i = 0; i < n; ++i) {
	pix = (Guchar)i;
	colorMap->getGray(&pix, &gray);
	imgData.lookup[i] = colToByte(gray);
      }
      break;
    case splashModeRGB8:
    case splashModeBGR8:
      imgData.lookup = (SplashColorPtr)gmalloc(3 * n);
      for (i = 0; i < n; ++i) {
	pix = (Guchar)i;
	colorMap->getRGB(&pix, &rgb);
	imgData.lookup[3*i] = colToByte(rgb.r);
	imgData.lookup[3*i+1] = colToByte(rgb.g);
	imgData.lookup[3*i+2] = colToByte(rgb.b);
      }
      break;
#if SPLASH_CMYK
    case splashModeCMYK8:
      imgData.lookup = (SplashColorPtr)gmalloc(4 * n);
      for (i = 0; i < n; ++i) {
	pix = (Guchar)i;
	colorMap->getCMYK(&pix, &cmyk);
	imgData.lookup[4*i] = colToByte(cmyk.c);
	imgData.lookup[4*i+1] = colToByte(cmyk.m);
	imgData.lookup[4*i+2] = colToByte(cmyk.y);
	imgData.lookup[4*i+3] = colToByte(cmyk.k);
      }
      break;
#endif
    }
  }

  if (colorMode == splashModeMono1) {
    srcMode = splashModeMono8;
  } else {
    srcMode = colorMode;
  }
  splash->drawImage(&imageSrc, &imgData, srcMode, gFalse, width, height, mat);

  splash->setSoftMask(NULL);
  gfree(imgData.lookup);
  delete imgData.imgStr;
  str->close();
}

void SplashOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
					     GfxColorSpace *blendingColorSpace,
					     GBool isolated, GBool knockout,
					     GBool forSoftMask) {
  SplashTransparencyGroup *transpGroup;
  SplashColor color;
  double xMin, yMin, xMax, yMax, x, y;
  int tx, ty, w, h;

  // transform the bbox
  state->transform(bbox[0], bbox[1], &x, &y);
  xMin = xMax = x;
  yMin = yMax = y;
  state->transform(bbox[0], bbox[3], &x, &y);
  if (x < xMin) {
    xMin = x;
  } else if (x > xMax) {
    xMax = x;
  }
  if (y < yMin) {
    yMin = y;
  } else if (y > yMax) {
    yMax = y;
  }
  state->transform(bbox[2], bbox[1], &x, &y);
  if (x < xMin) {
    xMin = x;
  } else if (x > xMax) {
    xMax = x;
  }
  if (y < yMin) {
    yMin = y;
  } else if (y > yMax) {
    yMax = y;
  }
  state->transform(bbox[2], bbox[3], &x, &y);
  if (x < xMin) {
    xMin = x;
  } else if (x > xMax) {
    xMax = x;
  }
  if (y < yMin) {
    yMin = y;
  } else if (y > yMax) {
    yMax = y;
  }
  tx = (int)floor(xMin);
  if (tx < 0) {
    tx = 0;
  } else if (tx > bitmap->getWidth()) {
    tx = bitmap->getWidth();
  }
  ty = (int)floor(yMin);
  if (ty < 0) {
    ty = 0;
  } else if (ty > bitmap->getHeight()) {
    ty = bitmap->getHeight();
  }
  w = (int)ceil(xMax) - tx + 1;
  if (tx + w > bitmap->getWidth()) {
    w = bitmap->getWidth() - tx;
  }
  if (w < 1) {
    w = 1;
  }
  h = (int)ceil(yMax) - ty + 1;
  if (ty + h > bitmap->getHeight()) {
    h = bitmap->getHeight() - ty;
  }
  if (h < 1) {
    h = 1;
  }

  // push a new stack entry
  transpGroup = new SplashTransparencyGroup();
  transpGroup->tx = tx;
  transpGroup->ty = ty;
  transpGroup->blendingColorSpace = blendingColorSpace;
  transpGroup->isolated = isolated;
  transpGroup->next = transpGroupStack;
  transpGroupStack = transpGroup;

  // save state
  transpGroup->origBitmap = bitmap;
  transpGroup->origSplash = splash;

  //~ this ignores the blendingColorSpace arg

  // create the temporary bitmap
  bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, gTrue,
			    bitmapTopDown); 
  splash = new Splash(bitmap, vectorAntialias,
		      transpGroup->origSplash->getScreen());
  if (isolated) {
    switch (colorMode) {
    case splashModeMono1:
    case splashModeMono8:
      color[0] = 0;
      break;
    case splashModeRGB8:
    case splashModeBGR8:
      color[0] = color[1] = color[2] = 0;
      break;
#if SPLASH_CMYK
    case splashModeCMYK8:
      color[0] = color[1] = color[2] = color[3] = 0;
      break;
#endif
    default:
      // make gcc happy
      break;
    }
    splash->clear(color, 0);
  } else {
    splash->blitTransparent(transpGroup->origBitmap, tx, ty, 0, 0, w, h);
    splash->setInNonIsolatedGroup(transpGroup->origBitmap, tx, ty);
  }
  transpGroup->tBitmap = bitmap;
  state->shiftCTM(-tx, -ty);
  updateCTM(state, 0, 0, 0, 0, 0, 0);
}

void SplashOutputDev::endTransparencyGroup(GfxState *state) {
  double *ctm;

  // restore state
  delete splash;
  bitmap = transpGroupStack->origBitmap;
  splash = transpGroupStack->origSplash;
  ctm = state->getCTM();
  state->shiftCTM(transpGroupStack->tx, transpGroupStack->ty);
  updateCTM(state, 0, 0, 0, 0, 0, 0);
}

void SplashOutputDev::paintTransparencyGroup(GfxState *state, double *bbox) {
  SplashBitmap *tBitmap;
  SplashTransparencyGroup *transpGroup;
  GBool isolated;
  int tx, ty;

  tx = transpGroupStack->tx;
  ty = transpGroupStack->ty;
  tBitmap = transpGroupStack->tBitmap;
  isolated = transpGroupStack->isolated;

  // paint the transparency group onto the parent bitmap
  // - the clip path was set in the parent's state)
  splash->composite(tBitmap, 0, 0, tx, ty,
		    tBitmap->getWidth(), tBitmap->getHeight(),
		    gFalse, !isolated);

  // pop the stack
  transpGroup = transpGroupStack;
  transpGroupStack = transpGroup->next;
  delete transpGroup;

  delete tBitmap;
}

void SplashOutputDev::setSoftMask(GfxState *state, double *bbox,
				  GBool alpha, Function *transferFunc,
				  GfxColor *backdropColor) {
  SplashBitmap *softMask, *tBitmap;
  Splash *tSplash;
  SplashTransparencyGroup *transpGroup;
  SplashColor color;
  SplashColorPtr p;
  GfxGray gray;
  GfxRGB rgb;
#if SPLASH_CMYK
  GfxCMYK cmyk;
#endif
  double lum, lum2;
  int tx, ty, x, y;

  tx = transpGroupStack->tx;
  ty = transpGroupStack->ty;
  tBitmap = transpGroupStack->tBitmap;

  // composite with backdrop color
  if (!alpha && colorMode != splashModeMono1) {
    //~ need to correctly handle the case where no blending color
    //~ space is given
    tSplash = new Splash(tBitmap, vectorAntialias,
			 transpGroupStack->origSplash->getScreen());
    if (transpGroupStack->blendingColorSpace) {
      switch (colorMode) {
      case splashModeMono1:
	// transparency is not supported in mono1 mode
	break;
      case splashModeMono8:
	transpGroupStack->blendingColorSpace->getGray(backdropColor, &gray);
	color[0] = colToByte(gray);
	tSplash->compositeBackground(color);
	break;
      case splashModeRGB8:
      case splashModeBGR8:
	transpGroupStack->blendingColorSpace->getRGB(backdropColor, &rgb);
	color[0] = colToByte(rgb.r);
	color[1] = colToByte(rgb.g);
	color[2] = colToByte(rgb.b);
	tSplash->compositeBackground(color);
	break;
#if SPLASH_CMYK
      case splashModeCMYK8:
	transpGroupStack->blendingColorSpace->getCMYK(backdropColor, &cmyk);
	color[0] = colToByte(cmyk.c);
	color[1] = colToByte(cmyk.m);
	color[2] = colToByte(cmyk.y);
	color[3] = colToByte(cmyk.k);
	tSplash->compositeBackground(color);
	break;
#endif
      }
      delete tSplash;
    }
  }

  softMask = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(),
			      1, splashModeMono8, gFalse);
  memset(softMask->getDataPtr(), 0,
	 softMask->getRowSize() * softMask->getHeight());
  p = softMask->getDataPtr() + ty * softMask->getRowSize() + tx;
  for (y = 0; y < tBitmap->getHeight(); ++y) {
    for (x = 0; x < tBitmap->getWidth(); ++x) {
      tBitmap->getPixel(x, y, color);
      if (alpha) {
	//~ unimplemented
      } else {
	// convert to luminosity
	switch (colorMode) {
	case splashModeMono1:
	case splashModeMono8:
	  lum = color[0] / 255.0;
	  break;
	case splashModeRGB8:
	case splashModeBGR8:
	  lum = (0.3 / 255.0) * color[0] +
	        (0.59 / 255.0) * color[1] +
	        (0.11 / 255.0) * color[2];
	  break;
#if SPLASH_CMYK
	case splashModeCMYK8:
	  lum = (1 - color[4] / 255.0)
	        - (0.3 / 255.0) * color[0]
	        - (0.59 / 255.0) * color[1]
	        - (0.11 / 255.0) * color[2];
	  if (lum < 0) {
	    lum = 0;
	  }
	  break;
#endif
	}
	if (transferFunc) {
	  transferFunc->transform(&lum, &lum2);
	} else {
	  lum2 = lum;
	}
	p[x] = (int)(lum2 * 255.0 + 0.5);
      }
    }
    p += softMask->getRowSize();
  }
  splash->setSoftMask(softMask);

  // pop the stack
  transpGroup = transpGroupStack;
  transpGroupStack = transpGroup->next;
  delete transpGroup;

  delete tBitmap;
}

void SplashOutputDev::clearSoftMask(GfxState *state) {
  splash->setSoftMask(NULL);
}

void SplashOutputDev::setPaperColor(SplashColorPtr paperColorA) {
  splashColorCopy(paperColor, paperColorA);
}

int SplashOutputDev::getBitmapWidth() {
  return bitmap->getWidth();
}

int SplashOutputDev::getBitmapHeight() {
  return bitmap->getHeight();
}

SplashBitmap *SplashOutputDev::takeBitmap() {
  SplashBitmap *ret;

  ret = bitmap;
  bitmap = new SplashBitmap(1, 1, bitmapRowPad, colorMode,
			    colorMode != splashModeMono1, bitmapTopDown);
  return ret;
}

void SplashOutputDev::getModRegion(int *xMin, int *yMin,
				   int *xMax, int *yMax) {
  splash->getModRegion(xMin, yMin, xMax, yMax);
}

void SplashOutputDev::clearModRegion() {
  splash->clearModRegion();
}

void SplashOutputDev::setFillColor(int r, int g, int b) {
  GfxRGB rgb;
  GfxGray gray;
#if SPLASH_CMYK
  GfxCMYK cmyk;
#endif

  rgb.r = byteToCol(r);
  rgb.g = byteToCol(g);
  rgb.b = byteToCol(b);
  gray = (GfxColorComp)(0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.g + 0.5);
  if (gray > gfxColorComp1) {
    gray = gfxColorComp1;
  }
#if SPLASH_CMYK
  cmyk.c = gfxColorComp1 - rgb.r;
  cmyk.m = gfxColorComp1 - rgb.g;
  cmyk.y = gfxColorComp1 - rgb.b;
  cmyk.k = 0;
  splash->setFillPattern(getColor(gray, &rgb, &cmyk));
#else
  splash->setFillPattern(getColor(gray, &rgb));
#endif
}

SplashFont *SplashOutputDev::getFont(GString *name, double *textMatA) {
  DisplayFontParam *dfp;
  Ref ref;
  SplashOutFontFileID *id;
  SplashFontFile *fontFile;
  SplashFont *fontObj;
  FoFiTrueType *ff;
  Gushort *codeToGID;
  Unicode u;
  SplashCoord textMat[4];
  int cmap, i;

  for (i = 0; i < 16; ++i) {
    if (!name->cmp(splashOutSubstFonts[i].name)) {
      break;
    }
  }
  if (i == 16) {
    return NULL;
  }
  ref.num = i;
  ref.gen = -1;
  id = new SplashOutFontFileID(&ref);

  // check the font file cache
  if ((fontFile = fontEngine->getFontFile(id))) {
    delete id;

  // load the font file
  } else {
    dfp = globalParams->getDisplayFont(name);
    if (dfp && dfp->kind == displayFontT1) {
      fontFile = fontEngine->loadType1Font(id, dfp->t1.fileName->getCString(),
					   gFalse, winAnsiEncoding);
    } else if (dfp && dfp->kind == displayFontTT) {
      if (!(ff = FoFiTrueType::load(dfp->tt.fileName->getCString()))) {
	return NULL;
      }
      for (cmap = 0; cmap < ff->getNumCmaps(); ++cmap) {
	if ((ff->getCmapPlatform(cmap) == 3 &&
	     ff->getCmapEncoding(cmap) == 1) ||
	    ff->getCmapPlatform(cmap) == 0) {
	  break;
	}
      }
      if (cmap == ff->getNumCmaps()) {
	delete ff;
	return NULL;
      }
      codeToGID = (Gushort *)gmallocn(256, sizeof(Gushort));
      for (i = 0; i < 256; ++i) {
	codeToGID[i] = 0;
	if (winAnsiEncoding[i] &&
	    (u = globalParams->mapNameToUnicode(winAnsiEncoding[i]))) {
	  codeToGID[i] = ff->mapCodeToGID(cmap, u);
	}
      }
      delete ff;
      fontFile = fontEngine->loadTrueTypeFont(id,
					      dfp->tt.fileName->getCString(),
					      gFalse, codeToGID, 256);
    } else {
      return NULL;
    }
  }

  // create the scaled font
  textMat[0] = (SplashCoord)textMatA[0];
  textMat[1] = (SplashCoord)textMatA[1];
  textMat[2] = (SplashCoord)textMatA[2];
  textMat[3] = (SplashCoord)textMatA[3];
  fontObj = fontEngine->getFont(fontFile, textMat, splash->getMatrix());

  return fontObj;
}

#if 1 //~tmp: turn off anti-aliasing temporarily
GBool SplashOutputDev::getVectorAntialias() {
  return splash->getVectorAntialias();
}

void SplashOutputDev::setVectorAntialias(GBool vaa) {
  splash->setVectorAntialias(vaa);
}
#endif
//========================================================================
//
// SplashOutputDev.h
//
// Copyright 2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef SPLASHOUTPUTDEV_H
#define SPLASHOUTPUTDEV_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "gtypes.h"
#include "SplashTypes.h"
#include "config.h"
#include "OutputDev.h"
#include "GfxState.h"

class Gfx8BitFont;
class SplashBitmap;
class Splash;
class SplashPath;
class SplashPattern;
class SplashFontEngine;
class SplashFont;
class T3FontCache;
struct T3FontCacheTag;
struct T3GlyphStack;
struct SplashTransparencyGroup;

//------------------------------------------------------------------------

// number of Type 3 fonts to cache
#define splashOutT3FontCacheSize 8

//------------------------------------------------------------------------
// SplashOutputDev
//------------------------------------------------------------------------

class SplashOutputDev: public OutputDev {
public:

  // Constructor.
  SplashOutputDev(SplashColorMode colorModeA, int bitmapRowPadA,
		  GBool reverseVideoA, SplashColorPtr paperColorA,
		  GBool bitmapTopDownA = gTrue,
		  GBool allowAntialiasA = gTrue);

  // Destructor.
  virtual ~SplashOutputDev();

  //----- get info about output device

  // Does this device use upside-down coordinates?
  // (Upside-down means (0,0) is the top left corner of the page.)
  virtual GBool upsideDown() { return gTrue; }

  // Does this device use drawChar() or drawString()?
  virtual GBool useDrawChar() { return gTrue; }

  // Does this device use beginType3Char/endType3Char?  Otherwise,
  // text in Type 3 fonts will be drawn with drawChar/drawString.
  virtual GBool interpretType3Chars() { return gTrue; }

  //----- initialization and control

  // Start a page.
  virtual void startPage(int pageNum, GfxState *state);

  // End a page.
  virtual void endPage();

  //----- save/restore graphics state
  virtual void saveState(GfxState *state);
  virtual void restoreState(GfxState *state);

  //----- update graphics state
  virtual void updateAll(GfxState *state);
  virtual void updateCTM(GfxState *state, double m11, double m12,
			 double m21, double m22, double m31, double m32);
  virtual void updateLineDash(GfxState *state);
  virtual void updateFlatness(GfxState *state);
  virtual void updateLineJoin(GfxState *state);
  virtual void updateLineCap(GfxState *state);
  virtual void updateMiterLimit(GfxState *state);
  virtual void updateLineWidth(GfxState *state);
  virtual void updateStrokeAdjust(GfxState *state);
  virtual void updateFillColor(GfxState *state);
  virtual void updateStrokeColor(GfxState *state);
  virtual void updateBlendMode(GfxState *state);
  virtual void updateFillOpacity(GfxState *state);
  virtual void updateStrokeOpacity(GfxState *state);

  //----- update text state
  virtual void updateFont(GfxState *state);

  //----- path painting
  virtual void stroke(GfxState *state);
  virtual void fill(GfxState *state);
  virtual void eoFill(GfxState *state);

  //----- path clipping
  virtual void clip(GfxState *state);
  virtual void eoClip(GfxState *state);
  virtual void clipToStrokePath(GfxState *state);

  //----- text drawing
  virtual void drawChar(GfxState *state, double x, double y,
			double dx, double dy,
			double originX, double originY,
			CharCode code, int nBytes, Unicode *u, int uLen);
  virtual GBool beginType3Char(GfxState *state, double x, double y,
			       double dx, double dy,
			       CharCode code, Unicode *u, int uLen);
  virtual void endType3Char(GfxState *state);
  virtual void endTextObject(GfxState *state);

  //----- image drawing
  virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
			     int width, int height, GBool invert,
			     GBool inlineImg);
  virtual void drawImage(GfxState *state, Object *ref, Stream *str,
			 int width, int height, GfxImageColorMap *colorMap,
			 int *maskColors, GBool inlineImg);
  virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str,
			       int width, int height,
			       GfxImageColorMap *colorMap,
			       Stream *maskStr, int maskWidth, int maskHeight,
			       GBool maskInvert);
  virtual void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
				   int width, int height,
				   GfxImageColorMap *colorMap,
				   Stream *maskStr,
				   int maskWidth, int maskHeight,
				   GfxImageColorMap *maskColorMap);

  //----- Type 3 font operators
  virtual void type3D0(GfxState *state, double wx, double wy);
  virtual void type3D1(GfxState *state, double wx, double wy,
		       double llx, double lly, double urx, double ury);

  //----- transparency groups and soft masks
  virtual void beginTransparencyGroup(GfxState *state, double *bbox,
				      GfxColorSpace *blendingColorSpace,
				      GBool isolated, GBool knockout,
				      GBool forSoftMask);
  virtual void endTransparencyGroup(GfxState *state);
  virtual void paintTransparencyGroup(GfxState *state, double *bbox);
  virtual void setSoftMask(GfxState *state, double *bbox, GBool alpha,
			   Function *transferFunc, GfxColor *backdropColor);
  virtual void clearSoftMask(GfxState *state);

  //----- special access

  // Called to indicate that a new PDF document has been loaded.
  void startDoc(XRef *xrefA);
 
  void setPaperColor(SplashColorPtr paperColorA);

  GBool isReverseVideo() { return reverseVideo; }
  void setReverseVideo(GBool reverseVideoA) { reverseVideo = reverseVideoA; }

  // Get the bitmap and its size.
  SplashBitmap *getBitmap() { return bitmap; }
  int getBitmapWidth();
  int getBitmapHeight();

  // Returns the last rasterized bitmap, transferring ownership to the
  // caller.
  SplashBitmap *takeBitmap();

  // Get the Splash object.
  Splash *getSplash() { return splash; }

  // Get the modified region.
  void getModRegion(int *xMin, int *yMin, int *xMax, int *yMax);

  // Clear the modified region.
  void clearModRegion();

  // Set the Splash fill color.
  void setFillColor(int r, int g, int b);

  // Get a font object for a Base-14 font, using the Latin-1 encoding.
  SplashFont *getFont(GString *name, double *textMatA);

  SplashFont *getCurrentFont() { return font; }

#if 1 //~tmp: turn off anti-aliasing temporarily
  virtual GBool getVectorAntialias();
  virtual void setVectorAntialias(GBool vaa);
#endif

private:

  void setupScreenParams(double hDPI, double vDPI);
#if SPLASH_CMYK
  SplashPattern *getColor(GfxGray gray, GfxRGB *rgb, GfxCMYK *cmyk);
#else
  SplashPattern *getColor(GfxGray gray, GfxRGB *rgb);
#endif
  SplashPath *convertPath(GfxState *state, GfxPath *path);
  void doUpdateFont(GfxState *state);
  void drawType3Glyph(T3FontCache *t3Font,
		      T3FontCacheTag *tag, Guchar *data);
  static GBool imageMaskSrc(void *data, SplashColorPtr line);
  static GBool imageSrc(void *data, SplashColorPtr colorLine,
			Guchar *alphaLine);
  static GBool alphaImageSrc(void *data, SplashColorPtr line,
			     Guchar *alphaLine);
  static GBool maskedImageSrc(void *data, SplashColorPtr line,
			      Guchar *alphaLine);

  SplashColorMode colorMode;
  int bitmapRowPad;
  GBool bitmapTopDown;
  GBool allowAntialias;
  GBool vectorAntialias;
  GBool reverseVideo;		// reverse video mode
  SplashColor paperColor;	// paper color
  SplashScreenParams screenParams;

  XRef *xref;			// xref table for current document

  SplashBitmap *bitmap;
  Splash *splash;
  SplashFontEngine *fontEngine;

  T3FontCache *			// Type 3 font cache
    t3FontCache[splashOutT3FontCacheSize];
  int nT3Fonts;			// number of valid entries in t3FontCache
  T3GlyphStack *t3GlyphStack;	// Type 3 glyph context stack

  SplashFont *font;		// current font
  GBool needFontUpdate;		// set when the font needs to be updated
  SplashPath *textClipPath;	// clipping path built with text object

  SplashTransparencyGroup *	// transparency group stack
    transpGroupStack;
};

#endif
//========================================================================
//
// Stream-CCITT.h
//
// Tables for CCITT Fax decoding.
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

struct CCITTCode {
  short bits;
  short n;
};

#define ccittEOL -2

//------------------------------------------------------------------------
// 2D codes
//------------------------------------------------------------------------

#define twoDimPass   0
#define twoDimHoriz  1
#define twoDimVert0  2
#define twoDimVertR1 3
#define twoDimVertL1 4
#define twoDimVertR2 5
#define twoDimVertL2 6
#define twoDimVertR3 7
#define twoDimVertL3 8

// 1-7 bit codes
static CCITTCode twoDimTab1[128] = {
  {-1, -1}, {-1, -1},		        // 000000x
  {7, twoDimVertL3},		        // 0000010
  {7, twoDimVertR3},		        // 0000011
  {6, twoDimVertL2}, {6, twoDimVertL2},	// 000010x
  {6, twoDimVertR2}, {6, twoDimVertR2},	// 000011x
  {4, twoDimPass}, {4, twoDimPass},     // 0001xxx
    {4, twoDimPass}, {4, twoDimPass},
    {4, twoDimPass}, {4, twoDimPass},
    {4, twoDimPass}, {4, twoDimPass},
  {3, twoDimHoriz}, {3, twoDimHoriz},	// 001xxxx
    {3, twoDimHoriz}, {3, twoDimHoriz},
    {3, twoDimHoriz}, {3, twoDimHoriz},
    {3, twoDimHoriz}, {3, twoDimHoriz},
    {3, twoDimHoriz}, {3, twoDimHoriz},
    {3, twoDimHoriz}, {3, twoDimHoriz},
    {3, twoDimHoriz}, {3, twoDimHoriz},
    {3, twoDimHoriz}, {3, twoDimHoriz},
  {3, twoDimVertL1}, {3, twoDimVertL1},	// 010xxxx
    {3, twoDimVertL1}, {3, twoDimVertL1},
    {3, twoDimVertL1}, {3, twoDimVertL1},
    {3, twoDimVertL1}, {3, twoDimVertL1},
    {3, twoDimVertL1}, {3, twoDimVertL1},
    {3, twoDimVertL1}, {3, twoDimVertL1},
    {3, twoDimVertL1}, {3, twoDimVertL1},
    {3, twoDimVertL1}, {3, twoDimVertL1},
  {3, twoDimVertR1}, {3, twoDimVertR1},	// 011xxxx
    {3, twoDimVertR1}, {3, twoDimVertR1},
    {3, twoDimVertR1}, {3, twoDimVertR1},
    {3, twoDimVertR1}, {3, twoDimVertR1},
    {3, twoDimVertR1}, {3, twoDimVertR1},
    {3, twoDimVertR1}, {3, twoDimVertR1},
    {3, twoDimVertR1}, {3, twoDimVertR1},
    {3, twoDimVertR1}, {3, twoDimVertR1},
  {1, twoDimVert0}, {1, twoDimVert0},	// 1xxxxxx
    {1, twoDimVert0}, {1, twoDimVert0},
    {1, twoDimVert0}, {1, twoDimVert0},
    {1, twoDimVert0}, {1, twoDimVert0},
    {1, twoDimVert0}, {1, twoDimVert0},
    {1, twoDimVert0}, {1, twoDimVert0},
    {1, twoDimVert0}, {1, twoDimVert0},
    {1, twoDimVert0}, {1, twoDimVert0},
    {1, twoDimVert0}, {1, twoDimVert0},
    {1, twoDimVert0}, {1, twoDimVert0},
    {1, twoDimVert0}, {1, twoDimVert0},
    {1, twoDimVert0}, {1, twoDimVert0},
    {1, twoDimVert0}, {1, twoDimVert0},
    {1, twoDimVert0}, {1, twoDimVert0},
    {1, twoDimVert0}, {1, twoDimVert0},
    {1, twoDimVert0}, {1, twoDimVert0},
    {1, twoDimVert0}, {1, twoDimVert0},
    {1, twoDimVert0}, {1, twoDimVert0},
    {1, twoDimVert0}, {1, twoDimVert0},
    {1, twoDimVert0}, {1, twoDimVert0},
    {1, twoDimVert0}, {1, twoDimVert0},
    {1, twoDimVert0}, {1, twoDimVert0},
    {1, twoDimVert0}, {1, twoDimVert0},
    {1, twoDimVert0}, {1, twoDimVert0},
    {1, twoDimVert0}, {1, twoDimVert0},
    {1, twoDimVert0}, {1, twoDimVert0},
    {1, twoDimVert0}, {1, twoDimVert0},
    {1, twoDimVert0}, {1, twoDimVert0},
    {1, twoDimVert0}, {1, twoDimVert0},
    {1, twoDimVert0}, {1, twoDimVert0},
    {1, twoDimVert0}, {1, twoDimVert0},
    {1, twoDimVert0}, {1, twoDimVert0}
};

//------------------------------------------------------------------------
// white run lengths
//------------------------------------------------------------------------

// 11-12 bit codes (upper 7 bits are 0)
static CCITTCode whiteTab1[32] = {
  {-1, -1},					// 00000
  {12, ccittEOL},				// 00001
  {-1, -1}, {-1, -1},				// 0001x
  {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1},	// 001xx
  {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1},	// 010xx
  {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1},	// 011xx
  {11, 1792}, {11, 1792},			// 1000x
  {12, 1984},					// 10010
  {12, 2048},					// 10011
  {12, 2112},					// 10100
  {12, 2176},					// 10101
  {12, 2240},					// 10110
  {12, 2304},					// 10111
  {11, 1856}, {11, 1856},			// 1100x
  {11, 1920}, {11, 1920},			// 1101x
  {12, 2368},					// 11100
  {12, 2432},					// 11101
  {12, 2496},					// 11110
  {12, 2560}					// 11111
};

// 1-9 bit codes
static CCITTCode whiteTab2[512] = {
  {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1},	// 0000000xx
  {8, 29}, {8, 29},				// 00000010x
  {8, 30}, {8, 30},				// 00000011x
  {8, 45}, {8, 45},				// 00000100x
  {8, 46}, {8, 46},				// 00000101x
  {7, 22}, {7, 22}, {7, 22}, {7, 22},		// 0000011xx
  {7, 23}, {7, 23}, {7, 23}, {7, 23},		// 0000100xx
  {8, 47}, {8, 47},				// 00001010x
  {8, 48}, {8, 48},				// 00001011x
  {6, 13}, {6, 13}, {6, 13}, {6, 13},		// 000011xxx
    {6, 13}, {6, 13}, {6, 13}, {6, 13},
  {7, 20}, {7, 20}, {7, 20}, {7, 20},		// 0001000xx
  {8, 33}, {8, 33},				// 00010010x
  {8, 34}, {8, 34},				// 00010011x
  {8, 35}, {8, 35},				// 00010100x
  {8, 36}, {8, 36},				// 00010101x
  {8, 37}, {8, 37},				// 00010110x
  {8, 38}, {8, 38},				// 00010111x
  {7, 19}, {7, 19}, {7, 19}, {7, 19},		// 0001100xx
  {8, 31}, {8, 31},				// 00011010x
  {8, 32}, {8, 32},				// 00011011x
  {6, 1}, {6, 1}, {6, 1}, {6, 1},		// 000111xxx
    {6, 1}, {6, 1}, {6, 1}, {6, 1},
  {6, 12}, {6, 12}, {6, 12}, {6, 12},		// 001000xxx
    {6, 12}, {6, 12}, {6, 12}, {6, 12},
  {8, 53}, {8, 53},				// 00100100x
  {8, 54}, {8, 54},				// 00100101x
  {7, 26}, {7, 26}, {7, 26}, {7, 26},		// 0010011xx
  {8, 39}, {8, 39},				// 00101000x
  {8, 40}, {8, 40},				// 00101001x
  {8, 41}, {8, 41},				// 00101010x
  {8, 42}, {8, 42},				// 00101011x
  {8, 43}, {8, 43},				// 00101100x
  {8, 44}, {8, 44},				// 00101101x
  {7, 21}, {7, 21}, {7, 21}, {7, 21},		// 0010111xx
  {7, 28}, {7, 28}, {7, 28}, {7, 28},		// 0011000xx
  {8, 61}, {8, 61},				// 00110010x
  {8, 62}, {8, 62},				// 00110011x
  {8, 63}, {8, 63},				// 00110100x
  {8, 0}, {8, 0},				// 00110101x
  {8, 320}, {8, 320},				// 00110110x
  {8, 384}, {8, 384},				// 00110111x
  {5, 10}, {5, 10}, {5, 10}, {5, 10},		// 00111xxxx
    {5, 10}, {5, 10}, {5, 10}, {5, 10},
    {5, 10}, {5, 10}, {5, 10}, {5, 10},
    {5, 10}, {5, 10}, {5, 10}, {5, 10},
  {5, 11}, {5, 11}, {5, 11}, {5, 11},		// 01000xxxx
    {5, 11}, {5, 11}, {5, 11}, {5, 11},
    {5, 11}, {5, 11}, {5, 11}, {5, 11},
    {5, 11}, {5, 11}, {5, 11}, {5, 11},
  {7, 27}, {7, 27}, {7, 27}, {7, 27},		// 0100100xx
  {8, 59}, {8, 59},				// 01001010x
  {8, 60}, {8, 60},				// 01001011x
  {9, 1472},					// 010011000
  {9, 1536},					// 010011001
  {9, 1600},					// 010011010
  {9, 1728},					// 010011011
  {7, 18}, {7, 18}, {7, 18}, {7, 18},		// 0100111xx
  {7, 24}, {7, 24}, {7, 24}, {7, 24},		// 0101000xx
  {8, 49}, {8, 49},				// 01010010x
  {8, 50}, {8, 50},				// 01010011x
  {8, 51}, {8, 51},				// 01010100x
  {8, 52}, {8, 52},				// 01010101x
  {7, 25}, {7, 25}, {7, 25}, {7, 25},		// 0101011xx
  {8, 55}, {8, 55},				// 01011000x
  {8, 56}, {8, 56},				// 01011001x
  {8, 57}, {8, 57},				// 01011010x
  {8, 58}, {8, 58},				// 01011011x
  {6, 192}, {6, 192}, {6, 192}, {6, 192},	// 010111xxx
    {6, 192}, {6, 192}, {6, 192}, {6, 192},
  {6, 1664}, {6, 1664}, {6, 1664}, {6, 1664},	// 011000xxx
    {6, 1664}, {6, 1664}, {6, 1664}, {6, 1664},
  {8, 448}, {8, 448},				// 01100100x
  {8, 512}, {8, 512},				// 01100101x
  {9, 704},					// 011001100
  {9, 768},					// 011001101
  {8, 640}, {8, 640},				// 01100111x
  {8, 576}, {8, 576},				// 01101000x
  {9, 832},					// 011010010
  {9, 896},					// 011010011
  {9, 960},					// 011010100
  {9, 1024},					// 011010101
  {9, 1088},					// 011010110
  {9, 1152},					// 011010111
  {9, 1216},					// 011011000
  {9, 1280},					// 011011001
  {9, 1344},					// 011011010
  {9, 1408},					// 011011011
  {7, 256}, {7, 256}, {7, 256}, {7, 256},	// 0110111xx
  {4, 2}, {4, 2}, {4, 2}, {4, 2},		// 0111xxxxx
    {4, 2}, {4, 2}, {4, 2}, {4, 2},
    {4, 2}, {4, 2}, {4, 2}, {4, 2},
    {4, 2}, {4, 2}, {4, 2}, {4, 2},
    {4, 2}, {4, 2}, {4, 2}, {4, 2},
    {4, 2}, {4, 2}, {4, 2}, {4, 2},
    {4, 2}, {4, 2}, {4, 2}, {4, 2},
    {4, 2}, {4, 2}, {4, 2}, {4, 2},
  {4, 3}, {4, 3}, {4, 3}, {4, 3},		// 1000xxxxx
    {4, 3}, {4, 3}, {4, 3}, {4, 3},
    {4, 3}, {4, 3}, {4, 3}, {4, 3},
    {4, 3}, {4, 3}, {4, 3}, {4, 3},
    {4, 3}, {4, 3}, {4, 3}, {4, 3},
    {4, 3}, {4, 3}, {4, 3}, {4, 3},
    {4, 3}, {4, 3}, {4, 3}, {4, 3},
    {4, 3}, {4, 3}, {4, 3}, {4, 3},
  {5, 128}, {5, 128}, {5, 128}, {5, 128},	// 10010xxxx
    {5, 128}, {5, 128}, {5, 128}, {5, 128},
    {5, 128}, {5, 128}, {5, 128}, {5, 128},
    {5, 128}, {5, 128}, {5, 128}, {5, 128},
  {5, 8}, {5, 8}, {5, 8}, {5, 8},		// 10011xxxx
    {5, 8}, {5, 8}, {5, 8}, {5, 8},
    {5, 8}, {5, 8}, {5, 8}, {5, 8},
    {5, 8}, {5, 8}, {5, 8}, {5, 8},
  {5, 9}, {5, 9}, {5, 9}, {5, 9},		// 10100xxxx
    {5, 9}, {5, 9}, {5, 9}, {5, 9},
    {5, 9}, {5, 9}, {5, 9}, {5, 9},
    {5, 9}, {5, 9}, {5, 9}, {5, 9},
  {6, 16}, {6, 16}, {6, 16}, {6, 16},		// 101010xxx
    {6, 16}, {6, 16}, {6, 16}, {6, 16},
  {6, 17}, {6, 17}, {6, 17}, {6, 17},		// 101011xxx
    {6, 17}, {6, 17}, {6, 17}, {6, 17},
  {4, 4}, {4, 4}, {4, 4}, {4, 4},		// 1011xxxxx
    {4, 4}, {4, 4}, {4, 4}, {4, 4},
    {4, 4}, {4, 4}, {4, 4}, {4, 4},
    {4, 4}, {4, 4}, {4, 4}, {4, 4},
    {4, 4}, {4, 4}, {4, 4}, {4, 4},
    {4, 4}, {4, 4}, {4, 4}, {4, 4},
    {4, 4}, {4, 4}, {4, 4}, {4, 4},
    {4, 4}, {4, 4}, {4, 4}, {4, 4},
  {4, 5}, {4, 5}, {4, 5}, {4, 5},		// 1100xxxxx
    {4, 5}, {4, 5}, {4, 5}, {4, 5},
    {4, 5}, {4, 5}, {4, 5}, {4, 5},
    {4, 5}, {4, 5}, {4, 5}, {4, 5},
    {4, 5}, {4, 5}, {4, 5}, {4, 5},
    {4, 5}, {4, 5}, {4, 5}, {4, 5},
    {4, 5}, {4, 5}, {4, 5}, {4, 5},
    {4, 5}, {4, 5}, {4, 5}, {4, 5},
  {6, 14}, {6, 14}, {6, 14}, {6, 14},		// 110100xxx
    {6, 14}, {6, 14}, {6, 14}, {6, 14},
  {6, 15}, {6, 15}, {6, 15}, {6, 15},		// 110101xxx
    {6, 15}, {6, 15}, {6, 15}, {6, 15},
  {5, 64}, {5, 64}, {5, 64}, {5, 64},		// 11011xxxx
    {5, 64}, {5, 64}, {5, 64}, {5, 64},
    {5, 64}, {5, 64}, {5, 64}, {5, 64},
    {5, 64}, {5, 64}, {5, 64}, {5, 64},
  {4, 6}, {4, 6}, {4, 6}, {4, 6},		// 1110xxxxx
    {4, 6}, {4, 6}, {4, 6}, {4, 6},
    {4, 6}, {4, 6}, {4, 6}, {4, 6},
    {4, 6}, {4, 6}, {4, 6}, {4, 6},
    {4, 6}, {4, 6}, {4, 6}, {4, 6},
    {4, 6}, {4, 6}, {4, 6}, {4, 6},
    {4, 6}, {4, 6}, {4, 6}, {4, 6},
    {4, 6}, {4, 6}, {4, 6}, {4, 6},
  {4, 7}, {4, 7}, {4, 7}, {4, 7},		// 1111xxxxx
    {4, 7}, {4, 7}, {4, 7}, {4, 7},
    {4, 7}, {4, 7}, {4, 7}, {4, 7},
    {4, 7}, {4, 7}, {4, 7}, {4, 7},
    {4, 7}, {4, 7}, {4, 7}, {4, 7},
    {4, 7}, {4, 7}, {4, 7}, {4, 7},
    {4, 7}, {4, 7}, {4, 7}, {4, 7},
    {4, 7}, {4, 7}, {4, 7}, {4, 7}
};

//------------------------------------------------------------------------
// black run lengths
//------------------------------------------------------------------------

// 10-13 bit codes (upper 6 bits are 0)
static CCITTCode blackTab1[128] = {
  {-1, -1}, {-1, -1},					// 000000000000x
  {12, ccittEOL}, {12, ccittEOL},			// 000000000001x
  {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1},		// 00000000001xx
  {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1},		// 00000000010xx
  {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1},		// 00000000011xx
  {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1},		// 00000000100xx
  {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1},		// 00000000101xx
  {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1},		// 00000000110xx
  {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1},		// 00000000111xx
  {11, 1792}, {11, 1792}, {11, 1792}, {11, 1792},	// 00000001000xx
  {12, 1984}, {12, 1984},				// 000000010010x
  {12, 2048}, {12, 2048},				// 000000010011x
  {12, 2112}, {12, 2112},				// 000000010100x
  {12, 2176}, {12, 2176},				// 000000010101x
  {12, 2240}, {12, 2240},				// 000000010110x
  {12, 2304}, {12, 2304},				// 000000010111x
  {11, 1856}, {11, 1856}, {11, 1856}, {11, 1856},	// 00000001100xx
  {11, 1920}, {11, 1920}, {11, 1920}, {11, 1920},	// 00000001101xx
  {12, 2368}, {12, 2368},				// 000000011100x
  {12, 2432}, {12, 2432},				// 000000011101x
  {12, 2496}, {12, 2496},				// 000000011110x
  {12, 2560}, {12, 2560},				// 000000011111x
  {10, 18}, {10, 18}, {10, 18}, {10, 18},		// 0000001000xxx
    {10, 18}, {10, 18}, {10, 18}, {10, 18},
  {12, 52}, {12, 52},					// 000000100100x
  {13, 640},						// 0000001001010
  {13, 704},						// 0000001001011
  {13, 768},						// 0000001001100
  {13, 832},						// 0000001001101
  {12, 55}, {12, 55},					// 000000100111x
  {12, 56}, {12, 56},					// 000000101000x
  {13, 1280},						// 0000001010010
  {13, 1344},						// 0000001010011
  {13, 1408},						// 0000001010100
  {13, 1472},						// 0000001010101
  {12, 59}, {12, 59},					// 000000101011x
  {12, 60}, {12, 60},					// 000000101100x
  {13, 1536},						// 0000001011010
  {13, 1600},						// 0000001011011
  {11, 24}, {11, 24}, {11, 24}, {11, 24},		// 00000010111xx
  {11, 25}, {11, 25}, {11, 25}, {11, 25},		// 00000011000xx
  {13, 1664},						// 0000001100100
  {13, 1728},						// 0000001100101
  {12, 320}, {12, 320},					// 000000110011x
  {12, 384}, {12, 384},					// 000000110100x
  {12, 448}, {12, 448},					// 000000110101x
  {13, 512},						// 0000001101100
  {13, 576},						// 0000001101101
  {12, 53}, {12, 53},					// 000000110111x
  {12, 54}, {12, 54},					// 000000111000x
  {13, 896},						// 0000001110010
  {13, 960},						// 0000001110011
  {13, 1024},						// 0000001110100
  {13, 1088},						// 0000001110101
  {13, 1152},						// 0000001110110
  {13, 1216},						// 0000001110111
  {10, 64}, {10, 64}, {10, 64}, {10, 64},		// 0000001111xxx
    {10, 64}, {10, 64}, {10, 64}, {10, 64}
};

// 7-12 bit codes (upper 4 bits are 0)
static CCITTCode blackTab2[192] = {
  {8, 13}, {8, 13}, {8, 13}, {8, 13},			// 00000100xxxx
    {8, 13}, {8, 13}, {8, 13}, {8, 13},
    {8, 13}, {8, 13}, {8, 13}, {8, 13},
    {8, 13}, {8, 13}, {8, 13}, {8, 13},
  {11, 23}, {11, 23},					// 00000101000x
  {12, 50},						// 000001010010
  {12, 51},						// 000001010011
  {12, 44},						// 000001010100
  {12, 45},						// 000001010101
  {12, 46},						// 000001010110
  {12, 47},						// 000001010111
  {12, 57},						// 000001011000
  {12, 58},						// 000001011001
  {12, 61},						// 000001011010
  {12, 256},						// 000001011011
  {10, 16}, {10, 16}, {10, 16}, {10, 16},		// 0000010111xx
  {10, 17}, {10, 17}, {10, 17}, {10, 17},		// 0000011000xx
  {12, 48},						// 000001100100
  {12, 49},						// 000001100101
  {12, 62},						// 000001100110
  {12, 63},						// 000001100111
  {12, 30},						// 000001101000
  {12, 31},						// 000001101001
  {12, 32},						// 000001101010
  {12, 33},						// 000001101011
  {12, 40},						// 000001101100
  {12, 41},						// 000001101101
  {11, 22}, {11, 22},					// 00000110111x
  {8, 14}, {8, 14}, {8, 14}, {8, 14},			// 00000111xxxx
    {8, 14}, {8, 14}, {8, 14}, {8, 14},
    {8, 14}, {8, 14}, {8, 14}, {8, 14},
    {8, 14}, {8, 14}, {8, 14}, {8, 14},
  {7, 10}, {7, 10}, {7, 10}, {7, 10},			// 0000100xxxxx
    {7, 10}, {7, 10}, {7, 10}, {7, 10},
    {7, 10}, {7, 10}, {7, 10}, {7, 10},
    {7, 10}, {7, 10}, {7, 10}, {7, 10},
    {7, 10}, {7, 10}, {7, 10}, {7, 10},
    {7, 10}, {7, 10}, {7, 10}, {7, 10},
    {7, 10}, {7, 10}, {7, 10}, {7, 10},
    {7, 10}, {7, 10}, {7, 10}, {7, 10},
  {7, 11}, {7, 11}, {7, 11}, {7, 11},			// 0000101xxxxx
    {7, 11}, {7, 11}, {7, 11}, {7, 11},
    {7, 11}, {7, 11}, {7, 11}, {7, 11},
    {7, 11}, {7, 11}, {7, 11}, {7, 11},
    {7, 11}, {7, 11}, {7, 11}, {7, 11},
    {7, 11}, {7, 11}, {7, 11}, {7, 11},
    {7, 11}, {7, 11}, {7, 11}, {7, 11},
    {7, 11}, {7, 11}, {7, 11}, {7, 11},
  {9, 15}, {9, 15}, {9, 15}, {9, 15},			// 000011000xxx
    {9, 15}, {9, 15}, {9, 15}, {9, 15},
  {12, 128},						// 000011001000
  {12, 192},						// 000011001001
  {12, 26},						// 000011001010
  {12, 27},						// 000011001011
  {12, 28},						// 000011001100
  {12, 29},						// 000011001101
  {11, 19}, {11, 19},					// 00001100111x
  {11, 20}, {11, 20},					// 00001101000x
  {12, 34},						// 000011010010
  {12, 35},						// 000011010011
  {12, 36},						// 000011010100
  {12, 37},						// 000011010101
  {12, 38},						// 000011010110
  {12, 39},						// 000011010111
  {11, 21}, {11, 21},					// 00001101100x
  {12, 42},						// 000011011010
  {12, 43},						// 000011011011
  {10, 0}, {10, 0}, {10, 0}, {10, 0},			// 0000110111xx
  {7, 12}, {7, 12}, {7, 12}, {7, 12},			// 0000111xxxxx
    {7, 12}, {7, 12}, {7, 12}, {7, 12},
    {7, 12}, {7, 12}, {7, 12}, {7, 12},
    {7, 12}, {7, 12}, {7, 12}, {7, 12},
    {7, 12}, {7, 12}, {7, 12}, {7, 12},
    {7, 12}, {7, 12}, {7, 12}, {7, 12},
    {7, 12}, {7, 12}, {7, 12}, {7, 12},
    {7, 12}, {7, 12}, {7, 12}, {7, 12}
};

// 2-6 bit codes
static CCITTCode blackTab3[64] = {
  {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1},		// 0000xx
  {6, 9},						// 000100
  {6, 8},						// 000101
  {5, 7}, {5, 7},					// 00011x
  {4, 6}, {4, 6}, {4, 6}, {4, 6},			// 0010xx
  {4, 5}, {4, 5}, {4, 5}, {4, 5},			// 0011xx
  {3, 1}, {3, 1}, {3, 1}, {3, 1},			// 010xxx
    {3, 1}, {3, 1}, {3, 1}, {3, 1},
  {3, 4}, {3, 4}, {3, 4}, {3, 4},			// 011xxx
    {3, 4}, {3, 4}, {3, 4}, {3, 4},
  {2, 3}, {2, 3}, {2, 3}, {2, 3},			// 10xxxx
    {2, 3}, {2, 3}, {2, 3}, {2, 3},
    {2, 3}, {2, 3}, {2, 3}, {2, 3},
    {2, 3}, {2, 3}, {2, 3}, {2, 3},
  {2, 2}, {2, 2}, {2, 2}, {2, 2},			// 11xxxx
    {2, 2}, {2, 2}, {2, 2}, {2, 2},
    {2, 2}, {2, 2}, {2, 2}, {2, 2},
    {2, 2}, {2, 2}, {2, 2}, {2, 2}
};
//========================================================================
//
// Stream.cc
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <limits.h>
#ifndef WIN32
#include <unistd.h>
#endif
#include <string.h>
#include <ctype.h>
#include "gmem.h"
#include "gfile.h"
#include "config.h"
#include "Error.h"
#include "Object.h"
#include "Lexer.h"
#include "GfxState.h"
#include "Stream.h"
#include "JBIG2Stream.h"
#include "JPXStream.h"
#include "Stream-CCITT.h"

#ifdef __DJGPP__
static GBool setDJSYSFLAGS = gFalse;
#endif

#ifdef VMS
#ifdef __GNUC__
#define SEEK_SET 0
#define SEEK_CUR 1
#define SEEK_END 2
#endif
#endif

//------------------------------------------------------------------------
// Stream (base class)
//------------------------------------------------------------------------

Stream::Stream() {
  ref = 1;
}

Stream::~Stream() {
}

void Stream::close() {
}

int Stream::getRawChar() {
  error(-1, "Internal: called getRawChar() on non-predictor stream");
  return EOF;
}

char *Stream::getLine(char *buf, int size) {
  int i;
  int c;

  if (lookChar() == EOF)
    return NULL;
  for (i = 0; i < size - 1; ++i) {
    c = getChar();
    if (c == EOF || c == '\n')
      break;
    if (c == '\r') {
      if ((c = lookChar()) == '\n')
	getChar();
      break;
    }
    buf[i] = c;
  }
  buf[i] = '\0';
  return buf;
}

GString *Stream::getPSFilter(int psLevel, char *indent) {
  return new GString();
}

Stream *Stream::addFilters(Object *dict) {
  Object obj, obj2;
  Object params, params2;
  Stream *str;
  int i;

  str = this;
  dict->dictLookup("Filter", &obj);
  if (obj.isNull()) {
    obj.free();
    dict->dictLookup("F", &obj);
  }
  dict->dictLookup("DecodeParms", &params);
  if (params.isNull()) {
    params.free();
    dict->dictLookup("DP", &params);
  }
  if (obj.isName()) {
    str = makeFilter(obj.getName(), str, &params);
  } else if (obj.isArray()) {
    for (i = 0; i < obj.arrayGetLength(); ++i) {
      obj.arrayGet(i, &obj2);
      if (params.isArray())
	params.arrayGet(i, &params2);
      else
	params2.initNull();
      if (obj2.isName()) {
	str = makeFilter(obj2.getName(), str, &params2);
      } else {
	error(getPos(), "Bad filter name");
	str = new EOFStream(str);
      }
      obj2.free();
      params2.free();
    }
  } else if (!obj.isNull()) {
    error(getPos(), "Bad 'Filter' attribute in stream");
  }
  obj.free();
  params.free();

  return str;
}

Stream *Stream::makeFilter(char *name, Stream *str, Object *params) {
  int pred;			// parameters
  int colors;
  int bits;
  int early;
  int encoding;
  GBool endOfLine, byteAlign, endOfBlock, black;
  int columns, rows;
  int colorXform;
  Object globals, obj;

  if (!strcmp(name, "ASCIIHexDecode") || !strcmp(name, "AHx")) {
    str = new ASCIIHexStream(str);
  } else if (!strcmp(name, "ASCII85Decode") || !strcmp(name, "A85")) {
    str = new ASCII85Stream(str);
  } else if (!strcmp(name, "LZWDecode") || !strcmp(name, "LZW")) {
    pred = 1;
    columns = 1;
    colors = 1;
    bits = 8;
    early = 1;
    if (params->isDict()) {
      params->dictLookup("Predictor", &obj);
      if (obj.isInt())
	pred = obj.getInt();
      obj.free();
      params->dictLookup("Columns", &obj);
      if (obj.isInt())
	columns = obj.getInt();
      obj.free();
      params->dictLookup("Colors", &obj);
      if (obj.isInt())
	colors = obj.getInt();
      obj.free();
      params->dictLookup("BitsPerComponent", &obj);
      if (obj.isInt())
	bits = obj.getInt();
      obj.free();
      params->dictLookup("EarlyChange", &obj);
      if (obj.isInt())
	early = obj.getInt();
      obj.free();
    }
    str = new LZWStream(str, pred, columns, colors, bits, early);
  } else if (!strcmp(name, "RunLengthDecode") || !strcmp(name, "RL")) {
    str = new RunLengthStream(str);
  } else if (!strcmp(name, "CCITTFaxDecode") || !strcmp(name, "CCF")) {
    encoding = 0;
    endOfLine = gFalse;
    byteAlign = gFalse;
    columns = 1728;
    rows = 0;
    endOfBlock = gTrue;
    black = gFalse;
    if (params->isDict()) {
      params->dictLookup("K", &obj);
      if (obj.isInt()) {
	encoding = obj.getInt();
      }
      obj.free();
      params->dictLookup("EndOfLine", &obj);
      if (obj.isBool()) {
	endOfLine = obj.getBool();
      }
      obj.free();
      params->dictLookup("EncodedByteAlign", &obj);
      if (obj.isBool()) {
	byteAlign = obj.getBool();
      }
      obj.free();
      params->dictLookup("Columns", &obj);
      if (obj.isInt()) {
	columns = obj.getInt();
      }
      obj.free();
      params->dictLookup("Rows", &obj);
      if (obj.isInt()) {
	rows = obj.getInt();
      }
      obj.free();
      params->dictLookup("EndOfBlock", &obj);
      if (obj.isBool()) {
	endOfBlock = obj.getBool();
      }
      obj.free();
      params->dictLookup("BlackIs1", &obj);
      if (obj.isBool()) {
	black = obj.getBool();
      }
      obj.free();
    }
    str = new CCITTFaxStream(str, encoding, endOfLine, byteAlign,
			     columns, rows, endOfBlock, black);
  } else if (!strcmp(name, "DCTDecode") || !strcmp(name, "DCT")) {
    colorXform = -1;
    if (params->isDict()) {
      if (params->dictLookup("ColorTransform", &obj)->isInt()) {
	colorXform = obj.getInt();
      }
      obj.free();
    }
    str = new DCTStream(str, colorXform);
  } else if (!strcmp(name, "FlateDecode") || !strcmp(name, "Fl")) {
    pred = 1;
    columns = 1;
    colors = 1;
    bits = 8;
    if (params->isDict()) {
      params->dictLookup("Predictor", &obj);
      if (obj.isInt())
	pred = obj.getInt();
      obj.free();
      params->dictLookup("Columns", &obj);
      if (obj.isInt())
	columns = obj.getInt();
      obj.free();
      params->dictLookup("Colors", &obj);
      if (obj.isInt())
	colors = obj.getInt();
      obj.free();
      params->dictLookup("BitsPerComponent", &obj);
      if (obj.isInt())
	bits = obj.getInt();
      obj.free();
    }
    str = new FlateStream(str, pred, columns, colors, bits);
  } else if (!strcmp(name, "JBIG2Decode")) {
    if (params->isDict()) {
      params->dictLookup("JBIG2Globals", &globals);
    }
    str = new JBIG2Stream(str, &globals);
    globals.free();
  } else if (!strcmp(name, "JPXDecode")) {
    str = new JPXStream(str);
  } else {
    error(getPos(), "Unknown filter '%s'", name);
    str = new EOFStream(str);
  }
  return str;
}

//------------------------------------------------------------------------
// BaseStream
//------------------------------------------------------------------------

BaseStream::BaseStream(Object *dictA) {
  dict = *dictA;
}

BaseStream::~BaseStream() {
  dict.free();
}

//------------------------------------------------------------------------
// FilterStream
//------------------------------------------------------------------------

FilterStream::FilterStream(Stream *strA) {
  str = strA;
}

FilterStream::~FilterStream() {
}

void FilterStream::close() {
  str->close();
}

void FilterStream::setPos(Guint pos, int dir) {
  error(-1, "Internal: called setPos() on FilterStream");
}

//------------------------------------------------------------------------
// ImageStream
//------------------------------------------------------------------------

ImageStream::ImageStream(Stream *strA, int widthA, int nCompsA, int nBitsA) {
  int imgLineSize;

  str = strA;
  width = widthA;
  nComps = nCompsA;
  nBits = nBitsA;

  nVals = width * nComps;
  if (nBits == 1) {
    imgLineSize = (nVals + 7) & ~7;
  } else {
    imgLineSize = nVals;
  }
  imgLine = (Guchar *)gmallocn(imgLineSize, sizeof(Guchar));
  imgIdx = nVals;
}

ImageStream::~ImageStream() {
  gfree(imgLine);
}

void ImageStream::reset() {
  str->reset();
}

GBool ImageStream::getPixel(Guchar *pix) {
  int i;

  if (imgIdx >= nVals) {
    getLine();
    imgIdx = 0;
  }
  for (i = 0; i < nComps; ++i) {
    pix[i] = imgLine[imgIdx++];
  }
  return gTrue;
}

Guchar *ImageStream::getLine() {
  Gulong buf, bitMask;
  int bits;
  int c;
  int i;

  if (nBits == 1) {
    for (i = 0; i < nVals; i += 8) {
      c = str->getChar();
      imgLine[i+0] = (Guchar)((c >> 7) & 1);
      imgLine[i+1] = (Guchar)((c >> 6) & 1);
      imgLine[i+2] = (Guchar)((c >> 5) & 1);
      imgLine[i+3] = (Guchar)((c >> 4) & 1);
      imgLine[i+4] = (Guchar)((c >> 3) & 1);
      imgLine[i+5] = (Guchar)((c >> 2) & 1);
      imgLine[i+6] = (Guchar)((c >> 1) & 1);
      imgLine[i+7] = (Guchar)(c & 1);
    }
  } else if (nBits == 8) {
    for (i = 0; i < nVals; ++i) {
      imgLine[i] = str->getChar();
    }
  } else {
    bitMask = (1 << nBits) - 1;
    buf = 0;
    bits = 0;
    for (i = 0; i < nVals; ++i) {
      if (bits < nBits) {
	buf = (buf << 8) | (str->getChar() & 0xff);
	bits += 8;
      }
      imgLine[i] = (Guchar)((buf >> (bits - nBits)) & bitMask);
      bits -= nBits;
    }
  }
  return imgLine;
}

void ImageStream::skipLine() {
  int n, i;

  n = (nVals * nBits + 7) >> 3;
  for (i = 0; i < n; ++i) {
    str->getChar();
  }
}

//------------------------------------------------------------------------
// StreamPredictor
//------------------------------------------------------------------------

StreamPredictor::StreamPredictor(Stream *strA, int predictorA,
				 int widthA, int nCompsA, int nBitsA) {
  str = strA;
  predictor = predictorA;
  width = widthA;
  nComps = nCompsA;
  nBits = nBitsA;
  predLine = NULL;
  ok = gFalse;

  nVals = width * nComps;
  if (width <= 0 || nComps <= 0 || nBits <= 0 ||
      nComps >= INT_MAX / nBits ||
      width >= INT_MAX / nComps / nBits ||
      nVals * nBits + 7 < 0) {
    return;
  }
  pixBytes = (nComps * nBits + 7) >> 3;
  rowBytes = ((nVals * nBits + 7) >> 3) + pixBytes;
  if (rowBytes <= 0) {
    return;
  }
  predLine = (Guchar *)gmalloc(rowBytes);
  memset(predLine, 0, rowBytes);
  predIdx = rowBytes;

  ok = gTrue;
}

StreamPredictor::~StreamPredictor() {
  gfree(predLine);
}

int StreamPredictor::lookChar() {
  if (predIdx >= rowBytes) {
    if (!getNextLine()) {
      return EOF;
    }
  }
  return predLine[predIdx];
}

int StreamPredictor::getChar() {
  if (predIdx >= rowBytes) {
    if (!getNextLine()) {
      return EOF;
    }
  }
  return predLine[predIdx++];
}

GBool StreamPredictor::getNextLine() {
  int curPred;
  Guchar upLeftBuf[gfxColorMaxComps * 2 + 1];
  int left, up, upLeft, p, pa, pb, pc;
  int c;
  Gulong inBuf, outBuf, bitMask;
  int inBits, outBits;
  int i, j, k, kk;

  // get PNG optimum predictor number
  if (predictor >= 10) {
    if ((curPred = str->getRawChar()) == EOF) {
      return gFalse;
    }
    curPred += 10;
  } else {
    curPred = predictor;
  }

  // read the raw line, apply PNG (byte) predictor
  memset(upLeftBuf, 0, pixBytes + 1);
  for (i = pixBytes; i < rowBytes; ++i) {
    for (j = pixBytes; j > 0; --j) {
      upLeftBuf[j] = upLeftBuf[j-1];
    }
    upLeftBuf[0] = predLine[i];
    if ((c = str->getRawChar()) == EOF) {
      if (i > pixBytes) {
	// this ought to return false, but some (broken) PDF files
	// contain truncated image data, and Adobe apparently reads the
	// last partial line
	break;
      }
      return gFalse;
    }
    switch (curPred) {
    case 11:			// PNG sub
      predLine[i] = predLine[i - pixBytes] + (Guchar)c;
      break;
    case 12:			// PNG up
      predLine[i] = predLine[i] + (Guchar)c;
      break;
    case 13:			// PNG average
      predLine[i] = ((predLine[i - pixBytes] + predLine[i]) >> 1) +
	            (Guchar)c;
      break;
    case 14:			// PNG Paeth
      left = predLine[i - pixBytes];
      up = predLine[i];
      upLeft = upLeftBuf[pixBytes];
      p = left + up - upLeft;
      if ((pa = p - left) < 0)
	pa = -pa;
      if ((pb = p - up) < 0)
	pb = -pb;
      if ((pc = p - upLeft) < 0)
	pc = -pc;
      if (pa <= pb && pa <= pc)
	predLine[i] = left + (Guchar)c;
      else if (pb <= pc)
	predLine[i] = up + (Guchar)c;
      else
	predLine[i] = upLeft + (Guchar)c;
      break;
    case 10:			// PNG none
    default:			// no predictor or TIFF predictor
      predLine[i] = (Guchar)c;
      break;
    }
  }

  // apply TIFF (component) predictor
  if (predictor == 2) {
    if (nBits == 1) {
      inBuf = predLine[pixBytes - 1];
      for (i = pixBytes; i < rowBytes; i += 8) {
	// 1-bit add is just xor
	inBuf = (inBuf << 8) | predLine[i];
	predLine[i] ^= inBuf >> nComps;
      }
    } else if (nBits == 8) {
      for (i = pixBytes; i < rowBytes; ++i) {
	predLine[i] += predLine[i - nComps];
      }
    } else {
      memset(upLeftBuf, 0, nComps + 1);
      bitMask = (1 << nBits) - 1;
      inBuf = outBuf = 0;
      inBits = outBits = 0;
      j = k = pixBytes;
      for (i = 0; i < width; ++i) {
	for (kk = 0; kk < nComps; ++kk) {
	  if (inBits < nBits) {
	    inBuf = (inBuf << 8) | (predLine[j++] & 0xff);
	    inBits += 8;
	  }
	  upLeftBuf[kk] = (Guchar)((upLeftBuf[kk] +
				    (inBuf >> (inBits - nBits))) & bitMask);
	  inBits -= nBits;
	  outBuf = (outBuf << nBits) | upLeftBuf[kk];
	  outBits += nBits;
	  if (outBits >= 8) {
	    predLine[k++] = (Guchar)(outBuf >> (outBits - 8));
	    outBits -= 8;
	  }
	}
      }
      if (outBits > 0) {
	predLine[k++] = (Guchar)((outBuf << (8 - outBits)) +
				 (inBuf & ((1 << (8 - outBits)) - 1)));
      }
    }
  }

  // reset to start of line
  predIdx = pixBytes;

  return gTrue;
}

//------------------------------------------------------------------------
// FileStream
//------------------------------------------------------------------------

FileStream::FileStream(FILE *fA, Guint startA, GBool limitedA,
		       Guint lengthA, Object *dictA):
    BaseStream(dictA) {
  f = fA;
  start = startA;
  limited = limitedA;
  length = lengthA;
  bufPtr = bufEnd = buf;
  bufPos = start;
  savePos = 0;
  saved = gFalse;
}

FileStream::~FileStream() {
  close();
}

Stream *FileStream::makeSubStream(Guint startA, GBool limitedA,
				  Guint lengthA, Object *dictA) {
  return new FileStream(f, startA, limitedA, lengthA, dictA);
}

void FileStream::reset() {
#if HAVE_FSEEKO
  savePos = (Guint)ftello(f);
  fseeko(f, start, SEEK_SET);
#elif HAVE_FSEEK64
  savePos = (Guint)ftell64(f);
  fseek64(f, start, SEEK_SET);
#else
  savePos = (Guint)ftell(f);
  fseek(f, start, SEEK_SET);
#endif
  saved = gTrue;
  bufPtr = bufEnd = buf;
  bufPos = start;
}

void FileStream::close() {
  if (saved) {
#if HAVE_FSEEKO
    fseeko(f, savePos, SEEK_SET);
#elif HAVE_FSEEK64
    fseek64(f, savePos, SEEK_SET);
#else
    fseek(f, savePos, SEEK_SET);
#endif
    saved = gFalse;
  }
}

GBool FileStream::fillBuf() {
  int n;

  bufPos += bufEnd - buf;
  bufPtr = bufEnd = buf;
  if (limited && bufPos >= start + length) {
    return gFalse;
  }
  if (limited && bufPos + fileStreamBufSize > start + length) {
    n = start + length - bufPos;
  } else {
    n = fileStreamBufSize;
  }
  n = fread(buf, 1, n, f);
  bufEnd = buf + n;
  if (bufPtr >= bufEnd) {
    return gFalse;
  }
  return gTrue;
}

void FileStream::setPos(Guint pos, int dir) {
  Guint size;

  if (dir >= 0) {
#if HAVE_FSEEKO
    fseeko(f, pos, SEEK_SET);
#elif HAVE_FSEEK64
    fseek64(f, pos, SEEK_SET);
#else
    fseek(f, pos, SEEK_SET);
#endif
    bufPos = pos;
  } else {
#if HAVE_FSEEKO
    fseeko(f, 0, SEEK_END);
    size = (Guint)ftello(f);
#elif HAVE_FSEEK64
    fseek64(f, 0, SEEK_END);
    size = (Guint)ftell64(f);
#else
    fseek(f, 0, SEEK_END);
    size = (Guint)ftell(f);
#endif
    if (pos > size)
      pos = (Guint)size;
#ifdef __CYGWIN32__
    //~ work around a bug in cygwin's implementation of fseek
    rewind(f);
#endif
#if HAVE_FSEEKO
    fseeko(f, -(int)pos, SEEK_END);
    bufPos = (Guint)ftello(f);
#elif HAVE_FSEEK64
    fseek64(f, -(int)pos, SEEK_END);
    bufPos = (Guint)ftell64(f);
#else
    fseek(f, -(int)pos, SEEK_END);
    bufPos = (Guint)ftell(f);
#endif
  }
  bufPtr = bufEnd = buf;
}

void FileStream::moveStart(int delta) {
  start += delta;
  bufPtr = bufEnd = buf;
  bufPos = start;
}

//------------------------------------------------------------------------
// MemStream
//------------------------------------------------------------------------

MemStream::MemStream(char *bufA, Guint startA, Guint lengthA, Object *dictA):
    BaseStream(dictA) {
  buf = bufA;
  start = startA;
  length = lengthA;
  bufEnd = buf + start + length;
  bufPtr = buf + start;
  needFree = gFalse;
}

MemStream::~MemStream() {
  if (needFree) {
    gfree(buf);
  }
}

Stream *MemStream::makeSubStream(Guint startA, GBool limited,
				 Guint lengthA, Object *dictA) {
  MemStream *subStr;
  Guint newLength;

  if (!limited || startA + lengthA > start + length) {
    newLength = start + length - startA;
  } else {
    newLength = lengthA;
  }
  subStr = new MemStream(buf, startA, newLength, dictA);
  return subStr;
}

void MemStream::reset() {
  bufPtr = buf + start;
}

void MemStream::close() {
}

void MemStream::setPos(Guint pos, int dir) {
  Guint i;

  if (dir >= 0) {
    i = pos;
  } else {
    i = start + length - pos;
  }
  if (i < start) {
    i = start;
  } else if (i > start + length) {
    i = start + length;
  }
  bufPtr = buf + i;
}

void MemStream::moveStart(int delta) {
  start += delta;
  length -= delta;
  bufPtr = buf + start;
}

//------------------------------------------------------------------------
// EmbedStream
//------------------------------------------------------------------------

EmbedStream::EmbedStream(Stream *strA, Object *dictA,
			 GBool limitedA, Guint lengthA):
    BaseStream(dictA) {
  str = strA;
  limited = limitedA;
  length = lengthA;
}

EmbedStream::~EmbedStream() {
}

Stream *EmbedStream::makeSubStream(Guint start, GBool limitedA,
				   Guint lengthA, Object *dictA) {
  error(-1, "Internal: called makeSubStream() on EmbedStream");
  return NULL;
}

int EmbedStream::getChar() {
  if (limited && !length) {
    return EOF;
  }
  --length;
  return str->getChar();
}

int EmbedStream::lookChar() {
  if (limited && !length) {
    return EOF;
  }
  return str->lookChar();
}

void EmbedStream::setPos(Guint pos, int dir) {
  error(-1, "Internal: called setPos() on EmbedStream");
}

Guint EmbedStream::getStart() {
  error(-1, "Internal: called getStart() on EmbedStream");
  return 0;
}

void EmbedStream::moveStart(int delta) {
  error(-1, "Internal: called moveStart() on EmbedStream");
}

//------------------------------------------------------------------------
// ASCIIHexStream
//------------------------------------------------------------------------

ASCIIHexStream::ASCIIHexStream(Stream *strA):
    FilterStream(strA) {
  buf = EOF;
  eof = gFalse;
}

ASCIIHexStream::~ASCIIHexStream() {
  delete str;
}

void ASCIIHexStream::reset() {
  str->reset();
  buf = EOF;
  eof = gFalse;
}

int ASCIIHexStream::lookChar() {
  int c1, c2, x;

  if (buf != EOF)
    return buf;
  if (eof) {
    buf = EOF;
    return EOF;
  }
  do {
    c1 = str->getChar();
  } while (isspace(c1));
  if (c1 == '>') {
    eof = gTrue;
    buf = EOF;
    return buf;
  }
  do {
    c2 = str->getChar();
  } while (isspace(c2));
  if (c2 == '>') {
    eof = gTrue;
    c2 = '0';
  }
  if (c1 >= '0' && c1 <= '9') {
    x = (c1 - '0') << 4;
  } else if (c1 >= 'A' && c1 <= 'F') {
    x = (c1 - 'A' + 10) << 4;
  } else if (c1 >= 'a' && c1 <= 'f') {
    x = (c1 - 'a' + 10) << 4;
  } else if (c1 == EOF) {
    eof = gTrue;
    x = 0;
  } else {
    error(getPos(), "Illegal character <%02x> in ASCIIHex stream", c1);
    x = 0;
  }
  if (c2 >= '0' && c2 <= '9') {
    x += c2 - '0';
  } else if (c2 >= 'A' && c2 <= 'F') {
    x += c2 - 'A' + 10;
  } else if (c2 >= 'a' && c2 <= 'f') {
    x += c2 - 'a' + 10;
  } else if (c2 == EOF) {
    eof = gTrue;
    x = 0;
  } else {
    error(getPos(), "Illegal character <%02x> in ASCIIHex stream", c2);
  }
  buf = x & 0xff;
  return buf;
}

GString *ASCIIHexStream::getPSFilter(int psLevel, char *indent) {
  GString *s;

  if (psLevel < 2) {
    return NULL;
  }
  if (!(s = str->getPSFilter(psLevel, indent))) {
    return NULL;
  }
  s->append(indent)->append("/ASCIIHexDecode filter\n");
  return s;
}

GBool ASCIIHexStream::isBinary(GBool last) {
  return str->isBinary(gFalse);
}

//------------------------------------------------------------------------
// ASCII85Stream
//------------------------------------------------------------------------

ASCII85Stream::ASCII85Stream(Stream *strA):
    FilterStream(strA) {
  index = n = 0;
  eof = gFalse;
}

ASCII85Stream::~ASCII85Stream() {
  delete str;
}

void ASCII85Stream::reset() {
  str->reset();
  index = n = 0;
  eof = gFalse;
}

int ASCII85Stream::lookChar() {
  int k;
  Gulong t;

  if (index >= n) {
    if (eof)
      return EOF;
    index = 0;
    do {
      c[0] = str->getChar();
    } while (Lexer::isSpace(c[0]));
    if (c[0] == '~' || c[0] == EOF) {
      eof = gTrue;
      n = 0;
      return EOF;
    } else if (c[0] == 'z') {
      b[0] = b[1] = b[2] = b[3] = 0;
      n = 4;
    } else {
      for (k = 1; k < 5; ++k) {
	do {
	  c[k] = str->getChar();
	} while (Lexer::isSpace(c[k]));
	if (c[k] == '~' || c[k] == EOF)
	  break;
      }
      n = k - 1;
      if (k < 5 && (c[k] == '~' || c[k] == EOF)) {
	for (++k; k < 5; ++k)
	  c[k] = 0x21 + 84;
	eof = gTrue;
      }
      t = 0;
      for (k = 0; k < 5; ++k)
	t = t * 85 + (c[k] - 0x21);
      for (k = 3; k >= 0; --k) {
	b[k] = (int)(t & 0xff);
	t >>= 8;
      }
    }
  }
  return b[index];
}

GString *ASCII85Stream::getPSFilter(int psLevel, char *indent) {
  GString *s;

  if (psLevel < 2) {
    return NULL;
  }
  if (!(s = str->getPSFilter(psLevel, indent))) {
    return NULL;
  }
  s->append(indent)->append("/ASCII85Decode filter\n");
  return s;
}

GBool ASCII85Stream::isBinary(GBool last) {
  return str->isBinary(gFalse);
}

//------------------------------------------------------------------------
// LZWStream
//------------------------------------------------------------------------

LZWStream::LZWStream(Stream *strA, int predictor, int columns, int colors,
		     int bits, int earlyA):
    FilterStream(strA) {
  if (predictor != 1) {
    pred = new StreamPredictor(this, predictor, columns, colors, bits);
    if (!pred->isOk()) {
      delete pred;
      pred = NULL;
    }
  } else {
    pred = NULL;
  }
  early = earlyA;
  eof = gFalse;
  inputBits = 0;
  clearTable();
}

LZWStream::~LZWStream() {
  if (pred) {
    delete pred;
  }
  delete str;
}

int LZWStream::getChar() {
  if (pred) {
    return pred->getChar();
  }
  if (eof) {
    return EOF;
  }
  if (seqIndex >= seqLength) {
    if (!processNextCode()) {
      return EOF;
    }
  }
  return seqBuf[seqIndex++];
}

int LZWStream::lookChar() {
  if (pred) {
    return pred->lookChar();
  }
  if (eof) {
    return EOF;
  }
  if (seqIndex >= seqLength) {
    if (!processNextCode()) {
      return EOF;
    }
  }
  return seqBuf[seqIndex];
}

int LZWStream::getRawChar() {
  if (eof) {
    return EOF;
  }
  if (seqIndex >= seqLength) {
    if (!processNextCode()) {
      return EOF;
    }
  }
  return seqBuf[seqIndex++];
}

void LZWStream::reset() {
  str->reset();
  eof = gFalse;
  inputBits = 0;
  clearTable();
}

GBool LZWStream::processNextCode() {
  int code;
  int nextLength;
  int i, j;

  // check for EOF
  if (eof) {
    return gFalse;
  }

  // check for eod and clear-table codes
 start:
  code = getCode();
  if (code == EOF || code == 257) {
    eof = gTrue;
    return gFalse;
  }
  if (code == 256) {
    clearTable();
    goto start;
  }
  if (nextCode >= 4097) {
    error(getPos(), "Bad LZW stream - expected clear-table code");
    clearTable();
  }

  // process the next code
  nextLength = seqLength + 1;
  if (code < 256) {
    seqBuf[0] = code;
    seqLength = 1;
  } else if (code < nextCode) {
    seqLength = table[code].length;
    for (i = seqLength - 1, j = code; i > 0; --i) {
      seqBuf[i] = table[j].tail;
      j = table[j].head;
    }
    seqBuf[0] = j;
  } else if (code == nextCode) {
    seqBuf[seqLength] = newChar;
    ++seqLength;
  } else {
    error(getPos(), "Bad LZW stream - unexpected code");
    eof = gTrue;
    return gFalse;
  }
  newChar = seqBuf[0];
  if (first) {
    first = gFalse;
  } else {
    table[nextCode].length = nextLength;
    table[nextCode].head = prevCode;
    table[nextCode].tail = newChar;
    ++nextCode;
    if (nextCode + early == 512)
      nextBits = 10;
    else if (nextCode + early == 1024)
      nextBits = 11;
    else if (nextCode + early == 2048)
      nextBits = 12;
  }
  prevCode = code;

  // reset buffer
  seqIndex = 0;

  return gTrue;
}

void LZWStream::clearTable() {
  nextCode = 258;
  nextBits = 9;
  seqIndex = seqLength = 0;
  first = gTrue;
}

int LZWStream::getCode() {
  int c;
  int code;

  while (inputBits < nextBits) {
    if ((c = str->getChar()) == EOF)
      return EOF;
    inputBuf = (inputBuf << 8) | (c & 0xff);
    inputBits += 8;
  }
  code = (inputBuf >> (inputBits - nextBits)) & ((1 << nextBits) - 1);
  inputBits -= nextBits;
  return code;
}

GString *LZWStream::getPSFilter(int psLevel, char *indent) {
  GString *s;

  if (psLevel < 2 || pred) {
    return NULL;
  }
  if (!(s = str->getPSFilter(psLevel, indent))) {
    return NULL;
  }
  s->append(indent)->append("<< ");
  if (!early) {
    s->append("/EarlyChange 0 ");
  }
  s->append(">> /LZWDecode filter\n");
  return s;
}

GBool LZWStream::isBinary(GBool last) {
  return str->isBinary(gTrue);
}

//------------------------------------------------------------------------
// RunLengthStream
//------------------------------------------------------------------------

RunLengthStream::RunLengthStream(Stream *strA):
    FilterStream(strA) {
  bufPtr = bufEnd = buf;
  eof = gFalse;
}

RunLengthStream::~RunLengthStream() {
  delete str;
}

void RunLengthStream::reset() {
  str->reset();
  bufPtr = bufEnd = buf;
  eof = gFalse;
}

GString *RunLengthStream::getPSFilter(int psLevel, char *indent) {
  GString *s;

  if (psLevel < 2) {
    return NULL;
  }
  if (!(s = str->getPSFilter(psLevel, indent))) {
    return NULL;
  }
  s->append(indent)->append("/RunLengthDecode filter\n");
  return s;
}

GBool RunLengthStream::isBinary(GBool last) {
  return str->isBinary(gTrue);
}

GBool RunLengthStream::fillBuf() {
  int c;
  int n, i;

  if (eof)
    return gFalse;
  c = str->getChar();
  if (c == 0x80 || c == EOF) {
    eof = gTrue;
    return gFalse;
  }
  if (c < 0x80) {
    n = c + 1;
    for (i = 0; i < n; ++i)
      buf[i] = (char)str->getChar();
  } else {
    n = 0x101 - c;
    c = str->getChar();
    for (i = 0; i < n; ++i)
      buf[i] = (char)c;
  }
  bufPtr = buf;
  bufEnd = buf + n;
  return gTrue;
}

//------------------------------------------------------------------------
// CCITTFaxStream
//------------------------------------------------------------------------

CCITTFaxStream::CCITTFaxStream(Stream *strA, int encodingA, GBool endOfLineA,
			       GBool byteAlignA, int columnsA, int rowsA,
			       GBool endOfBlockA, GBool blackA):
    FilterStream(strA) {
  encoding = encodingA;
  endOfLine = endOfLineA;
  byteAlign = byteAlignA;
  columns = columnsA;
  if (columns < 1) {
    columns = 1;
  }
  if (columns + 4 <= 0) {
    columns = INT_MAX - 4;
  }
  rows = rowsA;
  endOfBlock = endOfBlockA;
  black = blackA;
  refLine = (short *)gmallocn(columns + 3, sizeof(short));
  codingLine = (short *)gmallocn(columns + 2, sizeof(short));

  eof = gFalse;
  row = 0;
  nextLine2D = encoding < 0;
  inputBits = 0;
  codingLine[0] = 0;
  codingLine[1] = refLine[2] = columns;
  a0 = 1;

  buf = EOF;
}

CCITTFaxStream::~CCITTFaxStream() {
  delete str;
  gfree(refLine);
  gfree(codingLine);
}

void CCITTFaxStream::reset() {
  short code1;

  str->reset();
  eof = gFalse;
  row = 0;
  nextLine2D = encoding < 0;
  inputBits = 0;
  codingLine[0] = 0;
  codingLine[1] = columns;
  a0 = 1;
  buf = EOF;

  // skip any initial zero bits and end-of-line marker, and get the 2D
  // encoding tag
  while ((code1 = lookBits(12)) == 0) {
    eatBits(1);
  }
  if (code1 == 0x001) {
    eatBits(12);
  }
  if (encoding > 0) {
    nextLine2D = !lookBits(1);
    eatBits(1);
  }
}

int CCITTFaxStream::lookChar() {
  short code1, code2, code3;
  int a0New;
  GBool err, gotEOL;
  int ret;
  int bits, i;

  // if at eof just return EOF
  if (eof && codingLine[a0] >= columns) {
    return EOF;
  }

  // read the next row
  err = gFalse;
  if (codingLine[a0] >= columns) {

    // 2-D encoding
    if (nextLine2D) {
      // state:
      //   a0New = current position in coding line (0 <= a0New <= columns)
      //   codingLine[a0] = last change in coding line
      //                    (black-to-white if a0 is even,
      //                     white-to-black if a0 is odd)
      //   refLine[b1] = next change in reference line of opposite color
      //                 to a0
      // invariants:
      //   0 <= codingLine[a0] <= a0New
      //           <= refLine[b1] <= refLine[b1+1] <= columns
      //   0 <= a0 <= columns+1
      //   refLine[0] = 0
      //   refLine[n] = refLine[n+1] = columns
      //     -- for some 1 <= n <= columns+1
      // end condition:
      //   0 = codingLine[0] <= codingLine[1] < codingLine[2] < ...
      //     < codingLine[n-1] < codingLine[n] = columns
      //     -- where 1 <= n <= columns+1
      for (i = 0; codingLine[i] < columns; ++i) {
	refLine[i] = codingLine[i];
      }
      refLine[i] = refLine[i + 1] = columns;
      b1 = 1;
      a0New = codingLine[a0 = 0] = 0;
      do {
	code1 = getTwoDimCode();
	switch (code1) {
	case twoDimPass:
	  if (refLine[b1] < columns) {
	    a0New = refLine[b1 + 1];
	    b1 += 2;
	  }
	  break;
	case twoDimHoriz:
	  if ((a0 & 1) == 0) {
	    code1 = code2 = 0;
	    do {
	      code1 += code3 = getWhiteCode();
	    } while (code3 >= 64);
	    do {
	      code2 += code3 = getBlackCode();
	    } while (code3 >= 64);
	  } else {
	    code1 = code2 = 0;
	    do {
	      code1 += code3 = getBlackCode();
	    } while (code3 >= 64);
	    do {
	      code2 += code3 = getWhiteCode();
	    } while (code3 >= 64);
	  }
	  if (code1 > 0 || code2 > 0) {
	    if (a0New + code1 <= columns) {
	      codingLine[a0 + 1] = a0New + code1;
	    } else {
	      codingLine[a0 + 1] = columns;
	    }
	    ++a0;
	    if (codingLine[a0] + code2 <= columns) {
	      codingLine[a0 + 1] = codingLine[a0] + code2;
	    } else {
	      codingLine[a0 + 1] = columns;
	    }
	    ++a0;
	    a0New = codingLine[a0];
	    while (refLine[b1] <= a0New && refLine[b1] < columns) {
	      b1 += 2;
	    }
	  }
	  break;
	case twoDimVert0:
	  if (refLine[b1] < columns) {
	    a0New = codingLine[++a0] = refLine[b1];
	    ++b1;
	    while (refLine[b1] <= a0New && refLine[b1] < columns) {
	      b1 += 2;
	    }
	  } else {
	    a0New = codingLine[++a0] = columns;
	  }
	  break;
	case twoDimVertR1:
	  if (refLine[b1] + 1 < columns) {
	    a0New = codingLine[++a0] = refLine[b1] + 1;
	    ++b1;
	    while (refLine[b1] <= a0New && refLine[b1] < columns) {
	      b1 += 2;
	    }
	  } else {
	    a0New = codingLine[++a0] = columns;
	  }
	  break;
	case twoDimVertL1:
	  if (refLine[b1] - 1 > a0New || (a0 == 0 && refLine[b1] == 1)) {
	    a0New = codingLine[++a0] = refLine[b1] - 1;
	    --b1;
	    while (refLine[b1] <= a0New && refLine[b1] < columns) {
	      b1 += 2;
	    }
	  }
	  break;
	case twoDimVertR2:
	  if (refLine[b1] + 2 < columns) {
	    a0New = codingLine[++a0] = refLine[b1] + 2;
	    ++b1;
	    while (refLine[b1] <= a0New && refLine[b1] < columns) {
	      b1 += 2;
	    }
	  } else {
	    a0New = codingLine[++a0] = columns;
	  }
	  break;
	case twoDimVertL2:
	  if (refLine[b1] - 2 > a0New || (a0 == 0 && refLine[b1] == 2)) {
	    a0New = codingLine[++a0] = refLine[b1] - 2;
	    --b1;
	    while (refLine[b1] <= a0New && refLine[b1] < columns) {
	      b1 += 2;
	    }
	  }
	  break;
	case twoDimVertR3:
	  if (refLine[b1] + 3 < columns) {
	    a0New = codingLine[++a0] = refLine[b1] + 3;
	    ++b1;
	    while (refLine[b1] <= a0New && refLine[b1] < columns) {
	      b1 += 2;
	    }
	  } else {
	    a0New = codingLine[++a0] = columns;
	  }
	  break;
	case twoDimVertL3:
	  if (refLine[b1] - 3 > a0New || (a0 == 0 && refLine[b1] == 3)) {
	    a0New = codingLine[++a0] = refLine[b1] - 3;
	    --b1;
	    while (refLine[b1] <= a0New && refLine[b1] < columns) {
	      b1 += 2;
	    }
	  }
	  break;
	case EOF:
	  eof = gTrue;
	  codingLine[a0 = 0] = columns;
	  return EOF;
	default:
	  error(getPos(), "Bad 2D code %04x in CCITTFax stream", code1);
	  err = gTrue;
	  break;
	}
      } while (codingLine[a0] < columns);

    // 1-D encoding
    } else {
      codingLine[a0 = 0] = 0;
      while (1) {
	code1 = 0;
	do {
	  code1 += code3 = getWhiteCode();
	} while (code3 >= 64);
	codingLine[a0+1] = codingLine[a0] + code1;
	++a0;
	if (codingLine[a0] >= columns) {
	  break;
	}
	code2 = 0;
	do {
	  code2 += code3 = getBlackCode();
	} while (code3 >= 64);
	codingLine[a0+1] = codingLine[a0] + code2;
	++a0;
	if (codingLine[a0] >= columns) {
	  break;
	}
      }
    }

    if (codingLine[a0] != columns) {
      error(getPos(), "CCITTFax row is wrong length (%d)", codingLine[a0]);
      // force the row to be the correct length
      while (codingLine[a0] > columns) {
	--a0;
      }
      codingLine[++a0] = columns;
      err = gTrue;
    }

    // byte-align the row
    if (byteAlign) {
      inputBits &= ~7;
    }

    // check for end-of-line marker, skipping over any extra zero bits
    gotEOL = gFalse;
    if (!endOfBlock && row == rows - 1) {
      eof = gTrue;
    } else {
      code1 = lookBits(12);
      while (code1 == 0) {
	eatBits(1);
	code1 = lookBits(12);
      }
      if (code1 == 0x001) {
	eatBits(12);
	gotEOL = gTrue;
      } else if (code1 == EOF) {
	eof = gTrue;
      }
    }

    // get 2D encoding tag
    if (!eof && encoding > 0) {
      nextLine2D = !lookBits(1);
      eatBits(1);
    }

    // check for end-of-block marker
    if (endOfBlock && gotEOL) {
      code1 = lookBits(12);
      if (code1 == 0x001) {
	eatBits(12);
	if (encoding > 0) {
	  lookBits(1);
	  eatBits(1);
	}
	if (encoding >= 0) {
	  for (i = 0; i < 4; ++i) {
	    code1 = lookBits(12);
	    if (code1 != 0x001) {
	      error(getPos(), "Bad RTC code in CCITTFax stream");
	    }
	    eatBits(12);
	    if (encoding > 0) {
	      lookBits(1);
	      eatBits(1);
	    }
	  }
	}
	eof = gTrue;
      }

    // look for an end-of-line marker after an error -- we only do
    // this if we know the stream contains end-of-line markers because
    // the "just plow on" technique tends to work better otherwise
    } else if (err && endOfLine) {
      do {
	if (code1 == EOF) {
	  eof = gTrue;
	  return EOF;
	}
	eatBits(1);
	code1 = lookBits(13);
      } while ((code1 >> 1) != 0x001);
      eatBits(12); 
      if (encoding > 0) {
	eatBits(1);
	nextLine2D = !(code1 & 1);
      }
    }

    a0 = 0;
    outputBits = codingLine[1] - codingLine[0];
    if (outputBits == 0) {
      a0 = 1;
      outputBits = codingLine[2] - codingLine[1];
    }

    ++row;
  }

  // get a byte
  if (outputBits >= 8) {
    ret = ((a0 & 1) == 0) ? 0xff : 0x00;
    if ((outputBits -= 8) == 0) {
      ++a0;
      if (codingLine[a0] < columns) {
	outputBits = codingLine[a0 + 1] - codingLine[a0];
      }
    }
  } else {
    bits = 8;
    ret = 0;
    do {
      if (outputBits > bits) {
	i = bits;
	bits = 0;
	if ((a0 & 1) == 0) {
	  ret |= 0xff >> (8 - i);
	}
	outputBits -= i;
      } else {
	i = outputBits;
	bits -= outputBits;
	if ((a0 & 1) == 0) {
	  ret |= (0xff >> (8 - i)) << bits;
	}
	outputBits = 0;
	++a0;
	if (codingLine[a0] < columns) {
	  outputBits = codingLine[a0 + 1] - codingLine[a0];
	}
      }
    } while (bits > 0 && codingLine[a0] < columns);
  }
  buf = black ? (ret ^ 0xff) : ret;
  return buf;
}

short CCITTFaxStream::getTwoDimCode() {
  short code;
  CCITTCode *p;
  int n;

  code = 0; // make gcc happy
  if (endOfBlock) {
    code = lookBits(7);
    p = &twoDimTab1[code];
    if (p->bits > 0) {
      eatBits(p->bits);
      return p->n;
    }
  } else {
    for (n = 1; n <= 7; ++n) {
      code = lookBits(n);
      if (n < 7) {
	code <<= 7 - n;
      }
      p = &twoDimTab1[code];
      if (p->bits == n) {
	eatBits(n);
	return p->n;
      }
    }
  }
  error(getPos(), "Bad two dim code (%04x) in CCITTFax stream", code);
  return EOF;
}

short CCITTFaxStream::getWhiteCode() {
  short code;
  CCITTCode *p;
  int n;

  code = 0; // make gcc happy
  if (endOfBlock) {
    code = lookBits(12);
    if ((code >> 5) == 0) {
      p = &whiteTab1[code];
    } else {
      p = &whiteTab2[code >> 3];
    }
    if (p->bits > 0) {
      eatBits(p->bits);
      return p->n;
    }
  } else {
    for (n = 1; n <= 9; ++n) {
      code = lookBits(n);
      if (n < 9) {
	code <<= 9 - n;
      }
      p = &whiteTab2[code];
      if (p->bits == n) {
	eatBits(n);
	return p->n;
      }
    }
    for (n = 11; n <= 12; ++n) {
      code = lookBits(n);
      if (n < 12) {
	code <<= 12 - n;
      }
      p = &whiteTab1[code];
      if (p->bits == n) {
	eatBits(n);
	return p->n;
      }
    }
  }
  error(getPos(), "Bad white code (%04x) in CCITTFax stream", code);
  // eat a bit and return a positive number so that the caller doesn't
  // go into an infinite loop
  eatBits(1);
  return 1;
}

short CCITTFaxStream::getBlackCode() {
  short code;
  CCITTCode *p;
  int n;

  code = 0; // make gcc happy
  if (endOfBlock) {
    code = lookBits(13);
    if ((code >> 7) == 0) {
      p = &blackTab1[code];
    } else if ((code >> 9) == 0) {
      p = &blackTab2[(code >> 1) - 64];
    } else {
      p = &blackTab3[code >> 7];
    }
    if (p->bits > 0) {
      eatBits(p->bits);
      return p->n;
    }
  } else {
    for (n = 2; n <= 6; ++n) {
      code = lookBits(n);
      if (n < 6) {
	code <<= 6 - n;
      }
      p = &blackTab3[code];
      if (p->bits == n) {
	eatBits(n);
	return p->n;
      }
    }
    for (n = 7; n <= 12; ++n) {
      code = lookBits(n);
      if (n < 12) {
	code <<= 12 - n;
      }
      if (code >= 64) {
	p = &blackTab2[code - 64];
	if (p->bits == n) {
	  eatBits(n);
	  return p->n;
	}
      }
    }
    for (n = 10; n <= 13; ++n) {
      code = lookBits(n);
      if (n < 13) {
	code <<= 13 - n;
      }
      p = &blackTab1[code];
      if (p->bits == n) {
	eatBits(n);
	return p->n;
      }
    }
  }
  error(getPos(), "Bad black code (%04x) in CCITTFax stream", code);
  // eat a bit and return a positive number so that the caller doesn't
  // go into an infinite loop
  eatBits(1);
  return 1;
}

short CCITTFaxStream::lookBits(int n) {
  int c;

  while (inputBits < n) {
    if ((c = str->getChar()) == EOF) {
      if (inputBits == 0) {
	return EOF;
      }
      // near the end of the stream, the caller may ask for more bits
      // than are available, but there may still be a valid code in
      // however many bits are available -- we need to return correct
      // data in this case
      return (inputBuf << (n - inputBits)) & (0xffff >> (16 - n));
    }
    inputBuf = (inputBuf << 8) + c;
    inputBits += 8;
  }
  return (inputBuf >> (inputBits - n)) & (0xffff >> (16 - n));
}

GString *CCITTFaxStream::getPSFilter(int psLevel, char *indent) {
  GString *s;
  char s1[50];

  if (psLevel < 2) {
    return NULL;
  }
  if (!(s = str->getPSFilter(psLevel, indent))) {
    return NULL;
  }
  s->append(indent)->append("<< ");
  if (encoding != 0) {
    sprintf(s1, "/K %d ", encoding);
    s->append(s1);
  }
  if (endOfLine) {
    s->append("/EndOfLine true ");
  }
  if (byteAlign) {
    s->append("/EncodedByteAlign true ");
  }
  sprintf(s1, "/Columns %d ", columns);
  s->append(s1);
  if (rows != 0) {
    sprintf(s1, "/Rows %d ", rows);
    s->append(s1);
  }
  if (!endOfBlock) {
    s->append("/EndOfBlock false ");
  }
  if (black) {
    s->append("/BlackIs1 true ");
  }
  s->append(">> /CCITTFaxDecode filter\n");
  return s;
}

GBool CCITTFaxStream::isBinary(GBool last) {
  return str->isBinary(gTrue);
}

//------------------------------------------------------------------------
// DCTStream
//------------------------------------------------------------------------

// IDCT constants (20.12 fixed point format)
#define dctCos1    4017		// cos(pi/16)
#define dctSin1     799		// sin(pi/16)
#define dctCos3    3406		// cos(3*pi/16)
#define dctSin3    2276		// sin(3*pi/16)
#define dctCos6    1567		// cos(6*pi/16)
#define dctSin6    3784		// sin(6*pi/16)
#define dctSqrt2   5793		// sqrt(2)
#define dctSqrt1d2 2896		// sqrt(2) / 2

// color conversion parameters (16.16 fixed point format)
#define dctCrToR   91881	//  1.4020
#define dctCbToG  -22553	// -0.3441363
#define dctCrToG  -46802	// -0.71413636
#define dctCbToB  116130	//  1.772

// clip [-256,511] --> [0,255]
#define dctClipOffset 256
static Guchar dctClip[768];
static int dctClipInit = 0;

// zig zag decode map
static int dctZigZag[64] = {
   0,
   1,  8,
  16,  9,  2,
   3, 10, 17, 24,
  32, 25, 18, 11, 4,
   5, 12, 19, 26, 33, 40,
  48, 41, 34, 27, 20, 13,  6,
   7, 14, 21, 28, 35, 42, 49, 56,
  57, 50, 43, 36, 29, 22, 15,
  23, 30, 37, 44, 51, 58,
  59, 52, 45, 38, 31,
  39, 46, 53, 60,
  61, 54, 47,
  55, 62,
  63
};

DCTStream::DCTStream(Stream *strA, GBool colorXformA):
    FilterStream(strA) {
  int i, j;

  colorXform = colorXformA;
  progressive = interleaved = gFalse;
  width = height = 0;
  mcuWidth = mcuHeight = 0;
  numComps = 0;
  comp = 0;
  x = y = dy = 0;
  for (i = 0; i < 4; ++i) {
    for (j = 0; j < 32; ++j) {
      rowBuf[i][j] = NULL;
    }
    frameBuf[i] = NULL;
  }

  if (!dctClipInit) {
    for (i = -256; i < 0; ++i)
      dctClip[dctClipOffset + i] = 0;
    for (i = 0; i < 256; ++i)
      dctClip[dctClipOffset + i] = i;
    for (i = 256; i < 512; ++i)
      dctClip[dctClipOffset + i] = 255;
    dctClipInit = 1;
  }
}

DCTStream::~DCTStream() {
  close();
  delete str;
}

void DCTStream::reset() {
  int i, j;

  str->reset();

  progressive = interleaved = gFalse;
  width = height = 0;
  numComps = 0;
  numQuantTables = 0;
  numDCHuffTables = 0;
  numACHuffTables = 0;
  gotJFIFMarker = gFalse;
  gotAdobeMarker = gFalse;
  restartInterval = 0;

  if (!readHeader()) {
    y = height;
    return;
  }

  // compute MCU size
  if (numComps == 1) {
    compInfo[0].hSample = compInfo[0].vSample = 1;
  }
  mcuWidth = compInfo[0].hSample;
  mcuHeight = compInfo[0].vSample;
  for (i = 1; i < numComps; ++i) {
    if (compInfo[i].hSample > mcuWidth) {
      mcuWidth = compInfo[i].hSample;
    }
    if (compInfo[i].vSample > mcuHeight) {
      mcuHeight = compInfo[i].vSample;
    }
  }
  mcuWidth *= 8;
  mcuHeight *= 8;

  // figure out color transform
  if (colorXform == -1) {
    if (numComps == 3) {
      if (gotJFIFMarker) {
	colorXform = 1;
      } else if (compInfo[0].id == 82 && compInfo[1].id == 71 &&
		 compInfo[2].id == 66) { // ASCII "RGB"
	colorXform = 0;
      } else {
	colorXform = 1;
      }
    } else {
      colorXform = 0;
    }
  }

  if (progressive || !interleaved) {

    // allocate a buffer for the whole image
    bufWidth = ((width + mcuWidth - 1) / mcuWidth) * mcuWidth;
    bufHeight = ((height + mcuHeight - 1) / mcuHeight) * mcuHeight;
    for (i = 0; i < numComps; ++i) {
      frameBuf[i] = (int *)gmallocn(bufWidth * bufHeight, sizeof(int));
      memset(frameBuf[i], 0, bufWidth * bufHeight * sizeof(int));
    }

    // read the image data
    do {
      restartMarker = 0xd0;
      restart();
      readScan();
    } while (readHeader());

    // decode
    decodeImage();

    // initialize counters
    comp = 0;
    x = 0;
    y = 0;

  } else {

    // allocate a buffer for one row of MCUs
    bufWidth = ((width + mcuWidth - 1) / mcuWidth) * mcuWidth;
    for (i = 0; i < numComps; ++i) {
      for (j = 0; j < mcuHeight; ++j) {
	rowBuf[i][j] = (Guchar *)gmallocn(bufWidth, sizeof(Guchar));
      }
    }

    // initialize counters
    comp = 0;
    x = 0;
    y = 0;
    dy = mcuHeight;

    restartMarker = 0xd0;
    restart();
  }
}

void DCTStream::close() {
  int i, j;

  for (i = 0; i < 4; ++i) {
    for (j = 0; j < 32; ++j) {
      gfree(rowBuf[i][j]);
      rowBuf[i][j] = NULL;
    }
    gfree(frameBuf[i]);
    frameBuf[i] = NULL;
  }
  FilterStream::close();
}

int DCTStream::getChar() {
  int c;

  if (y >= height) {
    return EOF;
  }
  if (progressive || !interleaved) {
    c = frameBuf[comp][y * bufWidth + x];
    if (++comp == numComps) {
      comp = 0;
      if (++x == width) {
	x = 0;
	++y;
      }
    }
  } else {
    if (dy >= mcuHeight) {
      if (!readMCURow()) {
	y = height;
	return EOF;
      }
      comp = 0;
      x = 0;
      dy = 0;
    }
    c = rowBuf[comp][dy][x];
    if (++comp == numComps) {
      comp = 0;
      if (++x == width) {
	x = 0;
	++y;
	++dy;
	if (y == height) {
	  readTrailer();
	}
      }
    }
  }
  return c;
}

int DCTStream::lookChar() {
  if (y >= height) {
    return EOF;
  }
  if (progressive || !interleaved) {
    return frameBuf[comp][y * bufWidth + x];
  } else {
    if (dy >= mcuHeight) {
      if (!readMCURow()) {
	y = height;
	return EOF;
      }
      comp = 0;
      x = 0;
      dy = 0;
    }
    return rowBuf[comp][dy][x];
  }
}

void DCTStream::restart() {
  int i;

  inputBits = 0;
  restartCtr = restartInterval;
  for (i = 0; i < numComps; ++i) {
    compInfo[i].prevDC = 0;
  }
  eobRun = 0;
}

// Read one row of MCUs from a sequential JPEG stream.
GBool DCTStream::readMCURow() {
  int data1[64];
  Guchar data2[64];
  Guchar *p1, *p2;
  int pY, pCb, pCr, pR, pG, pB;
  int h, v, horiz, vert, hSub, vSub;
  int x1, x2, y2, x3, y3, x4, y4, x5, y5, cc, i;
  int c;

  for (x1 = 0; x1 < width; x1 += mcuWidth) {

    // deal with restart marker
    if (restartInterval > 0 && restartCtr == 0) {
      c = readMarker();
      if (c != restartMarker) {
	error(getPos(), "Bad DCT data: incorrect restart marker");
	return gFalse;
      }
      if (++restartMarker == 0xd8)
	restartMarker = 0xd0;
      restart();
    }

    // read one MCU
    for (cc = 0; cc < numComps; ++cc) {
      h = compInfo[cc].hSample;
      v = compInfo[cc].vSample;
      horiz = mcuWidth / h;
      vert = mcuHeight / v;
      hSub = horiz / 8;
      vSub = vert / 8;
      for (y2 = 0; y2 < mcuHeight; y2 += vert) {
	for (x2 = 0; x2 < mcuWidth; x2 += horiz) {
	  if (!readDataUnit(&dcHuffTables[scanInfo.dcHuffTable[cc]],
			    &acHuffTables[scanInfo.acHuffTable[cc]],
			    &compInfo[cc].prevDC,
			    data1)) {
	    return gFalse;
	  }
	  transformDataUnit(quantTables[compInfo[cc].quantTable],
			    data1, data2);
	  if (hSub == 1 && vSub == 1) {
	    for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) {
	      p1 = &rowBuf[cc][y2+y3][x1+x2];
	      p1[0] = data2[i];
	      p1[1] = data2[i+1];
	      p1[2] = data2[i+2];
	      p1[3] = data2[i+3];
	      p1[4] = data2[i+4];
	      p1[5] = data2[i+5];
	      p1[6] = data2[i+6];
	      p1[7] = data2[i+7];
	    }
	  } else if (hSub == 2 && vSub == 2) {
	    for (y3 = 0, i = 0; y3 < 16; y3 += 2, i += 8) {
	      p1 = &rowBuf[cc][y2+y3][x1+x2];
	      p2 = &rowBuf[cc][y2+y3+1][x1+x2];
	      p1[0] = p1[1] = p2[0] = p2[1] = data2[i];
	      p1[2] = p1[3] = p2[2] = p2[3] = data2[i+1];
	      p1[4] = p1[5] = p2[4] = p2[5] = data2[i+2];
	      p1[6] = p1[7] = p2[6] = p2[7] = data2[i+3];
	      p1[8] = p1[9] = p2[8] = p2[9] = data2[i+4];
	      p1[10] = p1[11] = p2[10] = p2[11] = data2[i+5];
	      p1[12] = p1[13] = p2[12] = p2[13] = data2[i+6];
	      p1[14] = p1[15] = p2[14] = p2[15] = data2[i+7];
	    }
	  } else {
	    i = 0;
	    for (y3 = 0, y4 = 0; y3 < 8; ++y3, y4 += vSub) {
	      for (x3 = 0, x4 = 0; x3 < 8; ++x3, x4 += hSub) {
		for (y5 = 0; y5 < vSub; ++y5)
		  for (x5 = 0; x5 < hSub; ++x5)
		    rowBuf[cc][y2+y4+y5][x1+x2+x4+x5] = data2[i];
		++i;
	      }
	    }
	  }
	}
      }
    }
    --restartCtr;

    // color space conversion
    if (colorXform) {
      // convert YCbCr to RGB
      if (numComps == 3) {
	for (y2 = 0; y2 < mcuHeight; ++y2) {
	  for (x2 = 0; x2 < mcuWidth; ++x2) {
	    pY = rowBuf[0][y2][x1+x2];
	    pCb = rowBuf[1][y2][x1+x2] - 128;
	    pCr = rowBuf[2][y2][x1+x2] - 128;
	    pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16;
	    rowBuf[0][y2][x1+x2] = dctClip[dctClipOffset + pR];
	    pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32768) >> 16;
	    rowBuf[1][y2][x1+x2] = dctClip[dctClipOffset + pG];
	    pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16;
	    rowBuf[2][y2][x1+x2] = dctClip[dctClipOffset + pB];
	  }
	}
      // convert YCbCrK to CMYK (K is passed through unchanged)
      } else if (numComps == 4) {
	for (y2 = 0; y2 < mcuHeight; ++y2) {
	  for (x2 = 0; x2 < mcuWidth; ++x2) {
	    pY = rowBuf[0][y2][x1+x2];
	    pCb = rowBuf[1][y2][x1+x2] - 128;
	    pCr = rowBuf[2][y2][x1+x2] - 128;
	    pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16;
	    rowBuf[0][y2][x1+x2] = 255 - dctClip[dctClipOffset + pR];
	    pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32768) >> 16;
	    rowBuf[1][y2][x1+x2] = 255 - dctClip[dctClipOffset + pG];
	    pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16;
	    rowBuf[2][y2][x1+x2] = 255 - dctClip[dctClipOffset + pB];
	  }
	}
      }
    }
  }
  return gTrue;
}

// Read one scan from a progressive or non-interleaved JPEG stream.
void DCTStream::readScan() {
  int data[64];
  int x1, y1, dx1, dy1, x2, y2, y3, cc, i;
  int h, v, horiz, vert, vSub;
  int *p1;
  int c;

  if (scanInfo.numComps == 1) {
    for (cc = 0; cc < numComps; ++cc) {
      if (scanInfo.comp[cc]) {
	break;
      }
    }
    dx1 = mcuWidth / compInfo[cc].hSample;
    dy1 = mcuHeight / compInfo[cc].vSample;
  } else {
    dx1 = mcuWidth;
    dy1 = mcuHeight;
  }

  for (y1 = 0; y1 < height; y1 += dy1) {
    for (x1 = 0; x1 < width; x1 += dx1) {

      // deal with restart marker
      if (restartInterval > 0 && restartCtr == 0) {
	c = readMarker();
	if (c != restartMarker) {
	  error(getPos(), "Bad DCT data: incorrect restart marker");
	  return;
	}
	if (++restartMarker == 0xd8) {
	  restartMarker = 0xd0;
	}
	restart();
      }

      // read one MCU
      for (cc = 0; cc < numComps; ++cc) {
	if (!scanInfo.comp[cc]) {
	  continue;
	}

	h = compInfo[cc].hSample;
	v = compInfo[cc].vSample;
	horiz = mcuWidth / h;
	vert = mcuHeight / v;
	vSub = vert / 8;
	for (y2 = 0; y2 < dy1; y2 += vert) {
	  for (x2 = 0; x2 < dx1; x2 += horiz) {

	    // pull out the current values
	    p1 = &frameBuf[cc][(y1+y2) * bufWidth + (x1+x2)];
	    for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) {
	      data[i] = p1[0];
	      data[i+1] = p1[1];
	      data[i+2] = p1[2];
	      data[i+3] = p1[3];
	      data[i+4] = p1[4];
	      data[i+5] = p1[5];
	      data[i+6] = p1[6];
	      data[i+7] = p1[7];
	      p1 += bufWidth * vSub;
	    }

	    // read one data unit
	    if (progressive) {
	      if (!readProgressiveDataUnit(
		       &dcHuffTables[scanInfo.dcHuffTable[cc]],
		       &acHuffTables[scanInfo.acHuffTable[cc]],
		       &compInfo[cc].prevDC,
		       data)) {
		return;
	      }
	    } else {
	      if (!readDataUnit(&dcHuffTables[scanInfo.dcHuffTable[cc]],
				&acHuffTables[scanInfo.acHuffTable[cc]],
				&compInfo[cc].prevDC,
				data)) {
		return;
	      }
	    }

	    // add the data unit into frameBuf
	    p1 = &frameBuf[cc][(y1+y2) * bufWidth + (x1+x2)];
	    for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) {
	      p1[0] = data[i];
	      p1[1] = data[i+1];
	      p1[2] = data[i+2];
	      p1[3] = data[i+3];
	      p1[4] = data[i+4];
	      p1[5] = data[i+5];
	      p1[6] = data[i+6];
	      p1[7] = data[i+7];
	      p1 += bufWidth * vSub;
	    }
	  }
	}
      }
      --restartCtr;
    }
  }
}

// Read one data unit from a sequential JPEG stream.
GBool DCTStream::readDataUnit(DCTHuffTable *dcHuffTable,
			      DCTHuffTable *acHuffTable,
			      int *prevDC, int data[64]) {
  int run, size, amp;
  int c;
  int i, j;

  if ((size = readHuffSym(dcHuffTable)) == 9999) {
    return gFalse;
  }
  if (size > 0) {
    if ((amp = readAmp(size)) == 9999) {
      return gFalse;
    }
  } else {
    amp = 0;
  }
  data[0] = *prevDC += amp;
  for (i = 1; i < 64; ++i) {
    data[i] = 0;
  }
  i = 1;
  while (i < 64) {
    run = 0;
    while ((c = readHuffSym(acHuffTable)) == 0xf0 && run < 0x30) {
      run += 0x10;
    }
    if (c == 9999) {
      return gFalse;
    }
    if (c == 0x00) {
      break;
    } else {
      run += (c >> 4) & 0x0f;
      size = c & 0x0f;
      amp = readAmp(size);
      if (amp == 9999) {
	return gFalse;
      }
      i += run;
      if (i < 64) {
	j = dctZigZag[i++];
	data[j] = amp;
      }
    }
  }
  return gTrue;
}

// Read one data unit from a sequential JPEG stream.
GBool DCTStream::readProgressiveDataUnit(DCTHuffTable *dcHuffTable,
					 DCTHuffTable *acHuffTable,
					 int *prevDC, int data[64]) {
  int run, size, amp, bit, c;
  int i, j, k;

  // get the DC coefficient
  i = scanInfo.firstCoeff;
  if (i == 0) {
    if (scanInfo.ah == 0) {
      if ((size = readHuffSym(dcHuffTable)) == 9999) {
	return gFalse;
      }
      if (size > 0) {
	if ((amp = readAmp(size)) == 9999) {
	  return gFalse;
	}
      } else {
	amp = 0;
      }
      data[0] += (*prevDC += amp) << scanInfo.al;
    } else {
      if ((bit = readBit()) == 9999) {
	return gFalse;
      }
      data[0] += bit << scanInfo.al;
    }
    ++i;
  }
  if (scanInfo.lastCoeff == 0) {
    return gTrue;
  }

  // check for an EOB run
  if (eobRun > 0) {
    while (i <= scanInfo.lastCoeff) {
      j = dctZigZag[i++];
      if (data[j] != 0) {
	if ((bit = readBit()) == EOF) {
	  return gFalse;
	}
	if (bit) {
	  data[j] += 1 << scanInfo.al;
	}
      }
    }
    --eobRun;
    return gTrue;
  }

  // read the AC coefficients
  while (i <= scanInfo.lastCoeff) {
    if ((c = readHuffSym(acHuffTable)) == 9999) {
      return gFalse;
    }

    // ZRL
    if (c == 0xf0) {
      k = 0;
      while (k < 16) {
	j = dctZigZag[i++];
	if (data[j] == 0) {
	  ++k;
	} else {
	  if ((bit = readBit()) == EOF) {
	    return gFalse;
	  }
	  if (bit) {
	    data[j] += 1 << scanInfo.al;
	  }
	}
      }

    // EOB run
    } else if ((c & 0x0f) == 0x00) {
      j = c >> 4;
      eobRun = 0;
      for (k = 0; k < j; ++k) {
	if ((bit = readBit()) == EOF) {
	  return gFalse;
	}
	eobRun = (eobRun << 1) | bit;
      }
      eobRun += 1 << j;
      while (i <= scanInfo.lastCoeff) {
	j = dctZigZag[i++];
	if (data[j] != 0) {
	  if ((bit = readBit()) == EOF) {
	    return gFalse;
	  }
	  if (bit) {
	    data[j] += 1 << scanInfo.al;
	  }
	}
      }
      --eobRun;
      break;

    // zero run and one AC coefficient
    } else {
      run = (c >> 4) & 0x0f;
      size = c & 0x0f;
      if ((amp = readAmp(size)) == 9999) {
	return gFalse;
      }
      k = 0;
      do {
	j = dctZigZag[i++];
	while (data[j] != 0) {
	  if ((bit = readBit()) == EOF) {
	    return gFalse;
	  }
	  if (bit) {
	    data[j] += 1 << scanInfo.al;
	  }
	  j = dctZigZag[i++];
	}
	++k;
      } while (k <= run);
      data[j] = amp << scanInfo.al;
    }
  }

  return gTrue;
}

// Decode a progressive JPEG image.
void DCTStream::decodeImage() {
  int dataIn[64];
  Guchar dataOut[64];
  Gushort *quantTable;
  int pY, pCb, pCr, pR, pG, pB;
  int x1, y1, x2, y2, x3, y3, x4, y4, x5, y5, cc, i;
  int h, v, horiz, vert, hSub, vSub;
  int *p0, *p1, *p2;

  for (y1 = 0; y1 < bufHeight; y1 += mcuHeight) {
    for (x1 = 0; x1 < bufWidth; x1 += mcuWidth) {
      for (cc = 0; cc < numComps; ++cc) {
	quantTable = quantTables[compInfo[cc].quantTable];
	h = compInfo[cc].hSample;
	v = compInfo[cc].vSample;
	horiz = mcuWidth / h;
	vert = mcuHeight / v;
	hSub = horiz / 8;
	vSub = vert / 8;
	for (y2 = 0; y2 < mcuHeight; y2 += vert) {
	  for (x2 = 0; x2 < mcuWidth; x2 += horiz) {

	    // pull out the coded data unit
	    p1 = &frameBuf[cc][(y1+y2) * bufWidth + (x1+x2)];
	    for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) {
	      dataIn[i]   = p1[0];
	      dataIn[i+1] = p1[1];
	      dataIn[i+2] = p1[2];
	      dataIn[i+3] = p1[3];
	      dataIn[i+4] = p1[4];
	      dataIn[i+5] = p1[5];
	      dataIn[i+6] = p1[6];
	      dataIn[i+7] = p1[7];
	      p1 += bufWidth * vSub;
	    }

	    // transform
	    transformDataUnit(quantTable, dataIn, dataOut);

	    // store back into frameBuf, doing replication for
	    // subsampled components
	    p1 = &frameBuf[cc][(y1+y2) * bufWidth + (x1+x2)];
	    if (hSub == 1 && vSub == 1) {
	      for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) {
		p1[0] = dataOut[i] & 0xff;
		p1[1] = dataOut[i+1] & 0xff;
		p1[2] = dataOut[i+2] & 0xff;
		p1[3] = dataOut[i+3] & 0xff;
		p1[4] = dataOut[i+4] & 0xff;
		p1[5] = dataOut[i+5] & 0xff;
		p1[6] = dataOut[i+6] & 0xff;
		p1[7] = dataOut[i+7] & 0xff;
		p1 += bufWidth;
	      }
	    } else if (hSub == 2 && vSub == 2) {
	      p2 = p1 + bufWidth;
	      for (y3 = 0, i = 0; y3 < 16; y3 += 2, i += 8) {
		p1[0] = p1[1] = p2[0] = p2[1] = dataOut[i] & 0xff;
		p1[2] = p1[3] = p2[2] = p2[3] = dataOut[i+1] & 0xff;
		p1[4] = p1[5] = p2[4] = p2[5] = dataOut[i+2] & 0xff;
		p1[6] = p1[7] = p2[6] = p2[7] = dataOut[i+3] & 0xff;
		p1[8] = p1[9] = p2[8] = p2[9] = dataOut[i+4] & 0xff;
		p1[10] = p1[11] = p2[10] = p2[11] = dataOut[i+5] & 0xff;
		p1[12] = p1[13] = p2[12] = p2[13] = dataOut[i+6] & 0xff;
		p1[14] = p1[15] = p2[14] = p2[15] = dataOut[i+7] & 0xff;
		p1 += bufWidth * 2;
		p2 += bufWidth * 2;
	      }
	    } else {
	      i = 0;
	      for (y3 = 0, y4 = 0; y3 < 8; ++y3, y4 += vSub) {
		for (x3 = 0, x4 = 0; x3 < 8; ++x3, x4 += hSub) {
		  p2 = p1 + x4;
		  for (y5 = 0; y5 < vSub; ++y5) {
		    for (x5 = 0; x5 < hSub; ++x5) {
		      p2[x5] = dataOut[i] & 0xff;
		    }
		    p2 += bufWidth;
		  }
		  ++i;
		}
		p1 += bufWidth * vSub;
	      }
	    }
	  }
	}
      }

      // color space conversion
      if (colorXform) {
	// convert YCbCr to RGB
	if (numComps == 3) {
	  for (y2 = 0; y2 < mcuHeight; ++y2) {
	    p0 = &frameBuf[0][(y1+y2) * bufWidth + x1];
	    p1 = &frameBuf[1][(y1+y2) * bufWidth + x1];
	    p2 = &frameBuf[2][(y1+y2) * bufWidth + x1];
	    for (x2 = 0; x2 < mcuWidth; ++x2) {
	      pY = *p0;
	      pCb = *p1 - 128;
	      pCr = *p2 - 128;
	      pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16;
	      *p0++ = dctClip[dctClipOffset + pR];
	      pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr +
		    32768) >> 16;
	      *p1++ = dctClip[dctClipOffset + pG];
	      pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16;
	      *p2++ = dctClip[dctClipOffset + pB];
	    }
	  }
	// convert YCbCrK to CMYK (K is passed through unchanged)
	} else if (numComps == 4) {
	  for (y2 = 0; y2 < mcuHeight; ++y2) {
	    p0 = &frameBuf[0][(y1+y2) * bufWidth + x1];
	    p1 = &frameBuf[1][(y1+y2) * bufWidth + x1];
	    p2 = &frameBuf[2][(y1+y2) * bufWidth + x1];
	    for (x2 = 0; x2 < mcuWidth; ++x2) {
	      pY = *p0;
	      pCb = *p1 - 128;
	      pCr = *p2 - 128;
	      pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16;
	      *p0++ = 255 - dctClip[dctClipOffset + pR];
	      pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr +
		    32768) >> 16;
	      *p1++ = 255 - dctClip[dctClipOffset + pG];
	      pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16;
	      *p2++ = 255 - dctClip[dctClipOffset + pB];
	    }
	  }
	}
      }
    }
  }
}

// Transform one data unit -- this performs the dequantization and
// IDCT steps.  This IDCT algorithm is taken from:
//   Christoph Loeffler, Adriaan Ligtenberg, George S. Moschytz,
//   "Practical Fast 1-D DCT Algorithms with 11 Multiplications",
//   IEEE Intl. Conf. on Acoustics, Speech & Signal Processing, 1989,
//   988-991.
// The stage numbers mentioned in the comments refer to Figure 1 in this
// paper.
void DCTStream::transformDataUnit(Gushort *quantTable,
				  int dataIn[64], Guchar dataOut[64]) {
  int v0, v1, v2, v3, v4, v5, v6, v7, t;
  int *p;
  int i;

  // dequant
  for (i = 0; i < 64; ++i) {
    dataIn[i] *= quantTable[i];
  }

  // inverse DCT on rows
  for (i = 0; i < 64; i += 8) {
    p = dataIn + i;

    // check for all-zero AC coefficients
    if (p[1] == 0 && p[2] == 0 && p[3] == 0 &&
	p[4] == 0 && p[5] == 0 && p[6] == 0 && p[7] == 0) {
      t = (dctSqrt2 * p[0] + 512) >> 10;
      p[0] = t;
      p[1] = t;
      p[2] = t;
      p[3] = t;
      p[4] = t;
      p[5] = t;
      p[6] = t;
      p[7] = t;
      continue;
    }

    // stage 4
    v0 = (dctSqrt2 * p[0] + 128) >> 8;
    v1 = (dctSqrt2 * p[4] + 128) >> 8;
    v2 = p[2];
    v3 = p[6];
    v4 = (dctSqrt1d2 * (p[1] - p[7]) + 128) >> 8;
    v7 = (dctSqrt1d2 * (p[1] + p[7]) + 128) >> 8;
    v5 = p[3] << 4;
    v6 = p[5] << 4;

    // stage 3
    t = (v0 - v1+ 1) >> 1;
    v0 = (v0 + v1 + 1) >> 1;
    v1 = t;
    t = (v2 * dctSin6 + v3 * dctCos6 + 128) >> 8;
    v2 = (v2 * dctCos6 - v3 * dctSin6 + 128) >> 8;
    v3 = t;
    t = (v4 - v6 + 1) >> 1;
    v4 = (v4 + v6 + 1) >> 1;
    v6 = t;
    t = (v7 + v5 + 1) >> 1;
    v5 = (v7 - v5 + 1) >> 1;
    v7 = t;

    // stage 2
    t = (v0 - v3 + 1) >> 1;
    v0 = (v0 + v3 + 1) >> 1;
    v3 = t;
    t = (v1 - v2 + 1) >> 1;
    v1 = (v1 + v2 + 1) >> 1;
    v2 = t;
    t = (v4 * dctSin3 + v7 * dctCos3 + 2048) >> 12;
    v4 = (v4 * dctCos3 - v7 * dctSin3 + 2048) >> 12;
    v7 = t;
    t = (v5 * dctSin1 + v6 * dctCos1 + 2048) >> 12;
    v5 = (v5 * dctCos1 - v6 * dctSin1 + 2048) >> 12;
    v6 = t;

    // stage 1
    p[0] = v0 + v7;
    p[7] = v0 - v7;
    p[1] = v1 + v6;
    p[6] = v1 - v6;
    p[2] = v2 + v5;
    p[5] = v2 - v5;
    p[3] = v3 + v4;
    p[4] = v3 - v4;
  }

  // inverse DCT on columns
  for (i = 0; i < 8; ++i) {
    p = dataIn + i;

    // check for all-zero AC coefficients
    if (p[1*8] == 0 && p[2*8] == 0 && p[3*8] == 0 &&
	p[4*8] == 0 && p[5*8] == 0 && p[6*8] == 0 && p[7*8] == 0) {
      t = (dctSqrt2 * dataIn[i+0] + 8192) >> 14;
      p[0*8] = t;
      p[1*8] = t;
      p[2*8] = t;
      p[3*8] = t;
      p[4*8] = t;
      p[5*8] = t;
      p[6*8] = t;
      p[7*8] = t;
      continue;
    }

    // stage 4
    v0 = (dctSqrt2 * p[0*8] + 2048) >> 12;
    v1 = (dctSqrt2 * p[4*8] + 2048) >> 12;
    v2 = p[2*8];
    v3 = p[6*8];
    v4 = (dctSqrt1d2 * (p[1*8] - p[7*8]) + 2048) >> 12;
    v7 = (dctSqrt1d2 * (p[1*8] + p[7*8]) + 2048) >> 12;
    v5 = p[3*8];
    v6 = p[5*8];

    // stage 3
    t = (v0 - v1 + 1) >> 1;
    v0 = (v0 + v1 + 1) >> 1;
    v1 = t;
    t = (v2 * dctSin6 + v3 * dctCos6 + 2048) >> 12;
    v2 = (v2 * dctCos6 - v3 * dctSin6 + 2048) >> 12;
    v3 = t;
    t = (v4 - v6 + 1) >> 1;
    v4 = (v4 + v6 + 1) >> 1;
    v6 = t;
    t = (v7 + v5 + 1) >> 1;
    v5 = (v7 - v5 + 1) >> 1;
    v7 = t;

    // stage 2
    t = (v0 - v3 + 1) >> 1;
    v0 = (v0 + v3 + 1) >> 1;
    v3 = t;
    t = (v1 - v2 + 1) >> 1;
    v1 = (v1 + v2 + 1) >> 1;
    v2 = t;
    t = (v4 * dctSin3 + v7 * dctCos3 + 2048) >> 12;
    v4 = (v4 * dctCos3 - v7 * dctSin3 + 2048) >> 12;
    v7 = t;
    t = (v5 * dctSin1 + v6 * dctCos1 + 2048) >> 12;
    v5 = (v5 * dctCos1 - v6 * dctSin1 + 2048) >> 12;
    v6 = t;

    // stage 1
    p[0*8] = v0 + v7;
    p[7*8] = v0 - v7;
    p[1*8] = v1 + v6;
    p[6*8] = v1 - v6;
    p[2*8] = v2 + v5;
    p[5*8] = v2 - v5;
    p[3*8] = v3 + v4;
    p[4*8] = v3 - v4;
  }

  // convert to 8-bit integers
  for (i = 0; i < 64; ++i) {
    dataOut[i] = dctClip[dctClipOffset + 128 + ((dataIn[i] + 8) >> 4)];
  }
}

int DCTStream::readHuffSym(DCTHuffTable *table) {
  Gushort code;
  int bit;
  int codeBits;

  code = 0;
  codeBits = 0;
  do {
    // add a bit to the code
    if ((bit = readBit()) == EOF)
      return 9999;
    code = (code << 1) + bit;
    ++codeBits;

    // look up code
    if (code - table->firstCode[codeBits] < table->numCodes[codeBits]) {
      code -= table->firstCode[codeBits];
      return table->sym[table->firstSym[codeBits] + code];
    }
  } while (codeBits < 16);

  error(getPos(), "Bad Huffman code in DCT stream");
  return 9999;
}

int DCTStream::readAmp(int size) {
  int amp, bit;
  int bits;

  amp = 0;
  for (bits = 0; bits < size; ++bits) {
    if ((bit = readBit()) == EOF)
      return 9999;
    amp = (amp << 1) + bit;
  }
  if (amp < (1 << (size - 1)))
    amp -= (1 << size) - 1;
  return amp;
}

int DCTStream::readBit() {
  int bit;
  int c, c2;

  if (inputBits == 0) {
    if ((c = str->getChar()) == EOF)
      return EOF;
    if (c == 0xff) {
      do {
	c2 = str->getChar();
      } while (c2 == 0xff);
      if (c2 != 0x00) {
	error(getPos(), "Bad DCT data: missing 00 after ff");
	return EOF;
      }
    }
    inputBuf = c;
    inputBits = 8;
  }
  bit = (inputBuf >> (inputBits - 1)) & 1;
  --inputBits;
  return bit;
}

GBool DCTStream::readHeader() {
  GBool doScan;
  int n;
  int c = 0;
  int i;

  // read headers
  doScan = gFalse;
  while (!doScan) {
    c = readMarker();
    switch (c) {
    case 0xc0:			// SOF0 (sequential)
    case 0xc1:			// SOF1 (extended sequential)
      if (!readBaselineSOF()) {
	return gFalse;
      }
      break;
    case 0xc2:			// SOF2 (progressive)
      if (!readProgressiveSOF()) {
	return gFalse;
      }
      break;
    case 0xc4:			// DHT
      if (!readHuffmanTables()) {
	return gFalse;
      }
      break;
    case 0xd8:			// SOI
      break;
    case 0xd9:			// EOI
      return gFalse;
    case 0xda:			// SOS
      if (!readScanInfo()) {
	return gFalse;
      }
      doScan = gTrue;
      break;
    case 0xdb:			// DQT
      if (!readQuantTables()) {
	return gFalse;
      }
      break;
    case 0xdd:			// DRI
      if (!readRestartInterval()) {
	return gFalse;
      }
      break;
    case 0xe0:			// APP0
      if (!readJFIFMarker()) {
	return gFalse;
      }
      break;
    case 0xee:			// APP14
      if (!readAdobeMarker()) {
	return gFalse;
      }
      break;
    case EOF:
      error(getPos(), "Bad DCT header");
      return gFalse;
    default:
      // skip APPn / COM / etc.
      if (c >= 0xe0) {
	n = read16() - 2;
	for (i = 0; i < n; ++i) {
	  str->getChar();
	}
      } else {
	error(getPos(), "Unknown DCT marker <%02x>", c);
	return gFalse;
      }
      break;
    }
  }

  return gTrue;
}

GBool DCTStream::readBaselineSOF() {
  int length;
  int prec;
  int i;
  int c;

  length = read16();
  prec = str->getChar();
  height = read16();
  width = read16();
  numComps = str->getChar();
  if (numComps <= 0 || numComps > 4) {
    error(getPos(), "Bad number of components in DCT stream");
    numComps = 0;
    return gFalse;
  }
  if (prec != 8) {
    error(getPos(), "Bad DCT precision %d", prec);
    return gFalse;
  }
  for (i = 0; i < numComps; ++i) {
    compInfo[i].id = str->getChar();
    c = str->getChar();
    compInfo[i].hSample = (c >> 4) & 0x0f;
    compInfo[i].vSample = c & 0x0f;
    compInfo[i].quantTable = str->getChar();
  }
  progressive = gFalse;
  return gTrue;
}

GBool DCTStream::readProgressiveSOF() {
  int length;
  int prec;
  int i;
  int c;

  length = read16();
  prec = str->getChar();
  height = read16();
  width = read16();
  numComps = str->getChar();
  if (numComps <= 0 || numComps > 4) {
    error(getPos(), "Bad number of components in DCT stream");
    numComps = 0;
    return gFalse;
  }
  if (prec != 8) {
    error(getPos(), "Bad DCT precision %d", prec);
    return gFalse;
  }
  for (i = 0; i < numComps; ++i) {
    compInfo[i].id = str->getChar();
    c = str->getChar();
    compInfo[i].hSample = (c >> 4) & 0x0f;
    compInfo[i].vSample = c & 0x0f;
    compInfo[i].quantTable = str->getChar();
  }
  progressive = gTrue;
  return gTrue;
}

GBool DCTStream::readScanInfo() {
  int length;
  int id, c;
  int i, j;

  length = read16() - 2;
  scanInfo.numComps = str->getChar();
  if (scanInfo.numComps <= 0 || scanInfo.numComps > 4) {
    error(getPos(), "Bad number of components in DCT stream");
    scanInfo.numComps = 0;
    return gFalse;
  }
  --length;
  if (length != 2 * scanInfo.numComps + 3) {
    error(getPos(), "Bad DCT scan info block");
    return gFalse;
  }
  interleaved = scanInfo.numComps == numComps;
  for (j = 0; j < numComps; ++j) {
    scanInfo.comp[j] = gFalse;
  }
  for (i = 0; i < scanInfo.numComps; ++i) {
    id = str->getChar();
    // some (broken) DCT streams reuse ID numbers, but at least they
    // keep the components in order, so we check compInfo[i] first to
    // work around the problem
    if (id == compInfo[i].id) {
      j = i;
    } else {
      for (j = 0; j < numComps; ++j) {
	if (id == compInfo[j].id) {
	  break;
	}
      }
      if (j == numComps) {
	error(getPos(), "Bad DCT component ID in scan info block");
	return gFalse;
      }
    }
    scanInfo.comp[j] = gTrue;
    c = str->getChar();
    scanInfo.dcHuffTable[j] = (c >> 4) & 0x0f;
    scanInfo.acHuffTable[j] = c & 0x0f;
  }
  scanInfo.firstCoeff = str->getChar();
  scanInfo.lastCoeff = str->getChar();
  c = str->getChar();
  scanInfo.ah = (c >> 4) & 0x0f;
  scanInfo.al = c & 0x0f;
  return gTrue;
}

GBool DCTStream::readQuantTables() {
  int length, prec, i, index;

  length = read16() - 2;
  while (length > 0) {
    index = str->getChar();
    prec = (index >> 4) & 0x0f;
    index &= 0x0f;
    if (prec > 1 || index >= 4) {
      error(getPos(), "Bad DCT quantization table");
      return gFalse;
    }
    if (index == numQuantTables) {
      numQuantTables = index + 1;
    }
    for (i = 0; i < 64; ++i) {
      if (prec) {
	quantTables[index][dctZigZag[i]] = read16();
      } else {
	quantTables[index][dctZigZag[i]] = str->getChar();
      }
    }
    if (prec) {
      length -= 129;
    } else {
      length -= 65;
    }
  }
  return gTrue;
}

GBool DCTStream::readHuffmanTables() {
  DCTHuffTable *tbl;
  int length;
  int index;
  Gushort code;
  Guchar sym;
  int i;
  int c;

  length = read16() - 2;
  while (length > 0) {
    index = str->getChar();
    --length;
    if ((index & 0x0f) >= 4) {
      error(getPos(), "Bad DCT Huffman table");
      return gFalse;
    }
    if (index & 0x10) {
      index &= 0x0f;
      if (index >= numACHuffTables)
	numACHuffTables = index+1;
      tbl = &acHuffTables[index];
    } else {
      index &= 0x0f;
      if (index >= numDCHuffTables)
	numDCHuffTables = index+1;
      tbl = &dcHuffTables[index];
    }
    sym = 0;
    code = 0;
    for (i = 1; i <= 16; ++i) {
      c = str->getChar();
      tbl->firstSym[i] = sym;
      tbl->firstCode[i] = code;
      tbl->numCodes[i] = c;
      sym += c;
      code = (code + c) << 1;
    }
    length -= 16;
    for (i = 0; i < sym; ++i)
      tbl->sym[i] = str->getChar();
    length -= sym;
  }
  return gTrue;
}

GBool DCTStream::readRestartInterval() {
  int length;

  length = read16();
  if (length != 4) {
    error(getPos(), "Bad DCT restart interval");
    return gFalse;
  }
  restartInterval = read16();
  return gTrue;
}

GBool DCTStream::readJFIFMarker() {
  int length, i;
  char buf[5];
  int c;

  length = read16();
  length -= 2;
  if (length >= 5) {
    for (i = 0; i < 5; ++i) {
      if ((c = str->getChar()) == EOF) {
	error(getPos(), "Bad DCT APP0 marker");
	return gFalse;
      }
      buf[i] = c;
    }
    length -= 5;
    if (!memcmp(buf, "JFIF\0", 5)) {
      gotJFIFMarker = gTrue;
    }
  }
  while (length > 0) {
    if (str->getChar() == EOF) {
      error(getPos(), "Bad DCT APP0 marker");
      return gFalse;
    }
    --length;
  }
  return gTrue;
}

GBool DCTStream::readAdobeMarker() {
  int length, i;
  char buf[12];
  int c;

  length = read16();
  if (length < 14) {
    goto err;
  }
  for (i = 0; i < 12; ++i) {
    if ((c = str->getChar()) == EOF) {
      goto err;
    }
    buf[i] = c;
  }
  if (strncmp(buf, "Adobe", 5)) {
    goto err;
  }
  colorXform = buf[11];
  gotAdobeMarker = gTrue;
  for (i = 14; i < length; ++i) {
    if (str->getChar() == EOF) {
      goto err;
    }
  }
  return gTrue;

 err:
  error(getPos(), "Bad DCT Adobe APP14 marker");
  return gFalse;
}

GBool DCTStream::readTrailer() {
  int c;

  c = readMarker();
  if (c != 0xd9) {		// EOI
    error(getPos(), "Bad DCT trailer");
    return gFalse;
  }
  return gTrue;
}

int DCTStream::readMarker() {
  int c;

  do {
    do {
      c = str->getChar();
    } while (c != 0xff && c != EOF);
    do {
      c = str->getChar();
    } while (c == 0xff);
  } while (c == 0x00);
  return c;
}

int DCTStream::read16() {
  int c1, c2;

  if ((c1 = str->getChar()) == EOF)
    return EOF;
  if ((c2 = str->getChar()) == EOF)
    return EOF;
  return (c1 << 8) + c2;
}

GString *DCTStream::getPSFilter(int psLevel, char *indent) {
  GString *s;

  if (psLevel < 2) {
    return NULL;
  }
  if (!(s = str->getPSFilter(psLevel, indent))) {
    return NULL;
  }
  s->append(indent)->append("<< >> /DCTDecode filter\n");
  return s;
}

GBool DCTStream::isBinary(GBool last) {
  return str->isBinary(gTrue);
}

//------------------------------------------------------------------------
// FlateStream
//------------------------------------------------------------------------

int FlateStream::codeLenCodeMap[flateMaxCodeLenCodes] = {
  16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
};

FlateDecode FlateStream::lengthDecode[flateMaxLitCodes-257] = {
  {0,   3},
  {0,   4},
  {0,   5},
  {0,   6},
  {0,   7},
  {0,   8},
  {0,   9},
  {0,  10},
  {1,  11},
  {1,  13},
  {1,  15},
  {1,  17},
  {2,  19},
  {2,  23},
  {2,  27},
  {2,  31},
  {3,  35},
  {3,  43},
  {3,  51},
  {3,  59},
  {4,  67},
  {4,  83},
  {4,  99},
  {4, 115},
  {5, 131},
  {5, 163},
  {5, 195},
  {5, 227},
  {0, 258},
  {0, 258},
  {0, 258}
};

FlateDecode FlateStream::distDecode[flateMaxDistCodes] = {
  { 0,     1},
  { 0,     2},
  { 0,     3},
  { 0,     4},
  { 1,     5},
  { 1,     7},
  { 2,     9},
  { 2,    13},
  { 3,    17},
  { 3,    25},
  { 4,    33},
  { 4,    49},
  { 5,    65},
  { 5,    97},
  { 6,   129},
  { 6,   193},
  { 7,   257},
  { 7,   385},
  { 8,   513},
  { 8,   769},
  { 9,  1025},
  { 9,  1537},
  {10,  2049},
  {10,  3073},
  {11,  4097},
  {11,  6145},
  {12,  8193},
  {12, 12289},
  {13, 16385},
  {13, 24577}
};

static FlateCode flateFixedLitCodeTabCodes[512] = {
  {7, 0x0100},
  {8, 0x0050},
  {8, 0x0010},
  {8, 0x0118},
  {7, 0x0110},
  {8, 0x0070},
  {8, 0x0030},
  {9, 0x00c0},
  {7, 0x0108},
  {8, 0x0060},
  {8, 0x0020},
  {9, 0x00a0},
  {8, 0x0000},
  {8, 0x0080},
  {8, 0x0040},
  {9, 0x00e0},
  {7, 0x0104},
  {8, 0x0058},
  {8, 0x0018},
  {9, 0x0090},
  {7, 0x0114},
  {8, 0x0078},
  {8, 0x0038},
  {9, 0x00d0},
  {7, 0x010c},
  {8, 0x0068},
  {8, 0x0028},
  {9, 0x00b0},
  {8, 0x0008},
  {8, 0x0088},
  {8, 0x0048},
  {9, 0x00f0},
  {7, 0x0102},
  {8, 0x0054},
  {8, 0x0014},
  {8, 0x011c},
  {7, 0x0112},
  {8, 0x0074},
  {8, 0x0034},
  {9, 0x00c8},
  {7, 0x010a},
  {8, 0x0064},
  {8, 0x0024},
  {9, 0x00a8},
  {8, 0x0004},
  {8, 0x0084},
  {8, 0x0044},
  {9, 0x00e8},
  {7, 0x0106},
  {8, 0x005c},
  {8, 0x001c},
  {9, 0x0098},
  {7, 0x0116},
  {8, 0x007c},
  {8, 0x003c},
  {9, 0x00d8},
  {7, 0x010e},
  {8, 0x006c},
  {8, 0x002c},
  {9, 0x00b8},
  {8, 0x000c},
  {8, 0x008c},
  {8, 0x004c},
  {9, 0x00f8},
  {7, 0x0101},
  {8, 0x0052},
  {8, 0x0012},
  {8, 0x011a},
  {7, 0x0111},
  {8, 0x0072},
  {8, 0x0032},
  {9, 0x00c4},
  {7, 0x0109},
  {8, 0x0062},
  {8, 0x0022},
  {9, 0x00a4},
  {8, 0x0002},
  {8, 0x0082},
  {8, 0x0042},
  {9, 0x00e4},
  {7, 0x0105},
  {8, 0x005a},
  {8, 0x001a},
  {9, 0x0094},
  {7, 0x0115},
  {8, 0x007a},
  {8, 0x003a},
  {9, 0x00d4},
  {7, 0x010d},
  {8, 0x006a},
  {8, 0x002a},
  {9, 0x00b4},
  {8, 0x000a},
  {8, 0x008a},
  {8, 0x004a},
  {9, 0x00f4},
  {7, 0x0103},
  {8, 0x0056},
  {8, 0x0016},
  {8, 0x011e},
  {7, 0x0113},
  {8, 0x0076},
  {8, 0x0036},
  {9, 0x00cc},
  {7, 0x010b},
  {8, 0x0066},
  {8, 0x0026},
  {9, 0x00ac},
  {8, 0x0006},
  {8, 0x0086},
  {8, 0x0046},
  {9, 0x00ec},
  {7, 0x0107},
  {8, 0x005e},
  {8, 0x001e},
  {9, 0x009c},
  {7, 0x0117},
  {8, 0x007e},
  {8, 0x003e},
  {9, 0x00dc},
  {7, 0x010f},
  {8, 0x006e},
  {8, 0x002e},
  {9, 0x00bc},
  {8, 0x000e},
  {8, 0x008e},
  {8, 0x004e},
  {9, 0x00fc},
  {7, 0x0100},
  {8, 0x0051},
  {8, 0x0011},
  {8, 0x0119},
  {7, 0x0110},
  {8, 0x0071},
  {8, 0x0031},
  {9, 0x00c2},
  {7, 0x0108},
  {8, 0x0061},
  {8, 0x0021},
  {9, 0x00a2},
  {8, 0x0001},
  {8, 0x0081},
  {8, 0x0041},
  {9, 0x00e2},
  {7, 0x0104},
  {8, 0x0059},
  {8, 0x0019},
  {9, 0x0092},
  {7, 0x0114},
  {8, 0x0079},
  {8, 0x0039},
  {9, 0x00d2},
  {7, 0x010c},
  {8, 0x0069},
  {8, 0x0029},
  {9, 0x00b2},
  {8, 0x0009},
  {8, 0x0089},
  {8, 0x0049},
  {9, 0x00f2},
  {7, 0x0102},
  {8, 0x0055},
  {8, 0x0015},
  {8, 0x011d},
  {7, 0x0112},
  {8, 0x0075},
  {8, 0x0035},
  {9, 0x00ca},
  {7, 0x010a},
  {8, 0x0065},
  {8, 0x0025},
  {9, 0x00aa},
  {8, 0x0005},
  {8, 0x0085},
  {8, 0x0045},
  {9, 0x00ea},
  {7, 0x0106},
  {8, 0x005d},
  {8, 0x001d},
  {9, 0x009a},
  {7, 0x0116},
  {8, 0x007d},
  {8, 0x003d},
  {9, 0x00da},
  {7, 0x010e},
  {8, 0x006d},
  {8, 0x002d},
  {9, 0x00ba},
  {8, 0x000d},
  {8, 0x008d},
  {8, 0x004d},
  {9, 0x00fa},
  {7, 0x0101},
  {8, 0x0053},
  {8, 0x0013},
  {8, 0x011b},
  {7, 0x0111},
  {8, 0x0073},
  {8, 0x0033},
  {9, 0x00c6},
  {7, 0x0109},
  {8, 0x0063},
  {8, 0x0023},
  {9, 0x00a6},
  {8, 0x0003},
  {8, 0x0083},
  {8, 0x0043},
  {9, 0x00e6},
  {7, 0x0105},
  {8, 0x005b},
  {8, 0x001b},
  {9, 0x0096},
  {7, 0x0115},
  {8, 0x007b},
  {8, 0x003b},
  {9, 0x00d6},
  {7, 0x010d},
  {8, 0x006b},
  {8, 0x002b},
  {9, 0x00b6},
  {8, 0x000b},
  {8, 0x008b},
  {8, 0x004b},
  {9, 0x00f6},
  {7, 0x0103},
  {8, 0x0057},
  {8, 0x0017},
  {8, 0x011f},
  {7, 0x0113},
  {8, 0x0077},
  {8, 0x0037},
  {9, 0x00ce},
  {7, 0x010b},
  {8, 0x0067},
  {8, 0x0027},
  {9, 0x00ae},
  {8, 0x0007},
  {8, 0x0087},
  {8, 0x0047},
  {9, 0x00ee},
  {7, 0x0107},
  {8, 0x005f},
  {8, 0x001f},
  {9, 0x009e},
  {7, 0x0117},
  {8, 0x007f},
  {8, 0x003f},
  {9, 0x00de},
  {7, 0x010f},
  {8, 0x006f},
  {8, 0x002f},
  {9, 0x00be},
  {8, 0x000f},
  {8, 0x008f},
  {8, 0x004f},
  {9, 0x00fe},
  {7, 0x0100},
  {8, 0x0050},
  {8, 0x0010},
  {8, 0x0118},
  {7, 0x0110},
  {8, 0x0070},
  {8, 0x0030},
  {9, 0x00c1},
  {7, 0x0108},
  {8, 0x0060},
  {8, 0x0020},
  {9, 0x00a1},
  {8, 0x0000},
  {8, 0x0080},
  {8, 0x0040},
  {9, 0x00e1},
  {7, 0x0104},
  {8, 0x0058},
  {8, 0x0018},
  {9, 0x0091},
  {7, 0x0114},
  {8, 0x0078},
  {8, 0x0038},
  {9, 0x00d1},
  {7, 0x010c},
  {8, 0x0068},
  {8, 0x0028},
  {9, 0x00b1},
  {8, 0x0008},
  {8, 0x0088},
  {8, 0x0048},
  {9, 0x00f1},
  {7, 0x0102},
  {8, 0x0054},
  {8, 0x0014},
  {8, 0x011c},
  {7, 0x0112},
  {8, 0x0074},
  {8, 0x0034},
  {9, 0x00c9},
  {7, 0x010a},
  {8, 0x0064},
  {8, 0x0024},
  {9, 0x00a9},
  {8, 0x0004},
  {8, 0x0084},
  {8, 0x0044},
  {9, 0x00e9},
  {7, 0x0106},
  {8, 0x005c},
  {8, 0x001c},
  {9, 0x0099},
  {7, 0x0116},
  {8, 0x007c},
  {8, 0x003c},
  {9, 0x00d9},
  {7, 0x010e},
  {8, 0x006c},
  {8, 0x002c},
  {9, 0x00b9},
  {8, 0x000c},
  {8, 0x008c},
  {8, 0x004c},
  {9, 0x00f9},
  {7, 0x0101},
  {8, 0x0052},
  {8, 0x0012},
  {8, 0x011a},
  {7, 0x0111},
  {8, 0x0072},
  {8, 0x0032},
  {9, 0x00c5},
  {7, 0x0109},
  {8, 0x0062},
  {8, 0x0022},
  {9, 0x00a5},
  {8, 0x0002},
  {8, 0x0082},
  {8, 0x0042},
  {9, 0x00e5},
  {7, 0x0105},
  {8, 0x005a},
  {8, 0x001a},
  {9, 0x0095},
  {7, 0x0115},
  {8, 0x007a},
  {8, 0x003a},
  {9, 0x00d5},
  {7, 0x010d},
  {8, 0x006a},
  {8, 0x002a},
  {9, 0x00b5},
  {8, 0x000a},
  {8, 0x008a},
  {8, 0x004a},
  {9, 0x00f5},
  {7, 0x0103},
  {8, 0x0056},
  {8, 0x0016},
  {8, 0x011e},
  {7, 0x0113},
  {8, 0x0076},
  {8, 0x0036},
  {9, 0x00cd},
  {7, 0x010b},
  {8, 0x0066},
  {8, 0x0026},
  {9, 0x00ad},
  {8, 0x0006},
  {8, 0x0086},
  {8, 0x0046},
  {9, 0x00ed},
  {7, 0x0107},
  {8, 0x005e},
  {8, 0x001e},
  {9, 0x009d},
  {7, 0x0117},
  {8, 0x007e},
  {8, 0x003e},
  {9, 0x00dd},
  {7, 0x010f},
  {8, 0x006e},
  {8, 0x002e},
  {9, 0x00bd},
  {8, 0x000e},
  {8, 0x008e},
  {8, 0x004e},
  {9, 0x00fd},
  {7, 0x0100},
  {8, 0x0051},
  {8, 0x0011},
  {8, 0x0119},
  {7, 0x0110},
  {8, 0x0071},
  {8, 0x0031},
  {9, 0x00c3},
  {7, 0x0108},
  {8, 0x0061},
  {8, 0x0021},
  {9, 0x00a3},
  {8, 0x0001},
  {8, 0x0081},
  {8, 0x0041},
  {9, 0x00e3},
  {7, 0x0104},
  {8, 0x0059},
  {8, 0x0019},
  {9, 0x0093},
  {7, 0x0114},
  {8, 0x0079},
  {8, 0x0039},
  {9, 0x00d3},
  {7, 0x010c},
  {8, 0x0069},
  {8, 0x0029},
  {9, 0x00b3},
  {8, 0x0009},
  {8, 0x0089},
  {8, 0x0049},
  {9, 0x00f3},
  {7, 0x0102},
  {8, 0x0055},
  {8, 0x0015},
  {8, 0x011d},
  {7, 0x0112},
  {8, 0x0075},
  {8, 0x0035},
  {9, 0x00cb},
  {7, 0x010a},
  {8, 0x0065},
  {8, 0x0025},
  {9, 0x00ab},
  {8, 0x0005},
  {8, 0x0085},
  {8, 0x0045},
  {9, 0x00eb},
  {7, 0x0106},
  {8, 0x005d},
  {8, 0x001d},
  {9, 0x009b},
  {7, 0x0116},
  {8, 0x007d},
  {8, 0x003d},
  {9, 0x00db},
  {7, 0x010e},
  {8, 0x006d},
  {8, 0x002d},
  {9, 0x00bb},
  {8, 0x000d},
  {8, 0x008d},
  {8, 0x004d},
  {9, 0x00fb},
  {7, 0x0101},
  {8, 0x0053},
  {8, 0x0013},
  {8, 0x011b},
  {7, 0x0111},
  {8, 0x0073},
  {8, 0x0033},
  {9, 0x00c7},
  {7, 0x0109},
  {8, 0x0063},
  {8, 0x0023},
  {9, 0x00a7},
  {8, 0x0003},
  {8, 0x0083},
  {8, 0x0043},
  {9, 0x00e7},
  {7, 0x0105},
  {8, 0x005b},
  {8, 0x001b},
  {9, 0x0097},
  {7, 0x0115},
  {8, 0x007b},
  {8, 0x003b},
  {9, 0x00d7},
  {7, 0x010d},
  {8, 0x006b},
  {8, 0x002b},
  {9, 0x00b7},
  {8, 0x000b},
  {8, 0x008b},
  {8, 0x004b},
  {9, 0x00f7},
  {7, 0x0103},
  {8, 0x0057},
  {8, 0x0017},
  {8, 0x011f},
  {7, 0x0113},
  {8, 0x0077},
  {8, 0x0037},
  {9, 0x00cf},
  {7, 0x010b},
  {8, 0x0067},
  {8, 0x0027},
  {9, 0x00af},
  {8, 0x0007},
  {8, 0x0087},
  {8, 0x0047},
  {9, 0x00ef},
  {7, 0x0107},
  {8, 0x005f},
  {8, 0x001f},
  {9, 0x009f},
  {7, 0x0117},
  {8, 0x007f},
  {8, 0x003f},
  {9, 0x00df},
  {7, 0x010f},
  {8, 0x006f},
  {8, 0x002f},
  {9, 0x00bf},
  {8, 0x000f},
  {8, 0x008f},
  {8, 0x004f},
  {9, 0x00ff}
};

FlateHuffmanTab FlateStream::fixedLitCodeTab = {
  flateFixedLitCodeTabCodes, 9
};

static FlateCode flateFixedDistCodeTabCodes[32] = {
  {5, 0x0000},
  {5, 0x0010},
  {5, 0x0008},
  {5, 0x0018},
  {5, 0x0004},
  {5, 0x0014},
  {5, 0x000c},
  {5, 0x001c},
  {5, 0x0002},
  {5, 0x0012},
  {5, 0x000a},
  {5, 0x001a},
  {5, 0x0006},
  {5, 0x0016},
  {5, 0x000e},
  {0, 0x0000},
  {5, 0x0001},
  {5, 0x0011},
  {5, 0x0009},
  {5, 0x0019},
  {5, 0x0005},
  {5, 0x0015},
  {5, 0x000d},
  {5, 0x001d},
  {5, 0x0003},
  {5, 0x0013},
  {5, 0x000b},
  {5, 0x001b},
  {5, 0x0007},
  {5, 0x0017},
  {5, 0x000f},
  {0, 0x0000}
};

FlateHuffmanTab FlateStream::fixedDistCodeTab = {
  flateFixedDistCodeTabCodes, 5
};

FlateStream::FlateStream(Stream *strA, int predictor, int columns,
			 int colors, int bits):
    FilterStream(strA) {
  if (predictor != 1) {
    pred = new StreamPredictor(this, predictor, columns, colors, bits);
    if (!pred->isOk()) {
      delete pred;
      pred = NULL;
    }
  } else {
    pred = NULL;
  }
  litCodeTab.codes = NULL;
  distCodeTab.codes = NULL;
  memset(buf, 0, flateWindow);
}

FlateStream::~FlateStream() {
  if (litCodeTab.codes != fixedLitCodeTab.codes) {
    gfree(litCodeTab.codes);
  }
  if (distCodeTab.codes != fixedDistCodeTab.codes) {
    gfree(distCodeTab.codes);
  }
  if (pred) {
    delete pred;
  }
  delete str;
}

void FlateStream::reset() {
  int cmf, flg;

  index = 0;
  remain = 0;
  codeBuf = 0;
  codeSize = 0;
  compressedBlock = gFalse;
  endOfBlock = gTrue;
  eof = gTrue;

  str->reset();

  // read header
  //~ need to look at window size?
  endOfBlock = eof = gTrue;
  cmf = str->getChar();
  flg = str->getChar();
  if (cmf == EOF || flg == EOF)
    return;
  if ((cmf & 0x0f) != 0x08) {
    error(getPos(), "Unknown compression method in flate stream");
    return;
  }
  if ((((cmf << 8) + flg) % 31) != 0) {
    error(getPos(), "Bad FCHECK in flate stream");
    return;
  }
  if (flg & 0x20) {
    error(getPos(), "FDICT bit set in flate stream");
    return;
  }

  eof = gFalse;
}

int FlateStream::getChar() {
  int c;

  if (pred) {
    return pred->getChar();
  }
  while (remain == 0) {
    if (endOfBlock && eof)
      return EOF;
    readSome();
  }
  c = buf[index];
  index = (index + 1) & flateMask;
  --remain;
  return c;
}

int FlateStream::lookChar() {
  int c;

  if (pred) {
    return pred->lookChar();
  }
  while (remain == 0) {
    if (endOfBlock && eof)
      return EOF;
    readSome();
  }
  c = buf[index];
  return c;
}

int FlateStream::getRawChar() {
  int c;

  while (remain == 0) {
    if (endOfBlock && eof)
      return EOF;
    readSome();
  }
  c = buf[index];
  index = (index + 1) & flateMask;
  --remain;
  return c;
}

GString *FlateStream::getPSFilter(int psLevel, char *indent) {
  GString *s;

  if (psLevel < 3 || pred) {
    return NULL;
  }
  if (!(s = str->getPSFilter(psLevel, indent))) {
    return NULL;
  }
  s->append(indent)->append("<< >> /FlateDecode filter\n");
  return s;
}

GBool FlateStream::isBinary(GBool last) {
  return str->isBinary(gTrue);
}

void FlateStream::readSome() {
  int code1, code2;
  int len, dist;
  int i, j, k;
  int c;

  if (endOfBlock) {
    if (!startBlock())
      return;
  }

  if (compressedBlock) {
    if ((code1 = getHuffmanCodeWord(&litCodeTab)) == EOF)
      goto err;
    if (code1 < 256) {
      buf[index] = code1;
      remain = 1;
    } else if (code1 == 256) {
      endOfBlock = gTrue;
      remain = 0;
    } else {
      code1 -= 257;
      code2 = lengthDecode[code1].bits;
      if (code2 > 0 && (code2 = getCodeWord(code2)) == EOF)
	goto err;
      len = lengthDecode[code1].first + code2;
      if ((code1 = getHuffmanCodeWord(&distCodeTab)) == EOF)
	goto err;
      code2 = distDecode[code1].bits;
      if (code2 > 0 && (code2 = getCodeWord(code2)) == EOF)
	goto err;
      dist = distDecode[code1].first + code2;
      i = index;
      j = (index - dist) & flateMask;
      for (k = 0; k < len; ++k) {
	buf[i] = buf[j];
	i = (i + 1) & flateMask;
	j = (j + 1) & flateMask;
      }
      remain = len;
    }

  } else {
    len = (blockLen < flateWindow) ? blockLen : flateWindow;
    for (i = 0, j = index; i < len; ++i, j = (j + 1) & flateMask) {
      if ((c = str->getChar()) == EOF) {
	endOfBlock = eof = gTrue;
	break;
      }
      buf[j] = c & 0xff;
    }
    remain = i;
    blockLen -= len;
    if (blockLen == 0)
      endOfBlock = gTrue;
  }

  return;

err:
  error(getPos(), "Unexpected end of file in flate stream");
  endOfBlock = eof = gTrue;
  remain = 0;
}

GBool FlateStream::startBlock() {
  int blockHdr;
  int c;
  int check;

  // free the code tables from the previous block
  if (litCodeTab.codes != fixedLitCodeTab.codes) {
    gfree(litCodeTab.codes);
  }
  litCodeTab.codes = NULL;
  if (distCodeTab.codes != fixedDistCodeTab.codes) {
    gfree(distCodeTab.codes);
  }
  distCodeTab.codes = NULL;

  // read block header
  blockHdr = getCodeWord(3);
  if (blockHdr & 1)
    eof = gTrue;
  blockHdr >>= 1;

  // uncompressed block
  if (blockHdr == 0) {
    compressedBlock = gFalse;
    if ((c = str->getChar()) == EOF)
      goto err;
    blockLen = c & 0xff;
    if ((c = str->getChar()) == EOF)
      goto err;
    blockLen |= (c & 0xff) << 8;
    if ((c = str->getChar()) == EOF)
      goto err;
    check = c & 0xff;
    if ((c = str->getChar()) == EOF)
      goto err;
    check |= (c & 0xff) << 8;
    if (check != (~blockLen & 0xffff))
      error(getPos(), "Bad uncompressed block length in flate stream");
    codeBuf = 0;
    codeSize = 0;

  // compressed block with fixed codes
  } else if (blockHdr == 1) {
    compressedBlock = gTrue;
    loadFixedCodes();

  // compressed block with dynamic codes
  } else if (blockHdr == 2) {
    compressedBlock = gTrue;
    if (!readDynamicCodes()) {
      goto err;
    }

  // unknown block type
  } else {
    goto err;
  }

  endOfBlock = gFalse;
  return gTrue;

err:
  error(getPos(), "Bad block header in flate stream");
  endOfBlock = eof = gTrue;
  return gFalse;
}

void FlateStream::loadFixedCodes() {
  litCodeTab.codes = fixedLitCodeTab.codes;
  litCodeTab.maxLen = fixedLitCodeTab.maxLen;
  distCodeTab.codes = fixedDistCodeTab.codes;
  distCodeTab.maxLen = fixedDistCodeTab.maxLen;
}

GBool FlateStream::readDynamicCodes() {
  int numCodeLenCodes;
  int numLitCodes;
  int numDistCodes;
  int codeLenCodeLengths[flateMaxCodeLenCodes];
  FlateHuffmanTab codeLenCodeTab;
  int len, repeat, code;
  int i;

  codeLenCodeTab.codes = NULL;

  // read lengths
  if ((numLitCodes = getCodeWord(5)) == EOF) {
    goto err;
  }
  numLitCodes += 257;
  if ((numDistCodes = getCodeWord(5)) == EOF) {
    goto err;
  }
  numDistCodes += 1;
  if ((numCodeLenCodes = getCodeWord(4)) == EOF) {
    goto err;
  }
  numCodeLenCodes += 4;
  if (numLitCodes > flateMaxLitCodes ||
      numDistCodes > flateMaxDistCodes ||
      numCodeLenCodes > flateMaxCodeLenCodes) {
    goto err;
  }

  // build the code length code table
  for (i = 0; i < flateMaxCodeLenCodes; ++i) {
    codeLenCodeLengths[i] = 0;
  }
  for (i = 0; i < numCodeLenCodes; ++i) {
    if ((codeLenCodeLengths[codeLenCodeMap[i]] = getCodeWord(3)) == -1) {
      goto err;
    }
  }
  compHuffmanCodes(codeLenCodeLengths, flateMaxCodeLenCodes, &codeLenCodeTab);

  // build the literal and distance code tables
  len = 0;
  repeat = 0;
  i = 0;
  while (i < numLitCodes + numDistCodes) {
    if ((code = getHuffmanCodeWord(&codeLenCodeTab)) == EOF) {
      goto err;
    }
    if (code == 16) {
      if ((repeat = getCodeWord(2)) == EOF) {
	goto err;
      }
      repeat += 3;
      if (i + repeat > numLitCodes + numDistCodes) {
	goto err;
      }
      for (; repeat > 0; --repeat) {
	codeLengths[i++] = len;
      }
    } else if (code == 17) {
      if ((repeat = getCodeWord(3)) == EOF) {
	goto err;
      }
      repeat += 3;
      if (i + repeat > numLitCodes + numDistCodes) {
	goto err;
      }
      len = 0;
      for (; repeat > 0; --repeat) {
	codeLengths[i++] = 0;
      }
    } else if (code == 18) {
      if ((repeat = getCodeWord(7)) == EOF) {
	goto err;
      }
      repeat += 11;
      if (i + repeat > numLitCodes + numDistCodes) {
	goto err;
      }
      len = 0;
      for (; repeat > 0; --repeat) {
	codeLengths[i++] = 0;
      }
    } else {
      codeLengths[i++] = len = code;
    }
  }
  compHuffmanCodes(codeLengths, numLitCodes, &litCodeTab);
  compHuffmanCodes(codeLengths + numLitCodes, numDistCodes, &distCodeTab);

  gfree(codeLenCodeTab.codes);
  return gTrue;

err:
  error(getPos(), "Bad dynamic code table in flate stream");
  gfree(codeLenCodeTab.codes);
  return gFalse;
}

// Convert an array <lengths> of <n> lengths, in value order, into a
// Huffman code lookup table.
void FlateStream::compHuffmanCodes(int *lengths, int n, FlateHuffmanTab *tab) {
  int tabSize, len, code, code2, skip, val, i, t;

  // find max code length
  tab->maxLen = 0;
  for (val = 0; val < n; ++val) {
    if (lengths[val] > tab->maxLen) {
      tab->maxLen = lengths[val];
    }
  }

  // allocate the table
  tabSize = 1 << tab->maxLen;
  tab->codes = (FlateCode *)gmallocn(tabSize, sizeof(FlateCode));

  // clear the table
  for (i = 0; i < tabSize; ++i) {
    tab->codes[i].len = 0;
    tab->codes[i].val = 0;
  }

  // build the table
  for (len = 1, code = 0, skip = 2;
       len <= tab->maxLen;
       ++len, code <<= 1, skip <<= 1) {
    for (val = 0; val < n; ++val) {
      if (lengths[val] == len) {

	// bit-reverse the code
	code2 = 0;
	t = code;
	for (i = 0; i < len; ++i) {
	  code2 = (code2 << 1) | (t & 1);
	  t >>= 1;
	}

	// fill in the table entries
	for (i = code2; i < tabSize; i += skip) {
	  tab->codes[i].len = (Gushort)len;
	  tab->codes[i].val = (Gushort)val;
	}

	++code;
      }
    }
  }
}

int FlateStream::getHuffmanCodeWord(FlateHuffmanTab *tab) {
  FlateCode *code;
  int c;

  while (codeSize < tab->maxLen) {
    if ((c = str->getChar()) == EOF) {
      break;
    }
    codeBuf |= (c & 0xff) << codeSize;
    codeSize += 8;
  }
  code = &tab->codes[codeBuf & ((1 << tab->maxLen) - 1)];
  if (codeSize == 0 || codeSize < code->len || code->len == 0) {
    return EOF;
  }
  codeBuf >>= code->len;
  codeSize -= code->len;
  return (int)code->val;
}

int FlateStream::getCodeWord(int bits) {
  int c;

  while (codeSize < bits) {
    if ((c = str->getChar()) == EOF)
      return EOF;
    codeBuf |= (c & 0xff) << codeSize;
    codeSize += 8;
  }
  c = codeBuf & ((1 << bits) - 1);
  codeBuf >>= bits;
  codeSize -= bits;
  return c;
}

//------------------------------------------------------------------------
// EOFStream
//------------------------------------------------------------------------

EOFStream::EOFStream(Stream *strA):
    FilterStream(strA) {
}

EOFStream::~EOFStream() {
  delete str;
}

//------------------------------------------------------------------------
// FixedLengthEncoder
//------------------------------------------------------------------------

FixedLengthEncoder::FixedLengthEncoder(Stream *strA, int lengthA):
    FilterStream(strA) {
  length = lengthA;
  count = 0;
}

FixedLengthEncoder::~FixedLengthEncoder() {
  if (str->isEncoder())
    delete str;
}

void FixedLengthEncoder::reset() {
  str->reset();
  count = 0;
}

int FixedLengthEncoder::getChar() {
  if (length >= 0 && count >= length)
    return EOF;
  ++count;
  return str->getChar();
}

int FixedLengthEncoder::lookChar() {
  if (length >= 0 && count >= length)
    return EOF;
  return str->getChar();
}

GBool FixedLengthEncoder::isBinary(GBool last) {
  return str->isBinary(gTrue);
}

//------------------------------------------------------------------------
// ASCIIHexEncoder
//------------------------------------------------------------------------

ASCIIHexEncoder::ASCIIHexEncoder(Stream *strA):
    FilterStream(strA) {
  bufPtr = bufEnd = buf;
  lineLen = 0;
  eof = gFalse;
}

ASCIIHexEncoder::~ASCIIHexEncoder() {
  if (str->isEncoder()) {
    delete str;
  }
}

void ASCIIHexEncoder::reset() {
  str->reset();
  bufPtr = bufEnd = buf;
  lineLen = 0;
  eof = gFalse;
}

GBool ASCIIHexEncoder::fillBuf() {
  static char *hex = "0123456789abcdef";
  int c;

  if (eof) {
    return gFalse;
  }
  bufPtr = bufEnd = buf;
  if ((c = str->getChar()) == EOF) {
    *bufEnd++ = '>';
    eof = gTrue;
  } else {
    if (lineLen >= 64) {
      *bufEnd++ = '\n';
      lineLen = 0;
    }
    *bufEnd++ = hex[(c >> 4) & 0x0f];
    *bufEnd++ = hex[c & 0x0f];
    lineLen += 2;
  }
  return gTrue;
}

//------------------------------------------------------------------------
// ASCII85Encoder
//------------------------------------------------------------------------

ASCII85Encoder::ASCII85Encoder(Stream *strA):
    FilterStream(strA) {
  bufPtr = bufEnd = buf;
  lineLen = 0;
  eof = gFalse;
}

ASCII85Encoder::~ASCII85Encoder() {
  if (str->isEncoder())
    delete str;
}

void ASCII85Encoder::reset() {
  str->reset();
  bufPtr = bufEnd = buf;
  lineLen = 0;
  eof = gFalse;
}

GBool ASCII85Encoder::fillBuf() {
  Gulong t;
  char buf1[5];
  int c0, c1, c2, c3;
  int n, i;

  if (eof) {
    return gFalse;
  }
  c0 = str->getChar();
  c1 = str->getChar();
  c2 = str->getChar();
  c3 = str->getChar();
  bufPtr = bufEnd = buf;
  if (c3 == EOF) {
    if (c0 == EOF) {
      n = 0;
      t = 0;
    } else {
      if (c1 == EOF) {
	n = 1;
	t = c0 << 24;
      } else if (c2 == EOF) {
	n = 2;
	t = (c0 << 24) | (c1 << 16);
      } else {
	n = 3;
	t = (c0 << 24) | (c1 << 16) | (c2 << 8);
      }
      for (i = 4; i >= 0; --i) {
	buf1[i] = (char)(t % 85 + 0x21);
	t /= 85;
      }
      for (i = 0; i <= n; ++i) {
	*bufEnd++ = buf1[i];
	if (++lineLen == 65) {
	  *bufEnd++ = '\n';
	  lineLen = 0;
	}
      }
    }
    *bufEnd++ = '~';
    *bufEnd++ = '>';
    eof = gTrue;
  } else {
    t = (c0 << 24) | (c1 << 16) | (c2 << 8) | c3;
    if (t == 0) {
      *bufEnd++ = 'z';
      if (++lineLen == 65) {
	*bufEnd++ = '\n';
	lineLen = 0;
      }
    } else {
      for (i = 4; i >= 0; --i) {
	buf1[i] = (char)(t % 85 + 0x21);
	t /= 85;
      }
      for (i = 0; i <= 4; ++i) {
	*bufEnd++ = buf1[i];
	if (++lineLen == 65) {
	  *bufEnd++ = '\n';
	  lineLen = 0;
	}
      }
    }
  }
  return gTrue;
}

//------------------------------------------------------------------------
// RunLengthEncoder
//------------------------------------------------------------------------

RunLengthEncoder::RunLengthEncoder(Stream *strA):
    FilterStream(strA) {
  bufPtr = bufEnd = nextEnd = buf;
  eof = gFalse;
}

RunLengthEncoder::~RunLengthEncoder() {
  if (str->isEncoder())
    delete str;
}

void RunLengthEncoder::reset() {
  str->reset();
  bufPtr = bufEnd = nextEnd = buf;
  eof = gFalse;
}

//
// When fillBuf finishes, buf[] looks like this:
//   +-----+--------------+-----------------+--
//   + tag | ... data ... | next 0, 1, or 2 |
//   +-----+--------------+-----------------+--
//    ^                    ^                 ^
//    bufPtr               bufEnd            nextEnd
//
GBool RunLengthEncoder::fillBuf() {
  int c, c1, c2;
  int n;

  // already hit EOF?
  if (eof)
    return gFalse;

  // grab two bytes
  if (nextEnd < bufEnd + 1) {
    if ((c1 = str->getChar()) == EOF) {
      eof = gTrue;
      return gFalse;
    }
  } else {
    c1 = bufEnd[0] & 0xff;
  }
  if (nextEnd < bufEnd + 2) {
    if ((c2 = str->getChar()) == EOF) {
      eof = gTrue;
      buf[0] = 0;
      buf[1] = c1;
      bufPtr = buf;
      bufEnd = &buf[2];
      return gTrue;
    }
  } else {
    c2 = bufEnd[1] & 0xff;
  }

  // check for repeat
  c = 0; // make gcc happy
  if (c1 == c2) {
    n = 2;
    while (n < 128 && (c = str->getChar()) == c1)
      ++n;
    buf[0] = (char)(257 - n);
    buf[1] = c1;
    bufEnd = &buf[2];
    if (c == EOF) {
      eof = gTrue;
    } else if (n < 128) {
      buf[2] = c;
      nextEnd = &buf[3];
    } else {
      nextEnd = bufEnd;
    }

  // get up to 128 chars
  } else {
    buf[1] = c1;
    buf[2] = c2;
    n = 2;
    while (n < 128) {
      if ((c = str->getChar()) == EOF) {
	eof = gTrue;
	break;
      }
      ++n;
      buf[n] = c;
      if (buf[n] == buf[n-1])
	break;
    }
    if (buf[n] == buf[n-1]) {
      buf[0] = (char)(n-2-1);
      bufEnd = &buf[n-1];
      nextEnd = &buf[n+1];
    } else {
      buf[0] = (char)(n-1);
      bufEnd = nextEnd = &buf[n+1];
    }
  }
  bufPtr = buf;
  return gTrue;
}
//========================================================================
//
// Stream.h
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef STREAM_H
#define STREAM_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include <stdio.h>
#include "gtypes.h"
#include "Object.h"

class BaseStream;

//------------------------------------------------------------------------

enum StreamKind {
  strFile,
  strASCIIHex,
  strASCII85,
  strLZW,
  strRunLength,
  strCCITTFax,
  strDCT,
  strFlate,
  strJBIG2,
  strJPX,
  strWeird			// internal-use stream types
};

enum StreamColorSpaceMode {
  streamCSNone,
  streamCSDeviceGray,
  streamCSDeviceRGB,
  streamCSDeviceCMYK
};

//------------------------------------------------------------------------

// This is in Stream.h instead of Decrypt.h to avoid really annoying
// include file dependency loops.
enum CryptAlgorithm {
  cryptRC4,
  cryptAES
};

//------------------------------------------------------------------------
// Stream (base class)
//------------------------------------------------------------------------

class Stream {
public:

  // Constructor.
  Stream();

  // Destructor.
  virtual ~Stream();

  // Reference counting.
  int incRef() { return ++ref; }
  int decRef() { return --ref; }

  // Get kind of stream.
  virtual StreamKind getKind() = 0;

  // Reset stream to beginning.
  virtual void reset() = 0;

  // Close down the stream.
  virtual void close();

  // Get next char from stream.
  virtual int getChar() = 0;

  // Peek at next char in stream.
  virtual int lookChar() = 0;

  // Get next char from stream without using the predictor.
  // This is only used by StreamPredictor.
  virtual int getRawChar();

  // Get next line from stream.
  virtual char *getLine(char *buf, int size);

  // Get current position in file.
  virtual int getPos() = 0;

  // Go to a position in the stream.  If <dir> is negative, the
  // position is from the end of the file; otherwise the position is
  // from the start of the file.
  virtual void setPos(Guint pos, int dir = 0) = 0;

  // Get PostScript command for the filter(s).
  virtual GString *getPSFilter(int psLevel, char *indent);

  // Does this stream type potentially contain non-printable chars?
  virtual GBool isBinary(GBool last = gTrue) = 0;

  // Get the BaseStream of this stream.
  virtual BaseStream *getBaseStream() = 0;

  // Get the stream after the last decoder (this may be a BaseStream
  // or a DecryptStream).
  virtual Stream *getUndecodedStream() = 0;

  // Get the dictionary associated with this stream.
  virtual Dict *getDict() = 0;

  // Is this an encoding filter?
  virtual GBool isEncoder() { return gFalse; }

  // Get image parameters which are defined by the stream contents.
  virtual void getImageParams(int *bitsPerComponent,
			      StreamColorSpaceMode *csMode) {}

  // Return the next stream in the "stack".
  virtual Stream *getNextStream() { return NULL; }

  // Add filters to this stream according to the parameters in <dict>.
  // Returns the new stream.
  Stream *addFilters(Object *dict);

private:

  Stream *makeFilter(char *name, Stream *str, Object *params);

  int ref;			// reference count
};

//------------------------------------------------------------------------
// BaseStream
//
// This is the base class for all streams that read directly from a file.
//------------------------------------------------------------------------

class BaseStream: public Stream {
public:

  BaseStream(Object *dictA);
  virtual ~BaseStream();
  virtual Stream *makeSubStream(Guint start, GBool limited,
				Guint length, Object *dict) = 0;
  virtual void setPos(Guint pos, int dir = 0) = 0;
  virtual GBool isBinary(GBool last = gTrue) { return last; }
  virtual BaseStream *getBaseStream() { return this; }
  virtual Stream *getUndecodedStream() { return this; }
  virtual Dict *getDict() { return dict.getDict(); }
  virtual GString *getFileName() { return NULL; }

  // Get/set position of first byte of stream within the file.
  virtual Guint getStart() = 0;
  virtual void moveStart(int delta) = 0;

private:

  Object dict;
};

//------------------------------------------------------------------------
// FilterStream
//
// This is the base class for all streams that filter another stream.
//------------------------------------------------------------------------

class FilterStream: public Stream {
public:

  FilterStream(Stream *strA);
  virtual ~FilterStream();
  virtual void close();
  virtual int getPos() { return str->getPos(); }
  virtual void setPos(Guint pos, int dir = 0);
  virtual BaseStream *getBaseStream() { return str->getBaseStream(); }
  virtual Stream *getUndecodedStream() { return str->getUndecodedStream(); }
  virtual Dict *getDict() { return str->getDict(); }
  virtual Stream *getNextStream() { return str; }

protected:

  Stream *str;
};

//------------------------------------------------------------------------
// ImageStream
//------------------------------------------------------------------------

class ImageStream {
public:

  // Create an image stream object for an image with the specified
  // parameters.  Note that these are the actual image parameters,
  // which may be different from the predictor parameters.
  ImageStream(Stream *strA, int widthA, int nCompsA, int nBitsA);

  ~ImageStream();

  // Reset the stream.
  void reset();

  // Gets the next pixel from the stream.  <pix> should be able to hold
  // at least nComps elements.  Returns false at end of file.
  GBool getPixel(Guchar *pix);

  // Returns a pointer to the next line of pixels.  Returns NULL at
  // end of file.
  Guchar *getLine();

  // Skip an entire line from the image.
  void skipLine();

private:

  Stream *str;			// base stream
  int width;			// pixels per line
  int nComps;			// components per pixel
  int nBits;			// bits per component
  int nVals;			// components per line
  Guchar *imgLine;		// line buffer
  int imgIdx;			// current index in imgLine
};

//------------------------------------------------------------------------
// StreamPredictor
//------------------------------------------------------------------------

class StreamPredictor {
public:

  // Create a predictor object.  Note that the parameters are for the
  // predictor, and may not match the actual image parameters.
  StreamPredictor(Stream *strA, int predictorA,
		  int widthA, int nCompsA, int nBitsA);

  ~StreamPredictor();

  GBool isOk() { return ok; }

  int lookChar();
  int getChar();

private:

  GBool getNextLine();

  Stream *str;			// base stream
  int predictor;		// predictor
  int width;			// pixels per line
  int nComps;			// components per pixel
  int nBits;			// bits per component
  int nVals;			// components per line
  int pixBytes;			// bytes per pixel
  int rowBytes;			// bytes per line
  Guchar *predLine;		// line buffer
  int predIdx;			// current index in predLine
  GBool ok;
};

//------------------------------------------------------------------------
// FileStream
//------------------------------------------------------------------------

#define fileStreamBufSize 256

class FileStream: public BaseStream {
public:

  FileStream(FILE *fA, Guint startA, GBool limitedA,
	     Guint lengthA, Object *dictA);
  virtual ~FileStream();
  virtual Stream *makeSubStream(Guint startA, GBool limitedA,
				Guint lengthA, Object *dictA);
  virtual StreamKind getKind() { return strFile; }
  virtual void reset();
  virtual void close();
  virtual int getChar()
    { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); }
  virtual int lookChar()
    { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); }
  virtual int getPos() { return bufPos + (bufPtr - buf); }
  virtual void setPos(Guint pos, int dir = 0);
  virtual Guint getStart() { return start; }
  virtual void moveStart(int delta);

private:

  GBool fillBuf();

  FILE *f;
  Guint start;
  GBool limited;
  Guint length;
  char buf[fileStreamBufSize];
  char *bufPtr;
  char *bufEnd;
  Guint bufPos;
  int savePos;
  GBool saved;
};

//------------------------------------------------------------------------
// MemStream
//------------------------------------------------------------------------

class MemStream: public BaseStream {
public:

  MemStream(char *bufA, Guint startA, Guint lengthA, Object *dictA);
  virtual ~MemStream();
  virtual Stream *makeSubStream(Guint start, GBool limited,
				Guint lengthA, Object *dictA);
  virtual StreamKind getKind() { return strWeird; }
  virtual void reset();
  virtual void close();
  virtual int getChar()
    { return (bufPtr < bufEnd) ? (*bufPtr++ & 0xff) : EOF; }
  virtual int lookChar()
    { return (bufPtr < bufEnd) ? (*bufPtr & 0xff) : EOF; }
  virtual int getPos() { return (int)(bufPtr - buf); }
  virtual void setPos(Guint pos, int dir = 0);
  virtual Guint getStart() { return start; }
  virtual void moveStart(int delta);

private:

  char *buf;
  Guint start;
  Guint length;
  char *bufEnd;
  char *bufPtr;
  GBool needFree;
};

//------------------------------------------------------------------------
// EmbedStream
//
// This is a special stream type used for embedded streams (inline
// images).  It reads directly from the base stream -- after the
// EmbedStream is deleted, reads from the base stream will proceed where
// the BaseStream left off.  Note that this is very different behavior
// that creating a new FileStream (using makeSubStream).
//------------------------------------------------------------------------

class EmbedStream: public BaseStream {
public:

  EmbedStream(Stream *strA, Object *dictA, GBool limitedA, Guint lengthA);
  virtual ~EmbedStream();
  virtual Stream *makeSubStream(Guint start, GBool limitedA,
				Guint lengthA, Object *dictA);
  virtual StreamKind getKind() { return str->getKind(); }
  virtual void reset() {}
  virtual int getChar();
  virtual int lookChar();
  virtual int getPos() { return str->getPos(); }
  virtual void setPos(Guint pos, int dir = 0);
  virtual Guint getStart();
  virtual void moveStart(int delta);

private:

  Stream *str;
  GBool limited;
  Guint length;
};

//------------------------------------------------------------------------
// ASCIIHexStream
//------------------------------------------------------------------------

class ASCIIHexStream: public FilterStream {
public:

  ASCIIHexStream(Stream *strA);
  virtual ~ASCIIHexStream();
  virtual StreamKind getKind() { return strASCIIHex; }
  virtual void reset();
  virtual int getChar()
    { int c = lookChar(); buf = EOF; return c; }
  virtual int lookChar();
  virtual GString *getPSFilter(int psLevel, char *indent);
  virtual GBool isBinary(GBool last = gTrue);

private:

  int buf;
  GBool eof;
};

//------------------------------------------------------------------------
// ASCII85Stream
//------------------------------------------------------------------------

class ASCII85Stream: public FilterStream {
public:

  ASCII85Stream(Stream *strA);
  virtual ~ASCII85Stream();
  virtual StreamKind getKind() { return strASCII85; }
  virtual void reset();
  virtual int getChar()
    { int ch = lookChar(); ++index; return ch; }
  virtual int lookChar();
  virtual GString *getPSFilter(int psLevel, char *indent);
  virtual GBool isBinary(GBool last = gTrue);

private:

  int c[5];
  int b[4];
  int index, n;
  GBool eof;
};

//------------------------------------------------------------------------
// LZWStream
//------------------------------------------------------------------------

class LZWStream: public FilterStream {
public:

  LZWStream(Stream *strA, int predictor, int columns, int colors,
	    int bits, int earlyA);
  virtual ~LZWStream();
  virtual StreamKind getKind() { return strLZW; }
  virtual void reset();
  virtual int getChar();
  virtual int lookChar();
  virtual int getRawChar();
  virtual GString *getPSFilter(int psLevel, char *indent);
  virtual GBool isBinary(GBool last = gTrue);

private:

  StreamPredictor *pred;	// predictor
  int early;			// early parameter
  GBool eof;			// true if at eof
  int inputBuf;			// input buffer
  int inputBits;		// number of bits in input buffer
  struct {			// decoding table
    int length;
    int head;
    Guchar tail;
  } table[4097];
  int nextCode;			// next code to be used
  int nextBits;			// number of bits in next code word
  int prevCode;			// previous code used in stream
  int newChar;			// next char to be added to table
  Guchar seqBuf[4097];		// buffer for current sequence
  int seqLength;		// length of current sequence
  int seqIndex;			// index into current sequence
  GBool first;			// first code after a table clear

  GBool processNextCode();
  void clearTable();
  int getCode();
};

//------------------------------------------------------------------------
// RunLengthStream
//------------------------------------------------------------------------

class RunLengthStream: public FilterStream {
public:

  RunLengthStream(Stream *strA);
  virtual ~RunLengthStream();
  virtual StreamKind getKind() { return strRunLength; }
  virtual void reset();
  virtual int getChar()
    { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); }
  virtual int lookChar()
    { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); }
  virtual GString *getPSFilter(int psLevel, char *indent);
  virtual GBool isBinary(GBool last = gTrue);

private:

  char buf[128];		// buffer
  char *bufPtr;			// next char to read
  char *bufEnd;			// end of buffer
  GBool eof;

  GBool fillBuf();
};

//------------------------------------------------------------------------
// CCITTFaxStream
//------------------------------------------------------------------------

struct CCITTCodeTable;

class CCITTFaxStream: public FilterStream {
public:

  CCITTFaxStream(Stream *strA, int encodingA, GBool endOfLineA,
		 GBool byteAlignA, int columnsA, int rowsA,
		 GBool endOfBlockA, GBool blackA);
  virtual ~CCITTFaxStream();
  virtual StreamKind getKind() { return strCCITTFax; }
  virtual void reset();
  virtual int getChar()
    { int c = lookChar(); buf = EOF; return c; }
  virtual int lookChar();
  virtual GString *getPSFilter(int psLevel, char *indent);
  virtual GBool isBinary(GBool last = gTrue);

private:

  int encoding;			// 'K' parameter
  GBool endOfLine;		// 'EndOfLine' parameter
  GBool byteAlign;		// 'EncodedByteAlign' parameter
  int columns;			// 'Columns' parameter
  int rows;			// 'Rows' parameter
  GBool endOfBlock;		// 'EndOfBlock' parameter
  GBool black;			// 'BlackIs1' parameter
  GBool eof;			// true if at eof
  GBool nextLine2D;		// true if next line uses 2D encoding
  int row;			// current row
  int inputBuf;			// input buffer
  int inputBits;		// number of bits in input buffer
  short *refLine;		// reference line changing elements
  int b1;			// index into refLine
  short *codingLine;		// coding line changing elements
  int a0;			// index into codingLine
  int outputBits;		// remaining ouput bits
  int buf;			// character buffer

  short getTwoDimCode();
  short getWhiteCode();
  short getBlackCode();
  short lookBits(int n);
  void eatBits(int n) { if ((inputBits -= n) < 0) inputBits = 0; }
};

//------------------------------------------------------------------------
// DCTStream
//------------------------------------------------------------------------

// DCT component info
struct DCTCompInfo {
  int id;			// component ID
  int hSample, vSample;		// horiz/vert sampling resolutions
  int quantTable;		// quantization table number
  int prevDC;			// DC coefficient accumulator
};

struct DCTScanInfo {
  GBool comp[4];		// comp[i] is set if component i is
				//   included in this scan
  int numComps;			// number of components in the scan
  int dcHuffTable[4];		// DC Huffman table numbers
  int acHuffTable[4];		// AC Huffman table numbers
  int firstCoeff, lastCoeff;	// first and last DCT coefficient
  int ah, al;			// successive approximation parameters
};

// DCT Huffman decoding table
struct DCTHuffTable {
  Guchar firstSym[17];		// first symbol for this bit length
  Gushort firstCode[17];	// first code for this bit length
  Gushort numCodes[17];		// number of codes of this bit length
  Guchar sym[256];		// symbols
};

class DCTStream: public FilterStream {
public:

  DCTStream(Stream *strA, int colorXformA);
  virtual ~DCTStream();
  virtual StreamKind getKind() { return strDCT; }
  virtual void reset();
  virtual void close();
  virtual int getChar();
  virtual int lookChar();
  virtual GString *getPSFilter(int psLevel, char *indent);
  virtual GBool isBinary(GBool last = gTrue);
  Stream *getRawStream() { return str; }

private:

  GBool progressive;		// set if in progressive mode
  GBool interleaved;		// set if in interleaved mode
  int width, height;		// image size
  int mcuWidth, mcuHeight;	// size of min coding unit, in data units
  int bufWidth, bufHeight;	// frameBuf size
  DCTCompInfo compInfo[4];	// info for each component
  DCTScanInfo scanInfo;		// info for the current scan
  int numComps;			// number of components in image
  int colorXform;		// color transform: -1 = unspecified
				//                   0 = none
				//                   1 = YUV/YUVK -> RGB/CMYK
  GBool gotJFIFMarker;		// set if APP0 JFIF marker was present
  GBool gotAdobeMarker;		// set if APP14 Adobe marker was present
  int restartInterval;		// restart interval, in MCUs
  Gushort quantTables[4][64];	// quantization tables
  int numQuantTables;		// number of quantization tables
  DCTHuffTable dcHuffTables[4];	// DC Huffman tables
  DCTHuffTable acHuffTables[4];	// AC Huffman tables
  int numDCHuffTables;		// number of DC Huffman tables
  int numACHuffTables;		// number of AC Huffman tables
  Guchar *rowBuf[4][32];	// buffer for one MCU (non-progressive mode)
  int *frameBuf[4];		// buffer for frame (progressive mode)
  int comp, x, y, dy;		// current position within image/MCU
  int restartCtr;		// MCUs left until restart
  int restartMarker;		// next restart marker
  int eobRun;			// number of EOBs left in the current run
  int inputBuf;			// input buffer for variable length codes
  int inputBits;		// number of valid bits in input buffer

  void restart();
  GBool readMCURow();
  void readScan();
  GBool readDataUnit(DCTHuffTable *dcHuffTable,
		     DCTHuffTable *acHuffTable,
		     int *prevDC, int data[64]);
  GBool readProgressiveDataUnit(DCTHuffTable *dcHuffTable,
				DCTHuffTable *acHuffTable,
				int *prevDC, int data[64]);
  void decodeImage();
  void transformDataUnit(Gushort *quantTable,
			 int dataIn[64], Guchar dataOut[64]);
  int readHuffSym(DCTHuffTable *table);
  int readAmp(int size);
  int readBit();
  GBool readHeader();
  GBool readBaselineSOF();
  GBool readProgressiveSOF();
  GBool readScanInfo();
  GBool readQuantTables();
  GBool readHuffmanTables();
  GBool readRestartInterval();
  GBool readJFIFMarker();
  GBool readAdobeMarker();
  GBool readTrailer();
  int readMarker();
  int read16();
};

//------------------------------------------------------------------------
// FlateStream
//------------------------------------------------------------------------

#define flateWindow          32768    // buffer size
#define flateMask            (flateWindow-1)
#define flateMaxHuffman         15    // max Huffman code length
#define flateMaxCodeLenCodes    19    // max # code length codes
#define flateMaxLitCodes       288    // max # literal codes
#define flateMaxDistCodes       30    // max # distance codes

// Huffman code table entry
struct FlateCode {
  Gushort len;			// code length, in bits
  Gushort val;			// value represented by this code
};

struct FlateHuffmanTab {
  FlateCode *codes;
  int maxLen;
};

// Decoding info for length and distance code words
struct FlateDecode {
  int bits;			// # extra bits
  int first;			// first length/distance
};

class FlateStream: public FilterStream {
public:

  FlateStream(Stream *strA, int predictor, int columns,
	      int colors, int bits);
  virtual ~FlateStream();
  virtual StreamKind getKind() { return strFlate; }
  virtual void reset();
  virtual int getChar();
  virtual int lookChar();
  virtual int getRawChar();
  virtual GString *getPSFilter(int psLevel, char *indent);
  virtual GBool isBinary(GBool last = gTrue);

private:

  StreamPredictor *pred;	// predictor
  Guchar buf[flateWindow];	// output data buffer
  int index;			// current index into output buffer
  int remain;			// number valid bytes in output buffer
  int codeBuf;			// input buffer
  int codeSize;			// number of bits in input buffer
  int				// literal and distance code lengths
    codeLengths[flateMaxLitCodes + flateMaxDistCodes];
  FlateHuffmanTab litCodeTab;	// literal code table
  FlateHuffmanTab distCodeTab;	// distance code table
  GBool compressedBlock;	// set if reading a compressed block
  int blockLen;			// remaining length of uncompressed block
  GBool endOfBlock;		// set when end of block is reached
  GBool eof;			// set when end of stream is reached

  static int			// code length code reordering
    codeLenCodeMap[flateMaxCodeLenCodes];
  static FlateDecode		// length decoding info
    lengthDecode[flateMaxLitCodes-257];
  static FlateDecode		// distance decoding info
    distDecode[flateMaxDistCodes];
  static FlateHuffmanTab	// fixed literal code table
    fixedLitCodeTab;
  static FlateHuffmanTab	// fixed distance code table
    fixedDistCodeTab;

  void readSome();
  GBool startBlock();
  void loadFixedCodes();
  GBool readDynamicCodes();
  void compHuffmanCodes(int *lengths, int n, FlateHuffmanTab *tab);
  int getHuffmanCodeWord(FlateHuffmanTab *tab);
  int getCodeWord(int bits);
};

//------------------------------------------------------------------------
// EOFStream
//------------------------------------------------------------------------

class EOFStream: public FilterStream {
public:

  EOFStream(Stream *strA);
  virtual ~EOFStream();
  virtual StreamKind getKind() { return strWeird; }
  virtual void reset() {}
  virtual int getChar() { return EOF; }
  virtual int lookChar() { return EOF; }
  virtual GString *getPSFilter(int psLevel, char *indent)  { return NULL; }
  virtual GBool isBinary(GBool last = gTrue) { return gFalse; }
};

//------------------------------------------------------------------------
// FixedLengthEncoder
//------------------------------------------------------------------------

class FixedLengthEncoder: public FilterStream {
public:

  FixedLengthEncoder(Stream *strA, int lengthA);
  ~FixedLengthEncoder();
  virtual StreamKind getKind() { return strWeird; }
  virtual void reset();
  virtual int getChar();
  virtual int lookChar();
  virtual GString *getPSFilter(int psLevel, char *indent) { return NULL; }
  virtual GBool isBinary(GBool last = gTrue);
  virtual GBool isEncoder() { return gTrue; }

private:

  int length;
  int count;
};

//------------------------------------------------------------------------
// ASCIIHexEncoder
//------------------------------------------------------------------------

class ASCIIHexEncoder: public FilterStream {
public:

  ASCIIHexEncoder(Stream *strA);
  virtual ~ASCIIHexEncoder();
  virtual StreamKind getKind() { return strWeird; }
  virtual void reset();
  virtual int getChar()
    { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); }
  virtual int lookChar()
    { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); }
  virtual GString *getPSFilter(int psLevel, char *indent) { return NULL; }
  virtual GBool isBinary(GBool last = gTrue) { return gFalse; }
  virtual GBool isEncoder() { return gTrue; }

private:

  char buf[4];
  char *bufPtr;
  char *bufEnd;
  int lineLen;
  GBool eof;

  GBool fillBuf();
};

//------------------------------------------------------------------------
// ASCII85Encoder
//------------------------------------------------------------------------

class ASCII85Encoder: public FilterStream {
public:

  ASCII85Encoder(Stream *strA);
  virtual ~ASCII85Encoder();
  virtual StreamKind getKind() { return strWeird; }
  virtual void reset();
  virtual int getChar()
    { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); }
  virtual int lookChar()
    { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); }
  virtual GString *getPSFilter(int psLevel, char *indent) { return NULL; }
  virtual GBool isBinary(GBool last = gTrue) { return gFalse; }
  virtual GBool isEncoder() { return gTrue; }

private:

  char buf[8];
  char *bufPtr;
  char *bufEnd;
  int lineLen;
  GBool eof;

  GBool fillBuf();
};

//------------------------------------------------------------------------
// RunLengthEncoder
//------------------------------------------------------------------------

class RunLengthEncoder: public FilterStream {
public:

  RunLengthEncoder(Stream *strA);
  virtual ~RunLengthEncoder();
  virtual StreamKind getKind() { return strWeird; }
  virtual void reset();
  virtual int getChar()
    { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); }
  virtual int lookChar()
    { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); }
  virtual GString *getPSFilter(int psLevel, char *indent) { return NULL; }
  virtual GBool isBinary(GBool last = gTrue) { return gTrue; }
  virtual GBool isEncoder() { return gTrue; }

private:

  char buf[131];
  char *bufPtr;
  char *bufEnd;
  char *nextEnd;
  GBool eof;

  GBool fillBuf();
};

#endif
//========================================================================
//
// TextOutputDev.cc
//
// Copyright 1997-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <math.h>
#include <ctype.h>
#ifdef WIN32
#include <fcntl.h> // for O_BINARY
#include <io.h>    // for setmode
#endif
#include "gmem.h"
#include "GString.h"
#include "GList.h"
#include "config.h"
#include "Error.h"
#include "GlobalParams.h"
#include "UnicodeMap.h"
#include "UnicodeTypeTable.h"
#include "GfxState.h"
#include "Link.h"
#include "TextOutputDev.h"

#ifdef MACOS
// needed for setting type/creator of MacOS files
#include "ICSupport.h"
#endif

//------------------------------------------------------------------------
// parameters
//------------------------------------------------------------------------

// Each bucket in a text pool includes baselines within a range of
// this many points.
#define textPoolStep 4

// Inter-character space width which will cause addChar to start a new
// word.
#define minWordBreakSpace 0.1

// Negative inter-character space width, i.e., overlap, which will
// cause addChar to start a new word.
#define minDupBreakOverlap 0.2

// Max distance between baselines of two lines within a block, as a
// fraction of the font size.
#define maxLineSpacingDelta 1.5

// Max difference in primary font sizes on two lines in the same
// block.  Delta1 is used when examining new lines above and below the
// current block; delta2 is used when examining text that overlaps the
// current block; delta3 is used when examining text to the left and
// right of the current block.
#define maxBlockFontSizeDelta1 0.05
#define maxBlockFontSizeDelta2 0.6
#define maxBlockFontSizeDelta3 0.2

// Max difference in font sizes inside a word.
#define maxWordFontSizeDelta 0.05

// Maximum distance between baselines of two words on the same line,
// e.g., distance between subscript or superscript and the primary
// baseline, as a fraction of the font size.
#define maxIntraLineDelta 0.5

// Minimum inter-word spacing, as a fraction of the font size.  (Only
// used for raw ordering.)
#define minWordSpacing 0.15

// Maximum inter-word spacing, as a fraction of the font size.
#define maxWordSpacing 1.5

// Maximum horizontal spacing which will allow a word to be pulled
// into a block.
#define minColSpacing1 0.3

// Minimum spacing between columns, as a fraction of the font size.
#define minColSpacing2 1.0

// Maximum vertical spacing between blocks within a flow, as a
// multiple of the font size.
#define maxBlockSpacing 2.5

// Minimum spacing between characters within a word, as a fraction of
// the font size.
#define minCharSpacing -0.2

// Maximum spacing between characters within a word, as a fraction of
// the font size, when there is no obvious extra-wide character
// spacing.
#define maxCharSpacing 0.03

// When extra-wide character spacing is detected, the inter-character
// space threshold is set to the minimum inter-character space
// multiplied by this constant.
#define maxWideCharSpacingMul 1.3

// Upper limit on spacing between characters in a word.
#define maxWideCharSpacing 0.4

// Max difference in primary,secondary coordinates (as a fraction of
// the font size) allowed for duplicated text (fake boldface, drop
// shadows) which is to be discarded.
#define dupMaxPriDelta 0.1
#define dupMaxSecDelta 0.2

// Max width of underlines (in points).
#define maxUnderlineWidth 3

// Min distance between baseline and underline (in points).
//~ this should be font-size-dependent
#define minUnderlineGap -2

// Max distance between baseline and underline (in points).
//~ this should be font-size-dependent
#define maxUnderlineGap 4

// Max horizontal distance between edge of word and start of underline
// (in points).
//~ this should be font-size-dependent
#define underlineSlack 1

// Max distance between edge of text and edge of link border
#define hyperlinkSlack 2

//------------------------------------------------------------------------
// TextUnderline
//------------------------------------------------------------------------

class TextUnderline {
public:

  TextUnderline(double x0A, double y0A, double x1A, double y1A)
    { x0 = x0A; y0 = y0A; x1 = x1A; y1 = y1A; horiz = y0 == y1; }
  ~TextUnderline() {}

  double x0, y0, x1, y1;
  GBool horiz;
};

//------------------------------------------------------------------------
// TextLink
//------------------------------------------------------------------------

class TextLink {
public:

  TextLink(int xMinA, int yMinA, int xMaxA, int yMaxA, Link *linkA)
    { xMin = xMinA; yMin = yMinA; xMax = xMaxA; yMax = yMaxA; link = linkA; }
  ~TextLink() {}

  int xMin, yMin, xMax, yMax;
  Link *link;
};

//------------------------------------------------------------------------
// TextFontInfo
//------------------------------------------------------------------------

TextFontInfo::TextFontInfo(GfxState *state) {
  gfxFont = state->getFont();
#if TEXTOUT_WORD_LIST
  fontName = (gfxFont && gfxFont->getOrigName())
                 ? gfxFont->getOrigName()->copy()
                 : (GString *)NULL;
  flags = gfxFont ? gfxFont->getFlags() : 0;
#endif
}

TextFontInfo::~TextFontInfo() {
#if TEXTOUT_WORD_LIST
  if (fontName) {
    delete fontName;
  }
#endif
}

GBool TextFontInfo::matches(GfxState *state) {
  return state->getFont() == gfxFont;
}

//------------------------------------------------------------------------
// TextWord
//------------------------------------------------------------------------

TextWord::TextWord(GfxState *state, int rotA, double x0, double y0,
		   int charPosA, TextFontInfo *fontA, double fontSizeA) {
  GfxFont *gfxFont;
  double x, y, ascent, descent;

  rot = rotA;
  charPos = charPosA;
  charLen = 0;
  font = fontA;
  fontSize = fontSizeA;
  state->transform(x0, y0, &x, &y);
  if ((gfxFont = font->gfxFont)) {
    ascent = gfxFont->getAscent() * fontSize;
    descent = gfxFont->getDescent() * fontSize;
  } else {
    // this means that the PDF file draws text without a current font,
    // which should never happen
    ascent = 0.95 * fontSize;
    descent = -0.35 * fontSize;
  }
  switch (rot) {
  case 0:
    yMin = y - ascent;
    yMax = y - descent;
    if (yMin == yMax) {
      // this is a sanity check for a case that shouldn't happen -- but
      // if it does happen, we want to avoid dividing by zero later
      yMin = y;
      yMax = y + 1;
    }
    base = y;
    break;
  case 1:
    xMin = x + descent;
    xMax = x + ascent;
    if (xMin == xMax) {
      // this is a sanity check for a case that shouldn't happen -- but
      // if it does happen, we want to avoid dividing by zero later
      xMin = x;
      xMax = x + 1;
    }
    base = x;
    break;
  case 2:
    yMin = y + descent;
    yMax = y + ascent;
    if (yMin == yMax) {
      // this is a sanity check for a case that shouldn't happen -- but
      // if it does happen, we want to avoid dividing by zero later
      yMin = y;
      yMax = y + 1;
    }
    base = y;
    break;
  case 3:
    xMin = x - ascent;
    xMax = x - descent;
    if (xMin == xMax) {
      // this is a sanity check for a case that shouldn't happen -- but
      // if it does happen, we want to avoid dividing by zero later
      xMin = x;
      xMax = x + 1;
    }
    base = x;
    break;
  }
  text = NULL;
  edge = NULL;
  len = size = 0;
  spaceAfter = gFalse;
  next = NULL;

#if TEXTOUT_WORD_LIST
  GfxRGB rgb;

  if ((state->getRender() & 3) == 1) {
    state->getStrokeRGB(&rgb);
  } else {
    state->getFillRGB(&rgb);
  }
  colorR = colToDbl(rgb.r);
  colorG = colToDbl(rgb.g);
  colorB = colToDbl(rgb.b);
#endif

  underlined = gFalse;
  link = NULL;
}

TextWord::~TextWord() {
  gfree(text);
  gfree(edge);
}

void TextWord::addChar(GfxState *state, double x, double y,
		       double dx, double dy, Unicode u) {
  if (len == size) {
    size += 16;
    text = (Unicode *)greallocn(text, size, sizeof(Unicode));
    edge = (double *)greallocn(edge, size + 1, sizeof(double));
  }
  text[len] = u;
  switch (rot) {
  case 0:
    if (len == 0) {
      xMin = x;
    }
    edge[len] = x;
    xMax = edge[len+1] = x + dx;
    break;
  case 1:
    if (len == 0) {
      yMin = y;
    }
    edge[len] = y;
    yMax = edge[len+1] = y + dy;
    break;
  case 2:
    if (len == 0) {
      xMax = x;
    }
    edge[len] = x;
    xMin = edge[len+1] = x + dx;
    break;
  case 3:
    if (len == 0) {
      yMax = y;
    }
    edge[len] = y;
    yMin = edge[len+1] = y + dy;
    break;
  }
  ++len;
}

void TextWord::merge(TextWord *word) {
  int i;

  if (word->xMin < xMin) {
    xMin = word->xMin;
  }
  if (word->yMin < yMin) {
    yMin = word->yMin;
  }
  if (word->xMax > xMax) {
    xMax = word->xMax;
  }
  if (word->yMax > yMax) {
    yMax = word->yMax;
  }
  if (len + word->len > size) {
    size = len + word->len;
    text = (Unicode *)greallocn(text, size, sizeof(Unicode));
    edge = (double *)greallocn(edge, size + 1, sizeof(double));
  }
  for (i = 0; i < word->len; ++i) {
    text[len + i] = word->text[i];
    edge[len + i] = word->edge[i];
  }
  edge[len + word->len] = word->edge[word->len];
  len += word->len;
  charLen += word->charLen;
}

inline int TextWord::primaryCmp(TextWord *word) {
  double cmp;

  cmp = 0; // make gcc happy
  switch (rot) {
  case 0:
    cmp = xMin - word->xMin;
    break;
  case 1:
    cmp = yMin - word->yMin;
    break;
  case 2:
    cmp = word->xMax - xMax;
    break;
  case 3:
    cmp = word->yMax - yMax;
    break;
  }
  return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
}

double TextWord::primaryDelta(TextWord *word) {
  double delta;

  delta = 0; // make gcc happy
  switch (rot) {
  case 0:
    delta = word->xMin - xMax;
    break;
  case 1:
    delta = word->yMin - yMax;
    break;
  case 2:
    delta = xMin - word->xMax;
    break;
  case 3:
    delta = yMin - word->yMax;
    break;
  }
  return delta;
}

int TextWord::cmpYX(const void *p1, const void *p2) {
  TextWord *word1 = *(TextWord **)p1;
  TextWord *word2 = *(TextWord **)p2;
  double cmp;

  cmp = word1->yMin - word2->yMin;
  if (cmp == 0) {
    cmp = word1->xMin - word2->xMin;
  }
  return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
}

#if TEXTOUT_WORD_LIST

GString *TextWord::getText() {
  GString *s;
  UnicodeMap *uMap;
  char buf[8];
  int n, i;

  s = new GString();
  if (!(uMap = globalParams->getTextEncoding())) {
    return s;
  }
  for (i = 0; i < len; ++i) {
    n = uMap->mapUnicode(text[i], buf, sizeof(buf));
    s->append(buf, n);
  }
  uMap->decRefCnt();
  return s;
}

void TextWord::getCharBBox(int charIdx, double *xMinA, double *yMinA,
			   double *xMaxA, double *yMaxA) {
  if (charIdx < 0 || charIdx >= len) {
    return;
  }
  switch (rot) {
  case 0:
    *xMinA = edge[charIdx];
    *xMaxA = edge[charIdx + 1];
    *yMinA = yMin;
    *yMaxA = yMax;
    break;
  case 1:
    *xMinA = xMin;
    *xMaxA = xMax;
    *yMinA = edge[charIdx];
    *yMaxA = edge[charIdx + 1];
    break;
  case 2:
    *xMinA = edge[charIdx + 1];
    *xMaxA = edge[charIdx];
    *yMinA = yMin;
    *yMaxA = yMax;
    break;
  case 3:
    *xMinA = xMin;
    *xMaxA = xMax;
    *yMinA = edge[charIdx + 1];
    *yMaxA = edge[charIdx];
    break;
  }
}

#endif // TEXTOUT_WORD_LIST

//------------------------------------------------------------------------
// TextPool
//------------------------------------------------------------------------

TextPool::TextPool() {
  minBaseIdx = 0;
  maxBaseIdx = -1;
  pool = NULL;
  cursor = NULL;
  cursorBaseIdx = -1;
}

TextPool::~TextPool() {
  int baseIdx;
  TextWord *word, *word2;

  for (baseIdx = minBaseIdx; baseIdx <= maxBaseIdx; ++baseIdx) {
    for (word = pool[baseIdx - minBaseIdx]; word; word = word2) {
      word2 = word->next;
      delete word;
    }
  }
  gfree(pool);
}

int TextPool::getBaseIdx(double base) {
  int baseIdx;

  baseIdx = (int)(base / textPoolStep);
  if (baseIdx < minBaseIdx) {
    return minBaseIdx;
  }
  if (baseIdx > maxBaseIdx) {
    return maxBaseIdx;
  }
  return baseIdx;
}

void TextPool::addWord(TextWord *word) {
  TextWord **newPool;
  int wordBaseIdx, newMinBaseIdx, newMaxBaseIdx, baseIdx;
  TextWord *w0, *w1;

  // expand the array if needed
  wordBaseIdx = (int)(word->base / textPoolStep);
  if (minBaseIdx > maxBaseIdx) {
    minBaseIdx = wordBaseIdx - 128;
    maxBaseIdx = wordBaseIdx + 128;
    pool = (TextWord **)gmallocn(maxBaseIdx - minBaseIdx + 1,
				 sizeof(TextWord *));
    for (baseIdx = minBaseIdx; baseIdx <= maxBaseIdx; ++baseIdx) {
      pool[baseIdx - minBaseIdx] = NULL;
    }
  } else if (wordBaseIdx < minBaseIdx) {
    newMinBaseIdx = wordBaseIdx - 128;
    newPool = (TextWord **)gmallocn(maxBaseIdx - newMinBaseIdx + 1,
				    sizeof(TextWord *));
    for (baseIdx = newMinBaseIdx; baseIdx < minBaseIdx; ++baseIdx) {
      newPool[baseIdx - newMinBaseIdx] = NULL;
    }
    memcpy(&newPool[minBaseIdx - newMinBaseIdx], pool,
	   (maxBaseIdx - minBaseIdx + 1) * sizeof(TextWord *));
    gfree(pool);
    pool = newPool;
    minBaseIdx = newMinBaseIdx;
  } else if (wordBaseIdx > maxBaseIdx) {
    newMaxBaseIdx = wordBaseIdx + 128;
    pool = (TextWord **)greallocn(pool, newMaxBaseIdx - minBaseIdx + 1,
				  sizeof(TextWord *));
    for (baseIdx = maxBaseIdx + 1; baseIdx <= newMaxBaseIdx; ++baseIdx) {
      pool[baseIdx - minBaseIdx] = NULL;
    }
    maxBaseIdx = newMaxBaseIdx;
  }

  // insert the new word
  if (cursor && wordBaseIdx == cursorBaseIdx &&
      word->primaryCmp(cursor) > 0) {
    w0 = cursor;
    w1 = cursor->next;
  } else {
    w0 = NULL;
    w1 = pool[wordBaseIdx - minBaseIdx];
  }
  for (; w1 && word->primaryCmp(w1) > 0; w0 = w1, w1 = w1->next) ;
  word->next = w1;
  if (w0) {
    w0->next = word;
  } else {
    pool[wordBaseIdx - minBaseIdx] = word;
  }
  cursor = word;
  cursorBaseIdx = wordBaseIdx;
}

//------------------------------------------------------------------------
// TextLine
//------------------------------------------------------------------------

TextLine::TextLine(TextBlock *blkA, int rotA, double baseA) {
  blk = blkA;
  rot = rotA;
  xMin = yMin = 0;
  xMax = yMax = -1;
  base = baseA;
  words = lastWord = NULL;
  text = NULL;
  edge = NULL;
  col = NULL;
  len = 0;
  convertedLen = 0;
  hyphenated = gFalse;
  next = NULL;
}

TextLine::~TextLine() {
  TextWord *word;

  while (words) {
    word = words;
    words = words->next;
    delete word;
  }
  gfree(text);
  gfree(edge);
  gfree(col);
}

void TextLine::addWord(TextWord *word) {
  if (lastWord) {
    lastWord->next = word;
  } else {
    words = word;
  }
  lastWord = word;

  if (xMin > xMax) {
    xMin = word->xMin;
    xMax = word->xMax;
    yMin = word->yMin;
    yMax = word->yMax;
  } else {
    if (word->xMin < xMin) {
      xMin = word->xMin;
    }
    if (word->xMax > xMax) {
      xMax = word->xMax;
    }
    if (word->yMin < yMin) {
      yMin = word->yMin;
    }
    if (word->yMax > yMax) {
      yMax = word->yMax;
    }
  }
}

double TextLine::primaryDelta(TextLine *line) {
  double delta;

  delta = 0; // make gcc happy
  switch (rot) {
  case 0:
    delta = line->xMin - xMax;
    break;
  case 1:
    delta = line->yMin - yMax;
    break;
  case 2:
    delta = xMin - line->xMax;
    break;
  case 3:
    delta = yMin - line->yMax;
    break;
  }
  return delta;
}

int TextLine::primaryCmp(TextLine *line) {
  double cmp;

  cmp = 0; // make gcc happy
  switch (rot) {
  case 0:
    cmp = xMin - line->xMin;
    break;
  case 1:
    cmp = yMin - line->yMin;
    break;
  case 2:
    cmp = line->xMax - xMax;
    break;
  case 3:
    cmp = line->yMax - yMax;
    break;
  }
  return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
}

int TextLine::secondaryCmp(TextLine *line) {
  double cmp;

  cmp = (rot == 0 || rot == 3) ? base - line->base : line->base - base;
  return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
}

int TextLine::cmpYX(TextLine *line) {
  int cmp;

  if ((cmp = secondaryCmp(line))) {
    return cmp;
  }
  return primaryCmp(line);
}

int TextLine::cmpXY(const void *p1, const void *p2) {
  TextLine *line1 = *(TextLine **)p1;
  TextLine *line2 = *(TextLine **)p2;
  int cmp;

  if ((cmp = line1->primaryCmp(line2))) {
    return cmp;
  }
  return line1->secondaryCmp(line2);
}

void TextLine::coalesce(UnicodeMap *uMap) {
  TextWord *word0, *word1;
  double space, delta, minSpace;
  GBool isUnicode;
  char buf[8];
  int i, j;

  if (words->next) {

    // compute the inter-word space threshold
    if (words->len > 1 || words->next->len > 1) {
      minSpace = 0;
    } else {
      minSpace = words->primaryDelta(words->next);
      for (word0 = words->next, word1 = word0->next;
	   word1 && minSpace > 0;
	   word0 = word1, word1 = word0->next) {
	if (word1->len > 1) {
	  minSpace = 0;
	}
	delta = word0->primaryDelta(word1);
	if (delta < minSpace) {
	  minSpace = delta;
	}
      }
    }
    if (minSpace <= 0) {
      space = maxCharSpacing * words->fontSize;
    } else {
      space = maxWideCharSpacingMul * minSpace;
      if (space > maxWideCharSpacing * words->fontSize) {
	space = maxWideCharSpacing * words->fontSize;
      }
    }

    // merge words
    word0 = words;
    word1 = words->next;
    while (word1) {
      if (word0->primaryDelta(word1) >= space) {
	word0->spaceAfter = gTrue;
	word0 = word1;
	word1 = word1->next;
      } else if (word0->font == word1->font &&
		 word0->underlined == word1->underlined &&
		 fabs(word0->fontSize - word1->fontSize) <
		   maxWordFontSizeDelta * words->fontSize &&
		 word1->charPos == word0->charPos + word0->charLen) {
	word0->merge(word1);
	word0->next = word1->next;
	delete word1;
	word1 = word0->next;
      } else {
	word0 = word1;
	word1 = word1->next;
      }
    }
  }

  // build the line text
  isUnicode = uMap ? uMap->isUnicode() : gFalse;
  len = 0;
  for (word1 = words; word1; word1 = word1->next) {
    len += word1->len;
    if (word1->spaceAfter) {
      ++len;
    }
  }
  text = (Unicode *)gmallocn(len, sizeof(Unicode));
  edge = (double *)gmallocn(len + 1, sizeof(double));
  i = 0;
  for (word1 = words; word1; word1 = word1->next) {
    for (j = 0; j < word1->len; ++j) {
      text[i] = word1->text[j];
      edge[i] = word1->edge[j];
      ++i;
    }
    edge[i] = word1->edge[word1->len];
    if (word1->spaceAfter) {
      text[i] = (Unicode)0x0020;
      ++i;
    }
  }

  // compute convertedLen and set up the col array
  col = (int *)gmallocn(len + 1, sizeof(int));
  convertedLen = 0;
  for (i = 0; i < len; ++i) {
    col[i] = convertedLen;
    if (isUnicode) {
      ++convertedLen;
    } else if (uMap) {
      convertedLen += uMap->mapUnicode(text[i], buf, sizeof(buf));
    }
  }
  col[len] = convertedLen;

  // check for hyphen at end of line
  //~ need to check for other chars used as hyphens
  hyphenated = text[len - 1] == (Unicode)'-';
}

//------------------------------------------------------------------------
// TextLineFrag
//------------------------------------------------------------------------

class TextLineFrag {
public:

  TextLine *line;		// the line object
  int start, len;		// offset and length of this fragment
				//   (in Unicode chars)
  double xMin, xMax;		// bounding box coordinates
  double yMin, yMax;
  double base;			// baseline virtual coordinate
  int col;			// first column

  void init(TextLine *lineA, int startA, int lenA);
  void computeCoords(GBool oneRot);

  static int cmpYXPrimaryRot(const void *p1, const void *p2);
  static int cmpYXLineRot(const void *p1, const void *p2);
  static int cmpXYLineRot(const void *p1, const void *p2);
  static int cmpXYColumnPrimaryRot(const void *p1, const void *p2);
  static int cmpXYColumnLineRot(const void *p1, const void *p2);
};

void TextLineFrag::init(TextLine *lineA, int startA, int lenA) {
  line = lineA;
  start = startA;
  len = lenA;
  col = line->col[start];
}

void TextLineFrag::computeCoords(GBool oneRot) {
  TextBlock *blk;
  double d0, d1, d2, d3, d4;

  if (oneRot) {

    switch (line->rot) {
    case 0:
      xMin = line->edge[start];
      xMax = line->edge[start + len];
      yMin = line->yMin;
      yMax = line->yMax;
      break;
    case 1:
      xMin = line->xMin;
      xMax = line->xMax;
      yMin = line->edge[start];
      yMax = line->edge[start + len];
      break;
    case 2:
      xMin = line->edge[start + len];
      xMax = line->edge[start];
      yMin = line->yMin;
      yMax = line->yMax;
      break;
    case 3:
      xMin = line->xMin;
      xMax = line->xMax;
      yMin = line->edge[start + len];
      yMax = line->edge[start];
      break;
    }
    base = line->base;

  } else {

    if (line->rot == 0 && line->blk->page->primaryRot == 0) {

      xMin = line->edge[start];
      xMax = line->edge[start + len];
      yMin = line->yMin;
      yMax = line->yMax;
      base = line->base;

    } else {

      blk = line->blk;
      d0 = line->edge[start];
      d1 = line->edge[start + len];
      d2 = d3 = d4 = 0; // make gcc happy

      switch (line->rot) {
      case 0:
	d2 = line->yMin;
	d3 = line->yMax;
	d4 = line->base;
	d0 = (d0 - blk->xMin) / (blk->xMax - blk->xMin);
	d1 = (d1 - blk->xMin) / (blk->xMax - blk->xMin);
	d2 = (d2 - blk->yMin) / (blk->yMax - blk->yMin);
	d3 = (d3 - blk->yMin) / (blk->yMax - blk->yMin);
	d4 = (d4 - blk->yMin) / (blk->yMax - blk->yMin);
	break;
      case 1:
	d2 = line->xMax;
	d3 = line->xMin;
	d4 = line->base;
	d0 = (d0 - blk->yMin) / (blk->yMax - blk->yMin);
	d1 = (d1 - blk->yMin) / (blk->yMax - blk->yMin);
	d2 = (blk->xMax - d2) / (blk->xMax - blk->xMin);
	d3 = (blk->xMax - d3) / (blk->xMax - blk->xMin);
	d4 = (blk->xMax - d4) / (blk->xMax - blk->xMin);
	break;
      case 2:
	d2 = line->yMax;
	d3 = line->yMin;
	d4 = line->base;
	d0 = (blk->xMax - d0) / (blk->xMax - blk->xMin);
	d1 = (blk->xMax - d1) / (blk->xMax - blk->xMin);
	d2 = (blk->yMax - d2) / (blk->yMax - blk->yMin);
	d3 = (blk->yMax - d3) / (blk->yMax - blk->yMin);
	d4 = (blk->yMax - d4) / (blk->yMax - blk->yMin);
	break;
      case 3:
	d2 = line->xMin;
	d3 = line->xMax;
	d4 = line->base;
	d0 = (blk->yMax - d0) / (blk->yMax - blk->yMin);
	d1 = (blk->yMax - d1) / (blk->yMax - blk->yMin);
	d2 = (d2 - blk->xMin) / (blk->xMax - blk->xMin);
	d3 = (d3 - blk->xMin) / (blk->xMax - blk->xMin);
	d4 = (d4 - blk->xMin) / (blk->xMax - blk->xMin);
	break;
      }

      switch (line->blk->page->primaryRot) {
      case 0:
	xMin = blk->xMin + d0 * (blk->xMax - blk->xMin);
	xMax = blk->xMin + d1 * (blk->xMax - blk->xMin);
	yMin = blk->yMin + d2 * (blk->yMax - blk->yMin);
	yMax = blk->yMin + d3 * (blk->yMax - blk->yMin);
	base = blk->yMin + base * (blk->yMax - blk->yMin);
	break;
      case 1:
	xMin = blk->xMax - d3 * (blk->xMax - blk->xMin);
	xMax = blk->xMax - d2 * (blk->xMax - blk->xMin);
	yMin = blk->yMin + d0 * (blk->yMax - blk->yMin);
	yMax = blk->yMin + d1 * (blk->yMax - blk->yMin);
	base = blk->xMax - d4 * (blk->xMax - blk->xMin);
	break;
      case 2:
	xMin = blk->xMax - d1 * (blk->xMax - blk->xMin);
	xMax = blk->xMax - d0 * (blk->xMax - blk->xMin);
	yMin = blk->yMax - d3 * (blk->yMax - blk->yMin);
	yMax = blk->yMax - d2 * (blk->yMax - blk->yMin);
	base = blk->yMax - d4 * (blk->yMax - blk->yMin);
	break;
      case 3:
	xMin = blk->xMin + d2 * (blk->xMax - blk->xMin);
	xMax = blk->xMin + d3 * (blk->xMax - blk->xMin);
	yMin = blk->yMax - d1 * (blk->yMax - blk->yMin);
	yMax = blk->yMax - d0 * (blk->yMax - blk->yMin);
	base = blk->xMin + d4 * (blk->xMax - blk->xMin);
	break;
      }

    }
  }
}

int TextLineFrag::cmpYXPrimaryRot(const void *p1, const void *p2) {
  TextLineFrag *frag1 = (TextLineFrag *)p1;
  TextLineFrag *frag2 = (TextLineFrag *)p2;
  double cmp;

  cmp = 0; // make gcc happy
  switch (frag1->line->blk->page->primaryRot) {
  case 0:
    if (fabs(cmp = frag1->yMin - frag2->yMin) < 0.01) {
      cmp = frag1->xMin - frag2->xMin;
    }
    break;
  case 1:
    if (fabs(cmp = frag2->xMax - frag1->xMax) < 0.01) {
      cmp = frag1->yMin - frag2->yMin;
    }
    break;
  case 2:
    if (fabs(cmp = frag2->yMin - frag1->yMin) < 0.01) {
      cmp = frag2->xMax - frag1->xMax;
    }
    break;
  case 3:
    if (fabs(cmp = frag1->xMax - frag2->xMax) < 0.01) {
      cmp = frag2->yMax - frag1->yMax;
    }
    break;
  }
  return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
}

int TextLineFrag::cmpYXLineRot(const void *p1, const void *p2) {
  TextLineFrag *frag1 = (TextLineFrag *)p1;
  TextLineFrag *frag2 = (TextLineFrag *)p2;
  double cmp;

  cmp = 0; // make gcc happy
  switch (frag1->line->rot) {
  case 0:
    if ((cmp = frag1->yMin - frag2->yMin) == 0) {
      cmp = frag1->xMin - frag2->xMin;
    }
    break;
  case 1:
    if ((cmp = frag2->xMax - frag1->xMax) == 0) {
      cmp = frag1->yMin - frag2->yMin;
    }
    break;
  case 2:
    if ((cmp = frag2->yMin - frag1->yMin) == 0) {
      cmp = frag2->xMax - frag1->xMax;
    }
    break;
  case 3:
    if ((cmp = frag1->xMax - frag2->xMax) == 0) {
      cmp = frag2->yMax - frag1->yMax;
    }
    break;
  }
  return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
}

int TextLineFrag::cmpXYLineRot(const void *p1, const void *p2) {
  TextLineFrag *frag1 = (TextLineFrag *)p1;
  TextLineFrag *frag2 = (TextLineFrag *)p2;
  double cmp;

  cmp = 0; // make gcc happy
  switch (frag1->line->rot) {
  case 0:
    if ((cmp = frag1->xMin - frag2->xMin) == 0) {
      cmp = frag1->yMin - frag2->yMin;
    }
    break;
  case 1:
    if ((cmp = frag1->yMin - frag2->yMin) == 0) {
      cmp = frag2->xMax - frag1->xMax;
    }
    break;
  case 2:
    if ((cmp = frag2->xMax - frag1->xMax) == 0) {
      cmp = frag2->yMin - frag1->yMin;
    }
    break;
  case 3:
    if ((cmp = frag2->yMax - frag1->yMax) == 0) {
      cmp = frag1->xMax - frag2->xMax;
    }
    break;
  }
  return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
}

int TextLineFrag::cmpXYColumnPrimaryRot(const void *p1, const void *p2) {
  TextLineFrag *frag1 = (TextLineFrag *)p1;
  TextLineFrag *frag2 = (TextLineFrag *)p2;
  double cmp;

  // if columns overlap, compare y values
  if (frag1->col < frag2->col + (frag2->line->col[frag2->start + frag2->len] -
				 frag2->line->col[frag2->start]) &&
      frag2->col < frag1->col + (frag1->line->col[frag1->start + frag1->len] -
				 frag1->line->col[frag1->start])) {
    cmp = 0; // make gcc happy
    switch (frag1->line->blk->page->primaryRot) {
    case 0: cmp = frag1->yMin - frag2->yMin; break;
    case 1: cmp = frag2->xMax - frag1->xMax; break;
    case 2: cmp = frag2->yMin - frag1->yMin; break;
    case 3: cmp = frag1->xMax - frag2->xMax; break;
    }
    return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
  }

  // otherwise, compare starting column
  return frag1->col - frag2->col;
}

int TextLineFrag::cmpXYColumnLineRot(const void *p1, const void *p2) {
  TextLineFrag *frag1 = (TextLineFrag *)p1;
  TextLineFrag *frag2 = (TextLineFrag *)p2;
  double cmp;

  // if columns overlap, compare y values
  if (frag1->col < frag2->col + (frag2->line->col[frag2->start + frag2->len] -
				 frag2->line->col[frag2->start]) &&
      frag2->col < frag1->col + (frag1->line->col[frag1->start + frag1->len] -
				 frag1->line->col[frag1->start])) {
    cmp = 0; // make gcc happy
    switch (frag1->line->rot) {
    case 0: cmp = frag1->yMin - frag2->yMin; break;
    case 1: cmp = frag2->xMax - frag1->xMax; break;
    case 2: cmp = frag2->yMin - frag1->yMin; break;
    case 3: cmp = frag1->xMax - frag2->xMax; break;
    }
    return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
  }

  // otherwise, compare starting column
  return frag1->col - frag2->col;
}

//------------------------------------------------------------------------
// TextBlock
//------------------------------------------------------------------------

TextBlock::TextBlock(TextPage *pageA, int rotA) {
  page = pageA;
  rot = rotA;
  xMin = yMin = 0;
  xMax = yMax = -1;
  priMin = 0;
  priMax = page->pageWidth;
  pool = new TextPool();
  lines = NULL;
  curLine = NULL;
  next = NULL;
  stackNext = NULL;
}

TextBlock::~TextBlock() {
  TextLine *line;

  delete pool;
  while (lines) {
    line = lines;
    lines = lines->next;
    delete line;
  }
}

void TextBlock::addWord(TextWord *word) {
  pool->addWord(word);
  if (xMin > xMax) {
    xMin = word->xMin;
    xMax = word->xMax;
    yMin = word->yMin;
    yMax = word->yMax;
  } else {
    if (word->xMin < xMin) {
      xMin = word->xMin;
    }
    if (word->xMax > xMax) {
      xMax = word->xMax;
    }
    if (word->yMin < yMin) {
      yMin = word->yMin;
    }
    if (word->yMax > yMax) {
      yMax = word->yMax;
    }
  }
}

void TextBlock::coalesce(UnicodeMap *uMap) {
  TextWord *word0, *word1, *word2, *bestWord0, *bestWord1, *lastWord;
  TextLine *line, *line0, *line1;
  int poolMinBaseIdx, startBaseIdx, minBaseIdx, maxBaseIdx;
  int baseIdx, bestWordBaseIdx, idx0, idx1;
  double minBase, maxBase;
  double fontSize, delta, priDelta, secDelta;
  TextLine **lineArray;
  GBool found;
  int col1, col2;
  int i, j, k;

  // discard duplicated text (fake boldface, drop shadows)
  for (idx0 = pool->minBaseIdx; idx0 <= pool->maxBaseIdx; ++idx0) {
    word0 = pool->getPool(idx0);
    while (word0) {
      priDelta = dupMaxPriDelta * word0->fontSize;
      secDelta = dupMaxSecDelta * word0->fontSize;
      if (rot == 0 || rot == 3) {
	maxBaseIdx = pool->getBaseIdx(word0->base + secDelta);
      } else {
	maxBaseIdx = pool->getBaseIdx(word0->base - secDelta);
      }
      found = gFalse;
      word1 = word2 = NULL; // make gcc happy
      for (idx1 = idx0; idx1 <= maxBaseIdx; ++idx1) {
	if (idx1 == idx0) {
	  word1 = word0;
	  word2 = word0->next;
	} else {
	  word1 = NULL;
	  word2 = pool->getPool(idx1);
	}
	for (; word2; word1 = word2, word2 = word2->next) {
	  if (word2->len == word0->len &&
	      !memcmp(word2->text, word0->text,
		      word0->len * sizeof(Unicode))) {
	    switch (rot) {
	    case 0:
	    case 2:
	      found = fabs(word0->xMin - word2->xMin) < priDelta &&
		      fabs(word0->xMax - word2->xMax) < priDelta &&
		      fabs(word0->yMin - word2->yMin) < secDelta &&
		      fabs(word0->yMax - word2->yMax) < secDelta;
	      break;
	    case 1:
	    case 3:
	      found = fabs(word0->xMin - word2->xMin) < secDelta &&
		      fabs(word0->xMax - word2->xMax) < secDelta &&
		      fabs(word0->yMin - word2->yMin) < priDelta &&
		      fabs(word0->yMax - word2->yMax) < priDelta;
	      break;
	    }
	  }
	  if (found) {
	    break;
	  }
	}
	if (found) {
	  break;
	}
      }
      if (found) {
	if (word1) {
	  word1->next = word2->next;
	} else {
	  pool->setPool(idx1, word2->next);
	}
	delete word2;
      } else {
	word0 = word0->next;
      }
    }
  }

  // build the lines
  curLine = NULL;
  poolMinBaseIdx = pool->minBaseIdx;
  charCount = 0;
  nLines = 0;
  while (1) {

    // find the first non-empty line in the pool
    for (;
	 poolMinBaseIdx <= pool->maxBaseIdx && !pool->getPool(poolMinBaseIdx);
	 ++poolMinBaseIdx) ;
    if (poolMinBaseIdx > pool->maxBaseIdx) {
      break;
    }

    // look for the left-most word in the first four lines of the
    // pool -- this avoids starting with a superscript word
    startBaseIdx = poolMinBaseIdx;
    for (baseIdx = poolMinBaseIdx + 1;
	 baseIdx < poolMinBaseIdx + 4 && baseIdx <= pool->maxBaseIdx;
	 ++baseIdx) {
      if (!pool->getPool(baseIdx)) {
	continue;
      }
      if (pool->getPool(baseIdx)->primaryCmp(pool->getPool(startBaseIdx))
	  < 0) {
	startBaseIdx = baseIdx;
      }
    }

    // create a new line
    word0 = pool->getPool(startBaseIdx);
    pool->setPool(startBaseIdx, word0->next);
    word0->next = NULL;
    line = new TextLine(this, word0->rot, word0->base);
    line->addWord(word0);
    lastWord = word0;

    // compute the search range
    fontSize = word0->fontSize;
    minBase = word0->base - maxIntraLineDelta * fontSize;
    maxBase = word0->base + maxIntraLineDelta * fontSize;
    minBaseIdx = pool->getBaseIdx(minBase);
    maxBaseIdx = pool->getBaseIdx(maxBase);

    // find the rest of the words in this line
    while (1) {

      // find the left-most word whose baseline is in the range for
      // this line
      bestWordBaseIdx = 0;
      bestWord0 = bestWord1 = NULL;
      for (baseIdx = minBaseIdx; baseIdx <= maxBaseIdx; ++baseIdx) {
	for (word0 = NULL, word1 = pool->getPool(baseIdx);
	     word1;
	     word0 = word1, word1 = word1->next) {
	  if (word1->base >= minBase &&
	      word1->base <= maxBase &&
	      (delta = lastWord->primaryDelta(word1)) >=
	        minCharSpacing * fontSize) {
	    if (delta < maxWordSpacing * fontSize &&
		(!bestWord1 || word1->primaryCmp(bestWord1) < 0)) {
	      bestWordBaseIdx = baseIdx;
	      bestWord0 = word0;
	      bestWord1 = word1;
	    }
	    break;
	  }
	}
      }
      if (!bestWord1) {
	break;
      }

      // remove it from the pool, and add it to the line
      if (bestWord0) {
	bestWord0->next = bestWord1->next;
      } else {
	pool->setPool(bestWordBaseIdx, bestWord1->next);
      }
      bestWord1->next = NULL;
      line->addWord(bestWord1);
      lastWord = bestWord1;
    }

    // add the line
    if (curLine && line->cmpYX(curLine) > 0) {
      line0 = curLine;
      line1 = curLine->next;
    } else {
      line0 = NULL;
      line1 = lines;
    }
    for (;
	 line1 && line->cmpYX(line1) > 0;
	 line0 = line1, line1 = line1->next) ;
    if (line0) {
      line0->next = line;
    } else {
      lines = line;
    }
    line->next = line1;
    curLine = line;
    line->coalesce(uMap);
    charCount += line->len;
    ++nLines;
  }

  // sort lines into xy order for column assignment
  lineArray = (TextLine **)gmallocn(nLines, sizeof(TextLine *));
  for (line = lines, i = 0; line; line = line->next, ++i) {
    lineArray[i] = line;
  }
  qsort(lineArray, nLines, sizeof(TextLine *), &TextLine::cmpXY);

  // column assignment
  nColumns = 0;
  for (i = 0; i < nLines; ++i) {
    line0 = lineArray[i];
    col1 = 0;
    for (j = 0; j < i; ++j) {
      line1 = lineArray[j];
      if (line1->primaryDelta(line0) >= 0) {
	col2 = line1->col[line1->len] + 1;
      } else {
	k = 0; // make gcc happy
	switch (rot) {
	case 0:
	  for (k = 0;
	       k < line1->len &&
		 line0->xMin >= 0.5 * (line1->edge[k] + line1->edge[k+1]);
	       ++k) ;
	  break;
	case 1:
	  for (k = 0;
	       k < line1->len &&
		 line0->yMin >= 0.5 * (line1->edge[k] + line1->edge[k+1]);
	       ++k) ;
	  break;
	case 2:
	  for (k = 0;
	       k < line1->len &&
		 line0->xMax <= 0.5 * (line1->edge[k] + line1->edge[k+1]);
	       ++k) ;
	  break;
	case 3:
	  for (k = 0;
	       k < line1->len &&
		 line0->yMax <= 0.5 * (line1->edge[k] + line1->edge[k+1]);
	       ++k) ;
	  break;
	}
	col2 = line1->col[k];
      }
      if (col2 > col1) {
	col1 = col2;
      }
    }
    for (k = 0; k <= line0->len; ++k) {
      line0->col[k] += col1;
    }
    if (line0->col[line0->len] > nColumns) {
      nColumns = line0->col[line0->len];
    }
  }
  gfree(lineArray);
}

void TextBlock::updatePriMinMax(TextBlock *blk) {
  double newPriMin, newPriMax;
  GBool gotPriMin, gotPriMax;

  gotPriMin = gotPriMax = gFalse;
  newPriMin = newPriMax = 0; // make gcc happy
  switch (page->primaryRot) {
  case 0:
  case 2:
    if (blk->yMin < yMax && blk->yMax > yMin) {
      if (blk->xMin < xMin) {
	newPriMin = blk->xMax;
	gotPriMin = gTrue;
      }
      if (blk->xMax > xMax) {
	newPriMax = blk->xMin;
	gotPriMax = gTrue;
      }
    }
    break;
  case 1:
  case 3:
    if (blk->xMin < xMax && blk->xMax > xMin) {
      if (blk->yMin < yMin) {
	newPriMin = blk->yMax;
	gotPriMin = gTrue;
      }
      if (blk->yMax > yMax) {
	newPriMax = blk->yMin;
	gotPriMax = gTrue;
      }
    }
    break;
  }
  if (gotPriMin) {
    if (newPriMin > xMin) {
      newPriMin = xMin;
    }
    if (newPriMin > priMin) {
      priMin = newPriMin;
    }
  }
  if (gotPriMax) {
    if (newPriMax < xMax) {
      newPriMax = xMax;
    }
    if (newPriMax < priMax) {
      priMax = newPriMax;
    }
  }
}

int TextBlock::cmpXYPrimaryRot(const void *p1, const void *p2) {
  TextBlock *blk1 = *(TextBlock **)p1;
  TextBlock *blk2 = *(TextBlock **)p2;
  double cmp;

  cmp = 0; // make gcc happy
  switch (blk1->page->primaryRot) {
  case 0:
    if ((cmp = blk1->xMin - blk2->xMin) == 0) {
      cmp = blk1->yMin - blk2->yMin;
    }
    break;
  case 1:
    if ((cmp = blk1->yMin - blk2->yMin) == 0) {
      cmp = blk2->xMax - blk1->xMax;
    }
    break;
  case 2:
    if ((cmp = blk2->xMax - blk1->xMax) == 0) {
      cmp = blk2->yMin - blk1->yMin;
    }
    break;
  case 3:
    if ((cmp = blk2->yMax - blk1->yMax) == 0) {
      cmp = blk1->xMax - blk2->xMax;
    }
    break;
  }
  return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
}

int TextBlock::cmpYXPrimaryRot(const void *p1, const void *p2) {
  TextBlock *blk1 = *(TextBlock **)p1;
  TextBlock *blk2 = *(TextBlock **)p2;
  double cmp;

  cmp = 0; // make gcc happy
  switch (blk1->page->primaryRot) {
  case 0:
    if ((cmp = blk1->yMin - blk2->yMin) == 0) {
      cmp = blk1->xMin - blk2->xMin;
    }
    break;
  case 1:
    if ((cmp = blk2->xMax - blk1->xMax) == 0) {
      cmp = blk1->yMin - blk2->yMin;
    }
    break;
  case 2:
    if ((cmp = blk2->yMin - blk1->yMin) == 0) {
      cmp = blk2->xMax - blk1->xMax;
    }
    break;
  case 3:
    if ((cmp = blk1->xMax - blk2->xMax) == 0) {
      cmp = blk2->yMax - blk1->yMax;
    }
    break;
  }
  return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
}

int TextBlock::primaryCmp(TextBlock *blk) {
  double cmp;

  cmp = 0; // make gcc happy
  switch (rot) {
  case 0:
    cmp = xMin - blk->xMin;
    break;
  case 1:
    cmp = yMin - blk->yMin;
    break;
  case 2:
    cmp = blk->xMax - xMax;
    break;
  case 3:
    cmp = blk->yMax - yMax;
    break;
  }
  return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
}

double TextBlock::secondaryDelta(TextBlock *blk) {
  double delta;

  delta = 0; // make gcc happy
  switch (rot) {
  case 0:
    delta = blk->yMin - yMax;
    break;
  case 1:
    delta = xMin - blk->xMax;
    break;
  case 2:
    delta = yMin - blk->yMax;
    break;
  case 3:
    delta = blk->xMin - xMax;
    break;
  }
  return delta;
}

GBool TextBlock::isBelow(TextBlock *blk) {
  GBool below;

  below = gFalse; // make gcc happy
  switch (page->primaryRot) {
  case 0:
    below = xMin >= blk->priMin && xMax <= blk->priMax &&
            yMin > blk->yMin;
    break;
  case 1:
    below = yMin >= blk->priMin && yMax <= blk->priMax &&
            xMax < blk->xMax;
    break;
  case 2:
    below = xMin >= blk->priMin && xMax <= blk->priMax &&
            yMax < blk->yMax;
    break;
  case 3:
    below = yMin >= blk->priMin && yMax <= blk->priMax &&
            xMin > blk->xMin;
    break;
  }

  return below;
}

//------------------------------------------------------------------------
// TextFlow
//------------------------------------------------------------------------

TextFlow::TextFlow(TextPage *pageA, TextBlock *blk) {
  page = pageA;
  xMin = blk->xMin;
  xMax = blk->xMax;
  yMin = blk->yMin;
  yMax = blk->yMax;
  priMin = blk->priMin;
  priMax = blk->priMax;
  blocks = lastBlk = blk;
  next = NULL;
}

TextFlow::~TextFlow() {
  TextBlock *blk;

  while (blocks) {
    blk = blocks;
    blocks = blocks->next;
    delete blk;
  }
}

void TextFlow::addBlock(TextBlock *blk) {
  if (lastBlk) {
    lastBlk->next = blk;
  } else {
    blocks = blk;
  }
  lastBlk = blk;
  if (blk->xMin < xMin) {
    xMin = blk->xMin;
  }
  if (blk->xMax > xMax) {
    xMax = blk->xMax;
  }
  if (blk->yMin < yMin) {
    yMin = blk->yMin;
  }
  if (blk->yMax > yMax) {
    yMax = blk->yMax;
  }
}

GBool TextFlow::blockFits(TextBlock *blk, TextBlock *prevBlk) {
  GBool fits;

  // lower blocks must use smaller fonts
  if (blk->lines->words->fontSize > lastBlk->lines->words->fontSize) {
    return gFalse;
  }

  fits = gFalse; // make gcc happy
  switch (page->primaryRot) {
  case 0:
    fits = blk->xMin >= priMin && blk->xMax <= priMax;
    break;
  case 1:
    fits = blk->yMin >= priMin && blk->yMax <= priMax;
    break;
  case 2:
    fits = blk->xMin >= priMin && blk->xMax <= priMax;
    break;
  case 3:
    fits = blk->yMin >= priMin && blk->yMax <= priMax;
    break;
  }
  return fits;
}

#if TEXTOUT_WORD_LIST

//------------------------------------------------------------------------
// TextWordList
//------------------------------------------------------------------------

TextWordList::TextWordList(TextPage *text, GBool physLayout) {
  TextFlow *flow;
  TextBlock *blk;
  TextLine *line;
  TextWord *word;
  TextWord **wordArray;
  int nWords, i;

  words = new GList();

  if (text->rawOrder) {
    for (word = text->rawWords; word; word = word->next) {
      words->append(word);
    }

  } else if (physLayout) {
    // this is inefficient, but it's also the least useful of these
    // three cases
    nWords = 0;
    for (flow = text->flows; flow; flow = flow->next) {
      for (blk = flow->blocks; blk; blk = blk->next) {
	for (line = blk->lines; line; line = line->next) {
	  for (word = line->words; word; word = word->next) {
	    ++nWords;
	  }
	}
      }
    }
    wordArray = (TextWord **)gmallocn(nWords, sizeof(TextWord *));
    i = 0;
    for (flow = text->flows; flow; flow = flow->next) {
      for (blk = flow->blocks; blk; blk = blk->next) {
	for (line = blk->lines; line; line = line->next) {
	  for (word = line->words; word; word = word->next) {
	    wordArray[i++] = word;
	  }
	}
      }
    }
    qsort(wordArray, nWords, sizeof(TextWord *), &TextWord::cmpYX);
    for (i = 0; i < nWords; ++i) {
      words->append(wordArray[i]);
    }
    gfree(wordArray);

  } else {
    for (flow = text->flows; flow; flow = flow->next) {
      for (blk = flow->blocks; blk; blk = blk->next) {
	for (line = blk->lines; line; line = line->next) {
	  for (word = line->words; word; word = word->next) {
	    words->append(word);
	  }
	}
      }
    }
  }
}

TextWordList::~TextWordList() {
  delete words;
}

int TextWordList::getLength() {
  return words->getLength();
}

TextWord *TextWordList::get(int idx) {
  if (idx < 0 || idx >= words->getLength()) {
    return NULL;
  }
  return (TextWord *)words->get(idx);
}

#endif // TEXTOUT_WORD_LIST

//------------------------------------------------------------------------
// TextPage
//------------------------------------------------------------------------

TextPage::TextPage(GBool rawOrderA) {
  int rot;

  rawOrder = rawOrderA;
  curWord = NULL;
  charPos = 0;
  curFont = NULL;
  curFontSize = 0;
  nest = 0;
  nTinyChars = 0;
  lastCharOverlap = gFalse;
  if (!rawOrder) {
    for (rot = 0; rot < 4; ++rot) {
      pools[rot] = new TextPool();
    }
  }
  flows = NULL;
  blocks = NULL;
  rawWords = NULL;
  rawLastWord = NULL;
  fonts = new GList();
  lastFindXMin = lastFindYMin = 0;
  haveLastFind = gFalse;
  underlines = new GList();
  links = new GList();
}

TextPage::~TextPage() {
  int rot;

  clear();
  if (!rawOrder) {
    for (rot = 0; rot < 4; ++rot) {
      delete pools[rot];
    }
  }
  delete fonts;
  deleteGList(underlines, TextUnderline);
  deleteGList(links, TextLink);
}

void TextPage::startPage(GfxState *state) {
  clear();
  if (state) {
    pageWidth = state->getPageWidth();
    pageHeight = state->getPageHeight();
  } else {
    pageWidth = pageHeight = 0;
  }
}

void TextPage::endPage() {
  if (curWord) {
    endWord();
  }
}

void TextPage::clear() {
  int rot;
  TextFlow *flow;
  TextWord *word;

  if (curWord) {
    delete curWord;
    curWord = NULL;
  }
  if (rawOrder) {
    while (rawWords) {
      word = rawWords;
      rawWords = rawWords->next;
      delete word;
    }
  } else {
    for (rot = 0; rot < 4; ++rot) {
      delete pools[rot];
    }
    while (flows) {
      flow = flows;
      flows = flows->next;
      delete flow;
    }
    gfree(blocks);
  }
  deleteGList(fonts, TextFontInfo);

  curWord = NULL;
  charPos = 0;
  curFont = NULL;
  curFontSize = 0;
  nest = 0;
  nTinyChars = 0;
  if (!rawOrder) {
    for (rot = 0; rot < 4; ++rot) {
      pools[rot] = new TextPool();
    }
  }
  flows = NULL;
  blocks = NULL;
  rawWords = NULL;
  rawLastWord = NULL;
  fonts = new GList();
}

void TextPage::updateFont(GfxState *state) {
  GfxFont *gfxFont;
  double *fm;
  char *name;
  int code, mCode, letterCode, anyCode;
  double w;
  int i;

  // get the font info object
  curFont = NULL;
  for (i = 0; i < fonts->getLength(); ++i) {
    curFont = (TextFontInfo *)fonts->get(i);
    if (curFont->matches(state)) {
      break;
    }
    curFont = NULL;
  }
  if (!curFont) {
    curFont = new TextFontInfo(state);
    fonts->append(curFont);
  }

  // adjust the font size
  gfxFont = state->getFont();
  curFontSize = state->getTransformedFontSize();
  if (gfxFont && gfxFont->getType() == fontType3) {
    // This is a hack which makes it possible to deal with some Type 3
    // fonts.  The problem is that it's impossible to know what the
    // base coordinate system used in the font is without actually
    // rendering the font.  This code tries to guess by looking at the
    // width of the character 'm' (which breaks if the font is a
    // subset that doesn't contain 'm').
    mCode = letterCode = anyCode = -1;
    for (code = 0; code < 256; ++code) {
      name = ((Gfx8BitFont *)gfxFont)->getCharName(code);
      if (name && name[0] == 'm' && name[1] == '\0') {
	mCode = code;
      }
      if (letterCode < 0 && name && name[1] == '\0' &&
	  ((name[0] >= 'A' && name[0] <= 'Z') ||
	   (name[0] >= 'a' && name[0] <= 'z'))) {
	letterCode = code;
      }
      if (anyCode < 0 && name &&
	  ((Gfx8BitFont *)gfxFont)->getWidth(code) > 0) {
	anyCode = code;
      }
    }
    if (mCode >= 0 &&
	(w = ((Gfx8BitFont *)gfxFont)->getWidth(mCode)) > 0) {
      // 0.6 is a generic average 'm' width -- yes, this is a hack
      curFontSize *= w / 0.6;
    } else if (letterCode >= 0 &&
	       (w = ((Gfx8BitFont *)gfxFont)->getWidth(letterCode)) > 0) {
      // even more of a hack: 0.5 is a generic letter width
      curFontSize *= w / 0.5;
    } else if (anyCode >= 0 &&
	       (w = ((Gfx8BitFont *)gfxFont)->getWidth(anyCode)) > 0) {
      // better than nothing: 0.5 is a generic character width
      curFontSize *= w / 0.5;
    }
    fm = gfxFont->getFontMatrix();
    if (fm[0] != 0) {
      curFontSize *= fabs(fm[3] / fm[0]);
    }
  }
}

void TextPage::beginWord(GfxState *state, double x0, double y0) {
  double *fontm;
  double m[4], m2[4];
  int rot;

  // This check is needed because Type 3 characters can contain
  // text-drawing operations (when TextPage is being used via
  // {X,Win}SplashOutputDev rather than TextOutputDev).
  if (curWord) {
    ++nest;
    return;
  }

  // compute the rotation
  state->getFontTransMat(&m[0], &m[1], &m[2], &m[3]);
  if (state->getFont()->getType() == fontType3) {
    fontm = state->getFont()->getFontMatrix();
    m2[0] = fontm[0] * m[0] + fontm[1] * m[2];
    m2[1] = fontm[0] * m[1] + fontm[1] * m[3];
    m2[2] = fontm[2] * m[0] + fontm[3] * m[2];
    m2[3] = fontm[2] * m[1] + fontm[3] * m[3];
    m[0] = m2[0];
    m[1] = m2[1];
    m[2] = m2[2];
    m[3] = m2[3];
  }
  if (fabs(m[0] * m[3]) > fabs(m[1] * m[2])) {
    rot = (m[3] < 0) ? 0 : 2;
  } else {
    rot = (m[2] > 0) ? 1 : 3;
  }

  curWord = new TextWord(state, rot, x0, y0, charPos, curFont, curFontSize);
}

void TextPage::addChar(GfxState *state, double x, double y,
		       double dx, double dy,
		       CharCode c, int nBytes, Unicode *u, int uLen) {
  double x1, y1, w1, h1, dx2, dy2, base, sp, delta;
  GBool overlap;
  int i;

  // subtract char and word spacing from the dx,dy values
  sp = state->getCharSpace();
  if (c == (CharCode)0x20) {
    sp += state->getWordSpace();
  }
  state->textTransformDelta(sp * state->getHorizScaling(), 0, &dx2, &dy2);
  dx -= dx2;
  dy -= dy2;
  state->transformDelta(dx, dy, &w1, &h1);

  // throw away chars that aren't inside the page bounds
  // (and also do a sanity check on the character size)
  state->transform(x, y, &x1, &y1);
  if (x1 + w1 < 0 || x1 > pageWidth ||
      y1 + h1 < 0 || y1 > pageHeight ||
      w1 > pageWidth || h1 > pageHeight) {
    charPos += nBytes;
    return;
  }

  // check the tiny chars limit
  if (!globalParams->getTextKeepTinyChars() &&
      fabs(w1) < 3 && fabs(h1) < 3) {
    if (++nTinyChars > 50000) {
      charPos += nBytes;
      return;
    }
  }

  // break words at space character
  if (uLen == 1 && u[0] == (Unicode)0x20) {
    if (curWord) {
      ++curWord->charLen;
    }
    charPos += nBytes;
    endWord();
    return;
  }

  // start a new word if:
  // (1) this character doesn't fall in the right place relative to
  //     the end of the previous word (this places upper and lower
  //     constraints on the position deltas along both the primary
  //     and secondary axes), or
  // (2) this character overlaps the previous one (duplicated text), or
  // (3) the previous character was an overlap (we want each duplicated
  //     character to be in a word by itself at this stage),
  // (4) the font size has changed
  if (curWord && curWord->len > 0) {
    base = sp = delta = 0; // make gcc happy
    switch (curWord->rot) {
    case 0:
      base = y1;
      sp = x1 - curWord->xMax;
      delta = x1 - curWord->edge[curWord->len - 1];
      break;
    case 1:
      base = x1;
      sp = y1 - curWord->yMax;
      delta = y1 - curWord->edge[curWord->len - 1];
      break;
    case 2:
      base = y1;
      sp = curWord->xMin - x1;
      delta = curWord->edge[curWord->len - 1] - x1;
      break;
    case 3:
      base = x1;
      sp = curWord->yMin - y1;
      delta = curWord->edge[curWord->len - 1] - y1;
      break;
    }
    overlap = fabs(delta) < dupMaxPriDelta * curWord->fontSize &&
              fabs(base - curWord->base) < dupMaxSecDelta * curWord->fontSize;
    if (overlap || lastCharOverlap ||
	sp < -minDupBreakOverlap * curWord->fontSize ||
	sp > minWordBreakSpace * curWord->fontSize ||
	fabs(base - curWord->base) > 0.5 ||
	curFontSize != curWord->fontSize) {
      endWord();
    }
    lastCharOverlap = overlap;
  } else {
    lastCharOverlap = gFalse;
  }

  if (uLen != 0) {
    // start a new word if needed
    if (!curWord) {
      beginWord(state, x, y);
    }

    // page rotation and/or transform matrices can cause text to be
    // drawn in reverse order -- in this case, swap the begin/end
    // coordinates and break text into individual chars
    if ((curWord->rot == 0 && w1 < 0) ||
	(curWord->rot == 1 && h1 < 0) ||
	(curWord->rot == 2 && w1 > 0) ||
	(curWord->rot == 3 && h1 > 0)) {
      endWord();
      beginWord(state, x + dx, y + dy);
      x1 += w1;
      y1 += h1;
      w1 = -w1;
      h1 = -h1;
    }

    // add the characters to the current word
    w1 /= uLen;
    h1 /= uLen;
    for (i = 0; i < uLen; ++i) {
      curWord->addChar(state, x1 + i*w1, y1 + i*h1, w1, h1, u[i]);
    }
  }
  if (curWord) {
    curWord->charLen += nBytes;
  }
  charPos += nBytes;
}

void TextPage::endWord() {
  // This check is needed because Type 3 characters can contain
  // text-drawing operations (when TextPage is being used via
  // {X,Win}SplashOutputDev rather than TextOutputDev).
  if (nest > 0) {
    --nest;
    return;
  }

  if (curWord) {
    addWord(curWord);
    curWord = NULL;
  }
}

void TextPage::addWord(TextWord *word) {
  // throw away zero-length words -- they don't have valid xMin/xMax
  // values, and they're useless anyway
  if (word->len == 0) {
    delete word;
    return;
  }

  if (rawOrder) {
    if (rawLastWord) {
      rawLastWord->next = word;
    } else {
      rawWords = word;
    }
    rawLastWord = word;
  } else {
    pools[word->rot]->addWord(word);
  }
}

void TextPage::addUnderline(double x0, double y0, double x1, double y1) {
  underlines->append(new TextUnderline(x0, y0, x1, y1));
}

void TextPage::addLink(int xMin, int yMin, int xMax, int yMax, Link *link) {
  links->append(new TextLink(xMin, yMin, xMax, yMax, link));
}

void TextPage::coalesce(GBool physLayout, GBool doHTML) {
  UnicodeMap *uMap;
  TextPool *pool;
  TextWord *word0, *word1, *word2;
  TextLine *line;
  TextBlock *blkList, *blkStack, *blk, *lastBlk, *blk0, *blk1;
  TextBlock **blkArray;
  TextFlow *flow, *lastFlow;
  TextUnderline *underline;
  TextLink *link;
  int rot, poolMinBaseIdx, baseIdx, startBaseIdx, endBaseIdx;
  double minBase, maxBase, newMinBase, newMaxBase;
  double fontSize, colSpace1, colSpace2, lineSpace, intraLineSpace, blkSpace;
  GBool found;
  int count[4];
  int lrCount;
  int firstBlkIdx, nBlocksLeft;
  int col1, col2;
  int i, j, n;

  if (rawOrder) {
    primaryRot = 0;
    primaryLR = gTrue;
    return;
  }

  uMap = globalParams->getTextEncoding();
  blkList = NULL;
  lastBlk = NULL;
  nBlocks = 0;
  primaryRot = -1;

#if 0 // for debugging
  printf("*** initial words ***\n");
  for (rot = 0; rot < 4; ++rot) {
    pool = pools[rot];
    for (baseIdx = pool->minBaseIdx; baseIdx <= pool->maxBaseIdx; ++baseIdx) {
      for (word0 = pool->getPool(baseIdx); word0; word0 = word0->next) {
	printf("    word: x=%.2f..%.2f y=%.2f..%.2f base=%.2f fontSize=%.2f rot=%d link=%p '",
	       word0->xMin, word0->xMax, word0->yMin, word0->yMax,
	       word0->base, word0->fontSize, rot*90, word0->link);
	for (i = 0; i < word0->len; ++i) {
	  fputc(word0->text[i] & 0xff, stdout);
	}
	printf("'\n");
      }
    }
  }
  printf("\n");
#endif

#if 0 //~ for debugging
  for (i = 0; i < underlines->getLength(); ++i) {
    underline = (TextUnderline *)underlines->get(i);
    printf("underline: x=%g..%g y=%g..%g horiz=%d\n",
	   underline->x0, underline->x1, underline->y0, underline->y1,
	   underline->horiz);
  }
#endif

  if (doHTML) {

    //----- handle underlining
    for (i = 0; i < underlines->getLength(); ++i) {
      underline = (TextUnderline *)underlines->get(i);
      if (underline->horiz) {
	// rot = 0
	if (pools[0]->minBaseIdx <= pools[0]->maxBaseIdx) {
	  startBaseIdx = pools[0]->getBaseIdx(underline->y0 + minUnderlineGap);
	  endBaseIdx = pools[0]->getBaseIdx(underline->y0 + maxUnderlineGap);
	  for (j = startBaseIdx; j <= endBaseIdx; ++j) {
	    for (word0 = pools[0]->getPool(j); word0; word0 = word0->next) {
	      //~ need to check the y value against the word baseline
	      if (underline->x0 < word0->xMin + underlineSlack &&
		  word0->xMax - underlineSlack < underline->x1) {
		word0->underlined = gTrue;
	      }
	    }
	  }
	}

	// rot = 2
	if (pools[2]->minBaseIdx <= pools[2]->maxBaseIdx) {
	  startBaseIdx = pools[2]->getBaseIdx(underline->y0 - maxUnderlineGap);
	  endBaseIdx = pools[2]->getBaseIdx(underline->y0 - minUnderlineGap);
	  for (j = startBaseIdx; j <= endBaseIdx; ++j) {
	    for (word0 = pools[2]->getPool(j); word0; word0 = word0->next) {
	      if (underline->x0 < word0->xMin + underlineSlack &&
		  word0->xMax - underlineSlack < underline->x1) {
		word0->underlined = gTrue;
	      }
	    }
	  }
	}
      } else {
	// rot = 1
	if (pools[1]->minBaseIdx <= pools[1]->maxBaseIdx) {
	  startBaseIdx = pools[1]->getBaseIdx(underline->x0 - maxUnderlineGap);
	  endBaseIdx = pools[1]->getBaseIdx(underline->x0 - minUnderlineGap);
	  for (j = startBaseIdx; j <= endBaseIdx; ++j) {
	    for (word0 = pools[1]->getPool(j); word0; word0 = word0->next) {
	      if (underline->y0 < word0->yMin + underlineSlack &&
		  word0->yMax - underlineSlack < underline->y1) {
		word0->underlined = gTrue;
	      }
	    }
	  }
	}

	// rot = 3
	if (pools[3]->minBaseIdx <= pools[3]->maxBaseIdx) {
	  startBaseIdx = pools[3]->getBaseIdx(underline->x0 + minUnderlineGap);
	  endBaseIdx = pools[3]->getBaseIdx(underline->x0 + maxUnderlineGap);
	  for (j = startBaseIdx; j <= endBaseIdx; ++j) {
	    for (word0 = pools[3]->getPool(j); word0; word0 = word0->next) {
	      if (underline->y0 < word0->yMin + underlineSlack &&
		  word0->yMax - underlineSlack < underline->y1) {
		word0->underlined = gTrue;
	      }
	    }
	  }
	}
      }
    }

    //----- handle links
    for (i = 0; i < links->getLength(); ++i) {
      link = (TextLink *)links->get(i);

      // rot = 0
      if (pools[0]->minBaseIdx <= pools[0]->maxBaseIdx) {
	startBaseIdx = pools[0]->getBaseIdx(link->yMin);
	endBaseIdx = pools[0]->getBaseIdx(link->yMax);
	for (j = startBaseIdx; j <= endBaseIdx; ++j) {
	  for (word0 = pools[0]->getPool(j); word0; word0 = word0->next) {
	    if (link->xMin < word0->xMin + hyperlinkSlack &&
		word0->xMax - hyperlinkSlack < link->xMax &&
		link->yMin < word0->yMin + hyperlinkSlack &&
		word0->yMax - hyperlinkSlack < link->yMax) {
	      word0->link = link->link;
	    }
	  }
	}
      }

      // rot = 2
      if (pools[2]->minBaseIdx <= pools[2]->maxBaseIdx) {
	startBaseIdx = pools[2]->getBaseIdx(link->yMin);
	endBaseIdx = pools[2]->getBaseIdx(link->yMax);
	for (j = startBaseIdx; j <= endBaseIdx; ++j) {
	  for (word0 = pools[2]->getPool(j); word0; word0 = word0->next) {
	    if (link->xMin < word0->xMin + hyperlinkSlack &&
		word0->xMax - hyperlinkSlack < link->xMax &&
		link->yMin < word0->yMin + hyperlinkSlack &&
		word0->yMax - hyperlinkSlack < link->yMax) {
	      word0->link = link->link;
	    }
	  }
	}
      }

      // rot = 1
      if (pools[1]->minBaseIdx <= pools[1]->maxBaseIdx) {
	startBaseIdx = pools[1]->getBaseIdx(link->xMin);
	endBaseIdx = pools[1]->getBaseIdx(link->xMax);
	for (j = startBaseIdx; j <= endBaseIdx; ++j) {
	  for (word0 = pools[1]->getPool(j); word0; word0 = word0->next) {
	    if (link->yMin < word0->yMin + hyperlinkSlack &&
		word0->yMax - hyperlinkSlack < link->yMax &&
		link->xMin < word0->xMin + hyperlinkSlack &&
		word0->xMax - hyperlinkSlack < link->xMax) {
	      word0->link = link->link;
	    }
	  }
	}
      }

      // rot = 3
      if (pools[3]->minBaseIdx <= pools[3]->maxBaseIdx) {
	startBaseIdx = pools[3]->getBaseIdx(link->xMin);
	endBaseIdx = pools[3]->getBaseIdx(link->xMax);
	for (j = startBaseIdx; j <= endBaseIdx; ++j) {
	  for (word0 = pools[3]->getPool(j); word0; word0 = word0->next) {
	    if (link->yMin < word0->yMin + hyperlinkSlack &&
		word0->yMax - hyperlinkSlack < link->yMax &&
		link->xMin < word0->xMin + hyperlinkSlack &&
		word0->xMax - hyperlinkSlack < link->xMax) {
	      word0->link = link->link;
	    }
	  }
	}
      }
    }
  }

  //----- assemble the blocks

  //~ add an outer loop for writing mode (vertical text)

  // build blocks for each rotation value
  for (rot = 0; rot < 4; ++rot) {
    pool = pools[rot];
    poolMinBaseIdx = pool->minBaseIdx;
    count[rot] = 0;

    // add blocks until no more words are left
    while (1) {

      // find the first non-empty line in the pool
      for (;
	   poolMinBaseIdx <= pool->maxBaseIdx &&
	     !pool->getPool(poolMinBaseIdx);
	   ++poolMinBaseIdx) ;
      if (poolMinBaseIdx > pool->maxBaseIdx) {
	break;
      }

      // look for the left-most word in the first four lines of the
      // pool -- this avoids starting with a superscript word
      startBaseIdx = poolMinBaseIdx;
      for (baseIdx = poolMinBaseIdx + 1;
	   baseIdx < poolMinBaseIdx + 4 && baseIdx <= pool->maxBaseIdx;
	   ++baseIdx) {
	if (!pool->getPool(baseIdx)) {
	  continue;
	}
	if (pool->getPool(baseIdx)->primaryCmp(pool->getPool(startBaseIdx))
	    < 0) {
	  startBaseIdx = baseIdx;
	}
      }

      // create a new block
      word0 = pool->getPool(startBaseIdx);
      pool->setPool(startBaseIdx, word0->next);
      word0->next = NULL;
      blk = new TextBlock(this, rot);
      blk->addWord(word0);

      fontSize = word0->fontSize;
      minBase = maxBase = word0->base;
      colSpace1 = minColSpacing1 * fontSize;
      colSpace2 = minColSpacing2 * fontSize;
      lineSpace = maxLineSpacingDelta * fontSize;
      intraLineSpace = maxIntraLineDelta * fontSize;

      // add words to the block
      do {
	found = gFalse;

	// look for words on the line above the current top edge of
	// the block
	newMinBase = minBase;
	for (baseIdx = pool->getBaseIdx(minBase);
	     baseIdx >= pool->getBaseIdx(minBase - lineSpace);
	     --baseIdx) {
	  word0 = NULL;
	  word1 = pool->getPool(baseIdx);
	  while (word1) {
	    if (word1->base < minBase &&
		word1->base >= minBase - lineSpace &&
		((rot == 0 || rot == 2)
		 ? (word1->xMin < blk->xMax && word1->xMax > blk->xMin)
		 : (word1->yMin < blk->yMax && word1->yMax > blk->yMin)) &&
		fabs(word1->fontSize - fontSize) <
		  maxBlockFontSizeDelta1 * fontSize) {
	      word2 = word1;
	      if (word0) {
		word0->next = word1->next;
	      } else {
		pool->setPool(baseIdx, word1->next);
	      }
	      word1 = word1->next;
	      word2->next = NULL;
	      blk->addWord(word2);
	      found = gTrue;
	      newMinBase = word2->base;
	    } else {
	      word0 = word1;
	      word1 = word1->next;
	    }
	  }
	}
	minBase = newMinBase;

	// look for words on the line below the current bottom edge of
	// the block
	newMaxBase = maxBase;
	for (baseIdx = pool->getBaseIdx(maxBase);
	     baseIdx <= pool->getBaseIdx(maxBase + lineSpace);
	     ++baseIdx) {
	  word0 = NULL;
	  word1 = pool->getPool(baseIdx);
	  while (word1) {
	    if (word1->base > maxBase &&
		word1->base <= maxBase + lineSpace &&
		((rot == 0 || rot == 2)
		 ? (word1->xMin < blk->xMax && word1->xMax > blk->xMin)
		 : (word1->yMin < blk->yMax && word1->yMax > blk->yMin)) &&
		fabs(word1->fontSize - fontSize) <
		  maxBlockFontSizeDelta1 * fontSize) {
	      word2 = word1;
	      if (word0) {
		word0->next = word1->next;
	      } else {
		pool->setPool(baseIdx, word1->next);
	      }
	      word1 = word1->next;
	      word2->next = NULL;
	      blk->addWord(word2);
	      found = gTrue;
	      newMaxBase = word2->base;
	    } else {
	      word0 = word1;
	      word1 = word1->next;
	    }
	  }
	}
	maxBase = newMaxBase;

	// look for words that are on lines already in the block, and
	// that overlap the block horizontally
	for (baseIdx = pool->getBaseIdx(minBase - intraLineSpace);
	     baseIdx <= pool->getBaseIdx(maxBase + intraLineSpace);
	     ++baseIdx) {
	  word0 = NULL;
	  word1 = pool->getPool(baseIdx);
	  while (word1) {
	    if (word1->base >= minBase - intraLineSpace &&
		word1->base <= maxBase + intraLineSpace &&
		((rot == 0 || rot == 2)
		 ? (word1->xMin < blk->xMax + colSpace1 &&
		    word1->xMax > blk->xMin - colSpace1)
		 : (word1->yMin < blk->yMax + colSpace1 &&
		    word1->yMax > blk->yMin - colSpace1)) &&
		fabs(word1->fontSize - fontSize) <
		  maxBlockFontSizeDelta2 * fontSize) {
	      word2 = word1;
	      if (word0) {
		word0->next = word1->next;
	      } else {
		pool->setPool(baseIdx, word1->next);
	      }
	      word1 = word1->next;
	      word2->next = NULL;
	      blk->addWord(word2);
	      found = gTrue;
	    } else {
	      word0 = word1;
	      word1 = word1->next;
	    }
	  }
	}

	// only check for outlying words (the next two chunks of code)
	// if we didn't find anything else
	if (found) {
	  continue;
	}

	// scan down the left side of the block, looking for words
	// that are near (but not overlapping) the block; if there are
	// three or fewer, add them to the block
	n = 0;
	for (baseIdx = pool->getBaseIdx(minBase - intraLineSpace);
	     baseIdx <= pool->getBaseIdx(maxBase + intraLineSpace);
	     ++baseIdx) {
	  word1 = pool->getPool(baseIdx);
	  while (word1) {
	    if (word1->base >= minBase - intraLineSpace &&
		word1->base <= maxBase + intraLineSpace &&
		((rot == 0 || rot == 2)
		 ? (word1->xMax <= blk->xMin &&
		    word1->xMax > blk->xMin - colSpace2)
		 : (word1->yMax <= blk->yMin &&
		    word1->yMax > blk->yMin - colSpace2)) &&
		fabs(word1->fontSize - fontSize) <
		  maxBlockFontSizeDelta3 * fontSize) {
	      ++n;
	      break;
	    }
	    word1 = word1->next;
	  }
	}
	if (n > 0 && n <= 3) {
	  for (baseIdx = pool->getBaseIdx(minBase - intraLineSpace);
	       baseIdx <= pool->getBaseIdx(maxBase + intraLineSpace);
	       ++baseIdx) {
	    word0 = NULL;
	    word1 = pool->getPool(baseIdx);
	    while (word1) {
	      if (word1->base >= minBase - intraLineSpace &&
		  word1->base <= maxBase + intraLineSpace &&
		  ((rot == 0 || rot == 2)
		   ? (word1->xMax <= blk->xMin &&
		      word1->xMax > blk->xMin - colSpace2)
		   : (word1->yMax <= blk->yMin &&
		      word1->yMax > blk->yMin - colSpace2)) &&
		  fabs(word1->fontSize - fontSize) <
		    maxBlockFontSizeDelta3 * fontSize) {
		word2 = word1;
		if (word0) {
		  word0->next = word1->next;
		} else {
		  pool->setPool(baseIdx, word1->next);
		}
		word1 = word1->next;
		word2->next = NULL;
		blk->addWord(word2);
		if (word2->base < minBase) {
		  minBase = word2->base;
		} else if (word2->base > maxBase) {
		  maxBase = word2->base;
		}
		found = gTrue;
		break;
	      } else {
		word0 = word1;
		word1 = word1->next;
	      }
	    }
	  }
	}

	// scan down the right side of the block, looking for words
	// that are near (but not overlapping) the block; if there are
	// three or fewer, add them to the block
	n = 0;
	for (baseIdx = pool->getBaseIdx(minBase - intraLineSpace);
	     baseIdx <= pool->getBaseIdx(maxBase + intraLineSpace);
	     ++baseIdx) {
	  word1 = pool->getPool(baseIdx);
	  while (word1) {
	    if (word1->base >= minBase - intraLineSpace &&
		word1->base <= maxBase + intraLineSpace &&
		((rot == 0 || rot == 2)
		 ? (word1->xMin >= blk->xMax &&
		    word1->xMin < blk->xMax + colSpace2)
		 : (word1->yMin >= blk->yMax &&
		    word1->yMin < blk->yMax + colSpace2)) &&
		fabs(word1->fontSize - fontSize) <
		  maxBlockFontSizeDelta3 * fontSize) {
	      ++n;
	      break;
	    }
	    word1 = word1->next;
	  }
	}
	if (n > 0 && n <= 3) {
	  for (baseIdx = pool->getBaseIdx(minBase - intraLineSpace);
	       baseIdx <= pool->getBaseIdx(maxBase + intraLineSpace);
	       ++baseIdx) {
	    word0 = NULL;
	    word1 = pool->getPool(baseIdx);
	    while (word1) {
	      if (word1->base >= minBase - intraLineSpace &&
		  word1->base <= maxBase + intraLineSpace &&
		  ((rot == 0 || rot == 2)
		   ? (word1->xMin >= blk->xMax &&
		      word1->xMin < blk->xMax + colSpace2)
		   : (word1->yMin >= blk->yMax &&
		      word1->yMin < blk->yMax + colSpace2)) &&
		  fabs(word1->fontSize - fontSize) <
		    maxBlockFontSizeDelta3 * fontSize) {
		word2 = word1;
		if (word0) {
		  word0->next = word1->next;
		} else {
		  pool->setPool(baseIdx, word1->next);
		}
		word1 = word1->next;
		word2->next = NULL;
		blk->addWord(word2);
		if (word2->base < minBase) {
		  minBase = word2->base;
		} else if (word2->base > maxBase) {
		  maxBase = word2->base;
		}
		found = gTrue;
		break;
	      } else {
		word0 = word1;
		word1 = word1->next;
	      }
	    }
	  }
	}

      } while (found);

      //~ need to compute the primary writing mode (horiz/vert) in
      //~ addition to primary rotation

      // coalesce the block, and add it to the list
      blk->coalesce(uMap);
      if (lastBlk) {
	lastBlk->next = blk;
      } else {
	blkList = blk;
      }
      lastBlk = blk;
      count[rot] += blk->charCount;
      if (primaryRot < 0 || count[rot] > count[primaryRot]) {
	primaryRot = rot;
      }
      ++nBlocks;
    }
  }

#if 0 // for debugging 
  printf("*** rotation ***\n");
  for (rot = 0; rot < 4; ++rot) {
    printf("  %d: %6d\n", rot, count[rot]);
  }
  printf("  primary rot = %d\n", primaryRot);
  printf("\n");
#endif

#if 0 // for debugging
  printf("*** blocks ***\n");
  for (blk = blkList; blk; blk = blk->next) {
    printf("block: rot=%d x=%.2f..%.2f y=%.2f..%.2f\n",
	   blk->rot, blk->xMin, blk->xMax, blk->yMin, blk->yMax);
    for (line = blk->lines; line; line = line->next) {
      printf("  line: x=%.2f..%.2f y=%.2f..%.2f base=%.2f\n",
	     line->xMin, line->xMax, line->yMin, line->yMax, line->base);
      for (word0 = line->words; word0; word0 = word0->next) {
	printf("    word: x=%.2f..%.2f y=%.2f..%.2f base=%.2f fontSize=%.2f space=%d: '",
	       word0->xMin, word0->xMax, word0->yMin, word0->yMax,
	       word0->base, word0->fontSize, word0->spaceAfter);
	for (i = 0; i < word0->len; ++i) {
	  fputc(word0->text[i] & 0xff, stdout);
	}
	printf("'\n");
      }
    }
  }
  printf("\n");
#endif

  // determine the primary direction
  lrCount = 0;
  for (blk = blkList; blk; blk = blk->next) {
    for (line = blk->lines; line; line = line->next) {
      for (word0 = line->words; word0; word0 = word0->next) {
	for (i = 0; i < word0->len; ++i) {
	  if (unicodeTypeL(word0->text[i])) {
	    ++lrCount;
	  } else if (unicodeTypeR(word0->text[i])) {
	    --lrCount;
	  }
	}
      }
    }
  }
  primaryLR = lrCount >= 0;

#if 0 // for debugging
  printf("*** direction ***\n");
  printf("lrCount = %d\n", lrCount);
  printf("primaryLR = %d\n", primaryLR);
#endif

  //----- column assignment

  // sort blocks into xy order for column assignment
  blocks = (TextBlock **)gmallocn(nBlocks, sizeof(TextBlock *));
  for (blk = blkList, i = 0; blk; blk = blk->next, ++i) {
    blocks[i] = blk;
  }
  qsort(blocks, nBlocks, sizeof(TextBlock *), &TextBlock::cmpXYPrimaryRot);

  // column assignment
  for (i = 0; i < nBlocks; ++i) {
    blk0 = blocks[i];
    col1 = 0;
    for (j = 0; j < i; ++j) {
      blk1 = blocks[j];
      col2 = 0; // make gcc happy
      switch (primaryRot) {
      case 0:
	if (blk0->xMin > blk1->xMax) {
	  col2 = blk1->col + blk1->nColumns + 3;
	} else if (blk1->xMax == blk1->xMin) {
	  col2 = blk1->col;
	} else {
	  col2 = blk1->col + (int)(((blk0->xMin - blk1->xMin) /
				    (blk1->xMax - blk1->xMin)) *
				   blk1->nColumns);
	}
	break;
      case 1:
	if (blk0->yMin > blk1->yMax) {
	  col2 = blk1->col + blk1->nColumns + 3;
	} else if (blk1->yMax == blk1->yMin) {
	  col2 = blk1->col;
	} else {
	  col2 = blk1->col + (int)(((blk0->yMin - blk1->yMin) /
				    (blk1->yMax - blk1->yMin)) *
				   blk1->nColumns);
	}
	break;
      case 2:
	if (blk0->xMax < blk1->xMin) {
	  col2 = blk1->col + blk1->nColumns + 3;
	} else if (blk1->xMin == blk1->xMax) {
	  col2 = blk1->col;
	} else {
	  col2 = blk1->col + (int)(((blk0->xMax - blk1->xMax) /
				    (blk1->xMin - blk1->xMax)) *
				   blk1->nColumns);
	}
	break;
      case 3:
	if (blk0->yMax < blk1->yMin) {
	  col2 = blk1->col + blk1->nColumns + 3;
	} else if (blk1->yMin == blk1->yMax) {
	  col2 = blk1->col;
	} else {
	  col2 = blk1->col + (int)(((blk0->yMax - blk1->yMax) /
				    (blk1->yMin - blk1->yMax)) *
				   blk1->nColumns);
	}
	break;
      }
      if (col2 > col1) {
	col1 = col2;
      }
    }
    blk0->col = col1;
    for (line = blk0->lines; line; line = line->next) {
      for (j = 0; j <= line->len; ++j) {
	line->col[j] += col1;
      }
    }
  }

#if 0 // for debugging
  printf("*** blocks, after column assignment ***\n");
  for (blk = blkList; blk; blk = blk->next) {
    printf("block: rot=%d x=%.2f..%.2f y=%.2f..%.2f col=%d nCols=%d\n",
	   blk->rot, blk->xMin, blk->xMax, blk->yMin, blk->yMax, blk->col,
	   blk->nColumns);
    for (line = blk->lines; line; line = line->next) {
      printf("  line:\n");
      for (word0 = line->words; word0; word0 = word0->next) {
	printf("    word: x=%.2f..%.2f y=%.2f..%.2f base=%.2f fontSize=%.2f space=%d: '",
	       word0->xMin, word0->xMax, word0->yMin, word0->yMax,
	       word0->base, word0->fontSize, word0->spaceAfter);
	for (i = 0; i < word0->len; ++i) {
	  fputc(word0->text[i] & 0xff, stdout);
	}
	printf("'\n");
      }
    }
  }
  printf("\n");
#endif

  //----- reading order sort

  // sort blocks into yx order (in preparation for reading order sort)
  qsort(blocks, nBlocks, sizeof(TextBlock *), &TextBlock::cmpYXPrimaryRot);

  // compute space on left and right sides of each block
  for (i = 0; i < nBlocks; ++i) {
    blk0 = blocks[i];
    for (j = 0; j < nBlocks; ++j) {
      blk1 = blocks[j];
      if (blk1 != blk0) {
	blk0->updatePriMinMax(blk1);
      }
    }
  }

#if 0 // for debugging
  printf("*** blocks, after yx sort ***\n");
  for (i = 0; i < nBlocks; ++i) {
    blk = blocks[i];
    printf("block: rot=%d x=%.2f..%.2f y=%.2f..%.2f space=%.2f..%.2f\n",
	   blk->rot, blk->xMin, blk->xMax, blk->yMin, blk->yMax,
	   blk->priMin, blk->priMax);
    for (line = blk->lines; line; line = line->next) {
      printf("  line:\n");
      for (word0 = line->words; word0; word0 = word0->next) {
	printf("    word: x=%.2f..%.2f y=%.2f..%.2f base=%.2f fontSize=%.2f space=%d: '",
	       word0->xMin, word0->xMax, word0->yMin, word0->yMax,
	       word0->base, word0->fontSize, word0->spaceAfter);
	for (j = 0; j < word0->len; ++j) {
	  fputc(word0->text[j] & 0xff, stdout);
	}
	printf("'\n");
      }
    }
  }
  printf("\n");
#endif

  // build the flows
  //~ this needs to be adjusted for writing mode (vertical text)
  //~ this also needs to account for right-to-left column ordering
  blkArray = (TextBlock **)gmallocn(nBlocks, sizeof(TextBlock *));
  memcpy(blkArray, blocks, nBlocks * sizeof(TextBlock *));
  flows = lastFlow = NULL;
  firstBlkIdx = 0;
  nBlocksLeft = nBlocks;
  while (nBlocksLeft > 0) {

    // find the upper-left-most block
    for (; !blkArray[firstBlkIdx]; ++firstBlkIdx) ;
    i = firstBlkIdx;
    blk = blkArray[i];
    for (j = firstBlkIdx + 1; j < nBlocks; ++j) {
      blk1 = blkArray[j];
      if (blk1) {
	if (blk && blk->secondaryDelta(blk1) > 0) {
	  break;
	}
	if (blk1->primaryCmp(blk) < 0) {
	  i = j;
	  blk = blk1;
	}
      }
    }
    blkArray[i] = NULL;
    --nBlocksLeft;
    blk->next = NULL;

    // create a new flow, starting with the upper-left-most block
    flow = new TextFlow(this, blk);
    if (lastFlow) {
      lastFlow->next = flow;
    } else {
      flows = flow;
    }
    lastFlow = flow;
    fontSize = blk->lines->words->fontSize;

    // push the upper-left-most block on the stack
    blk->stackNext = NULL;
    blkStack = blk;

    // find the other blocks in this flow
    while (blkStack) {

      // find the upper-left-most block under (but within
      // maxBlockSpacing of) the top block on the stack
      blkSpace = maxBlockSpacing * blkStack->lines->words->fontSize;
      blk = NULL;
      i = -1;
      for (j = firstBlkIdx; j < nBlocks; ++j) {
	blk1 = blkArray[j];
	if (blk1) {
	  if (blkStack->secondaryDelta(blk1) > blkSpace) {
	    break;
	  }
	  if (blk && blk->secondaryDelta(blk1) > 0) {
	    break;
	  }
	  if (blk1->isBelow(blkStack) &&
	      (!blk || blk1->primaryCmp(blk) < 0)) {
	    i = j;
	    blk = blk1;
	  }
	}
      }

      // if a suitable block was found, add it to the flow and push it
      // onto the stack
      if (blk && flow->blockFits(blk, blkStack)) {
	blkArray[i] = NULL;
	--nBlocksLeft;
	blk->next = NULL;
	flow->addBlock(blk);
	fontSize = blk->lines->words->fontSize;
	blk->stackNext = blkStack;
	blkStack = blk;

      // otherwise (if there is no block under the top block or the
      // block is not suitable), pop the stack
      } else {
	blkStack = blkStack->stackNext;
      }
    }
  }
  gfree(blkArray);

#if 0 // for debugging
  printf("*** flows ***\n");
  for (flow = flows; flow; flow = flow->next) {
    printf("flow: x=%.2f..%.2f y=%.2f..%.2f pri:%.2f..%.2f\n",
	   flow->xMin, flow->xMax, flow->yMin, flow->yMax,
	   flow->priMin, flow->priMax);
    for (blk = flow->blocks; blk; blk = blk->next) {
      printf("  block: rot=%d x=%.2f..%.2f y=%.2f..%.2f pri=%.2f..%.2f\n",
	     blk->rot, blk->xMin, blk->xMax, blk->yMin, blk->yMax,
	     blk->priMin, blk->priMax);
      for (line = blk->lines; line; line = line->next) {
	printf("    line:\n");
	for (word0 = line->words; word0; word0 = word0->next) {
	  printf("      word: x=%.2f..%.2f y=%.2f..%.2f base=%.2f fontSize=%.2f space=%d: '",
		 word0->xMin, word0->xMax, word0->yMin, word0->yMax,
		 word0->base, word0->fontSize, word0->spaceAfter);
	  for (i = 0; i < word0->len; ++i) {
	    fputc(word0->text[i] & 0xff, stdout);
	  }
	  printf("'\n");
	}
      }
    }
  }
  printf("\n");
#endif

  if (uMap) {
    uMap->decRefCnt();
  }
}

GBool TextPage::findText(Unicode *s, int len,
			 GBool startAtTop, GBool stopAtBottom,
			 GBool startAtLast, GBool stopAtLast,
			 GBool caseSensitive, GBool backward,
			 double *xMin, double *yMin,
			 double *xMax, double *yMax) {
  TextBlock *blk;
  TextLine *line;
  Unicode *s2, *txt;
  Unicode *p;
  int txtSize, m, i, j, k;
  double xStart, yStart, xStop, yStop;
  double xMin0, yMin0, xMax0, yMax0;
  double xMin1, yMin1, xMax1, yMax1;
  GBool found;

  //~ needs to handle right-to-left text

  if (rawOrder) {
    return gFalse;
  }

  // convert the search string to uppercase
  if (!caseSensitive) {
    s2 = (Unicode *)gmallocn(len, sizeof(Unicode));
    for (i = 0; i < len; ++i) {
      s2[i] = unicodeToUpper(s[i]);
    }
  } else {
    s2 = s;
  }

  txt = NULL;
  txtSize = 0;

  xStart = yStart = xStop = yStop = 0;
  if (startAtLast && haveLastFind) {
    xStart = lastFindXMin;
    yStart = lastFindYMin;
  } else if (!startAtTop) {
    xStart = *xMin;
    yStart = *yMin;
  }
  if (stopAtLast && haveLastFind) {
    xStop = lastFindXMin;
    yStop = lastFindYMin;
  } else if (!stopAtBottom) {
    xStop = *xMax;
    yStop = *yMax;
  }

  found = gFalse;
  xMin0 = xMax0 = yMin0 = yMax0 = 0; // make gcc happy
  xMin1 = xMax1 = yMin1 = yMax1 = 0; // make gcc happy

  for (i = backward ? nBlocks - 1 : 0;
       backward ? i >= 0 : i < nBlocks;
       i += backward ? -1 : 1) {
    blk = blocks[i];

    // check: is the block above the top limit?
    if (!startAtTop && (backward ? blk->yMin > yStart : blk->yMax < yStart)) {
      continue;
    }

    // check: is the block below the bottom limit?
    if (!stopAtBottom && (backward ? blk->yMax < yStop : blk->yMin > yStop)) {
      break;
    }

    for (line = blk->lines; line; line = line->next) {

      // check: is the line above the top limit?
      if (!startAtTop &&
	  (backward ? line->yMin > yStart : line->yMin < yStart)) {
	continue;
      }

      // check: is the line below the bottom limit?
      if (!stopAtBottom &&
	  (backward ? line->yMin < yStop : line->yMin > yStop)) {
	continue;
      }

      // convert the line to uppercase
      m = line->len;
      if (!caseSensitive) {
	if (m > txtSize) {
	  txt = (Unicode *)greallocn(txt, m, sizeof(Unicode));
	  txtSize = m;
	}
	for (k = 0; k < m; ++k) {
	  txt[k] = unicodeToUpper(line->text[k]);
	}
      } else {
	txt = line->text;
      }

      // search each position in this line
      j = backward ? m - len : 0;
      p = txt + j;
      while (backward ? j >= 0 : j <= m - len) {

	// compare the strings
	for (k = 0; k < len; ++k) {
	  if (p[k] != s2[k]) {
	    break;
	  }
	}

	// found it
	if (k == len) {
	  switch (line->rot) {
	  case 0:
	    xMin1 = line->edge[j];
	    xMax1 = line->edge[j + len];
	    yMin1 = line->yMin;
	    yMax1 = line->yMax;
	    break;
	  case 1:
	    xMin1 = line->xMin;
	    xMax1 = line->xMax;
	    yMin1 = line->edge[j];
	    yMax1 = line->edge[j + len];
	    break;
	  case 2:
	    xMin1 = line->edge[j + len];
	    xMax1 = line->edge[j];
	    yMin1 = line->yMin;
	    yMax1 = line->yMax;
	    break;
	  case 3:
	    xMin1 = line->xMin;
	    xMax1 = line->xMax;
	    yMin1 = line->edge[j + len];
	    yMax1 = line->edge[j];
	    break;
	  }
	  if (backward) {
	    if ((startAtTop ||
		 yMin1 < yStart || (yMin1 == yStart && xMin1 < xStart)) &&
		(stopAtBottom ||
		 yMin1 > yStop || (yMin1 == yStop && xMin1 > xStop))) {
	      if (!found ||
		  yMin1 > yMin0 || (yMin1 == yMin0 && xMin1 > xMin0)) {
		xMin0 = xMin1;
		xMax0 = xMax1;
		yMin0 = yMin1;
		yMax0 = yMax1;
		found = gTrue;
	      }
	    }
	  } else {
	    if ((startAtTop ||
		 yMin1 > yStart || (yMin1 == yStart && xMin1 > xStart)) &&
		(stopAtBottom ||
		 yMin1 < yStop || (yMin1 == yStop && xMin1 < xStop))) {
	      if (!found ||
		  yMin1 < yMin0 || (yMin1 == yMin0 && xMin1 < xMin0)) {
		xMin0 = xMin1;
		xMax0 = xMax1;
		yMin0 = yMin1;
		yMax0 = yMax1;
		found = gTrue;
	      }
	    }
	  }
	}
	if (backward) {
	  --j;
	  --p;
	} else {
	  ++j;
	  ++p;
	}
      }
    }
  }

  if (!caseSensitive) {
    gfree(s2);
    gfree(txt);
  }

  if (found) {
    *xMin = xMin0;
    *xMax = xMax0;
    *yMin = yMin0;
    *yMax = yMax0;
    lastFindXMin = xMin0;
    lastFindYMin = yMin0;
    haveLastFind = gTrue;
    return gTrue;
  }

  return gFalse;
}

GString *TextPage::getText(double xMin, double yMin,
			   double xMax, double yMax) {
  GString *s;
  UnicodeMap *uMap;
  GBool isUnicode;
  TextBlock *blk;
  TextLine *line;
  TextLineFrag *frags;
  int nFrags, fragsSize;
  TextLineFrag *frag;
  char space[8], eol[16];
  int spaceLen, eolLen;
  int lastRot;
  double x, y, delta;
  int col, idx0, idx1, i, j;
  GBool multiLine, oneRot;

  s = new GString();

  if (rawOrder) {
    return s;
  }

  // get the output encoding
  if (!(uMap = globalParams->getTextEncoding())) {
    return s;
  }
  isUnicode = uMap->isUnicode();
  spaceLen = uMap->mapUnicode(0x20, space, sizeof(space));
  eolLen = 0; // make gcc happy
  switch (globalParams->getTextEOL()) {
  case eolUnix:
    eolLen = uMap->mapUnicode(0x0a, eol, sizeof(eol));
    break;
  case eolDOS:
    eolLen = uMap->mapUnicode(0x0d, eol, sizeof(eol));
    eolLen += uMap->mapUnicode(0x0a, eol + eolLen, sizeof(eol) - eolLen);
    break;
  case eolMac:
    eolLen = uMap->mapUnicode(0x0d, eol, sizeof(eol));
    break;
  }

  //~ writing mode (horiz/vert)

  // collect the line fragments that are in the rectangle
  fragsSize = 256;
  frags = (TextLineFrag *)gmallocn(fragsSize, sizeof(TextLineFrag));
  nFrags = 0;
  lastRot = -1;
  oneRot = gTrue;
  for (i = 0; i < nBlocks; ++i) {
    blk = blocks[i];
    if (xMin < blk->xMax && blk->xMin < xMax &&
	yMin < blk->yMax && blk->yMin < yMax) {
      for (line = blk->lines; line; line = line->next) {
	if (xMin < line->xMax && line->xMin < xMax &&
	    yMin < line->yMax && line->yMin < yMax) {
	  idx0 = idx1 = -1;
	  switch (line->rot) {
	  case 0:
	    y = 0.5 * (line->yMin + line->yMax);
	    if (yMin < y && y < yMax) {
	      j = 0;
	      while (j < line->len) {
		if (0.5 * (line->edge[j] + line->edge[j+1]) > xMin) {
		  idx0 = j;
		  break;
		}
		++j;
	      }
	      j = line->len - 1;
	      while (j >= 0) {
		if (0.5 * (line->edge[j] + line->edge[j+1]) < xMax) {
		  idx1 = j;
		  break;
		}
		--j;
	      }
	    }
	    break;
	  case 1:
	    x = 0.5 * (line->xMin + line->xMax);
	    if (xMin < x && x < xMax) {
	      j = 0;
	      while (j < line->len) {
		if (0.5 * (line->edge[j] + line->edge[j+1]) > yMin) {
		  idx0 = j;
		  break;
		}
		++j;
	      }
	      j = line->len - 1;
	      while (j >= 0) {
		if (0.5 * (line->edge[j] + line->edge[j+1]) < yMax) {
		  idx1 = j;
		  break;
		}
		--j;
	      }
	    }
	    break;
	  case 2:
	    y = 0.5 * (line->yMin + line->yMax);
	    if (yMin < y && y < yMax) {
	      j = 0;
	      while (j < line->len) {
		if (0.5 * (line->edge[j] + line->edge[j+1]) < xMax) {
		  idx0 = j;
		  break;
		}
		++j;
	      }
	      j = line->len - 1;
	      while (j >= 0) {
		if (0.5 * (line->edge[j] + line->edge[j+1]) > xMin) {
		  idx1 = j;
		  break;
		}
		--j;
	      }
	    }
	    break;
	  case 3:
	    x = 0.5 * (line->xMin + line->xMax);
	    if (xMin < x && x < xMax) {
	      j = 0;
	      while (j < line->len) {
		if (0.5 * (line->edge[j] + line->edge[j+1]) < yMax) {
		  idx0 = j;
		  break;
		}
		++j;
	      }
	      j = line->len - 1;
	      while (j >= 0) {
		if (0.5 * (line->edge[j] + line->edge[j+1]) > yMin) {
		  idx1 = j;
		  break;
		}
		--j;
	      }
	    }
	    break;
	  }
	  if (idx0 >= 0 && idx1 >= 0) {
	    if (nFrags == fragsSize) {
	      fragsSize *= 2;
	      frags = (TextLineFrag *)
		          greallocn(frags, fragsSize, sizeof(TextLineFrag));
	    }
	    frags[nFrags].init(line, idx0, idx1 - idx0 + 1);
	    ++nFrags;
	    if (lastRot >= 0 && line->rot != lastRot) {
	      oneRot = gFalse;
	    }
	    lastRot = line->rot;
	  }
	}
      }
    }
  }

  // sort the fragments and generate the string
  if (nFrags > 0) {

    for (i = 0; i < nFrags; ++i) {
      frags[i].computeCoords(oneRot);
    }
    assignColumns(frags, nFrags, oneRot);

    // if all lines in the region have the same rotation, use it;
    // otherwise, use the page's primary rotation
    if (oneRot) {
      qsort(frags, nFrags, sizeof(TextLineFrag),
	    &TextLineFrag::cmpYXLineRot);
    } else {
      qsort(frags, nFrags, sizeof(TextLineFrag),
	    &TextLineFrag::cmpYXPrimaryRot);
    }
    i = 0;
    while (i < nFrags) {
      delta = maxIntraLineDelta * frags[i].line->words->fontSize;
      for (j = i+1;
	   j < nFrags && fabs(frags[j].base - frags[i].base) < delta;
	   ++j) ;
      qsort(frags + i, j - i, sizeof(TextLineFrag),
	    oneRot ? &TextLineFrag::cmpXYColumnLineRot
	           : &TextLineFrag::cmpXYColumnPrimaryRot);
      i = j;
    }

    col = 0;
    multiLine = gFalse;
    for (i = 0; i < nFrags; ++i) {
      frag = &frags[i];

      // insert a return
      if (frag->col < col ||
	  (i > 0 && fabs(frag->base - frags[i-1].base) >
	              maxIntraLineDelta * frags[i-1].line->words->fontSize)) {
	s->append(eol, eolLen);
	col = 0;
	multiLine = gTrue;
      }

      // column alignment
      for (; col < frag->col; ++col) {
	s->append(space, spaceLen);
      }

      // get the fragment text
      col += dumpFragment(frag->line->text + frag->start, frag->len, uMap, s);
    }

    if (multiLine) {
      s->append(eol, eolLen);
    }
  }

  gfree(frags);
  uMap->decRefCnt();

  return s;
}

GBool TextPage::findCharRange(int pos, int length,
			      double *xMin, double *yMin,
			      double *xMax, double *yMax) {
  TextBlock *blk;
  TextLine *line;
  TextWord *word;
  double xMin0, xMax0, yMin0, yMax0;
  double xMin1, xMax1, yMin1, yMax1;
  GBool first;
  int i, j0, j1;

  if (rawOrder) {
    return gFalse;
  }

  //~ this doesn't correctly handle:
  //~ - ranges split across multiple lines (the highlighted region
  //~   is the bounding box of all the parts of the range)
  //~ - cases where characters don't convert one-to-one into Unicode
  first = gTrue;
  xMin0 = xMax0 = yMin0 = yMax0 = 0; // make gcc happy
  xMin1 = xMax1 = yMin1 = yMax1 = 0; // make gcc happy
  for (i = 0; i < nBlocks; ++i) {
    blk = blocks[i];
    for (line = blk->lines; line; line = line->next) {
      for (word = line->words; word; word = word->next) {
	if (pos < word->charPos + word->charLen &&
	    word->charPos < pos + length) {
	  j0 = pos - word->charPos;
	  if (j0 < 0) {
	    j0 = 0;
	  }
	  j1 = pos + length - 1 - word->charPos;
	  if (j1 >= word->len) {
	    j1 = word->len - 1;
	  }
	  switch (line->rot) {
	  case 0:
	    xMin1 = word->edge[j0];
	    xMax1 = word->edge[j1 + 1];
	    yMin1 = word->yMin;
	    yMax1 = word->yMax;
	    break;
	  case 1:
	    xMin1 = word->xMin;
	    xMax1 = word->xMax;
	    yMin1 = word->edge[j0];
	    yMax1 = word->edge[j1 + 1];
	    break;
	  case 2:
	    xMin1 = word->edge[j1 + 1];
	    xMax1 = word->edge[j0];
	    yMin1 = word->yMin;
	    yMax1 = word->yMax;
	    break;
	  case 3:
	    xMin1 = word->xMin;
	    xMax1 = word->xMax;
	    yMin1 = word->edge[j1 + 1];
	    yMax1 = word->edge[j0];
	    break;
	  }
	  if (first || xMin1 < xMin0) {
	    xMin0 = xMin1;
	  }
	  if (first || xMax1 > xMax0) {
	    xMax0 = xMax1;
	  }
	  if (first || yMin1 < yMin0) {
	    yMin0 = yMin1;
	  }
	  if (first || yMax1 > yMax0) {
	    yMax0 = yMax1;
	  }
	  first = gFalse;
	}
      }
    }
  }
  if (!first) {
    *xMin = xMin0;
    *xMax = xMax0;
    *yMin = yMin0;
    *yMax = yMax0;
    return gTrue;
  }
  return gFalse;
}

void TextPage::dump(void *outputStream, TextOutputFunc outputFunc,
		    GBool physLayout) {
  UnicodeMap *uMap;
  TextFlow *flow;
  TextBlock *blk;
  TextLine *line;
  TextLineFrag *frags;
  TextWord *word;
  int nFrags, fragsSize;
  TextLineFrag *frag;
  char space[8], eol[16], eop[8];
  int spaceLen, eolLen, eopLen;
  GBool pageBreaks;
  GString *s;
  double delta;
  int col, i, j, d, n;

  // get the output encoding
  if (!(uMap = globalParams->getTextEncoding())) {
    return;
  }
  spaceLen = uMap->mapUnicode(0x20, space, sizeof(space));
  eolLen = 0; // make gcc happy
  switch (globalParams->getTextEOL()) {
  case eolUnix:
    eolLen = uMap->mapUnicode(0x0a, eol, sizeof(eol));
    break;
  case eolDOS:
    eolLen = uMap->mapUnicode(0x0d, eol, sizeof(eol));
    eolLen += uMap->mapUnicode(0x0a, eol + eolLen, sizeof(eol) - eolLen);
    break;
  case eolMac:
    eolLen = uMap->mapUnicode(0x0d, eol, sizeof(eol));
    break;
  }
  eopLen = uMap->mapUnicode(0x0c, eop, sizeof(eop));
  pageBreaks = globalParams->getTextPageBreaks();

  //~ writing mode (horiz/vert)

  // output the page in raw (content stream) order
  if (rawOrder) {

    for (word = rawWords; word; word = word->next) {
      s = new GString();
      dumpFragment(word->text, word->len, uMap, s);
      (*outputFunc)(outputStream, s->getCString(), s->getLength());
      delete s;
      if (word->next &&
	  fabs(word->next->base - word->base) <
	    maxIntraLineDelta * word->fontSize) {
	if (word->next->xMin > word->xMax + minWordSpacing * word->fontSize) {
	  (*outputFunc)(outputStream, space, spaceLen);
	}
      } else {
	(*outputFunc)(outputStream, eol, eolLen);
      }
    }

  // output the page, maintaining the original physical layout
  } else if (physLayout) {

    // collect the line fragments for the page and sort them
    fragsSize = 256;
    frags = (TextLineFrag *)gmallocn(fragsSize, sizeof(TextLineFrag));
    nFrags = 0;
    for (i = 0; i < nBlocks; ++i) {
      blk = blocks[i];
      for (line = blk->lines; line; line = line->next) {
	if (nFrags == fragsSize) {
	  fragsSize *= 2;
	  frags = (TextLineFrag *)greallocn(frags,
					    fragsSize, sizeof(TextLineFrag));
	}
	frags[nFrags].init(line, 0, line->len);
	frags[nFrags].computeCoords(gTrue);
	++nFrags;
      }
    }
    qsort(frags, nFrags, sizeof(TextLineFrag), &TextLineFrag::cmpYXPrimaryRot);
    i = 0;
    while (i < nFrags) {
      delta = maxIntraLineDelta * frags[i].line->words->fontSize;
      for (j = i+1;
	   j < nFrags && fabs(frags[j].base - frags[i].base) < delta;
	   ++j) ;
      qsort(frags + i, j - i, sizeof(TextLineFrag),
	    &TextLineFrag::cmpXYColumnPrimaryRot);
      i = j;
    }

#if 0 // for debugging
    printf("*** line fragments ***\n");
    for (i = 0; i < nFrags; ++i) {
      frag = &frags[i];
      printf("frag: x=%.2f..%.2f y=%.2f..%.2f base=%.2f '",
	     frag->xMin, frag->xMax, frag->yMin, frag->yMax, frag->base);
      for (n = 0; n < frag->len; ++n) {
	fputc(frag->line->text[frag->start + n] & 0xff, stdout);
      }
      printf("'\n");
    }
    printf("\n");
#endif

    // generate output
    col = 0;
    for (i = 0; i < nFrags; ++i) {
      frag = &frags[i];

      // column alignment
      for (; col < frag->col; ++col) {
	(*outputFunc)(outputStream, space, spaceLen);
      }

      // print the line
      s = new GString();
      col += dumpFragment(frag->line->text + frag->start, frag->len, uMap, s);
      (*outputFunc)(outputStream, s->getCString(), s->getLength());
      delete s;

      // print one or more returns if necessary
      if (i == nFrags - 1 ||
	  frags[i+1].col < col ||
	  fabs(frags[i+1].base - frag->base) >
	    maxIntraLineDelta * frag->line->words->fontSize) {
	if (i < nFrags - 1) {
	  d = (int)((frags[i+1].base - frag->base) /
		    frag->line->words->fontSize);
	  if (d < 1) {
	    d = 1;
	  } else if (d > 5) {
	    d = 5;
	  }
	} else {
	  d = 1;
	}
	for (; d > 0; --d) {
	  (*outputFunc)(outputStream, eol, eolLen);
	}
	col = 0;
      }
    }

    gfree(frags);

  // output the page, "undoing" the layout
  } else {
    for (flow = flows; flow; flow = flow->next) {
      for (blk = flow->blocks; blk; blk = blk->next) {
	for (line = blk->lines; line; line = line->next) {
	  n = line->len;
	  if (line->hyphenated && (line->next || blk->next)) {
	    --n;
	  }
	  s = new GString();
	  dumpFragment(line->text, n, uMap, s);
	  (*outputFunc)(outputStream, s->getCString(), s->getLength());
	  delete s;
	  if (!line->hyphenated) {
	    if (line->next) {
	      (*outputFunc)(outputStream, space, spaceLen);
	    } else if (blk->next) {
	      //~ this is a bit of a kludge - we should really do a more
	      //~ intelligent determination of paragraphs
	      if (blk->next->lines->words->fontSize ==
		  blk->lines->words->fontSize) {
		(*outputFunc)(outputStream, space, spaceLen);
	      } else {
		(*outputFunc)(outputStream, eol, eolLen);
	      }
	    }
	  }
	}
      }
      (*outputFunc)(outputStream, eol, eolLen);
      (*outputFunc)(outputStream, eol, eolLen);
    }
  }

  // end of page
  if (pageBreaks) {
    (*outputFunc)(outputStream, eop, eopLen);
  }

  uMap->decRefCnt();
}

void TextPage::assignColumns(TextLineFrag *frags, int nFrags, GBool oneRot) {
  TextLineFrag *frag0, *frag1;
  int rot, col1, col2, i, j, k;

  // all text in the region has the same rotation -- recompute the
  // column numbers based only on the text in the region
  if (oneRot) {
    qsort(frags, nFrags, sizeof(TextLineFrag), &TextLineFrag::cmpXYLineRot);
    rot = frags[0].line->rot;
    for (i = 0; i < nFrags; ++i) {
      frag0 = &frags[i];
      col1 = 0;
      for (j = 0; j < i; ++j) {
	frag1 = &frags[j];
	col2 = 0; // make gcc happy
	switch (rot) {
	case 0:
	  if (frag0->xMin >= frag1->xMax) {
	    col2 = frag1->col + (frag1->line->col[frag1->start + frag1->len] -
				 frag1->line->col[frag1->start]) + 1;
	  } else {
	    for (k = frag1->start;
		 k < frag1->start + frag1->len &&
		   frag0->xMin >= 0.5 * (frag1->line->edge[k] +
					 frag1->line->edge[k+1]);
		 ++k) ;
	    col2 = frag1->col +
	           frag1->line->col[k] - frag1->line->col[frag1->start];
	  }
	  break;
	case 1:
	  if (frag0->yMin >= frag1->yMax) {
	    col2 = frag1->col + (frag1->line->col[frag1->start + frag1->len] -
				 frag1->line->col[frag1->start]) + 1;
	  } else {
	    for (k = frag1->start;
		 k < frag1->start + frag1->len &&
		   frag0->yMin >= 0.5 * (frag1->line->edge[k] +
					 frag1->line->edge[k+1]);
		 ++k) ;
	    col2 = frag1->col +
	           frag1->line->col[k] - frag1->line->col[frag1->start];
	  }
	  break;
	case 2:
	  if (frag0->xMax <= frag1->xMin) {
	    col2 = frag1->col + (frag1->line->col[frag1->start + frag1->len] -
				 frag1->line->col[frag1->start]) + 1;
	  } else {
	    for (k = frag1->start;
		 k < frag1->start + frag1->len &&
		   frag0->xMax <= 0.5 * (frag1->line->edge[k] +
					 frag1->line->edge[k+1]);
		 ++k) ;
	    col2 = frag1->col +
	           frag1->line->col[k] - frag1->line->col[frag1->start];
	  }
	  break;
	case 3:
	  if (frag0->yMax <= frag1->yMin) {
	    col2 = frag1->col + (frag1->line->col[frag1->start + frag1->len] -
				 frag1->line->col[frag1->start]) + 1;
	  } else {
	    for (k = frag1->start;
		 k < frag1->start + frag1->len &&
		   frag0->yMax <= 0.5 * (frag1->line->edge[k] +
					 frag1->line->edge[k+1]);
		 ++k) ;
	    col2 = frag1->col +
	           frag1->line->col[k] - frag1->line->col[frag1->start];
	  }
	  break;
	}
	if (col2 > col1) {
	  col1 = col2;
	}
      }
      frag0->col = col1;
    }

  // the region includes text at different rotations -- use the
  // globally assigned column numbers, offset by the minimum column
  // number (i.e., shift everything over to column 0)
  } else {
    col1 = frags[0].col;
    for (i = 1; i < nFrags; ++i) {
      if (frags[i].col < col1) {
	col1 = frags[i].col;
      }
    }
    for (i = 0; i < nFrags; ++i) {
      frags[i].col -= col1;
    }
  }
}

int TextPage::dumpFragment(Unicode *text, int len, UnicodeMap *uMap,
			   GString *s) {
  char lre[8], rle[8], popdf[8], buf[8];
  int lreLen, rleLen, popdfLen, n;
  int nCols, i, j, k;

  nCols = 0;

  if (uMap->isUnicode()) {

    lreLen = uMap->mapUnicode(0x202a, lre, sizeof(lre));
    rleLen = uMap->mapUnicode(0x202b, rle, sizeof(rle));
    popdfLen = uMap->mapUnicode(0x202c, popdf, sizeof(popdf));

    if (primaryLR) {

      i = 0;
      while (i < len) {
	// output a left-to-right section
	for (j = i; j < len && !unicodeTypeR(text[j]); ++j) ;
	for (k = i; k < j; ++k) {
	  n = uMap->mapUnicode(text[k], buf, sizeof(buf));
	  s->append(buf, n);
	  ++nCols;
	}
	i = j;
	// output a right-to-left section
	for (j = i; j < len && !unicodeTypeL(text[j]); ++j) ;
	if (j > i) {
	  s->append(rle, rleLen);
	  for (k = j - 1; k >= i; --k) {
	    n = uMap->mapUnicode(text[k], buf, sizeof(buf));
	    s->append(buf, n);
	    ++nCols;
	  }
	  s->append(popdf, popdfLen);
	  i = j;
	}
      }

    } else {

      s->append(rle, rleLen);
      i = len - 1;
      while (i >= 0) {
	// output a right-to-left section
	for (j = i; j >= 0 && !unicodeTypeL(text[j]); --j) ;
	for (k = i; k > j; --k) {
	  n = uMap->mapUnicode(text[k], buf, sizeof(buf));
	  s->append(buf, n);
	  ++nCols;
	}
	i = j;
	// output a left-to-right section
	for (j = i; j >= 0 && !unicodeTypeR(text[j]); --j) ;
	if (j < i) {
	  s->append(lre, lreLen);
	  for (k = j + 1; k <= i; ++k) {
	    n = uMap->mapUnicode(text[k], buf, sizeof(buf));
	    s->append(buf, n);
	    ++nCols;
	  }
	  s->append(popdf, popdfLen);
	  i = j;
	}
      }
      s->append(popdf, popdfLen);

    }

  } else {
    for (i = 0; i < len; ++i) {
      n = uMap->mapUnicode(text[i], buf, sizeof(buf));
      s->append(buf, n);
      nCols += n;
    }
  }

  return nCols;
}

#if TEXTOUT_WORD_LIST
TextWordList *TextPage::makeWordList(GBool physLayout) {
  return new TextWordList(this, physLayout);
}
#endif

//------------------------------------------------------------------------
// TextOutputDev
//------------------------------------------------------------------------

static void outputToFile(void *stream, char *text, int len) {
  fwrite(text, 1, len, (FILE *)stream);
}

TextOutputDev::TextOutputDev(char *fileName, GBool physLayoutA,
			     GBool rawOrderA, GBool append) {
  text = NULL;
  physLayout = physLayoutA;
  rawOrder = rawOrderA;
  doHTML = gFalse;
  ok = gTrue;

  // open file
  needClose = gFalse;
  if (fileName) {
    if (!strcmp(fileName, "-")) {
      outputStream = stdout;
#ifdef WIN32
      // keep DOS from munging the end-of-line characters
      setmode(fileno(stdout), O_BINARY);
#endif
    } else if ((outputStream = fopen(fileName, append ? "ab" : "wb"))) {
      needClose = gTrue;
    } else {
      error(-1, "Couldn't open text file '%s'", fileName);
      ok = gFalse;
      return;
    }
    outputFunc = &outputToFile;
  } else {
    outputStream = NULL;
  }

  // set up text object
  text = new TextPage(rawOrderA);
}

TextOutputDev::TextOutputDev(TextOutputFunc func, void *stream,
			     GBool physLayoutA, GBool rawOrderA) {
  outputFunc = func;
  outputStream = stream;
  needClose = gFalse;
  physLayout = physLayoutA;
  rawOrder = rawOrderA;
  doHTML = gFalse;
  text = new TextPage(rawOrderA);
  ok = gTrue;
}

TextOutputDev::~TextOutputDev() {
  if (needClose) {
#ifdef MACOS
    ICS_MapRefNumAndAssign((short)((FILE *)outputStream)->handle);
#endif
    fclose((FILE *)outputStream);
  }
  if (text) {
    delete text;
  }
}

void TextOutputDev::startPage(int pageNum, GfxState *state) {
  text->startPage(state);
}

void TextOutputDev::endPage() {
  text->endPage();
  text->coalesce(physLayout, doHTML);
  if (outputStream) {
    text->dump(outputStream, outputFunc, physLayout);
  }
}

void TextOutputDev::updateFont(GfxState *state) {
  text->updateFont(state);
}

void TextOutputDev::beginString(GfxState *state, GString *s) {
}

void TextOutputDev::endString(GfxState *state) {
}

void TextOutputDev::drawChar(GfxState *state, double x, double y,
			     double dx, double dy,
			     double originX, double originY,
			     CharCode c, int nBytes, Unicode *u, int uLen) {
  text->addChar(state, x, y, dx, dy, c, nBytes, u, uLen);
}

void TextOutputDev::stroke(GfxState *state) {
  GfxPath *path;
  GfxSubpath *subpath;
  double x[2], y[2];

  if (!doHTML) {
    return;
  }
  path = state->getPath();
  if (path->getNumSubpaths() != 1) {
    return;
  }
  subpath = path->getSubpath(0);
  if (subpath->getNumPoints() != 2) {
    return;
  }
  state->transform(subpath->getX(0), subpath->getY(0), &x[0], &y[0]);
  state->transform(subpath->getX(1), subpath->getY(1), &x[1], &y[1]);

  // look for a vertical or horizontal line
  if (x[0] == x[1] || y[0] == y[1]) {
    text->addUnderline(x[0], y[0], x[1], y[1]);
  }
}

void TextOutputDev::fill(GfxState *state) {
  GfxPath *path;
  GfxSubpath *subpath;
  double x[5], y[5];
  double rx0, ry0, rx1, ry1, t;
  int i;

  if (!doHTML) {
    return;
  }
  path = state->getPath();
  if (path->getNumSubpaths() != 1) {
    return;
  }
  subpath = path->getSubpath(0);
  if (subpath->getNumPoints() != 5) {
    return;
  }
  for (i = 0; i < 5; ++i) {
    if (subpath->getCurve(i)) {
      return;
    }
    state->transform(subpath->getX(i), subpath->getY(i), &x[i], &y[i]);
  }

  // look for a rectangle
  if (x[0] == x[1] && y[1] == y[2] && x[2] == x[3] && y[3] == y[4] &&
      x[0] == x[4] && y[0] == y[4]) {
    rx0 = x[0];
    ry0 = y[0];
    rx1 = x[2];
    ry1 = y[1];
  } else if (y[0] == y[1] && x[1] == x[2] && y[2] == y[3] && x[3] == x[4] &&
	     x[0] == x[4] && y[0] == y[4]) {
    rx0 = x[0];
    ry0 = y[0];
    rx1 = x[1];
    ry1 = y[2];
  } else {
    return;
  }
  if (rx1 < rx0) {
    t = rx0;
    rx0 = rx1;
    rx1 = t;
  }
  if (ry1 < ry0) {
    t = ry0;
    ry0 = ry1;
    ry1 = t;
  }

  // skinny horizontal rectangle
  if (ry1 - ry0 < rx1 - rx0) {
    if (ry1 - ry0 < maxUnderlineWidth) {
      ry0 = 0.5 * (ry0 + ry1);
      text->addUnderline(rx0, ry0, rx1, ry0);
    }

  // skinny vertical rectangle
  } else {
    if (rx1 - rx0 < maxUnderlineWidth) {
      rx0 = 0.5 * (rx0 + rx1);
      text->addUnderline(rx0, ry0, rx0, ry1);
    }
  }
}

void TextOutputDev::eoFill(GfxState *state) {
  if (!doHTML) {
    return;
  }
  fill(state);
}

void TextOutputDev::processLink(Link *link, Catalog *catalog) {
  double x1, y1, x2, y2;
  int xMin, yMin, xMax, yMax, x, y;

  if (!doHTML) {
    return;
  }
  link->getRect(&x1, &y1, &x2, &y2);
  cvtUserToDev(x1, y1, &x, &y);
  xMin = xMax = x;
  yMin = yMax = y;
  cvtUserToDev(x1, y2, &x, &y);
  if (x < xMin) {
    xMin = x;
  } else if (x > xMax) {
    xMax = x;
  }
  if (y < yMin) {
    yMin = y;
  } else if (y > yMax) {
    yMax = y;
  }
  cvtUserToDev(x2, y1, &x, &y);
  if (x < xMin) {
    xMin = x;
  } else if (x > xMax) {
    xMax = x;
  }
  if (y < yMin) {
    yMin = y;
  } else if (y > yMax) {
    yMax = y;
  }
  cvtUserToDev(x2, y2, &x, &y);
  if (x < xMin) {
    xMin = x;
  } else if (x > xMax) {
    xMax = x;
  }
  if (y < yMin) {
    yMin = y;
  } else if (y > yMax) {
    yMax = y;
  }
  text->addLink(xMin, yMin, xMax, yMax, link);
}

GBool TextOutputDev::findText(Unicode *s, int len,
			      GBool startAtTop, GBool stopAtBottom,
			      GBool startAtLast, GBool stopAtLast,
			      GBool caseSensitive, GBool backward,
			      double *xMin, double *yMin,
			      double *xMax, double *yMax) {
  return text->findText(s, len, startAtTop, stopAtBottom,
			startAtLast, stopAtLast, caseSensitive, backward,
			xMin, yMin, xMax, yMax);
}

GString *TextOutputDev::getText(double xMin, double yMin,
				double xMax, double yMax) {
  return text->getText(xMin, yMin, xMax, yMax);
}

GBool TextOutputDev::findCharRange(int pos, int length,
				   double *xMin, double *yMin,
				   double *xMax, double *yMax) {
  return text->findCharRange(pos, length, xMin, yMin, xMax, yMax);
}

#if TEXTOUT_WORD_LIST
TextWordList *TextOutputDev::makeWordList() {
  return text->makeWordList(physLayout);
}
#endif

TextPage *TextOutputDev::takeText() {
  TextPage *ret;

  ret = text;
  text = new TextPage(rawOrder);
  return ret;
}
//========================================================================
//
// TextOutputDev.h
//
// Copyright 1997-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef TEXTOUTPUTDEV_H
#define TEXTOUTPUTDEV_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include <stdio.h>
#include "gtypes.h"
#include "GfxFont.h"
#include "OutputDev.h"

class GString;
class GList;
class GfxFont;
class GfxState;
class UnicodeMap;
class Link;

class TextWord;
class TextPool;
class TextLine;
class TextLineFrag;
class TextBlock;
class TextFlow;
class TextWordList;
class TextPage;

//------------------------------------------------------------------------

typedef void (*TextOutputFunc)(void *stream, char *text, int len);

//------------------------------------------------------------------------
// TextFontInfo
//------------------------------------------------------------------------

class TextFontInfo {
public:

  TextFontInfo(GfxState *state);
  ~TextFontInfo();

  GBool matches(GfxState *state);

#if TEXTOUT_WORD_LIST
  // Get the font name (which may be NULL).
  GString *getFontName() { return fontName; }

  // Get font descriptor flags.
  GBool isFixedWidth() { return flags & fontFixedWidth; }
  GBool isSerif() { return flags & fontSerif; }
  GBool isSymbolic() { return flags & fontSymbolic; }
  GBool isItalic() { return flags & fontItalic; }
  GBool isBold() { return flags & fontBold; }
#endif

private:

  GfxFont *gfxFont;
#if TEXTOUT_WORD_LIST
  GString *fontName;
  int flags;
#endif

  friend class TextWord;
  friend class TextPage;
};

//------------------------------------------------------------------------
// TextWord
//------------------------------------------------------------------------

class TextWord {
public:

  // Constructor.
  TextWord(GfxState *state, int rotA, double x0, double y0,
	   int charPosA, TextFontInfo *fontA, double fontSize);

  // Destructor.
  ~TextWord();

  // Add a character to the word.
  void addChar(GfxState *state, double x, double y,
	       double dx, double dy, Unicode u);

  // Merge <word> onto the end of <this>.
  void merge(TextWord *word);

  // Compares <this> to <word>, returning -1 (<), 0 (=), or +1 (>),
  // based on a primary-axis comparison, e.g., x ordering if rot=0.
  int primaryCmp(TextWord *word);

  // Return the distance along the primary axis between <this> and
  // <word>.
  double primaryDelta(TextWord *word);

  static int cmpYX(const void *p1, const void *p2);

  // Get the TextFontInfo object associated with this word.
  TextFontInfo *getFontInfo() { return font; }

  // Get the next TextWord on the linked list.
  TextWord *getNext() { return next; }

#if TEXTOUT_WORD_LIST
  int getLength() { return len; }
  Unicode getChar(int idx) { return text[idx]; }
  GString *getText();
  GString *getFontName() { return font->fontName; }
  void getColor(double *r, double *g, double *b)
    { *r = colorR; *g = colorG; *b = colorB; }
  void getBBox(double *xMinA, double *yMinA, double *xMaxA, double *yMaxA)
    { *xMinA = xMin; *yMinA = yMin; *xMaxA = xMax; *yMaxA = yMax; }
  void getCharBBox(int charIdx, double *xMinA, double *yMinA,
		   double *xMaxA, double *yMaxA);
  double getFontSize() { return fontSize; }
  int getRotation() { return rot; }
  int getCharPos() { return charPos; }
  int getCharLen() { return charLen; }
  GBool getSpaceAfter() { return spaceAfter; }
#endif

  GBool isUnderlined() { return underlined; }
  Link *getLink() { return link; }

private:

  int rot;			// rotation, multiple of 90 degrees
				//   (0, 1, 2, or 3)
  double xMin, xMax;		// bounding box x coordinates
  double yMin, yMax;		// bounding box y coordinates
  double base;			// baseline x or y coordinate
  Unicode *text;		// the text
  double *edge;			// "near" edge x or y coord of each char
				//   (plus one extra entry for the last char)
  int len;			// length of text and edge arrays
  int size;			// size of text and edge arrays
  int charPos;                  // character position (within content stream)
  int charLen;                  // number of content stream characters in
                                //   this word
  TextFontInfo *font;		// font information
  double fontSize;		// font size
  GBool spaceAfter;		// set if there is a space between this
				//   word and the next word on the line
  TextWord *next;		// next word in line

#if TEXTOUT_WORD_LIST
  double colorR,		// word color
         colorG,
         colorB;
#endif

  GBool underlined;
  Link *link;

  friend class TextPool;
  friend class TextLine;
  friend class TextBlock;
  friend class TextFlow;
  friend class TextWordList;
  friend class TextPage;
};

//------------------------------------------------------------------------
// TextPool
//------------------------------------------------------------------------

class TextPool {
public:

  TextPool();
  ~TextPool();

  TextWord *getPool(int baseIdx) { return pool[baseIdx - minBaseIdx]; }
  void setPool(int baseIdx, TextWord *p) { pool[baseIdx - minBaseIdx] = p; }

  int getBaseIdx(double base);

  void addWord(TextWord *word);

private:

  int minBaseIdx;		// min baseline bucket index
  int maxBaseIdx;		// max baseline bucket index
  TextWord **pool;		// array of linked lists, one for each
				//   baseline value (multiple of 4 pts)
  TextWord *cursor;		// pointer to last-accessed word
  int cursorBaseIdx;		// baseline bucket index of last-accessed word

  friend class TextBlock;
  friend class TextPage;
};

//------------------------------------------------------------------------
// TextLine
//------------------------------------------------------------------------

class TextLine {
public:

  TextLine(TextBlock *blkA, int rotA, double baseA);
  ~TextLine();

  void addWord(TextWord *word);

  // Return the distance along the primary axis between <this> and
  // <line>.
  double primaryDelta(TextLine *line);

  // Compares <this> to <line>, returning -1 (<), 0 (=), or +1 (>),
  // based on a primary-axis comparison, e.g., x ordering if rot=0.
  int primaryCmp(TextLine *line);

  // Compares <this> to <line>, returning -1 (<), 0 (=), or +1 (>),
  // based on a secondary-axis comparison of the baselines, e.g., y
  // ordering if rot=0.
  int secondaryCmp(TextLine *line);

  int cmpYX(TextLine *line);

  static int cmpXY(const void *p1, const void *p2);

  void coalesce(UnicodeMap *uMap);

  // Get the head of the linked list of TextWords.
  TextWord *getWords() { return words; }

  // Get the next TextLine on the linked list.
  TextLine *getNext() { return next; }

  // Returns true if the last char of the line is a hyphen.
  GBool isHyphenated() { return hyphenated; }

private:

  TextBlock *blk;		// parent block
  int rot;			// text rotation
  double xMin, xMax;		// bounding box x coordinates
  double yMin, yMax;		// bounding box y coordinates
  double base;			// baseline x or y coordinate
  TextWord *words;		// words in this line
  TextWord *lastWord;		// last word in this line
  Unicode *text;		// Unicode text of the line, including
				//   spaces between words
  double *edge;			// "near" edge x or y coord of each char
				//   (plus one extra entry for the last char)
  int *col;			// starting column number of each Unicode char
  int len;			// number of Unicode chars
  int convertedLen;		// total number of converted characters
  GBool hyphenated;		// set if last char is a hyphen
  TextLine *next;		// next line in block

  friend class TextLineFrag;
  friend class TextBlock;
  friend class TextFlow;
  friend class TextWordList;
  friend class TextPage;
};

//------------------------------------------------------------------------
// TextBlock
//------------------------------------------------------------------------

class TextBlock {
public:

  TextBlock(TextPage *pageA, int rotA);
  ~TextBlock();

  void addWord(TextWord *word);

  void coalesce(UnicodeMap *uMap);

  // Update this block's priMin and priMax values, looking at <blk>.
  void updatePriMinMax(TextBlock *blk);

  static int cmpXYPrimaryRot(const void *p1, const void *p2);

  static int cmpYXPrimaryRot(const void *p1, const void *p2);

  int primaryCmp(TextBlock *blk);

  double secondaryDelta(TextBlock *blk);

  // Returns true if <this> is below <blk>, relative to the page's
  // primary rotation.
  GBool isBelow(TextBlock *blk);

  // Get the head of the linked list of TextLines.
  TextLine *getLines() { return lines; }

  // Get the next TextBlock on the linked list.
  TextBlock *getNext() { return next; }

private:

  TextPage *page;		// the parent page
  int rot;			// text rotation
  double xMin, xMax;		// bounding box x coordinates
  double yMin, yMax;		// bounding box y coordinates
  double priMin, priMax;	// whitespace bounding box along primary axis

  TextPool *pool;		// pool of words (used only until lines
				//   are built)
  TextLine *lines;		// linked list of lines
  TextLine *curLine;		// most recently added line
  int nLines;			// number of lines
  int charCount;		// number of characters in the block
  int col;			// starting column
  int nColumns;			// number of columns in the block

  TextBlock *next;
  TextBlock *stackNext;

  friend class TextLine;
  friend class TextLineFrag;
  friend class TextFlow;
  friend class TextWordList;
  friend class TextPage;
};

//------------------------------------------------------------------------
// TextFlow
//------------------------------------------------------------------------

class TextFlow {
public:

  TextFlow(TextPage *pageA, TextBlock *blk);
  ~TextFlow();

  // Add a block to the end of this flow.
  void addBlock(TextBlock *blk);

  // Returns true if <blk> fits below <prevBlk> in the flow, i.e., (1)
  // it uses a font no larger than the last block added to the flow,
  // and (2) it fits within the flow's [priMin, priMax] along the
  // primary axis.
  GBool blockFits(TextBlock *blk, TextBlock *prevBlk);

  // Get the head of the linked list of TextBlocks.
  TextBlock *getBlocks() { return blocks; }

  // Get the next TextFlow on the linked list.
  TextFlow *getNext() { return next; }

private:

  TextPage *page;		// the parent page
  double xMin, xMax;		// bounding box x coordinates
  double yMin, yMax;		// bounding box y coordinates
  double priMin, priMax;	// whitespace bounding box along primary axis
  TextBlock *blocks;		// blocks in flow
  TextBlock *lastBlk;		// last block in this flow
  TextFlow *next;

  friend class TextWordList;
  friend class TextPage;
};

#if TEXTOUT_WORD_LIST

//------------------------------------------------------------------------
// TextWordList
//------------------------------------------------------------------------

class TextWordList {
public:

  // Build a flat word list, in content stream order (if
  // text->rawOrder is true), physical layout order (if <physLayout>
  // is true and text->rawOrder is false), or reading order (if both
  // flags are false).
  TextWordList(TextPage *text, GBool physLayout);

  ~TextWordList();

  // Return the number of words on the list.
  int getLength();

  // Return the <idx>th word from the list.
  TextWord *get(int idx);

private:

  GList *words;			// [TextWord]
};

#endif // TEXTOUT_WORD_LIST

//------------------------------------------------------------------------
// TextPage
//------------------------------------------------------------------------

class TextPage {
public:

  // Constructor.
  TextPage(GBool rawOrderA);

  // Destructor.
  ~TextPage();

  // Start a new page.
  void startPage(GfxState *state);

  // End the current page.
  void endPage();

  // Update the current font.
  void updateFont(GfxState *state);

  // Begin a new word.
  void beginWord(GfxState *state, double x0, double y0);

  // Add a character to the current word.
  void addChar(GfxState *state, double x, double y,
	       double dx, double dy,
	       CharCode c, int nBytes, Unicode *u, int uLen);

  // End the current word, sorting it into the list of words.
  void endWord();

  // Add a word, sorting it into the list of words.
  void addWord(TextWord *word);

  // Add a (potential) underline.
  void addUnderline(double x0, double y0, double x1, double y1);

  // Add a hyperlink.
  void addLink(int xMin, int yMin, int xMax, int yMax, Link *link);

  // Coalesce strings that look like parts of the same line.
  void coalesce(GBool physLayout, GBool doHTML);

  // Find a string.  If <startAtTop> is true, starts looking at the
  // top of the page; else if <startAtLast> is true, starts looking
  // immediately after the last find result; else starts looking at
  // <xMin>,<yMin>.  If <stopAtBottom> is true, stops looking at the
  // bottom of the page; else if <stopAtLast> is true, stops looking
  // just before the last find result; else stops looking at
  // <xMax>,<yMax>.
  GBool findText(Unicode *s, int len,
		 GBool startAtTop, GBool stopAtBottom,
		 GBool startAtLast, GBool stopAtLast,
		 GBool caseSensitive, GBool backward,
		 double *xMin, double *yMin,
		 double *xMax, double *yMax);

  // Get the text which is inside the specified rectangle.
  GString *getText(double xMin, double yMin,
		   double xMax, double yMax);

  // Find a string by character position and length.  If found, sets
  // the text bounding rectangle and returns true; otherwise returns
  // false.
  GBool findCharRange(int pos, int length,
		      double *xMin, double *yMin,
		      double *xMax, double *yMax);

  // Dump contents of page to a file.
  void dump(void *outputStream, TextOutputFunc outputFunc,
	    GBool physLayout);

  // Get the head of the linked list of TextFlows.
  TextFlow *getFlows() { return flows; }

#if TEXTOUT_WORD_LIST
  // Build a flat word list, in content stream order (if
  // this->rawOrder is true), physical layout order (if <physLayout>
  // is true and this->rawOrder is false), or reading order (if both
  // flags are false).
  TextWordList *makeWordList(GBool physLayout);
#endif

private:

  void clear();
  void assignColumns(TextLineFrag *frags, int nFrags, int rot);
  int dumpFragment(Unicode *text, int len, UnicodeMap *uMap, GString *s);

  GBool rawOrder;		// keep text in content stream order

  double pageWidth, pageHeight;	// width and height of current page
  TextWord *curWord;		// currently active string
  int charPos;			// next character position (within content
				//   stream)
  TextFontInfo *curFont;	// current font
  double curFontSize;		// current font size
  int nest;			// current nesting level (for Type 3 fonts)
  int nTinyChars;		// number of "tiny" chars seen so far
  GBool lastCharOverlap;	// set if the last added char overlapped the
				//   previous char

  TextPool *pools[4];		// a "pool" of TextWords for each rotation
  TextFlow *flows;		// linked list of flows
  TextBlock **blocks;		// array of blocks, in yx order
  int nBlocks;			// number of blocks
  int primaryRot;		// primary rotation
  GBool primaryLR;		// primary direction (true means L-to-R,
				//   false means R-to-L)
  TextWord *rawWords;		// list of words, in raw order (only if
				//   rawOrder is set)
  TextWord *rawLastWord;	// last word on rawWords list

  GList *fonts;			// all font info objects used on this
				//   page [TextFontInfo]

  double lastFindXMin,		// coordinates of the last "find" result
         lastFindYMin;
  GBool haveLastFind;

  GList *underlines;		// [TextUnderline]
  GList *links;			// [TextLink]

  friend class TextLine;
  friend class TextLineFrag;
  friend class TextBlock;
  friend class TextFlow;
  friend class TextWordList;
};

//------------------------------------------------------------------------
// TextOutputDev
//------------------------------------------------------------------------

class TextOutputDev: public OutputDev {
public:

  // Open a text output file.  If <fileName> is NULL, no file is
  // written (this is useful, e.g., for searching text).  If
  // <physLayoutA> is true, the original physical layout of the text
  // is maintained.  If <rawOrder> is true, the text is kept in
  // content stream order.
  TextOutputDev(char *fileName, GBool physLayoutA,
		GBool rawOrderA, GBool append);

  // Create a TextOutputDev which will write to a generic stream.  If
  // <physLayoutA> is true, the original physical layout of the text
  // is maintained.  If <rawOrder> is true, the text is kept in
  // content stream order.
  TextOutputDev(TextOutputFunc func, void *stream,
		GBool physLayoutA, GBool rawOrderA);

  // Destructor.
  virtual ~TextOutputDev();

  // Check if file was successfully created.
  virtual GBool isOk() { return ok; }

  //---- get info about output device

  // Does this device use upside-down coordinates?
  // (Upside-down means (0,0) is the top left corner of the page.)
  virtual GBool upsideDown() { return gTrue; }

  // Does this device use drawChar() or drawString()?
  virtual GBool useDrawChar() { return gTrue; }

  // Does this device use beginType3Char/endType3Char?  Otherwise,
  // text in Type 3 fonts will be drawn with drawChar/drawString.
  virtual GBool interpretType3Chars() { return gFalse; }

  // Does this device need non-text content?
  virtual GBool needNonText() { return gFalse; }

  //----- initialization and control

  // Start a page.
  virtual void startPage(int pageNum, GfxState *state);

  // End a page.
  virtual void endPage();

  //----- update text state
  virtual void updateFont(GfxState *state);

  //----- text drawing
  virtual void beginString(GfxState *state, GString *s);
  virtual void endString(GfxState *state);
  virtual void drawChar(GfxState *state, double x, double y,
			double dx, double dy,
			double originX, double originY,
			CharCode c, int nBytes, Unicode *u, int uLen);

  //----- path painting
  virtual void stroke(GfxState *state);
  virtual void fill(GfxState *state);
  virtual void eoFill(GfxState *state);

  //----- link borders
  virtual void processLink(Link *link, Catalog *catalog);

  //----- special access

  // Find a string.  If <startAtTop> is true, starts looking at the
  // top of the page; else if <startAtLast> is true, starts looking
  // immediately after the last find result; else starts looking at
  // <xMin>,<yMin>.  If <stopAtBottom> is true, stops looking at the
  // bottom of the page; else if <stopAtLast> is true, stops looking
  // just before the last find result; else stops looking at
  // <xMax>,<yMax>.
  GBool findText(Unicode *s, int len,
		 GBool startAtTop, GBool stopAtBottom,
		 GBool startAtLast, GBool stopAtLast,
		 GBool caseSensitive, GBool backward,
		 double *xMin, double *yMin,
		 double *xMax, double *yMax);

  // Get the text which is inside the specified rectangle.
  GString *getText(double xMin, double yMin,
		   double xMax, double yMax);

  // Find a string by character position and length.  If found, sets
  // the text bounding rectangle and returns true; otherwise returns
  // false.
  GBool findCharRange(int pos, int length,
		      double *xMin, double *yMin,
		      double *xMax, double *yMax);

#if TEXTOUT_WORD_LIST
  // Build a flat word list, in content stream order (if
  // this->rawOrder is true), physical layout order (if
  // this->physLayout is true and this->rawOrder is false), or reading
  // order (if both flags are false).
  TextWordList *makeWordList();
#endif

  // Returns the TextPage object for the last rasterized page,
  // transferring ownership to the caller.
  TextPage *takeText();

  // Turn extra processing for HTML conversion on or off.
  void enableHTMLExtras(GBool doHTMLA) { doHTML = doHTMLA; }

private:

  TextOutputFunc outputFunc;	// output function
  void *outputStream;		// output stream
  GBool needClose;		// need to close the output file?
				//   (only if outputStream is a FILE*)
  TextPage *text;		// text for the current page
  GBool physLayout;		// maintain original physical layout when
				//   dumping text
  GBool rawOrder;		// keep text in content stream order
  GBool doHTML;			// extra processing for HTML conversion
  GBool ok;			// set up ok?
};

#endif
//========================================================================
//
// UTF8.h
//
// Copyright 2001-2003 Glyph & Cog, LLC
//
//========================================================================

static int mapUTF8(Unicode u, char *buf, int bufSize) {
  if        (u <= 0x0000007f) {
    if (bufSize < 1) {
      return 0;
    }
    buf[0] = (char)u;
    return 1;
  } else if (u <= 0x000007ff) {
    if (bufSize < 2) {
      return 0;
    }
    buf[0] = (char)(0xc0 + (u >> 6));
    buf[1] = (char)(0x80 + (u & 0x3f));
    return 2;
  } else if (u <= 0x0000ffff) {
    if (bufSize < 3) {
      return 0;
    }
    buf[0] = (char)(0xe0 + (u >> 12));
    buf[1] = (char)(0x80 + ((u >> 6) & 0x3f));
    buf[2] = (char)(0x80 + (u & 0x3f));
    return 3;
  } else if (u <= 0x0010ffff) {
    if (bufSize < 4) {
      return 0;
    }
    buf[0] = (char)(0xf0 + (u >> 18));
    buf[1] = (char)(0x80 + ((u >> 12) & 0x3f));
    buf[2] = (char)(0x80 + ((u >> 6) & 0x3f));
    buf[3] = (char)(0x80 + (u & 0x3f));
    return 4;
  } else {
    return 0;
  }
}

static int mapUCS2(Unicode u, char *buf, int bufSize) {
  if (u <= 0xffff) {
    if (bufSize < 2) {
      return 0;
    }
    buf[0] = (char)((u >> 8) & 0xff);
    buf[1] = (char)(u & 0xff);
    return 2;
  } else {
    return 0;
  }
}
//========================================================================
//
// UnicodeMap.cc
//
// Copyright 2001-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stdio.h>
#include <string.h>
#include "gmem.h"
#include "gfile.h"
#include "GString.h"
#include "GList.h"
#include "Error.h"
#include "GlobalParams.h"
#include "UnicodeMap.h"

//------------------------------------------------------------------------

#define maxExtCode 16

struct UnicodeMapExt {
  Unicode u;			// Unicode char
  char code[maxExtCode];
  Guint nBytes;
};

//------------------------------------------------------------------------

UnicodeMap *UnicodeMap::parse(GString *encodingNameA) {
  FILE *f;
  UnicodeMap *map;
  UnicodeMapRange *range;
  UnicodeMapExt *eMap;
  int size, eMapsSize;
  char buf[256];
  int line, nBytes, i, x;
  char *tok1, *tok2, *tok3;

  if (!(f = globalParams->getUnicodeMapFile(encodingNameA))) {
    error(-1, "Couldn't find unicodeMap file for the '%s' encoding",
	  encodingNameA->getCString());
    return NULL;
  }

  map = new UnicodeMap(encodingNameA->copy());

  size = 8;
  map->ranges = (UnicodeMapRange *)gmallocn(size, sizeof(UnicodeMapRange));
  eMapsSize = 0;

  line = 1;
  while (getLine(buf, sizeof(buf), f)) {
    if ((tok1 = strtok(buf, " \t\r\n")) &&
	(tok2 = strtok(NULL, " \t\r\n"))) {
      if (!(tok3 = strtok(NULL, " \t\r\n"))) {
	tok3 = tok2;
	tok2 = tok1;
      }
      nBytes = strlen(tok3) / 2;
      if (nBytes <= 4) {
	if (map->len == size) {
	  size *= 2;
	  map->ranges = (UnicodeMapRange *)
	    greallocn(map->ranges, size, sizeof(UnicodeMapRange));
	}
	range = &map->ranges[map->len];
	sscanf(tok1, "%x", &range->start);
	sscanf(tok2, "%x", &range->end);
	sscanf(tok3, "%x", &range->code);
	range->nBytes = nBytes;
	++map->len;
      } else if (tok2 == tok1) {
	if (map->eMapsLen == eMapsSize) {
	  eMapsSize += 16;
	  map->eMaps = (UnicodeMapExt *)
	    greallocn(map->eMaps, eMapsSize, sizeof(UnicodeMapExt));
	}
	eMap = &map->eMaps[map->eMapsLen];
	sscanf(tok1, "%x", &eMap->u);
	for (i = 0; i < nBytes; ++i) {
	  sscanf(tok3 + i*2, "%2x", &x);
	  eMap->code[i] = (char)x;
	}
	eMap->nBytes = nBytes;
	++map->eMapsLen;
      } else {
	error(-1, "Bad line (%d) in unicodeMap file for the '%s' encoding",
	      line, encodingNameA->getCString());
      }
    } else {
      error(-1, "Bad line (%d) in unicodeMap file for the '%s' encoding",
	    line, encodingNameA->getCString());
    }
    ++line;
  }

  fclose(f);

  return map;
}

UnicodeMap::UnicodeMap(GString *encodingNameA) {
  encodingName = encodingNameA;
  unicodeOut = gFalse;
  kind = unicodeMapUser;
  ranges = NULL;
  len = 0;
  eMaps = NULL;
  eMapsLen = 0;
  refCnt = 1;
#if MULTITHREADED
  gInitMutex(&mutex);
#endif
}

UnicodeMap::UnicodeMap(char *encodingNameA, GBool unicodeOutA,
		       UnicodeMapRange *rangesA, int lenA) {
  encodingName = new GString(encodingNameA);
  unicodeOut = unicodeOutA;
  kind = unicodeMapResident;
  ranges = rangesA;
  len = lenA;
  eMaps = NULL;
  eMapsLen = 0;
  refCnt = 1;
#if MULTITHREADED
  gInitMutex(&mutex);
#endif
}

UnicodeMap::UnicodeMap(char *encodingNameA, GBool unicodeOutA,
		       UnicodeMapFunc funcA) {
  encodingName = new GString(encodingNameA);
  unicodeOut = unicodeOutA;
  kind = unicodeMapFunc;
  func = funcA;
  eMaps = NULL;
  eMapsLen = 0;
  refCnt = 1;
#if MULTITHREADED
  gInitMutex(&mutex);
#endif
}

UnicodeMap::~UnicodeMap() {
  delete encodingName;
  if (kind == unicodeMapUser && ranges) {
    gfree(ranges);
  }
  if (eMaps) {
    gfree(eMaps);
  }
#if MULTITHREADED
  gDestroyMutex(&mutex);
#endif
}

void UnicodeMap::incRefCnt() {
#if MULTITHREADED
  gLockMutex(&mutex);
#endif
  ++refCnt;
#if MULTITHREADED
  gUnlockMutex(&mutex);
#endif
}

void UnicodeMap::decRefCnt() {
  GBool done;

#if MULTITHREADED
  gLockMutex(&mutex);
#endif
  done = --refCnt == 0;
#if MULTITHREADED
  gUnlockMutex(&mutex);
#endif
  if (done) {
    delete this;
  }
}

GBool UnicodeMap::match(GString *encodingNameA) {
  return !encodingName->cmp(encodingNameA);
}

UnicodeTextDirection UnicodeMap::getDirection(Unicode u)
{
  // TODO: add TopBottom detection (Japanese, Chinese etc)
  if( u >= 0x0590 && u <= 0x05ff ) { // hebrew
    return textDirRightLeft; 
  }
  return textDirLeftRight;
}

int UnicodeMap::mapUnicode(Unicode u, char *buf, int bufSize) {
  int a, b, m, n, i, j;
  Guint code;

  if (kind == unicodeMapFunc) {
    return (*func)(u, buf, bufSize);
  }

  a = 0;
  b = len;
  if (u >= ranges[a].start) {
    // invariant: ranges[a].start <= u < ranges[b].start
    while (b - a > 1) {
      m = (a + b) / 2;
      if (u >= ranges[m].start) {
	a = m;
      } else if (u < ranges[m].start) {
	b = m;
      }
    }
    if (u <= ranges[a].end) {
      n = ranges[a].nBytes;
      if (n > bufSize) {
	return 0;
      }
      code = ranges[a].code + (u - ranges[a].start);
      for (i = n - 1; i >= 0; --i) {
	buf[i] = (char)(code & 0xff);
	code >>= 8;
      }
      return n;
    }
  }

  for (i = 0; i < eMapsLen; ++i) {
    if (eMaps[i].u == u) {
      n = eMaps[i].nBytes;
      for (j = 0; j < n; ++j) {
	buf[j] = eMaps[i].code[j];
      }
      return n;
    }
  }

  return 0;
}

//------------------------------------------------------------------------

UnicodeMapCache::UnicodeMapCache() {
  int i;

  for (i = 0; i < unicodeMapCacheSize; ++i) {
    cache[i] = NULL;
  }
}

UnicodeMapCache::~UnicodeMapCache() {
  int i;

  for (i = 0; i < unicodeMapCacheSize; ++i) {
    if (cache[i]) {
      cache[i]->decRefCnt();
    }
  }
}

UnicodeMap *UnicodeMapCache::getUnicodeMap(GString *encodingName) {
  UnicodeMap *map;
  int i, j;

  if (cache[0] && cache[0]->match(encodingName)) {
    cache[0]->incRefCnt();
    return cache[0];
  }
  for (i = 1; i < unicodeMapCacheSize; ++i) {
    if (cache[i] && cache[i]->match(encodingName)) {
      map = cache[i];
      for (j = i; j >= 1; --j) {
	cache[j] = cache[j - 1];
      }
      cache[0] = map;
      map->incRefCnt();
      return map;
    }
  }
  if ((map = UnicodeMap::parse(encodingName))) {
    if (cache[unicodeMapCacheSize - 1]) {
      cache[unicodeMapCacheSize - 1]->decRefCnt();
    }
    for (j = unicodeMapCacheSize - 1; j >= 1; --j) {
      cache[j] = cache[j - 1];
    }
    cache[0] = map;
    map->incRefCnt();
    return map;
  }
  return NULL;
}
//========================================================================
//
// UnicodeMap.cc
//
// Copyright 2001-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stdio.h>
#include <string.h>
#include "gmem.h"
#include "gfile.h"
#include "GString.h"
#include "GList.h"
#include "Error.h"
#include "GlobalParams.h"
#include "UnicodeMap.h"

//------------------------------------------------------------------------

#define maxExtCode 16

struct UnicodeMapExt {
  Unicode u;			// Unicode char
  char code[maxExtCode];
  Guint nBytes;
};

//------------------------------------------------------------------------

UnicodeMap *UnicodeMap::parse(GString *encodingNameA) {
  FILE *f;
  UnicodeMap *map;
  UnicodeMapRange *range;
  UnicodeMapExt *eMap;
  int size, eMapsSize;
  char buf[256];
  int line, nBytes, i, x;
  char *tok1, *tok2, *tok3;

  if (!(f = globalParams->getUnicodeMapFile(encodingNameA))) {
    error(-1, "Couldn't find unicodeMap file for the '%s' encoding",
	  encodingNameA->getCString());
    return NULL;
  }

  map = new UnicodeMap(encodingNameA->copy());

  size = 8;
  map->ranges = (UnicodeMapRange *)gmallocn(size, sizeof(UnicodeMapRange));
  eMapsSize = 0;

  line = 1;
  while (getLine(buf, sizeof(buf), f)) {
    if ((tok1 = strtok(buf, " \t\r\n")) &&
	(tok2 = strtok(NULL, " \t\r\n"))) {
      if (!(tok3 = strtok(NULL, " \t\r\n"))) {
	tok3 = tok2;
	tok2 = tok1;
      }
      nBytes = strlen(tok3) / 2;
      if (nBytes <= 4) {
	if (map->len == size) {
	  size *= 2;
	  map->ranges = (UnicodeMapRange *)
	    greallocn(map->ranges, size, sizeof(UnicodeMapRange));
	}
	range = &map->ranges[map->len];
	sscanf(tok1, "%x", &range->start);
	sscanf(tok2, "%x", &range->end);
	sscanf(tok3, "%x", &range->code);
	range->nBytes = nBytes;
	++map->len;
      } else if (tok2 == tok1) {
	if (map->eMapsLen == eMapsSize) {
	  eMapsSize += 16;
	  map->eMaps = (UnicodeMapExt *)
	    greallocn(map->eMaps, eMapsSize, sizeof(UnicodeMapExt));
	}
	eMap = &map->eMaps[map->eMapsLen];
	sscanf(tok1, "%x", &eMap->u);
	for (i = 0; i < nBytes; ++i) {
	  sscanf(tok3 + i*2, "%2x", &x);
	  eMap->code[i] = (char)x;
	}
	eMap->nBytes = nBytes;
	++map->eMapsLen;
      } else {
	error(-1, "Bad line (%d) in unicodeMap file for the '%s' encoding",
	      line, encodingNameA->getCString());
      }
    } else {
      error(-1, "Bad line (%d) in unicodeMap file for the '%s' encoding",
	    line, encodingNameA->getCString());
    }
    ++line;
  }

  fclose(f);

  return map;
}

UnicodeMap::UnicodeMap(GString *encodingNameA) {
  encodingName = encodingNameA;
  unicodeOut = gFalse;
  kind = unicodeMapUser;
  ranges = NULL;
  len = 0;
  eMaps = NULL;
  eMapsLen = 0;
  refCnt = 1;
#if MULTITHREADED
  gInitMutex(&mutex);
#endif
}

UnicodeMap::UnicodeMap(char *encodingNameA, GBool unicodeOutA,
		       UnicodeMapRange *rangesA, int lenA) {
  encodingName = new GString(encodingNameA);
  unicodeOut = unicodeOutA;
  kind = unicodeMapResident;
  ranges = rangesA;
  len = lenA;
  eMaps = NULL;
  eMapsLen = 0;
  refCnt = 1;
#if MULTITHREADED
  gInitMutex(&mutex);
#endif
}

UnicodeMap::UnicodeMap(char *encodingNameA, GBool unicodeOutA,
		       UnicodeMapFunc funcA) {
  encodingName = new GString(encodingNameA);
  unicodeOut = unicodeOutA;
  kind = unicodeMapFunc;
  func = funcA;
  eMaps = NULL;
  eMapsLen = 0;
  refCnt = 1;
#if MULTITHREADED
  gInitMutex(&mutex);
#endif
}

UnicodeMap::~UnicodeMap() {
  delete encodingName;
  if (kind == unicodeMapUser && ranges) {
    gfree(ranges);
  }
  if (eMaps) {
    gfree(eMaps);
  }
#if MULTITHREADED
  gDestroyMutex(&mutex);
#endif
}

void UnicodeMap::incRefCnt() {
#if MULTITHREADED
  gLockMutex(&mutex);
#endif
  ++refCnt;
#if MULTITHREADED
  gUnlockMutex(&mutex);
#endif
}

void UnicodeMap::decRefCnt() {
  GBool done;

#if MULTITHREADED
  gLockMutex(&mutex);
#endif
  done = --refCnt == 0;
#if MULTITHREADED
  gUnlockMutex(&mutex);
#endif
  if (done) {
    delete this;
  }
}

GBool UnicodeMap::match(GString *encodingNameA) {
  return !encodingName->cmp(encodingNameA);
}

int UnicodeMap::mapUnicode(Unicode u, char *buf, int bufSize) {
  int a, b, m, n, i, j;
  Guint code;

  if (kind == unicodeMapFunc) {
    return (*func)(u, buf, bufSize);
  }

  a = 0;
  b = len;
  if (u >= ranges[a].start) {
    // invariant: ranges[a].start <= u < ranges[b].start
    while (b - a > 1) {
      m = (a + b) / 2;
      if (u >= ranges[m].start) {
	a = m;
      } else if (u < ranges[m].start) {
	b = m;
      }
    }
    if (u <= ranges[a].end) {
      n = ranges[a].nBytes;
      if (n > bufSize) {
	return 0;
      }
      code = ranges[a].code + (u - ranges[a].start);
      for (i = n - 1; i >= 0; --i) {
	buf[i] = (char)(code & 0xff);
	code >>= 8;
      }
      return n;
    }
  }

  for (i = 0; i < eMapsLen; ++i) {
    if (eMaps[i].u == u) {
      n = eMaps[i].nBytes;
      for (j = 0; j < n; ++j) {
	buf[j] = eMaps[i].code[j];
      }
      return n;
    }
  }

  return 0;
}

//------------------------------------------------------------------------

UnicodeMapCache::UnicodeMapCache() {
  int i;

  for (i = 0; i < unicodeMapCacheSize; ++i) {
    cache[i] = NULL;
  }
}

UnicodeMapCache::~UnicodeMapCache() {
  int i;

  for (i = 0; i < unicodeMapCacheSize; ++i) {
    if (cache[i]) {
      cache[i]->decRefCnt();
    }
  }
}

UnicodeMap *UnicodeMapCache::getUnicodeMap(GString *encodingName) {
  UnicodeMap *map;
  int i, j;

  if (cache[0] && cache[0]->match(encodingName)) {
    cache[0]->incRefCnt();
    return cache[0];
  }
  for (i = 1; i < unicodeMapCacheSize; ++i) {
    if (cache[i] && cache[i]->match(encodingName)) {
      map = cache[i];
      for (j = i; j >= 1; --j) {
	cache[j] = cache[j - 1];
      }
      cache[0] = map;
      map->incRefCnt();
      return map;
    }
  }
  if ((map = UnicodeMap::parse(encodingName))) {
    if (cache[unicodeMapCacheSize - 1]) {
      cache[unicodeMapCacheSize - 1]->decRefCnt();
    }
    for (j = unicodeMapCacheSize - 1; j >= 1; --j) {
      cache[j] = cache[j - 1];
    }
    cache[0] = map;
    map->incRefCnt();
    return map;
  }
  return NULL;
}
//========================================================================
//
// UnicodeMap.h
//
// Mapping from Unicode to an encoding.
//
// Copyright 2001-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef UNICODEMAP_H
#define UNICODEMAP_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "gtypes.h"
#include "CharTypes.h"

#if MULTITHREADED
#include "GMutex.h"
#endif

class GString;

//------------------------------------------------------------------------

enum UnicodeMapKind {
  unicodeMapUser,		// read from a file
  unicodeMapResident,		// static list of ranges
  unicodeMapFunc		// function pointer
};

enum UnicodeTextDirection {
  textDirUnknown,
  textDirLeftRight,
  textDirRightLeft,
  textDirTopBottom
};

typedef int (*UnicodeMapFunc)(Unicode u, char *buf, int bufSize);

struct UnicodeMapRange {
  Unicode start, end;		// range of Unicode chars
  Guint code, nBytes;		// first output code
};

struct UnicodeMapExt;

//------------------------------------------------------------------------

class UnicodeMap {
public:

  // Create the UnicodeMap specified by <encodingName>.  Sets the
  // initial reference count to 1.  Returns NULL on failure.
  static UnicodeMap *parse(GString *encodingNameA);

  // Create a resident UnicodeMap.
  UnicodeMap(char *encodingNameA, GBool unicodeOutA,
	     UnicodeMapRange *rangesA, int lenA);

  // Create a resident UnicodeMap that uses a function instead of a
  // list of ranges.
  UnicodeMap(char *encodingNameA, GBool unicodeOutA,
	     UnicodeMapFunc funcA);

  ~UnicodeMap();

  void incRefCnt();
  void decRefCnt();

  GString *getEncodingName() { return encodingName; }

  GBool isUnicode() { return unicodeOut; }

  // Return true if this UnicodeMap matches the specified
  // <encodingNameA>.
  GBool match(GString *encodingNameA);

  // Map Unicode to the target encoding.  Fills in <buf> with the
  // output and returns the number of bytes used.  Output will be
  // truncated at <bufSize> bytes.  No string terminator is written.
  // Returns 0 if no mapping is found.
  int mapUnicode(Unicode u, char *buf, int bufSize);


  // determines the direction of the text by checking 
  // unicode ranges
  static enum UnicodeTextDirection getDirection(Unicode u);

private:

  UnicodeMap(GString *encodingNameA);

  GString *encodingName;
  UnicodeMapKind kind;
  GBool unicodeOut;
  union {
    UnicodeMapRange *ranges;	// (user, resident)
    UnicodeMapFunc func;	// (func)
  };
  int len;			// (user, resident)
  UnicodeMapExt *eMaps;		// (user)
  int eMapsLen;			// (user)
  int refCnt;
#if MULTITHREADED
  GMutex mutex;
#endif
};

//------------------------------------------------------------------------

#define unicodeMapCacheSize 4

class UnicodeMapCache {
public:

  UnicodeMapCache();
  ~UnicodeMapCache();

  // Get the UnicodeMap for <encodingName>.  Increments its reference
  // count; there will be one reference for the cache plus one for the
  // caller of this function.  Returns NULL on failure.
  UnicodeMap *getUnicodeMap(GString *encodingName);

private:

  UnicodeMap *cache[unicodeMapCacheSize];
};

#endif
//========================================================================
//
// UnicodeMap.h
//
// Mapping from Unicode to an encoding.
//
// Copyright 2001-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef UNICODEMAP_H
#define UNICODEMAP_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "gtypes.h"
#include "CharTypes.h"

#if MULTITHREADED
#include "GMutex.h"
#endif

class GString;

//------------------------------------------------------------------------

enum UnicodeMapKind {
  unicodeMapUser,		// read from a file
  unicodeMapResident,		// static list of ranges
  unicodeMapFunc		// function pointer
};

typedef int (*UnicodeMapFunc)(Unicode u, char *buf, int bufSize);

struct UnicodeMapRange {
  Unicode start, end;		// range of Unicode chars
  Guint code, nBytes;		// first output code
};

struct UnicodeMapExt;

//------------------------------------------------------------------------

class UnicodeMap {
public:

  // Create the UnicodeMap specified by <encodingName>.  Sets the
  // initial reference count to 1.  Returns NULL on failure.
  static UnicodeMap *parse(GString *encodingNameA);

  // Create a resident UnicodeMap.
  UnicodeMap(char *encodingNameA, GBool unicodeOutA,
	     UnicodeMapRange *rangesA, int lenA);

  // Create a resident UnicodeMap that uses a function instead of a
  // list of ranges.
  UnicodeMap(char *encodingNameA, GBool unicodeOutA,
	     UnicodeMapFunc funcA);

  ~UnicodeMap();

  void incRefCnt();
  void decRefCnt();

  GString *getEncodingName() { return encodingName; }

  GBool isUnicode() { return unicodeOut; }

  // Return true if this UnicodeMap matches the specified
  // <encodingNameA>.
  GBool match(GString *encodingNameA);

  // Map Unicode to the target encoding.  Fills in <buf> with the
  // output and returns the number of bytes used.  Output will be
  // truncated at <bufSize> bytes.  No string terminator is written.
  // Returns 0 if no mapping is found.
  int mapUnicode(Unicode u, char *buf, int bufSize);

private:

  UnicodeMap(GString *encodingNameA);

  GString *encodingName;
  UnicodeMapKind kind;
  GBool unicodeOut;
  union {
    UnicodeMapRange *ranges;	// (user, resident)
    UnicodeMapFunc func;	// (func)
  };
  int len;			// (user, resident)
  UnicodeMapExt *eMaps;		// (user)
  int eMapsLen;			// (user)
  int refCnt;
#if MULTITHREADED
  GMutex mutex;
#endif
};

//------------------------------------------------------------------------

#define unicodeMapCacheSize 4

class UnicodeMapCache {
public:

  UnicodeMapCache();
  ~UnicodeMapCache();

  // Get the UnicodeMap for <encodingName>.  Increments its reference
  // count; there will be one reference for the cache plus one for the
  // caller of this function.  Returns NULL on failure.
  UnicodeMap *getUnicodeMap(GString *encodingName);

private:

  UnicodeMap *cache[unicodeMapCacheSize];
};

#endif
//========================================================================
//
// UnicodeMapTables.h
//
// Copyright 2001-2003 Glyph & Cog, LLC
//
//========================================================================

static UnicodeMapRange latin1UnicodeMapRanges[] = {
  { 0x000a, 0x000a, 0x0a, 1 },
  { 0x000c, 0x000d, 0x0c, 1 },
  { 0x0020, 0x007e, 0x20, 1 },
  { 0x00a0, 0x00a0, 0x20, 1 },
  { 0x00a1, 0x00ac, 0xa1, 1 },
  { 0x00ae, 0x00ff, 0xae, 1 },
  { 0x010c, 0x010c, 0x43, 1 },
  { 0x010d, 0x010d, 0x63, 1 },
  { 0x0131, 0x0131, 0x69, 1 },
  { 0x0141, 0x0141, 0x4c, 1 },
  { 0x0142, 0x0142, 0x6c, 1 },
  { 0x0152, 0x0152, 0x4f45, 2 },
  { 0x0153, 0x0153, 0x6f65, 2 },
  { 0x0160, 0x0160, 0x53, 1 },
  { 0x0161, 0x0161, 0x73, 1 },
  { 0x0178, 0x0178, 0x59, 1 },
  { 0x017d, 0x017d, 0x5a, 1 },
  { 0x017e, 0x017e, 0x7a, 1 },
  { 0x02c6, 0x02c6, 0x5e, 1 },
  { 0x02da, 0x02da, 0xb0, 1 },
  { 0x02dc, 0x02dc, 0x7e, 1 },
  { 0x2013, 0x2013, 0xad, 1 },
  { 0x2014, 0x2014, 0x2d2d, 2 },
  { 0x2018, 0x2018, 0x60, 1 },
  { 0x2019, 0x2019, 0x27, 1 },
  { 0x201a, 0x201a, 0x2c, 1 },
  { 0x201c, 0x201c, 0x22, 1 },
  { 0x201d, 0x201d, 0x22, 1 },
  { 0x201e, 0x201e, 0x2c2c, 2 },
  { 0x2022, 0x2022, 0xb7, 1 },
  { 0x2026, 0x2026, 0x2e2e2e, 3 },
  { 0x2039, 0x2039, 0x3c, 1 },
  { 0x203a, 0x203a, 0x3e, 1 },
  { 0x2044, 0x2044, 0x2f, 1 },
  { 0x2122, 0x2122, 0x544d, 2 },
  { 0x2212, 0x2212, 0x2d, 1 },
  { 0xf6f9, 0xf6f9, 0x4c, 1 },
  { 0xf6fa, 0xf6fa, 0x4f45, 2 },
  { 0xf6fc, 0xf6fc, 0xb0, 1 },
  { 0xf6fd, 0xf6fd, 0x53, 1 },
  { 0xf6fe, 0xf6fe, 0x7e, 1 },
  { 0xf6ff, 0xf6ff, 0x5a, 1 },
  { 0xf721, 0xf721, 0x21, 1 },
  { 0xf724, 0xf724, 0x24, 1 },
  { 0xf726, 0xf726, 0x26, 1 },
  { 0xf730, 0xf739, 0x30, 1 },
  { 0xf73f, 0xf73f, 0x3f, 1 },
  { 0xf761, 0xf77a, 0x41, 1 },
  { 0xf7a1, 0xf7a2, 0xa1, 1 },
  { 0xf7bf, 0xf7bf, 0xbf, 1 },
  { 0xf7e0, 0xf7f6, 0xc0, 1 },
  { 0xf7f8, 0xf7fe, 0xd8, 1 },
  { 0xf7ff, 0xf7ff, 0x59, 1 },
  { 0xfb00, 0xfb00, 0x6666, 2 },
  { 0xfb01, 0xfb01, 0x6669, 2 },
  { 0xfb02, 0xfb02, 0x666c, 2 },
  { 0xfb03, 0xfb03, 0x666669, 3 },
  { 0xfb04, 0xfb04, 0x66666c, 3 }
};
#define latin1UnicodeMapLen (sizeof(latin1UnicodeMapRanges) / sizeof(UnicodeMapRange))

static UnicodeMapRange ascii7UnicodeMapRanges[] = {
  { 0x000a, 0x000a, 0x0a, 1 },
  { 0x000c, 0x000d, 0x0c, 1 },
  { 0x0020, 0x005f, 0x20, 1 },
  { 0x0061, 0x007e, 0x61, 1 },
  { 0x00a6, 0x00a6, 0x7c, 1 },
  { 0x00a9, 0x00a9, 0x286329, 3 },
  { 0x00ae, 0x00ae, 0x285229, 3 },
  { 0x00b7, 0x00b7, 0x2a, 1 },
  { 0x00bc, 0x00bc, 0x312f34, 3 },
  { 0x00bd, 0x00bd, 0x312f32, 3 },
  { 0x00be, 0x00be, 0x332f34, 3 },
  { 0x00c0, 0x00c0, 0x41, 1 },
  { 0x00c1, 0x00c1, 0x41, 1 },
  { 0x00c2, 0x00c2, 0x41, 1 },
  { 0x00c3, 0x00c3, 0x41, 1 },
  { 0x00c4, 0x00c4, 0x41, 1 },
  { 0x00c5, 0x00c5, 0x41, 1 },
  { 0x00c6, 0x00c6, 0x4145, 2 },
  { 0x00c7, 0x00c7, 0x43, 1 },
  { 0x00c8, 0x00c8, 0x45, 1 },
  { 0x00c9, 0x00c9, 0x45, 1 },
  { 0x00ca, 0x00ca, 0x45, 1 },
  { 0x00cb, 0x00cb, 0x45, 1 },
  { 0x00cc, 0x00cc, 0x49, 1 },
  { 0x00cd, 0x00cd, 0x49, 1 },
  { 0x00ce, 0x00ce, 0x49, 1 },
  { 0x00cf, 0x00cf, 0x49, 1 },
  { 0x00d1, 0x00d2, 0x4e, 1 },
  { 0x00d3, 0x00d3, 0x4f, 1 },
  { 0x00d4, 0x00d4, 0x4f, 1 },
  { 0x00d5, 0x00d5, 0x4f, 1 },
  { 0x00d6, 0x00d6, 0x4f, 1 },
  { 0x00d7, 0x00d7, 0x78, 1 },
  { 0x00d8, 0x00d8, 0x4f, 1 },
  { 0x00d9, 0x00d9, 0x55, 1 },
  { 0x00da, 0x00da, 0x55, 1 },
  { 0x00db, 0x00db, 0x55, 1 },
  { 0x00dc, 0x00dc, 0x55, 1 },
  { 0x00dd, 0x00dd, 0x59, 1 },
  { 0x00e0, 0x00e0, 0x61, 1 },
  { 0x00e1, 0x00e1, 0x61, 1 },
  { 0x00e2, 0x00e2, 0x61, 1 },
  { 0x00e3, 0x00e3, 0x61, 1 },
  { 0x00e4, 0x00e4, 0x61, 1 },
  { 0x00e5, 0x00e5, 0x61, 1 },
  { 0x00e6, 0x00e6, 0x6165, 2 },
  { 0x00e7, 0x00e7, 0x63, 1 },
  { 0x00e8, 0x00e8, 0x65, 1 },
  { 0x00e9, 0x00e9, 0x65, 1 },
  { 0x00ea, 0x00ea, 0x65, 1 },
  { 0x00eb, 0x00eb, 0x65, 1 },
  { 0x00ec, 0x00ec, 0x69, 1 },
  { 0x00ed, 0x00ed, 0x69, 1 },
  { 0x00ee, 0x00ee, 0x69, 1 },
  { 0x00ef, 0x00ef, 0x69, 1 },
  { 0x00f1, 0x00f2, 0x6e, 1 },
  { 0x00f3, 0x00f3, 0x6f, 1 },
  { 0x00f4, 0x00f4, 0x6f, 1 },
  { 0x00f5, 0x00f5, 0x6f, 1 },
  { 0x00f6, 0x00f6, 0x6f, 1 },
  { 0x00f7, 0x00f7, 0x2f, 1 },
  { 0x00f8, 0x00f8, 0x6f, 1 },
  { 0x00f9, 0x00f9, 0x75, 1 },
  { 0x00fa, 0x00fa, 0x75, 1 },
  { 0x00fb, 0x00fb, 0x75, 1 },
  { 0x00fc, 0x00fc, 0x75, 1 },
  { 0x00fd, 0x00fd, 0x79, 1 },
  { 0x00ff, 0x00ff, 0x79, 1 },
  { 0x0131, 0x0131, 0x69, 1 },
  { 0x0141, 0x0141, 0x4c, 1 },
  { 0x0152, 0x0152, 0x4f45, 2 },
  { 0x0153, 0x0153, 0x6f65, 2 },
  { 0x0160, 0x0160, 0x53, 1 },
  { 0x0178, 0x0178, 0x59, 1 },
  { 0x017d, 0x017d, 0x5a, 1 },
  { 0x2013, 0x2013, 0x2d, 1 },
  { 0x2014, 0x2014, 0x2d2d, 2 },
  { 0x2018, 0x2018, 0x60, 1 },
  { 0x2019, 0x2019, 0x27, 1 },
  { 0x201c, 0x201c, 0x22, 1 },
  { 0x201d, 0x201d, 0x22, 1 },
  { 0x2022, 0x2022, 0x2a, 1 },
  { 0x2026, 0x2026, 0x2e2e2e, 3 },
  { 0x2122, 0x2122, 0x544d, 2 },
  { 0x2212, 0x2212, 0x2d, 1 },
  { 0xf6f9, 0xf6f9, 0x4c, 1 },
  { 0xf6fa, 0xf6fa, 0x4f45, 2 },
  { 0xf6fd, 0xf6fd, 0x53, 1 },
  { 0xf6fe, 0xf6fe, 0x7e, 1 },
  { 0xf6ff, 0xf6ff, 0x5a, 1 },
  { 0xf721, 0xf721, 0x21, 1 },
  { 0xf724, 0xf724, 0x24, 1 },
  { 0xf726, 0xf726, 0x26, 1 },
  { 0xf730, 0xf739, 0x30, 1 },
  { 0xf73f, 0xf73f, 0x3f, 1 },
  { 0xf761, 0xf77a, 0x41, 1 },
  { 0xf7e0, 0xf7e0, 0x41, 1 },
  { 0xf7e1, 0xf7e1, 0x41, 1 },
  { 0xf7e2, 0xf7e2, 0x41, 1 },
  { 0xf7e3, 0xf7e3, 0x41, 1 },
  { 0xf7e4, 0xf7e4, 0x41, 1 },
  { 0xf7e5, 0xf7e5, 0x41, 1 },
  { 0xf7e6, 0xf7e6, 0x4145, 2 },
  { 0xf7e7, 0xf7e7, 0x43, 1 },
  { 0xf7e8, 0xf7e8, 0x45, 1 },
  { 0xf7e9, 0xf7e9, 0x45, 1 },
  { 0xf7ea, 0xf7ea, 0x45, 1 },
  { 0xf7eb, 0xf7eb, 0x45, 1 },
  { 0xf7ec, 0xf7ec, 0x49, 1 },
  { 0xf7ed, 0xf7ed, 0x49, 1 },
  { 0xf7ee, 0xf7ee, 0x49, 1 },
  { 0xf7ef, 0xf7ef, 0x49, 1 },
  { 0xf7f1, 0xf7f2, 0x4e, 1 },
  { 0xf7f3, 0xf7f3, 0x4f, 1 },
  { 0xf7f4, 0xf7f4, 0x4f, 1 },
  { 0xf7f5, 0xf7f5, 0x4f, 1 },
  { 0xf7f6, 0xf7f6, 0x4f, 1 },
  { 0xf7f8, 0xf7f8, 0x4f, 1 },
  { 0xf7f9, 0xf7f9, 0x55, 1 },
  { 0xf7fa, 0xf7fa, 0x55, 1 },
  { 0xf7fb, 0xf7fb, 0x55, 1 },
  { 0xf7fc, 0xf7fc, 0x55, 1 },
  { 0xf7fd, 0xf7fd, 0x59, 1 },
  { 0xf7ff, 0xf7ff, 0x59, 1 },
  { 0xfb00, 0xfb00, 0x6666, 2 },
  { 0xfb01, 0xfb01, 0x6669, 2 },
  { 0xfb02, 0xfb02, 0x666c, 2 },
  { 0xfb03, 0xfb03, 0x666669, 3 },
  { 0xfb04, 0xfb04, 0x66666c, 3 }
};
#define ascii7UnicodeMapLen (sizeof(ascii7UnicodeMapRanges) / sizeof(UnicodeMapRange))

static UnicodeMapRange symbolUnicodeMapRanges[] = {
  { 0x0020, 0x0021, 0x20, 1 },
  { 0x0023, 0x0023, 0x23, 1 },
  { 0x0025, 0x0026, 0x25, 1 },
  { 0x0028, 0x0029, 0x28, 1 },
  { 0x002b, 0x002c, 0x2b, 1 },
  { 0x002e, 0x003f, 0x2e, 1 },
  { 0x005b, 0x005b, 0x5b, 1 },
  { 0x005d, 0x005d, 0x5d, 1 },
  { 0x005f, 0x005f, 0x5f, 1 },
  { 0x007b, 0x007d, 0x7b, 1 },
  { 0x00ac, 0x00ac, 0xd8, 1 },
  { 0x00b0, 0x00b1, 0xb0, 1 },
  { 0x00b5, 0x00b5, 0x6d, 1 },
  { 0x00d7, 0x00d7, 0xb4, 1 },
  { 0x00f7, 0x00f7, 0xb8, 1 },
  { 0x0192, 0x0192, 0xa6, 1 },
  { 0x0391, 0x0392, 0x41, 1 },
  { 0x0393, 0x0393, 0x47, 1 },
  { 0x0395, 0x0395, 0x45, 1 },
  { 0x0396, 0x0396, 0x5a, 1 },
  { 0x0397, 0x0397, 0x48, 1 },
  { 0x0398, 0x0398, 0x51, 1 },
  { 0x0399, 0x0399, 0x49, 1 },
  { 0x039a, 0x039d, 0x4b, 1 },
  { 0x039e, 0x039e, 0x58, 1 },
  { 0x039f, 0x03a0, 0x4f, 1 },
  { 0x03a1, 0x03a1, 0x52, 1 },
  { 0x03a3, 0x03a5, 0x53, 1 },
  { 0x03a6, 0x03a6, 0x46, 1 },
  { 0x03a7, 0x03a7, 0x43, 1 },
  { 0x03a8, 0x03a8, 0x59, 1 },
  { 0x03b1, 0x03b2, 0x61, 1 },
  { 0x03b3, 0x03b3, 0x67, 1 },
  { 0x03b4, 0x03b5, 0x64, 1 },
  { 0x03b6, 0x03b6, 0x7a, 1 },
  { 0x03b7, 0x03b7, 0x68, 1 },
  { 0x03b8, 0x03b8, 0x71, 1 },
  { 0x03b9, 0x03b9, 0x69, 1 },
  { 0x03ba, 0x03bb, 0x6b, 1 },
  { 0x03bd, 0x03bd, 0x6e, 1 },
  { 0x03be, 0x03be, 0x78, 1 },
  { 0x03bf, 0x03c0, 0x6f, 1 },
  { 0x03c1, 0x03c1, 0x72, 1 },
  { 0x03c2, 0x03c2, 0x56, 1 },
  { 0x03c3, 0x03c5, 0x73, 1 },
  { 0x03c6, 0x03c6, 0x66, 1 },
  { 0x03c7, 0x03c7, 0x63, 1 },
  { 0x03c8, 0x03c8, 0x79, 1 },
  { 0x03c9, 0x03c9, 0x77, 1 },
  { 0x03d1, 0x03d1, 0x4a, 1 },
  { 0x03d2, 0x03d2, 0xa1, 1 },
  { 0x03d5, 0x03d5, 0x6a, 1 },
  { 0x03d6, 0x03d6, 0x76, 1 },
  { 0x2022, 0x2022, 0xb7, 1 },
  { 0x2026, 0x2026, 0xbc, 1 },
  { 0x2032, 0x2032, 0xa2, 1 },
  { 0x2033, 0x2033, 0xb2, 1 },
  { 0x2044, 0x2044, 0xa4, 1 },
  { 0x2111, 0x2111, 0xc1, 1 },
  { 0x2118, 0x2118, 0xc3, 1 },
  { 0x211c, 0x211c, 0xc2, 1 },
  { 0x2126, 0x2126, 0x57, 1 },
  { 0x2135, 0x2135, 0xc0, 1 },
  { 0x2190, 0x2193, 0xac, 1 },
  { 0x2194, 0x2194, 0xab, 1 },
  { 0x21b5, 0x21b5, 0xbf, 1 },
  { 0x21d0, 0x21d3, 0xdc, 1 },
  { 0x21d4, 0x21d4, 0xdb, 1 },
  { 0x2200, 0x2200, 0x22, 1 },
  { 0x2202, 0x2202, 0xb6, 1 },
  { 0x2203, 0x2203, 0x24, 1 },
  { 0x2205, 0x2205, 0xc6, 1 },
  { 0x2206, 0x2206, 0x44, 1 },
  { 0x2207, 0x2207, 0xd1, 1 },
  { 0x2208, 0x2209, 0xce, 1 },
  { 0x220b, 0x220b, 0x27, 1 },
  { 0x220f, 0x220f, 0xd5, 1 },
  { 0x2211, 0x2211, 0xe5, 1 },
  { 0x2212, 0x2212, 0x2d, 1 },
  { 0x2217, 0x2217, 0x2a, 1 },
  { 0x221a, 0x221a, 0xd6, 1 },
  { 0x221d, 0x221d, 0xb5, 1 },
  { 0x221e, 0x221e, 0xa5, 1 },
  { 0x2220, 0x2220, 0xd0, 1 },
  { 0x2227, 0x2228, 0xd9, 1 },
  { 0x2229, 0x222a, 0xc7, 1 },
  { 0x222b, 0x222b, 0xf2, 1 },
  { 0x2234, 0x2234, 0x5c, 1 },
  { 0x223c, 0x223c, 0x7e, 1 },
  { 0x2245, 0x2245, 0x40, 1 },
  { 0x2248, 0x2248, 0xbb, 1 },
  { 0x2260, 0x2261, 0xb9, 1 },
  { 0x2264, 0x2264, 0xa3, 1 },
  { 0x2265, 0x2265, 0xb3, 1 },
  { 0x2282, 0x2282, 0xcc, 1 },
  { 0x2283, 0x2283, 0xc9, 1 },
  { 0x2284, 0x2284, 0xcb, 1 },
  { 0x2286, 0x2286, 0xcd, 1 },
  { 0x2287, 0x2287, 0xca, 1 },
  { 0x2295, 0x2295, 0xc5, 1 },
  { 0x2297, 0x2297, 0xc4, 1 },
  { 0x22a5, 0x22a5, 0x5e, 1 },
  { 0x22c5, 0x22c5, 0xd7, 1 },
  { 0x2320, 0x2320, 0xf3, 1 },
  { 0x2321, 0x2321, 0xf5, 1 },
  { 0x2329, 0x2329, 0xe1, 1 },
  { 0x232a, 0x232a, 0xf1, 1 },
  { 0x25ca, 0x25ca, 0xe0, 1 },
  { 0x2660, 0x2660, 0xaa, 1 },
  { 0x2663, 0x2663, 0xa7, 1 },
  { 0x2665, 0x2665, 0xa9, 1 },
  { 0x2666, 0x2666, 0xa8, 1 },
  { 0xf6d9, 0xf6d9, 0xd3, 1 },
  { 0xf6da, 0xf6da, 0xd2, 1 },
  { 0xf6db, 0xf6db, 0xd4, 1 },
  { 0xf8e5, 0xf8e5, 0x60, 1 },
  { 0xf8e6, 0xf8e7, 0xbd, 1 },
  { 0xf8e8, 0xf8ea, 0xe2, 1 },
  { 0xf8eb, 0xf8f4, 0xe6, 1 },
  { 0xf8f5, 0xf8f5, 0xf4, 1 },
  { 0xf8f6, 0xf8fe, 0xf6, 1 }
};
#define symbolUnicodeMapLen (sizeof(symbolUnicodeMapRanges) / sizeof(UnicodeMapRange))

static UnicodeMapRange zapfDingbatsUnicodeMapRanges[] = {
  { 0x0020, 0x0020, 0x20, 1 },
  { 0x2192, 0x2192, 0xd5, 1 },
  { 0x2194, 0x2195, 0xd6, 1 },
  { 0x2460, 0x2469, 0xac, 1 },
  { 0x25a0, 0x25a0, 0x6e, 1 },
  { 0x25b2, 0x25b2, 0x73, 1 },
  { 0x25bc, 0x25bc, 0x74, 1 },
  { 0x25c6, 0x25c6, 0x75, 1 },
  { 0x25cf, 0x25cf, 0x6c, 1 },
  { 0x25d7, 0x25d7, 0x77, 1 },
  { 0x2605, 0x2605, 0x48, 1 },
  { 0x260e, 0x260e, 0x25, 1 },
  { 0x261b, 0x261b, 0x2a, 1 },
  { 0x261e, 0x261e, 0x2b, 1 },
  { 0x2660, 0x2660, 0xab, 1 },
  { 0x2663, 0x2663, 0xa8, 1 },
  { 0x2665, 0x2665, 0xaa, 1 },
  { 0x2666, 0x2666, 0xa9, 1 },
  { 0x2701, 0x2704, 0x21, 1 },
  { 0x2706, 0x2709, 0x26, 1 },
  { 0x270c, 0x2727, 0x2c, 1 },
  { 0x2729, 0x274b, 0x49, 1 },
  { 0x274d, 0x274d, 0x6d, 1 },
  { 0x274f, 0x2752, 0x6f, 1 },
  { 0x2756, 0x2756, 0x76, 1 },
  { 0x2758, 0x275e, 0x78, 1 },
  { 0x2761, 0x2767, 0xa1, 1 },
  { 0x2776, 0x2794, 0xb6, 1 },
  { 0x2798, 0x27af, 0xd8, 1 },
  { 0x27b1, 0x27be, 0xf1, 1 }
};
#define zapfDingbatsUnicodeMapLen (sizeof(zapfDingbatsUnicodeMapRanges) / sizeof(UnicodeMapRange))
//========================================================================
//
// UnicodeTypeTable.cc
//
// Copyright 2004 Glyph & Cog, LLC
//
//========================================================================

#include <stdlib.h>
#include "CharTypes.h"
#include "UnicodeTypeTable.h"

struct UnicodeMapTableEntry {
  char *vector;
  char type;
};

struct UnicodeCaseTableVector {
  Unicode codes[256];
};

static UnicodeMapTableEntry typeTable[256] = {
  { "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNLNNNNNNNNNNLNNNNLNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLL", 'X' },
  { NULL, 'L' },
  { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLNNNNNNNNNNNNNNLLNNNNNNNNNNNNNNLLLLLNNNNNNNNNLNNNNNNNNNNNNNNNNN", 'X' },
  { "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLNNNNNNNNNNNLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLL", 'X' },
  { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
  { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNRNRNNRNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR", 'X' },
  { "RRRRNNNNNNNNNRNNNNNNNNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNRRRNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNNNNNNRNNNNNNNRRNNNNNNNRRNNNNNNNNNNRRRRRR", 'X' },
  { "RRRRRRRRRRRRRRNNRNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNNNNNNNNNNNNNNNNNNNNNNNNNNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNNNNNNNNNNRNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },
  { NULL, 'N' },
  { "NNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNNNNNLLLLNLLLNNNNLLLLLLLLLLLLLNNLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNLLLLLLLLNLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLNNLLLLLLLNNNNN", 'X' },
  { "NNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNNNNNLLLLNLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLNNNNNNNNNNNNNNNN", 'X' },
  { "NNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLNLNNNLLLLLLLLLNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNLLLLL", 'X' },
  { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNLLLLNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
  { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNLLLLLLLNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
  { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLNNNNNNNNNNNNLLLLLLLNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLNNNNNNNNNLLLLLLLLLLNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
  { "LLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLNLNLNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNLNNNNNLNNLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
  { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNLNNNNNNLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
  { "LLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNLLLLLLLLNLLNNNNNNNNNNNLLLLLLLNLNLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNN", 'X' },
  { "NNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
  { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLNNNNNLLLLLLNLLLLLLNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLNNNLLLLLLLLLLLNNNLLLLLLLLLLLLNNNNLLLLLLLLLLLLLNNNLLLLLLLLLLLLLNNN", 'X' },
  { "NNNNNNNNNNNNNNLRNNNNNNNNNNNNNNNNNNNNNNNNNNLRNLRNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNLNNNNNNNNNNNNNLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },
  { "NNLNNNNLNNLLLLLLLLLLNLNNNLLLLLNNNNNNLNLNLNLLLLNLLLNLLLLLLLNNLLLLNNNNNLLLLLNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },
  { NULL, 'N' },
  { "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },
  { "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNN", 'X' },
  { NULL, 'N' },
  { NULL, 'N' },
  { NULL, 'N' },
  { NULL, 'L' },
  { NULL, 'N' },
  { NULL, 'N' },
  { NULL, 'N' },
  { NULL, 'N' },
  { NULL, 'N' },
  { NULL, 'N' },
  { NULL, 'N' },
  { "NNNNNLLLNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLNNNNNNNLLLLLNNLLLLLNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLL", 'X' },
  { NULL, 'L' },
  { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNLLLLLLLLLLLLNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
  { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN", 'X' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { NULL, 'L' },
  { "LLLLLLLLLLLLLLLLLLLLLLLLRRRRRRNRRRRRRRRRRNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR", 'X' },
  { NULL, 'R' },
  { "RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNN", 'X' },
  { "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNN", 'X' },
  { "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNLL", 'X' }
};

static UnicodeCaseTableVector caseTable00 = {{
  0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
  0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
  0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
  0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,
  0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
  0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
  0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
  0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
  0x0040, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
  0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
  0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
  0x0078, 0x0079, 0x007a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
  0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
  0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
  0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
  0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,
  0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,
  0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,
  0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,
  0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,
  0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7,
  0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af,
  0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x03bc, 0x00b6, 0x00b7,
  0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,
  0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7,
  0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,
  0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00d7,
  0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00df,
  0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7,
  0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,
  0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7,
  0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff
}};
static UnicodeCaseTableVector caseTable01 = {{
  0x0101, 0x0101, 0x0103, 0x0103, 0x0105, 0x0105, 0x0107, 0x0107,
  0x0109, 0x0109, 0x010b, 0x010b, 0x010d, 0x010d, 0x010f, 0x010f,
  0x0111, 0x0111, 0x0113, 0x0113, 0x0115, 0x0115, 0x0117, 0x0117,
  0x0119, 0x0119, 0x011b, 0x011b, 0x011d, 0x011d, 0x011f, 0x011f,
  0x0121, 0x0121, 0x0123, 0x0123, 0x0125, 0x0125, 0x0127, 0x0127,
  0x0129, 0x0129, 0x012b, 0x012b, 0x012d, 0x012d, 0x012f, 0x012f,
  0x0130, 0x0131, 0x0133, 0x0133, 0x0135, 0x0135, 0x0137, 0x0137,
  0x0138, 0x013a, 0x013a, 0x013c, 0x013c, 0x013e, 0x013e, 0x0140,
  0x0140, 0x0142, 0x0142, 0x0144, 0x0144, 0x0146, 0x0146, 0x0148,
  0x0148, 0x0149, 0x014b, 0x014b, 0x014d, 0x014d, 0x014f, 0x014f,
  0x0151, 0x0151, 0x0153, 0x0153, 0x0155, 0x0155, 0x0157, 0x0157,
  0x0159, 0x0159, 0x015b, 0x015b, 0x015d, 0x015d, 0x015f, 0x015f,
  0x0161, 0x0161, 0x0163, 0x0163, 0x0165, 0x0165, 0x0167, 0x0167,
  0x0169, 0x0169, 0x016b, 0x016b, 0x016d, 0x016d, 0x016f, 0x016f,
  0x0171, 0x0171, 0x0173, 0x0173, 0x0175, 0x0175, 0x0177, 0x0177,
  0x00ff, 0x017a, 0x017a, 0x017c, 0x017c, 0x017e, 0x017e, 0x0073,
  0x0180, 0x0253, 0x0183, 0x0183, 0x0185, 0x0185, 0x0254, 0x0188,
  0x0188, 0x0256, 0x0257, 0x018c, 0x018c, 0x018d, 0x01dd, 0x0259,
  0x025b, 0x0192, 0x0192, 0x0260, 0x0263, 0x0195, 0x0269, 0x0268,
  0x0199, 0x0199, 0x019a, 0x019b, 0x026f, 0x0272, 0x019e, 0x0275,
  0x01a1, 0x01a1, 0x01a3, 0x01a3, 0x01a5, 0x01a5, 0x0280, 0x01a8,
  0x01a8, 0x0283, 0x01aa, 0x01ab, 0x01ad, 0x01ad, 0x0288, 0x01b0,
  0x01b0, 0x028a, 0x028b, 0x01b4, 0x01b4, 0x01b6, 0x01b6, 0x0292,
  0x01b9, 0x01b9, 0x01ba, 0x01bb, 0x01bd, 0x01bd, 0x01be, 0x01bf,
  0x01c0, 0x01c1, 0x01c2, 0x01c3, 0x01c6, 0x01c6, 0x01c6, 0x01c9,
  0x01c9, 0x01c9, 0x01cc, 0x01cc, 0x01cc, 0x01ce, 0x01ce, 0x01d0,
  0x01d0, 0x01d2, 0x01d2, 0x01d4, 0x01d4, 0x01d6, 0x01d6, 0x01d8,
  0x01d8, 0x01da, 0x01da, 0x01dc, 0x01dc, 0x01dd, 0x01df, 0x01df,
  0x01e1, 0x01e1, 0x01e3, 0x01e3, 0x01e5, 0x01e5, 0x01e7, 0x01e7,
  0x01e9, 0x01e9, 0x01eb, 0x01eb, 0x01ed, 0x01ed, 0x01ef, 0x01ef,
  0x01f0, 0x01f3, 0x01f3, 0x01f3, 0x01f5, 0x01f5, 0x0195, 0x01bf,
  0x01f9, 0x01f9, 0x01fb, 0x01fb, 0x01fd, 0x01fd, 0x01ff, 0x01ff
}};
static UnicodeCaseTableVector caseTable02 = {{
  0x0201, 0x0201, 0x0203, 0x0203, 0x0205, 0x0205, 0x0207, 0x0207,
  0x0209, 0x0209, 0x020b, 0x020b, 0x020d, 0x020d, 0x020f, 0x020f,
  0x0211, 0x0211, 0x0213, 0x0213, 0x0215, 0x0215, 0x0217, 0x0217,
  0x0219, 0x0219, 0x021b, 0x021b, 0x021d, 0x021d, 0x021f, 0x021f,
  0x019e, 0x0221, 0x0223, 0x0223, 0x0225, 0x0225, 0x0227, 0x0227,
  0x0229, 0x0229, 0x022b, 0x022b, 0x022d, 0x022d, 0x022f, 0x022f,
  0x0231, 0x0231, 0x0233, 0x0233, 0x0234, 0x0235, 0x0236, 0x0237,
  0x0238, 0x0239, 0x023a, 0x023b, 0x023c, 0x023d, 0x023e, 0x023f,
  0x0240, 0x0241, 0x0242, 0x0243, 0x0244, 0x0245, 0x0246, 0x0247,
  0x0248, 0x0249, 0x024a, 0x024b, 0x024c, 0x024d, 0x024e, 0x024f,
  0x0250, 0x0251, 0x0252, 0x0253, 0x0254, 0x0255, 0x0256, 0x0257,
  0x0258, 0x0259, 0x025a, 0x025b, 0x025c, 0x025d, 0x025e, 0x025f,
  0x0260, 0x0261, 0x0262, 0x0263, 0x0264, 0x0265, 0x0266, 0x0267,
  0x0268, 0x0269, 0x026a, 0x026b, 0x026c, 0x026d, 0x026e, 0x026f,
  0x0270, 0x0271, 0x0272, 0x0273, 0x0274, 0x0275, 0x0276, 0x0277,
  0x0278, 0x0279, 0x027a, 0x027b, 0x027c, 0x027d, 0x027e, 0x027f,
  0x0280, 0x0281, 0x0282, 0x0283, 0x0284, 0x0285, 0x0286, 0x0287,
  0x0288, 0x0289, 0x028a, 0x028b, 0x028c, 0x028d, 0x028e, 0x028f,
  0x0290, 0x0291, 0x0292, 0x0293, 0x0294, 0x0295, 0x0296, 0x0297,
  0x0298, 0x0299, 0x029a, 0x029b, 0x029c, 0x029d, 0x029e, 0x029f,
  0x02a0, 0x02a1, 0x02a2, 0x02a3, 0x02a4, 0x02a5, 0x02a6, 0x02a7,
  0x02a8, 0x02a9, 0x02aa, 0x02ab, 0x02ac, 0x02ad, 0x02ae, 0x02af,
  0x02b0, 0x02b1, 0x02b2, 0x02b3, 0x02b4, 0x02b5, 0x02b6, 0x02b7,
  0x02b8, 0x02b9, 0x02ba, 0x02bb, 0x02bc, 0x02bd, 0x02be, 0x02bf,
  0x02c0, 0x02c1, 0x02c2, 0x02c3, 0x02c4, 0x02c5, 0x02c6, 0x02c7,
  0x02c8, 0x02c9, 0x02ca, 0x02cb, 0x02cc, 0x02cd, 0x02ce, 0x02cf,
  0x02d0, 0x02d1, 0x02d2, 0x02d3, 0x02d4, 0x02d5, 0x02d6, 0x02d7,
  0x02d8, 0x02d9, 0x02da, 0x02db, 0x02dc, 0x02dd, 0x02de, 0x02df,
  0x02e0, 0x02e1, 0x02e2, 0x02e3, 0x02e4, 0x02e5, 0x02e6, 0x02e7,
  0x02e8, 0x02e9, 0x02ea, 0x02eb, 0x02ec, 0x02ed, 0x02ee, 0x02ef,
  0x02f0, 0x02f1, 0x02f2, 0x02f3, 0x02f4, 0x02f5, 0x02f6, 0x02f7,
  0x02f8, 0x02f9, 0x02fa, 0x02fb, 0x02fc, 0x02fd, 0x02fe, 0x02ff
}};
static UnicodeCaseTableVector caseTable03 = {{
  0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307,
  0x0308, 0x0309, 0x030a, 0x030b, 0x030c, 0x030d, 0x030e, 0x030f,
  0x0310, 0x0311, 0x0312, 0x0313, 0x0314, 0x0315, 0x0316, 0x0317,
  0x0318, 0x0319, 0x031a, 0x031b, 0x031c, 0x031d, 0x031e, 0x031f,
  0x0320, 0x0321, 0x0322, 0x0323, 0x0324, 0x0325, 0x0326, 0x0327,
  0x0328, 0x0329, 0x032a, 0x032b, 0x032c, 0x032d, 0x032e, 0x032f,
  0x0330, 0x0331, 0x0332, 0x0333, 0x0334, 0x0335, 0x0336, 0x0337,
  0x0338, 0x0339, 0x033a, 0x033b, 0x033c, 0x033d, 0x033e, 0x033f,
  0x0340, 0x0341, 0x0342, 0x0343, 0x0344, 0x03b9, 0x0346, 0x0347,
  0x0348, 0x0349, 0x034a, 0x034b, 0x034c, 0x034d, 0x034e, 0x034f,
  0x0350, 0x0351, 0x0352, 0x0353, 0x0354, 0x0355, 0x0356, 0x0357,
  0x0358, 0x0359, 0x035a, 0x035b, 0x035c, 0x035d, 0x035e, 0x035f,
  0x0360, 0x0361, 0x0362, 0x0363, 0x0364, 0x0365, 0x0366, 0x0367,
  0x0368, 0x0369, 0x036a, 0x036b, 0x036c, 0x036d, 0x036e, 0x036f,
  0x0370, 0x0371, 0x0372, 0x0373, 0x0374, 0x0375, 0x0376, 0x0377,
  0x0378, 0x0379, 0x037a, 0x037b, 0x037c, 0x037d, 0x037e, 0x037f,
  0x0380, 0x0381, 0x0382, 0x0383, 0x0384, 0x0385, 0x03ac, 0x0387,
  0x03ad, 0x03ae, 0x03af, 0x038b, 0x03cc, 0x038d, 0x03cd, 0x03ce,
  0x0390, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7,
  0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf,
  0x03c0, 0x03c1, 0x03a2, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7,
  0x03c8, 0x03c9, 0x03ca, 0x03cb, 0x03ac, 0x03ad, 0x03ae, 0x03af,
  0x03b0, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7,
  0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf,
  0x03c0, 0x03c1, 0x03c3, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7,
  0x03c8, 0x03c9, 0x03ca, 0x03cb, 0x03cc, 0x03cd, 0x03ce, 0x03cf,
  0x03b2, 0x03b8, 0x03d2, 0x03d3, 0x03d4, 0x03c6, 0x03c0, 0x03d7,
  0x03d9, 0x03d9, 0x03db, 0x03db, 0x03dd, 0x03dd, 0x03df, 0x03df,
  0x03e1, 0x03e1, 0x03e3, 0x03e3, 0x03e5, 0x03e5, 0x03e7, 0x03e7,
  0x03e9, 0x03e9, 0x03eb, 0x03eb, 0x03ed, 0x03ed, 0x03ef, 0x03ef,
  0x03ba, 0x03c1, 0x03f2, 0x03f3, 0x03b8, 0x03b5, 0x03f6, 0x03f8,
  0x03f8, 0x03f2, 0x03fb, 0x03fb, 0x03fc, 0x03fd, 0x03fe, 0x03ff
}};
static UnicodeCaseTableVector caseTable04 = {{
  0x0450, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457,
  0x0458, 0x0459, 0x045a, 0x045b, 0x045c, 0x045d, 0x045e, 0x045f,
  0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,
  0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, 0x043f,
  0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,
  0x0448, 0x0449, 0x044a, 0x044b, 0x044c, 0x044d, 0x044e, 0x044f,
  0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,
  0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, 0x043f,
  0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,
  0x0448, 0x0449, 0x044a, 0x044b, 0x044c, 0x044d, 0x044e, 0x044f,
  0x0450, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457,
  0x0458, 0x0459, 0x045a, 0x045b, 0x045c, 0x045d, 0x045e, 0x045f,
  0x0461, 0x0461, 0x0463, 0x0463, 0x0465, 0x0465, 0x0467, 0x0467,
  0x0469, 0x0469, 0x046b, 0x046b, 0x046d, 0x046d, 0x046f, 0x046f,
  0x0471, 0x0471, 0x0473, 0x0473, 0x0475, 0x0475, 0x0477, 0x0477,
  0x0479, 0x0479, 0x047b, 0x047b, 0x047d, 0x047d, 0x047f, 0x047f,
  0x0481, 0x0481, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x0487,
  0x0488, 0x0489, 0x048b, 0x048b, 0x048d, 0x048d, 0x048f, 0x048f,
  0x0491, 0x0491, 0x0493, 0x0493, 0x0495, 0x0495, 0x0497, 0x0497,
  0x0499, 0x0499, 0x049b, 0x049b, 0x049d, 0x049d, 0x049f, 0x049f,
  0x04a1, 0x04a1, 0x04a3, 0x04a3, 0x04a5, 0x04a5, 0x04a7, 0x04a7,
  0x04a9, 0x04a9, 0x04ab, 0x04ab, 0x04ad, 0x04ad, 0x04af, 0x04af,
  0x04b1, 0x04b1, 0x04b3, 0x04b3, 0x04b5, 0x04b5, 0x04b7, 0x04b7,
  0x04b9, 0x04b9, 0x04bb, 0x04bb, 0x04bd, 0x04bd, 0x04bf, 0x04bf,
  0x04c0, 0x04c2, 0x04c2, 0x04c4, 0x04c4, 0x04c6, 0x04c6, 0x04c8,
  0x04c8, 0x04ca, 0x04ca, 0x04cc, 0x04cc, 0x04ce, 0x04ce, 0x04cf,
  0x04d1, 0x04d1, 0x04d3, 0x04d3, 0x04d5, 0x04d5, 0x04d7, 0x04d7,
  0x04d9, 0x04d9, 0x04db, 0x04db, 0x04dd, 0x04dd, 0x04df, 0x04df,
  0x04e1, 0x04e1, 0x04e3, 0x04e3, 0x04e5, 0x04e5, 0x04e7, 0x04e7,
  0x04e9, 0x04e9, 0x04eb, 0x04eb, 0x04ed, 0x04ed, 0x04ef, 0x04ef,
  0x04f1, 0x04f1, 0x04f3, 0x04f3, 0x04f5, 0x04f5, 0x04f6, 0x04f7,
  0x04f9, 0x04f9, 0x04fa, 0x04fb, 0x04fc, 0x04fd, 0x04fe, 0x04ff
}};
static UnicodeCaseTableVector caseTable05 = {{
  0x0501, 0x0501, 0x0503, 0x0503, 0x0505, 0x0505, 0x0507, 0x0507,
  0x0509, 0x0509, 0x050b, 0x050b, 0x050d, 0x050d, 0x050f, 0x050f,
  0x0510, 0x0511, 0x0512, 0x0513, 0x0514, 0x0515, 0x0516, 0x0517,
  0x0518, 0x0519, 0x051a, 0x051b, 0x051c, 0x051d, 0x051e, 0x051f,
  0x0520, 0x0521, 0x0522, 0x0523, 0x0524, 0x0525, 0x0526, 0x0527,
  0x0528, 0x0529, 0x052a, 0x052b, 0x052c, 0x052d, 0x052e, 0x052f,
  0x0530, 0x0561, 0x0562, 0x0563, 0x0564, 0x0565, 0x0566, 0x0567,
  0x0568, 0x0569, 0x056a, 0x056b, 0x056c, 0x056d, 0x056e, 0x056f,
  0x0570, 0x0571, 0x0572, 0x0573, 0x0574, 0x0575, 0x0576, 0x0577,
  0x0578, 0x0579, 0x057a, 0x057b, 0x057c, 0x057d, 0x057e, 0x057f,
  0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0586, 0x0557,
  0x0558, 0x0559, 0x055a, 0x055b, 0x055c, 0x055d, 0x055e, 0x055f,
  0x0560, 0x0561, 0x0562, 0x0563, 0x0564, 0x0565, 0x0566, 0x0567,
  0x0568, 0x0569, 0x056a, 0x056b, 0x056c, 0x056d, 0x056e, 0x056f,
  0x0570, 0x0571, 0x0572, 0x0573, 0x0574, 0x0575, 0x0576, 0x0577,
  0x0578, 0x0579, 0x057a, 0x057b, 0x057c, 0x057d, 0x057e, 0x057f,
  0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0586, 0x0587,
  0x0588, 0x0589, 0x058a, 0x058b, 0x058c, 0x058d, 0x058e, 0x058f,
  0x0590, 0x0591, 0x0592, 0x0593, 0x0594, 0x0595, 0x0596, 0x0597,
  0x0598, 0x0599, 0x059a, 0x059b, 0x059c, 0x059d, 0x059e, 0x059f,
  0x05a0, 0x05a1, 0x05a2, 0x05a3, 0x05a4, 0x05a5, 0x05a6, 0x05a7,
  0x05a8, 0x05a9, 0x05aa, 0x05ab, 0x05ac, 0x05ad, 0x05ae, 0x05af,
  0x05b0, 0x05b1, 0x05b2, 0x05b3, 0x05b4, 0x05b5, 0x05b6, 0x05b7,
  0x05b8, 0x05b9, 0x05ba, 0x05bb, 0x05bc, 0x05bd, 0x05be, 0x05bf,
  0x05c0, 0x05c1, 0x05c2, 0x05c3, 0x05c4, 0x05c5, 0x05c6, 0x05c7,
  0x05c8, 0x05c9, 0x05ca, 0x05cb, 0x05cc, 0x05cd, 0x05ce, 0x05cf,
  0x05d0, 0x05d1, 0x05d2, 0x05d3, 0x05d4, 0x05d5, 0x05d6, 0x05d7,
  0x05d8, 0x05d9, 0x05da, 0x05db, 0x05dc, 0x05dd, 0x05de, 0x05df,
  0x05e0, 0x05e1, 0x05e2, 0x05e3, 0x05e4, 0x05e5, 0x05e6, 0x05e7,
  0x05e8, 0x05e9, 0x05ea, 0x05eb, 0x05ec, 0x05ed, 0x05ee, 0x05ef,
  0x05f0, 0x05f1, 0x05f2, 0x05f3, 0x05f4, 0x05f5, 0x05f6, 0x05f7,
  0x05f8, 0x05f9, 0x05fa, 0x05fb, 0x05fc, 0x05fd, 0x05fe, 0x05ff
}};
static UnicodeCaseTableVector caseTable1e = {{
  0x1e01, 0x1e01, 0x1e03, 0x1e03, 0x1e05, 0x1e05, 0x1e07, 0x1e07,
  0x1e09, 0x1e09, 0x1e0b, 0x1e0b, 0x1e0d, 0x1e0d, 0x1e0f, 0x1e0f,
  0x1e11, 0x1e11, 0x1e13, 0x1e13, 0x1e15, 0x1e15, 0x1e17, 0x1e17,
  0x1e19, 0x1e19, 0x1e1b, 0x1e1b, 0x1e1d, 0x1e1d, 0x1e1f, 0x1e1f,
  0x1e21, 0x1e21, 0x1e23, 0x1e23, 0x1e25, 0x1e25, 0x1e27, 0x1e27,
  0x1e29, 0x1e29, 0x1e2b, 0x1e2b, 0x1e2d, 0x1e2d, 0x1e2f, 0x1e2f,
  0x1e31, 0x1e31, 0x1e33, 0x1e33, 0x1e35, 0x1e35, 0x1e37, 0x1e37,
  0x1e39, 0x1e39, 0x1e3b, 0x1e3b, 0x1e3d, 0x1e3d, 0x1e3f, 0x1e3f,
  0x1e41, 0x1e41, 0x1e43, 0x1e43, 0x1e45, 0x1e45, 0x1e47, 0x1e47,
  0x1e49, 0x1e49, 0x1e4b, 0x1e4b, 0x1e4d, 0x1e4d, 0x1e4f, 0x1e4f,
  0x1e51, 0x1e51, 0x1e53, 0x1e53, 0x1e55, 0x1e55, 0x1e57, 0x1e57,
  0x1e59, 0x1e59, 0x1e5b, 0x1e5b, 0x1e5d, 0x1e5d, 0x1e5f, 0x1e5f,
  0x1e61, 0x1e61, 0x1e63, 0x1e63, 0x1e65, 0x1e65, 0x1e67, 0x1e67,
  0x1e69, 0x1e69, 0x1e6b, 0x1e6b, 0x1e6d, 0x1e6d, 0x1e6f, 0x1e6f,
  0x1e71, 0x1e71, 0x1e73, 0x1e73, 0x1e75, 0x1e75, 0x1e77, 0x1e77,
  0x1e79, 0x1e79, 0x1e7b, 0x1e7b, 0x1e7d, 0x1e7d, 0x1e7f, 0x1e7f,
  0x1e81, 0x1e81, 0x1e83, 0x1e83, 0x1e85, 0x1e85, 0x1e87, 0x1e87,
  0x1e89, 0x1e89, 0x1e8b, 0x1e8b, 0x1e8d, 0x1e8d, 0x1e8f, 0x1e8f,
  0x1e91, 0x1e91, 0x1e93, 0x1e93, 0x1e95, 0x1e95, 0x1e96, 0x1e97,
  0x1e98, 0x1e99, 0x1e9a, 0x1e61, 0x1e9c, 0x1e9d, 0x1e9e, 0x1e9f,
  0x1ea1, 0x1ea1, 0x1ea3, 0x1ea3, 0x1ea5, 0x1ea5, 0x1ea7, 0x1ea7,
  0x1ea9, 0x1ea9, 0x1eab, 0x1eab, 0x1ead, 0x1ead, 0x1eaf, 0x1eaf,
  0x1eb1, 0x1eb1, 0x1eb3, 0x1eb3, 0x1eb5, 0x1eb5, 0x1eb7, 0x1eb7,
  0x1eb9, 0x1eb9, 0x1ebb, 0x1ebb, 0x1ebd, 0x1ebd, 0x1ebf, 0x1ebf,
  0x1ec1, 0x1ec1, 0x1ec3, 0x1ec3, 0x1ec5, 0x1ec5, 0x1ec7, 0x1ec7,
  0x1ec9, 0x1ec9, 0x1ecb, 0x1ecb, 0x1ecd, 0x1ecd, 0x1ecf, 0x1ecf,
  0x1ed1, 0x1ed1, 0x1ed3, 0x1ed3, 0x1ed5, 0x1ed5, 0x1ed7, 0x1ed7,
  0x1ed9, 0x1ed9, 0x1edb, 0x1edb, 0x1edd, 0x1edd, 0x1edf, 0x1edf,
  0x1ee1, 0x1ee1, 0x1ee3, 0x1ee3, 0x1ee5, 0x1ee5, 0x1ee7, 0x1ee7,
  0x1ee9, 0x1ee9, 0x1eeb, 0x1eeb, 0x1eed, 0x1eed, 0x1eef, 0x1eef,
  0x1ef1, 0x1ef1, 0x1ef3, 0x1ef3, 0x1ef5, 0x1ef5, 0x1ef7, 0x1ef7,
  0x1ef9, 0x1ef9, 0x1efa, 0x1efb, 0x1efc, 0x1efd, 0x1efe, 0x1eff
}};
static UnicodeCaseTableVector caseTable1f = {{
  0x1f00, 0x1f01, 0x1f02, 0x1f03, 0x1f04, 0x1f05, 0x1f06, 0x1f07,
  0x1f00, 0x1f01, 0x1f02, 0x1f03, 0x1f04, 0x1f05, 0x1f06, 0x1f07,
  0x1f10, 0x1f11, 0x1f12, 0x1f13, 0x1f14, 0x1f15, 0x1f16, 0x1f17,
  0x1f10, 0x1f11, 0x1f12, 0x1f13, 0x1f14, 0x1f15, 0x1f1e, 0x1f1f,
  0x1f20, 0x1f21, 0x1f22, 0x1f23, 0x1f24, 0x1f25, 0x1f26, 0x1f27,
  0x1f20, 0x1f21, 0x1f22, 0x1f23, 0x1f24, 0x1f25, 0x1f26, 0x1f27,
  0x1f30, 0x1f31, 0x1f32, 0x1f33, 0x1f34, 0x1f35, 0x1f36, 0x1f37,
  0x1f30, 0x1f31, 0x1f32, 0x1f33, 0x1f34, 0x1f35, 0x1f36, 0x1f37,
  0x1f40, 0x1f41, 0x1f42, 0x1f43, 0x1f44, 0x1f45, 0x1f46, 0x1f47,
  0x1f40, 0x1f41, 0x1f42, 0x1f43, 0x1f44, 0x1f45, 0x1f4e, 0x1f4f,
  0x1f50, 0x1f51, 0x1f52, 0x1f53, 0x1f54, 0x1f55, 0x1f56, 0x1f57,
  0x1f58, 0x1f51, 0x1f5a, 0x1f53, 0x1f5c, 0x1f55, 0x1f5e, 0x1f57,
  0x1f60, 0x1f61, 0x1f62, 0x1f63, 0x1f64, 0x1f65, 0x1f66, 0x1f67,
  0x1f60, 0x1f61, 0x1f62, 0x1f63, 0x1f64, 0x1f65, 0x1f66, 0x1f67,
  0x1f70, 0x1f71, 0x1f72, 0x1f73, 0x1f74, 0x1f75, 0x1f76, 0x1f77,
  0x1f78, 0x1f79, 0x1f7a, 0x1f7b, 0x1f7c, 0x1f7d, 0x1f7e, 0x1f7f,
  0x1f80, 0x1f81, 0x1f82, 0x1f83, 0x1f84, 0x1f85, 0x1f86, 0x1f87,
  0x1f80, 0x1f81, 0x1f82, 0x1f83, 0x1f84, 0x1f85, 0x1f86, 0x1f87,
  0x1f90, 0x1f91, 0x1f92, 0x1f93, 0x1f94, 0x1f95, 0x1f96, 0x1f97,
  0x1f90, 0x1f91, 0x1f92, 0x1f93, 0x1f94, 0x1f95, 0x1f96, 0x1f97,
  0x1fa0, 0x1fa1, 0x1fa2, 0x1fa3, 0x1fa4, 0x1fa5, 0x1fa6, 0x1fa7,
  0x1fa0, 0x1fa1, 0x1fa2, 0x1fa3, 0x1fa4, 0x1fa5, 0x1fa6, 0x1fa7,
  0x1fb0, 0x1fb1, 0x1fb2, 0x1fb3, 0x1fb4, 0x1fb5, 0x1fb6, 0x1fb7,
  0x1fb0, 0x1fb1, 0x1f70, 0x1f71, 0x1fb3, 0x1fbd, 0x03b9, 0x1fbf,
  0x1fc0, 0x1fc1, 0x1fc2, 0x1fc3, 0x1fc4, 0x1fc5, 0x1fc6, 0x1fc7,
  0x1f72, 0x1f73, 0x1f74, 0x1f75, 0x1fc3, 0x1fcd, 0x1fce, 0x1fcf,
  0x1fd0, 0x1fd1, 0x1fd2, 0x1fd3, 0x1fd4, 0x1fd5, 0x1fd6, 0x1fd7,
  0x1fd0, 0x1fd1, 0x1f76, 0x1f77, 0x1fdc, 0x1fdd, 0x1fde, 0x1fdf,
  0x1fe0, 0x1fe1, 0x1fe2, 0x1fe3, 0x1fe4, 0x1fe5, 0x1fe6, 0x1fe7,
  0x1fe0, 0x1fe1, 0x1f7a, 0x1f7b, 0x1fe5, 0x1fed, 0x1fee, 0x1fef,
  0x1ff0, 0x1ff1, 0x1ff2, 0x1ff3, 0x1ff4, 0x1ff5, 0x1ff6, 0x1ff7,
  0x1f78, 0x1f79, 0x1f7c, 0x1f7d, 0x1ff3, 0x1ffd, 0x1ffe, 0x1fff
}};
static UnicodeCaseTableVector caseTable21 = {{
  0x2100, 0x2101, 0x2102, 0x2103, 0x2104, 0x2105, 0x2106, 0x2107,
  0x2108, 0x2109, 0x210a, 0x210b, 0x210c, 0x210d, 0x210e, 0x210f,
  0x2110, 0x2111, 0x2112, 0x2113, 0x2114, 0x2115, 0x2116, 0x2117,
  0x2118, 0x2119, 0x211a, 0x211b, 0x211c, 0x211d, 0x211e, 0x211f,
  0x2120, 0x2121, 0x2122, 0x2123, 0x2124, 0x2125, 0x03c9, 0x2127,
  0x2128, 0x2129, 0x006b, 0x00e5, 0x212c, 0x212d, 0x212e, 0x212f,
  0x2130, 0x2131, 0x2132, 0x2133, 0x2134, 0x2135, 0x2136, 0x2137,
  0x2138, 0x2139, 0x213a, 0x213b, 0x213c, 0x213d, 0x213e, 0x213f,
  0x2140, 0x2141, 0x2142, 0x2143, 0x2144, 0x2145, 0x2146, 0x2147,
  0x2148, 0x2149, 0x214a, 0x214b, 0x214c, 0x214d, 0x214e, 0x214f,
  0x2150, 0x2151, 0x2152, 0x2153, 0x2154, 0x2155, 0x2156, 0x2157,
  0x2158, 0x2159, 0x215a, 0x215b, 0x215c, 0x215d, 0x215e, 0x215f,
  0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177,
  0x2178, 0x2179, 0x217a, 0x217b, 0x217c, 0x217d, 0x217e, 0x217f,
  0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177,
  0x2178, 0x2179, 0x217a, 0x217b, 0x217c, 0x217d, 0x217e, 0x217f,
  0x2180, 0x2181, 0x2182, 0x2183, 0x2184, 0x2185, 0x2186, 0x2187,
  0x2188, 0x2189, 0x218a, 0x218b, 0x218c, 0x218d, 0x218e, 0x218f,
  0x2190, 0x2191, 0x2192, 0x2193, 0x2194, 0x2195, 0x2196, 0x2197,
  0x2198, 0x2199, 0x219a, 0x219b, 0x219c, 0x219d, 0x219e, 0x219f,
  0x21a0, 0x21a1, 0x21a2, 0x21a3, 0x21a4, 0x21a5, 0x21a6, 0x21a7,
  0x21a8, 0x21a9, 0x21aa, 0x21ab, 0x21ac, 0x21ad, 0x21ae, 0x21af,
  0x21b0, 0x21b1, 0x21b2, 0x21b3, 0x21b4, 0x21b5, 0x21b6, 0x21b7,
  0x21b8, 0x21b9, 0x21ba, 0x21bb, 0x21bc, 0x21bd, 0x21be, 0x21bf,
  0x21c0, 0x21c1, 0x21c2, 0x21c3, 0x21c4, 0x21c5, 0x21c6, 0x21c7,
  0x21c8, 0x21c9, 0x21ca, 0x21cb, 0x21cc, 0x21cd, 0x21ce, 0x21cf,
  0x21d0, 0x21d1, 0x21d2, 0x21d3, 0x21d4, 0x21d5, 0x21d6, 0x21d7,
  0x21d8, 0x21d9, 0x21da, 0x21db, 0x21dc, 0x21dd, 0x21de, 0x21df,
  0x21e0, 0x21e1, 0x21e2, 0x21e3, 0x21e4, 0x21e5, 0x21e6, 0x21e7,
  0x21e8, 0x21e9, 0x21ea, 0x21eb, 0x21ec, 0x21ed, 0x21ee, 0x21ef,
  0x21f0, 0x21f1, 0x21f2, 0x21f3, 0x21f4, 0x21f5, 0x21f6, 0x21f7,
  0x21f8, 0x21f9, 0x21fa, 0x21fb, 0x21fc, 0x21fd, 0x21fe, 0x21ff
}};
static UnicodeCaseTableVector caseTable24 = {{
  0x2400, 0x2401, 0x2402, 0x2403, 0x2404, 0x2405, 0x2406, 0x2407,
  0x2408, 0x2409, 0x240a, 0x240b, 0x240c, 0x240d, 0x240e, 0x240f,
  0x2410, 0x2411, 0x2412, 0x2413, 0x2414, 0x2415, 0x2416, 0x2417,
  0x2418, 0x2419, 0x241a, 0x241b, 0x241c, 0x241d, 0x241e, 0x241f,
  0x2420, 0x2421, 0x2422, 0x2423, 0x2424, 0x2425, 0x2426, 0x2427,
  0x2428, 0x2429, 0x242a, 0x242b, 0x242c, 0x242d, 0x242e, 0x242f,
  0x2430, 0x2431, 0x2432, 0x2433, 0x2434, 0x2435, 0x2436, 0x2437,
  0x2438, 0x2439, 0x243a, 0x243b, 0x243c, 0x243d, 0x243e, 0x243f,
  0x2440, 0x2441, 0x2442, 0x2443, 0x2444, 0x2445, 0x2446, 0x2447,
  0x2448, 0x2449, 0x244a, 0x244b, 0x244c, 0x244d, 0x244e, 0x244f,
  0x2450, 0x2451, 0x2452, 0x2453, 0x2454, 0x2455, 0x2456, 0x2457,
  0x2458, 0x2459, 0x245a, 0x245b, 0x245c, 0x245d, 0x245e, 0x245f,
  0x2460, 0x2461, 0x2462, 0x2463, 0x2464, 0x2465, 0x2466, 0x2467,
  0x2468, 0x2469, 0x246a, 0x246b, 0x246c, 0x246d, 0x246e, 0x246f,
  0x2470, 0x2471, 0x2472, 0x2473, 0x2474, 0x2475, 0x2476, 0x2477,
  0x2478, 0x2479, 0x247a, 0x247b, 0x247c, 0x247d, 0x247e, 0x247f,
  0x2480, 0x2481, 0x2482, 0x2483, 0x2484, 0x2485, 0x2486, 0x2487,
  0x2488, 0x2489, 0x248a, 0x248b, 0x248c, 0x248d, 0x248e, 0x248f,
  0x2490, 0x2491, 0x2492, 0x2493, 0x2494, 0x2495, 0x2496, 0x2497,
  0x2498, 0x2499, 0x249a, 0x249b, 0x249c, 0x249d, 0x249e, 0x249f,
  0x24a0, 0x24a1, 0x24a2, 0x24a3, 0x24a4, 0x24a5, 0x24a6, 0x24a7,
  0x24a8, 0x24a9, 0x24aa, 0x24ab, 0x24ac, 0x24ad, 0x24ae, 0x24af,
  0x24b0, 0x24b1, 0x24b2, 0x24b3, 0x24b4, 0x24b5, 0x24d0, 0x24d1,
  0x24d2, 0x24d3, 0x24d4, 0x24d5, 0x24d6, 0x24d7, 0x24d8, 0x24d9,
  0x24da, 0x24db, 0x24dc, 0x24dd, 0x24de, 0x24df, 0x24e0, 0x24e1,
  0x24e2, 0x24e3, 0x24e4, 0x24e5, 0x24e6, 0x24e7, 0x24e8, 0x24e9,
  0x24d0, 0x24d1, 0x24d2, 0x24d3, 0x24d4, 0x24d5, 0x24d6, 0x24d7,
  0x24d8, 0x24d9, 0x24da, 0x24db, 0x24dc, 0x24dd, 0x24de, 0x24df,
  0x24e0, 0x24e1, 0x24e2, 0x24e3, 0x24e4, 0x24e5, 0x24e6, 0x24e7,
  0x24e8, 0x24e9, 0x24ea, 0x24eb, 0x24ec, 0x24ed, 0x24ee, 0x24ef,
  0x24f0, 0x24f1, 0x24f2, 0x24f3, 0x24f4, 0x24f5, 0x24f6, 0x24f7,
  0x24f8, 0x24f9, 0x24fa, 0x24fb, 0x24fc, 0x24fd, 0x24fe, 0x24ff
}};
static UnicodeCaseTableVector caseTableff = {{
  0xff00, 0xff01, 0xff02, 0xff03, 0xff04, 0xff05, 0xff06, 0xff07,
  0xff08, 0xff09, 0xff0a, 0xff0b, 0xff0c, 0xff0d, 0xff0e, 0xff0f,
  0xff10, 0xff11, 0xff12, 0xff13, 0xff14, 0xff15, 0xff16, 0xff17,
  0xff18, 0xff19, 0xff1a, 0xff1b, 0xff1c, 0xff1d, 0xff1e, 0xff1f,
  0xff20, 0xff41, 0xff42, 0xff43, 0xff44, 0xff45, 0xff46, 0xff47,
  0xff48, 0xff49, 0xff4a, 0xff4b, 0xff4c, 0xff4d, 0xff4e, 0xff4f,
  0xff50, 0xff51, 0xff52, 0xff53, 0xff54, 0xff55, 0xff56, 0xff57,
  0xff58, 0xff59, 0xff5a, 0xff3b, 0xff3c, 0xff3d, 0xff3e, 0xff3f,
  0xff40, 0xff41, 0xff42, 0xff43, 0xff44, 0xff45, 0xff46, 0xff47,
  0xff48, 0xff49, 0xff4a, 0xff4b, 0xff4c, 0xff4d, 0xff4e, 0xff4f,
  0xff50, 0xff51, 0xff52, 0xff53, 0xff54, 0xff55, 0xff56, 0xff57,
  0xff58, 0xff59, 0xff5a, 0xff5b, 0xff5c, 0xff5d, 0xff5e, 0xff5f,
  0xff60, 0xff61, 0xff62, 0xff63, 0xff64, 0xff65, 0xff66, 0xff67,
  0xff68, 0xff69, 0xff6a, 0xff6b, 0xff6c, 0xff6d, 0xff6e, 0xff6f,
  0xff70, 0xff71, 0xff72, 0xff73, 0xff74, 0xff75, 0xff76, 0xff77,
  0xff78, 0xff79, 0xff7a, 0xff7b, 0xff7c, 0xff7d, 0xff7e, 0xff7f,
  0xff80, 0xff81, 0xff82, 0xff83, 0xff84, 0xff85, 0xff86, 0xff87,
  0xff88, 0xff89, 0xff8a, 0xff8b, 0xff8c, 0xff8d, 0xff8e, 0xff8f,
  0xff90, 0xff91, 0xff92, 0xff93, 0xff94, 0xff95, 0xff96, 0xff97,
  0xff98, 0xff99, 0xff9a, 0xff9b, 0xff9c, 0xff9d, 0xff9e, 0xff9f,
  0xffa0, 0xffa1, 0xffa2, 0xffa3, 0xffa4, 0xffa5, 0xffa6, 0xffa7,
  0xffa8, 0xffa9, 0xffaa, 0xffab, 0xffac, 0xffad, 0xffae, 0xffaf,
  0xffb0, 0xffb1, 0xffb2, 0xffb3, 0xffb4, 0xffb5, 0xffb6, 0xffb7,
  0xffb8, 0xffb9, 0xffba, 0xffbb, 0xffbc, 0xffbd, 0xffbe, 0xffbf,
  0xffc0, 0xffc1, 0xffc2, 0xffc3, 0xffc4, 0xffc5, 0xffc6, 0xffc7,
  0xffc8, 0xffc9, 0xffca, 0xffcb, 0xffcc, 0xffcd, 0xffce, 0xffcf,
  0xffd0, 0xffd1, 0xffd2, 0xffd3, 0xffd4, 0xffd5, 0xffd6, 0xffd7,
  0xffd8, 0xffd9, 0xffda, 0xffdb, 0xffdc, 0xffdd, 0xffde, 0xffdf,
  0xffe0, 0xffe1, 0xffe2, 0xffe3, 0xffe4, 0xffe5, 0xffe6, 0xffe7,
  0xffe8, 0xffe9, 0xffea, 0xffeb, 0xffec, 0xffed, 0xffee, 0xffef,
  0xfff0, 0xfff1, 0xfff2, 0xfff3, 0xfff4, 0xfff5, 0xfff6, 0xfff7,
  0xfff8, 0xfff9, 0xfffa, 0xfffb, 0xfffc, 0xfffd, 0xfffe, 0xffff
}};
static UnicodeCaseTableVector *caseTable[256] = {
  &caseTable00,
  &caseTable01,
  &caseTable02,
  &caseTable03,
  &caseTable04,
  &caseTable05,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  &caseTable1e,
  &caseTable1f,
  NULL,
  &caseTable21,
  NULL,
  NULL,
  &caseTable24,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  &caseTableff
};

static inline char getType(Unicode c) {
  int i;
  char type;

  if (c > 0xffff) {
    type = 'X';
  } else {
    i = (c >> 8) & 0xff;
    if ((type = typeTable[i].type) == 'X') {
      type = typeTable[i].vector[c & 0xff];
    }
  }
  return type;
}

GBool unicodeTypeL(Unicode c) {
  return getType(c) == 'L';
}

GBool unicodeTypeR(Unicode c) {
  return getType(c) == 'R';
}

Unicode unicodeToUpper(Unicode c) {
  int i;

  if (c > 0xffff) {
    return c;
  }
  i = (c >> 8) & 0xff;
  if (caseTable[i]) {
    return caseTable[i]->codes[c & 0xff];
  }
  return c;
}

//========================================================================
//
// UnicodeTypeTable.h
//
// Copyright 2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef UNICODETYPETABLE_H
#define UNICODETYPETABLE_H

#include "gtypes.h"

extern GBool unicodeTypeL(Unicode c);

extern GBool unicodeTypeR(Unicode c);

extern Unicode unicodeToUpper(Unicode c);

#endif
//========================================================================
//
// XPDFApp.cc
//
// Copyright 2002-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include "GString.h"
#include "GList.h"
#include "Error.h"
#include "XPDFViewer.h"
#include "XPDFApp.h"
#include "config.h"

// these macro defns conflict with xpdf's Object class
#ifdef LESSTIF_VERSION
#undef XtDisplay
#undef XtScreen
#undef XtWindow
#undef XtParent
#undef XtIsRealized
#endif

//------------------------------------------------------------------------

#define remoteCmdSize 512

//------------------------------------------------------------------------

static String fallbackResources[] = {
  "*.zoomComboBox*fontList: -*-helvetica-medium-r-normal--12-*-*-*-*-*-iso8859-1",
  "*XmTextField.fontList: -*-courier-medium-r-normal--12-*-*-*-*-*-iso8859-1",
  "*.fontList: -*-helvetica-medium-r-normal--12-*-*-*-*-*-iso8859-1",
  "*XmTextField.translations: #override\\n"
  "  Ctrl<Key>a:beginning-of-line()\\n"
  "  Ctrl<Key>b:backward-character()\\n"
  "  Ctrl<Key>d:delete-next-character()\\n"
  "  Ctrl<Key>e:end-of-line()\\n"
  "  Ctrl<Key>f:forward-character()\\n"
  "  Ctrl<Key>u:beginning-of-line()delete-to-end-of-line()\\n"
  "  Ctrl<Key>k:delete-to-end-of-line()\\n",
  "*.toolTipEnable: True",
  "*.toolTipPostDelay: 1500",
  "*.toolTipPostDuration: 0",
  "*.TipLabel.foreground: black",
  "*.TipLabel.background: LightYellow",
  "*.TipShell.borderWidth: 1",
  "*.TipShell.borderColor: black",
  NULL
};

static XrmOptionDescRec xOpts[] = {
  {"-display",       ".display",         XrmoptionSepArg,  NULL},
  {"-foreground",    "*Foreground",      XrmoptionSepArg,  NULL},
  {"-fg",            "*Foreground",      XrmoptionSepArg,  NULL},
  {"-background",    "*Background",      XrmoptionSepArg,  NULL},
  {"-bg",            "*Background",      XrmoptionSepArg,  NULL},
  {"-geometry",      ".geometry",        XrmoptionSepArg,  NULL},
  {"-g",             ".geometry",        XrmoptionSepArg,  NULL},
  {"-font",          "*.fontList",       XrmoptionSepArg,  NULL},
  {"-fn",            "*.fontList",       XrmoptionSepArg,  NULL},
  {"-title",         ".title",           XrmoptionSepArg,  NULL},
  {"-cmap",          ".installCmap",     XrmoptionNoArg,   (XPointer)"on"},
  {"-rgb",           ".rgbCubeSize",     XrmoptionSepArg,  NULL},
  {"-rv",            ".reverseVideo",    XrmoptionNoArg,   (XPointer)"true"},
  {"-papercolor",    ".paperColor",      XrmoptionSepArg,  NULL},
  {"-mattecolor",    ".matteColor",      XrmoptionSepArg,  NULL},
  {"-z",             ".initialZoom",     XrmoptionSepArg,  NULL}
};

#define nXOpts (sizeof(xOpts) / sizeof(XrmOptionDescRec))

struct XPDFAppResources {
  String geometry;
  String title;
  Bool installCmap;
  int rgbCubeSize;
  Bool reverseVideo;
  String paperColor;
  String matteColor;
  String fullScreenMatteColor;
  String initialZoom;
};

static Bool defInstallCmap = False;
static int defRGBCubeSize = defaultRGBCube;
static Bool defReverseVideo = False;

static XtResource xResources[] = {
  { "geometry",             "Geometry",             XtRString, sizeof(String), XtOffsetOf(XPDFAppResources, geometry),             XtRString, (XtPointer)NULL             },
  { "title",                "Title",                XtRString, sizeof(String), XtOffsetOf(XPDFAppResources, title),                XtRString, (XtPointer)NULL             },
  { "installCmap",          "InstallCmap",          XtRBool,   sizeof(Bool),   XtOffsetOf(XPDFAppResources, installCmap),          XtRBool,   (XtPointer)&defInstallCmap  },
  { "rgbCubeSize",          "RgbCubeSize",          XtRInt,    sizeof(int),    XtOffsetOf(XPDFAppResources, rgbCubeSize),          XtRInt,    (XtPointer)&defRGBCubeSize  },
  { "reverseVideo",         "ReverseVideo",         XtRBool,   sizeof(Bool),   XtOffsetOf(XPDFAppResources, reverseVideo),         XtRBool,   (XtPointer)&defReverseVideo },
  { "paperColor",           "PaperColor",           XtRString, sizeof(String), XtOffsetOf(XPDFAppResources, paperColor),           XtRString, (XtPointer)NULL             },
  { "matteColor",           "MatteColor",           XtRString, sizeof(String), XtOffsetOf(XPDFAppResources, matteColor),           XtRString, (XtPointer)"gray50"         },
  { "fullScreenMatteColor", "FullScreenMatteColor", XtRString, sizeof(String), XtOffsetOf(XPDFAppResources, fullScreenMatteColor), XtRString, (XtPointer)"black"          },
  { "initialZoom",          "InitialZoom",          XtRString, sizeof(String), XtOffsetOf(XPDFAppResources, initialZoom),          XtRString, (XtPointer)NULL             }
};

#define nXResources (sizeof(xResources) / sizeof(XtResource))

//------------------------------------------------------------------------
// XPDFApp
//------------------------------------------------------------------------

#if 0 //~ for debugging
static int xErrorHandler(Display *display, XErrorEvent *ev) {
  printf("X error:\n");
  printf("  resource ID = %08lx\n", ev->resourceid);
  printf("  serial = %lu\n", ev->serial);
  printf("  error_code = %d\n", ev->error_code);
  printf("  request_code = %d\n", ev->request_code);
  printf("  minor_code = %d\n", ev->minor_code);
  fflush(stdout);
  abort();
}
#endif

XPDFApp::XPDFApp(int *argc, char *argv[]) {
  appShell = XtAppInitialize(&appContext, xpdfAppName, xOpts, nXOpts,
			     argc, argv, fallbackResources, NULL, 0);
  display = XtDisplay(appShell);
  screenNum = XScreenNumberOfScreen(XtScreen(appShell));
#if XmVERSION > 1
  XtVaSetValues(XmGetXmDisplay(XtDisplay(appShell)),
		XmNenableButtonTab, True, NULL);
#endif
#if XmVERSION > 1
  // Drag-and-drop appears to be buggy -- I'm seeing weird crashes
  // deep in the Motif code when I destroy widgets in the XpdfForms
  // code.  Xpdf doesn't use it, so just turn it off.
  XtVaSetValues(XmGetXmDisplay(XtDisplay(appShell)),
		XmNdragInitiatorProtocolStyle, XmDRAG_NONE,
		XmNdragReceiverProtocolStyle, XmDRAG_NONE,
		NULL);
#endif

#if 0 //~ for debugging
  XSynchronize(display, True);
  XSetErrorHandler(&xErrorHandler);
#endif

  fullScreen = gFalse;
  remoteAtom = None;
  remoteViewer = NULL;
  remoteWin = None;

  getResources();

  viewers = new GList();

}

void XPDFApp::getResources() {
  XPDFAppResources resources;
  XColor xcol, xcol2;
  Colormap colormap;
  
  XtGetApplicationResources(appShell, &resources, xResources, nXResources,
			    NULL, 0);
  geometry = resources.geometry ? new GString(resources.geometry)
                                : (GString *)NULL;
  title = resources.title ? new GString(resources.title) : (GString *)NULL;
  installCmap = (GBool)resources.installCmap;
  rgbCubeSize = resources.rgbCubeSize;
  reverseVideo = (GBool)resources.reverseVideo;
  if (reverseVideo) {
    paperRGB[0] = paperRGB[1] = paperRGB[2] = 0;
    paperPixel = BlackPixel(display, screenNum);
  } else {
    paperRGB[0] = paperRGB[1] = paperRGB[2] = 0xff;
    paperPixel = WhitePixel(display, screenNum);
  }
  XtVaGetValues(appShell, XmNcolormap, &colormap, NULL);
  if (resources.paperColor) {
    if (XAllocNamedColor(display, colormap, resources.paperColor,
			 &xcol, &xcol2)) {
      paperRGB[0] = xcol.red >> 8;
      paperRGB[1] = xcol.green >> 8;
      paperRGB[2] = xcol.blue >> 8;
      paperPixel = xcol.pixel;
    } else {
      error(-1, "Couldn't allocate color '%s'", resources.paperColor);
    }
  }
  if (XAllocNamedColor(display, colormap, resources.matteColor,
		       &xcol, &xcol2)) {
    mattePixel = xcol.pixel;
  } else {
    mattePixel = paperPixel;
  }
  if (XAllocNamedColor(display, colormap, resources.fullScreenMatteColor,
		       &xcol, &xcol2)) {
    fullScreenMattePixel = xcol.pixel;
  } else {
    fullScreenMattePixel = paperPixel;
  }
  initialZoom = resources.initialZoom ? new GString(resources.initialZoom)
                                      : (GString *)NULL;
}

XPDFApp::~XPDFApp() {
  deleteGList(viewers, XPDFViewer);
  if (geometry) {
    delete geometry;
  }
  if (title) {
    delete title;
  }
  if (initialZoom) {
    delete initialZoom;
  }
}

XPDFViewer *XPDFApp::open(GString *fileName, int page,
			  GString *ownerPassword, GString *userPassword) {
  XPDFViewer *viewer;

  viewer = new XPDFViewer(this, fileName, page, NULL, fullScreen,
			  ownerPassword, userPassword);
  if (!viewer->isOk()) {
    delete viewer;
    return NULL;
  }
  if (remoteAtom != None) {
    remoteViewer = viewer;
    remoteWin = viewer->getWindow();
    XtAddEventHandler(remoteWin, PropertyChangeMask, False,
		      &remoteMsgCbk, this);
    XSetSelectionOwner(display, remoteAtom, XtWindow(remoteWin), CurrentTime);
  }
  viewers->append(viewer);
  return viewer;
}

XPDFViewer *XPDFApp::openAtDest(GString *fileName, GString *dest,
				GString *ownerPassword,
				GString *userPassword) {
  XPDFViewer *viewer;

  viewer = new XPDFViewer(this, fileName, 1, dest, fullScreen,
			  ownerPassword, userPassword);
  if (!viewer->isOk()) {
    delete viewer;
    return NULL;
  }
  if (remoteAtom != None) {
    remoteViewer = viewer;
    remoteWin = viewer->getWindow();
    XtAddEventHandler(remoteWin, PropertyChangeMask, False,
		      &remoteMsgCbk, this);
    XSetSelectionOwner(display, remoteAtom, XtWindow(remoteWin), CurrentTime);
  }
  viewers->append(viewer);
  return viewer;
}

XPDFViewer *XPDFApp::reopen(XPDFViewer *viewer, PDFDoc *doc, int page,
			    GBool fullScreenA) {
  int i;

  for (i = 0; i < viewers->getLength(); ++i) {
    if (((XPDFViewer *)viewers->get(i)) == viewer) {
      viewers->del(i);
      delete viewer;
    }
  }
  viewer = new XPDFViewer(this, doc, page, NULL, fullScreenA);
  if (!viewer->isOk()) {
    delete viewer;
    return NULL;
  }
  if (remoteAtom != None) {
    remoteViewer = viewer;
    remoteWin = viewer->getWindow();
    XtAddEventHandler(remoteWin, PropertyChangeMask, False,
		      &remoteMsgCbk, this);
    XSetSelectionOwner(display, remoteAtom, XtWindow(remoteWin), CurrentTime);
  }
  viewers->append(viewer);
  return viewer;
}

void XPDFApp::close(XPDFViewer *viewer, GBool closeLast) {
  int i;

  if (viewers->getLength() == 1) {
    if (viewer != (XPDFViewer *)viewers->get(0)) {
      return;
    }
    if (closeLast) {
      quit();
    } else {
      viewer->clear();
    }
  } else {
    for (i = 0; i < viewers->getLength(); ++i) {
      if (((XPDFViewer *)viewers->get(i)) == viewer) {
	viewers->del(i);
	if (remoteAtom != None && remoteViewer == viewer) {
	  remoteViewer = (XPDFViewer *)viewers->get(viewers->getLength() - 1);
	  remoteWin = remoteViewer->getWindow();
	  XSetSelectionOwner(display, remoteAtom, XtWindow(remoteWin),
			     CurrentTime);
	}
	delete viewer;
	return;
      }
    }
  }
}

void XPDFApp::quit() {
  if (remoteAtom != None) {
    XSetSelectionOwner(display, remoteAtom, None, CurrentTime);
  }
  while (viewers->getLength() > 0) {
    delete (XPDFViewer *)viewers->del(0);
  }
#if HAVE_XTAPPSETEXITFLAG
  XtAppSetExitFlag(appContext);
#else
  exit(0);
#endif
}

void XPDFApp::run() {
  XtAppMainLoop(appContext);
}

void XPDFApp::setRemoteName(char *remoteName) {
  remoteAtom = XInternAtom(display, remoteName, False);
  remoteXWin = XGetSelectionOwner(display, remoteAtom);
}

GBool XPDFApp::remoteServerRunning() {
  return remoteXWin != None;
}

void XPDFApp::remoteExec(char *cmd) {
  char cmd2[remoteCmdSize];
  int n;

  n = strlen(cmd);
  if (n > remoteCmdSize - 2) {
    n = remoteCmdSize - 2;
  }
  memcpy(cmd2, cmd, n);
  cmd2[n] = '\n';
  cmd2[n+1] = '\0';
  XChangeProperty(display, remoteXWin, remoteAtom, remoteAtom, 8,
		  PropModeReplace, (Guchar *)cmd2, n + 2);
  XFlush(display);
}

void XPDFApp::remoteOpen(GString *fileName, int page, GBool raise) {
  char cmd[remoteCmdSize];

  sprintf(cmd, "openFileAtPage(%.200s,%d)\n",
	  fileName->getCString(), page);
  if (raise) {
    strcat(cmd, "raise\n");
  }
  XChangeProperty(display, remoteXWin, remoteAtom, remoteAtom, 8,
		  PropModeReplace, (Guchar *)cmd, strlen(cmd) + 1);
  XFlush(display);
}

void XPDFApp::remoteOpenAtDest(GString *fileName, GString *dest, GBool raise) {
  char cmd[remoteCmdSize];

  sprintf(cmd, "openFileAtDest(%.200s,%.256s)\n",
	  fileName->getCString(), dest->getCString());
  if (raise) {
    strcat(cmd, "raise\n");
  }
  XChangeProperty(display, remoteXWin, remoteAtom, remoteAtom, 8,
		  PropModeReplace, (Guchar *)cmd, strlen(cmd) + 1);
  XFlush(display);
}

void XPDFApp::remoteReload(GBool raise) {
  char cmd[remoteCmdSize];

  strcpy(cmd, "reload\n");
  if (raise) {
    strcat(cmd, "raise\n");
  }
  XChangeProperty(display, remoteXWin, remoteAtom, remoteAtom, 8,
		  PropModeReplace, (Guchar *)cmd, strlen(cmd) + 1);
  XFlush(display);
}

void XPDFApp::remoteRaise() {
  XChangeProperty(display, remoteXWin, remoteAtom, remoteAtom, 8,
		  PropModeReplace, (Guchar *)"raise\n", 7);
  XFlush(display);
}

void XPDFApp::remoteQuit() {
  XChangeProperty(display, remoteXWin, remoteAtom, remoteAtom, 8,
		  PropModeReplace, (Guchar *)"quit\n", 6);
  XFlush(display);
}

void XPDFApp::remoteMsgCbk(Widget widget, XtPointer ptr,
			   XEvent *event, Boolean *cont) {
  XPDFApp *app = (XPDFApp *)ptr;
  char *cmd, *p0, *p1;
  Atom type;
  int format;
  Gulong size, remain;
  GString *cmdStr;

  if (event->xproperty.atom != app->remoteAtom) {
    *cont = True;
    return;
  }
  *cont = False;

  if (XGetWindowProperty(app->display, XtWindow(app->remoteWin),
			 app->remoteAtom, 0, remoteCmdSize/4,
			 True, app->remoteAtom,
			 &type, &format, &size, &remain,
			 (Guchar **)&cmd) != Success) {
    return;
  }
  if (!cmd) {
    return;
  }
  p0 = cmd;
  while (*p0 && (p1 = strchr(p0, '\n'))) {
    cmdStr = new GString(p0, p1 - p0);
    app->remoteViewer->execCmd(cmdStr, NULL);
    delete cmdStr;
    p0 = p1 + 1;
  }
  XFree((XPointer)cmd);
}
//========================================================================
//
// XPDFApp.h
//
// Copyright 2002-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef XPDFAPP_H
#define XPDFAPP_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#define Object XtObject
#include <Xm/XmAll.h>
#undef Object
#include "gtypes.h"
#include "SplashTypes.h"

class GString;
class GList;
class PDFDoc;
class XPDFViewer;

//------------------------------------------------------------------------

#define xpdfAppName "Xpdf"

//------------------------------------------------------------------------
// XPDFApp
//------------------------------------------------------------------------

class XPDFApp {
public:

  XPDFApp(int *argc, char *argv[]);
  ~XPDFApp();

  XPDFViewer *open(GString *fileName, int page = 1,
		   GString *ownerPassword = NULL,
		   GString *userPassword = NULL);
  XPDFViewer *openAtDest(GString *fileName, GString *dest,
			 GString *ownerPassword = NULL,
			 GString *userPassword = NULL);
  XPDFViewer *reopen(XPDFViewer *viewer, PDFDoc *doc, int page,
		     GBool fullScreenA);
  void close(XPDFViewer *viewer, GBool closeLast);
  void quit();

  void run();

  //----- remote server
  void setRemoteName(char *remoteName);
  GBool remoteServerRunning();
  void remoteExec(char *cmd);
  void remoteOpen(GString *fileName, int page, GBool raise);
  void remoteOpenAtDest(GString *fileName, GString *dest, GBool raise);
  void remoteReload(GBool raise);
  void remoteRaise();
  void remoteQuit();

  //----- resource/option values
  GString *getGeometry() { return geometry; }
  GString *getTitle() { return title; }
  GBool getInstallCmap() { return installCmap; }
  int getRGBCubeSize() { return rgbCubeSize; }
  GBool getReverseVideo() { return reverseVideo; }
  SplashColorPtr getPaperRGB() { return paperRGB; }
  Gulong getPaperPixel() { return paperPixel; }
  Gulong getMattePixel(GBool fullScreenA)
    { return fullScreenA ? fullScreenMattePixel : mattePixel; }
  GString *getInitialZoom() { return initialZoom; }
  void setFullScreen(GBool fullScreenA) { fullScreen = fullScreenA; }
  GBool getFullScreen() { return fullScreen; }

  XtAppContext getAppContext() { return appContext; }
  Widget getAppShell() { return appShell; }

private:

  void getResources();
  static void remoteMsgCbk(Widget widget, XtPointer ptr,
			   XEvent *event, Boolean *cont);

  Display *display;
  int screenNum;
  XtAppContext appContext;
  Widget appShell;
  GList *viewers;		// [XPDFViewer]

  Atom remoteAtom;
  Window remoteXWin;
  XPDFViewer *remoteViewer;
  Widget remoteWin;

  //----- resource/option values
  GString *geometry;
  GString *title;
  GBool installCmap;
  int rgbCubeSize;
  GBool reverseVideo;
  SplashColor paperRGB;
  Gulong paperPixel;
  Gulong mattePixel;
  Gulong fullScreenMattePixel;
  GString *initialZoom;
  GBool fullScreen;
};

#endif
//========================================================================
//
// XPDFCore.cc
//
// Copyright 2002-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <X11/keysym.h>
#include <X11/cursorfont.h>
#include <string.h>
#include "gmem.h"
#include "GString.h"
#include "GList.h"
#include "Error.h"
#include "GlobalParams.h"
#include "PDFDoc.h"
#include "Link.h"
#include "ErrorCodes.h"
#include "GfxState.h"
#include "CoreOutputDev.h"
#include "PSOutputDev.h"
#include "TextOutputDev.h"
#include "SplashBitmap.h"
#include "SplashPattern.h"
#include "XPDFApp.h"
#include "XPDFCore.h"

// these macro defns conflict with xpdf's Object class
#ifdef LESSTIF_VERSION
#undef XtDisplay
#undef XtScreen
#undef XtWindow
#undef XtParent
#undef XtIsRealized
#endif

//------------------------------------------------------------------------

// Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result.
static inline Guchar div255(int x) {
  return (Guchar)((x + (x >> 8) + 0x80) >> 8);
}

//------------------------------------------------------------------------

GString *XPDFCore::currentSelection = NULL;
XPDFCore *XPDFCore::currentSelectionOwner = NULL;
Atom XPDFCore::targetsAtom;

//------------------------------------------------------------------------
// XPDFCoreTile
//------------------------------------------------------------------------

class XPDFCoreTile: public PDFCoreTile {
public:
  XPDFCoreTile(int xDestA, int yDestA);
  virtual ~XPDFCoreTile();
  XImage *image;
};

XPDFCoreTile::XPDFCoreTile(int xDestA, int yDestA):
  PDFCoreTile(xDestA, yDestA)
{
  image = NULL;
}

XPDFCoreTile::~XPDFCoreTile() {
  if (image) {
    gfree(image->data);
    image->data = NULL;
    XDestroyImage(image);
  }
}

//------------------------------------------------------------------------
// XPDFCore
//------------------------------------------------------------------------

XPDFCore::XPDFCore(Widget shellA, Widget parentWidgetA,
		   SplashColorPtr paperColorA, Gulong paperPixelA,
		   Gulong mattePixelA, GBool fullScreenA, GBool reverseVideoA,
		   GBool installCmap, int rgbCubeSizeA):
  PDFCore(splashModeRGB8, 4, reverseVideoA, paperColorA, !fullScreenA)
{
  GString *initialZoom;

  shell = shellA;
  parentWidget = parentWidgetA;
  display = XtDisplay(parentWidget);
  screenNum = XScreenNumberOfScreen(XtScreen(parentWidget));
  targetsAtom = XInternAtom(display, "TARGETS", False);

  paperPixel = paperPixelA;
  mattePixel = mattePixelA;
  fullScreen = fullScreenA;

  setupX(installCmap, rgbCubeSizeA);

  scrolledWin = NULL;
  hScrollBar = NULL;
  vScrollBar = NULL;
  drawAreaFrame = NULL;
  drawArea = NULL;

  // get the initial zoom value
  if (fullScreen) {
    zoom = zoomPage;
  } else {
    initialZoom = globalParams->getInitialZoom();
    if (!initialZoom->cmp("page")) {
      zoom = zoomPage;
    } else if (!initialZoom->cmp("width")) {
      zoom = zoomWidth;
    } else {
      zoom = atoi(initialZoom->getCString());
      if (zoom <= 0) {
	zoom = defZoom;
      }
    }
    delete initialZoom;
  }

  linkAction = NULL;

  panning = gFalse;

  updateCbk = NULL;
  actionCbk = NULL;
  keyPressCbk = NULL;
  mouseCbk = NULL;

  // optional features default to on
  hyperlinksEnabled = gTrue;
  selectEnabled = gTrue;

  // do X-specific initialization and create the widgets
  initWindow();
  initPasswordDialog();
}

XPDFCore::~XPDFCore() {
  if (currentSelectionOwner == this && currentSelection) {
    delete currentSelection;
    currentSelection = NULL;
    currentSelectionOwner = NULL;
  }
  if (drawAreaGC) {
    XFreeGC(display, drawAreaGC);
  }
  if (scrolledWin) {
    XtDestroyWidget(scrolledWin);
  }
  if (busyCursor) {
    XFreeCursor(display, busyCursor);
  }
  if (linkCursor) {
    XFreeCursor(display, linkCursor);
  }
  if (selectCursor) {
    XFreeCursor(display, selectCursor);
  }
}

//------------------------------------------------------------------------
// loadFile / displayPage / displayDest
//------------------------------------------------------------------------

int XPDFCore::loadFile(GString *fileName, GString *ownerPassword,
		       GString *userPassword) {
  int err;

  err = PDFCore::loadFile(fileName, ownerPassword, userPassword);
  if (err == errNone) {
    // save the modification time
    modTime = getModTime(doc->getFileName()->getCString());

    // update the parent window
    if (updateCbk) {
      (*updateCbk)(updateCbkData, doc->getFileName(), -1,
		   doc->getNumPages(), NULL);
    }
  }
  return err;
}

int XPDFCore::loadFile(BaseStream *stream, GString *ownerPassword,
		       GString *userPassword) {
  int err;

  err = PDFCore::loadFile(stream, ownerPassword, userPassword);
  if (err == errNone) {
    // no file
    modTime = 0;

    // update the parent window
    if (updateCbk) {
      (*updateCbk)(updateCbkData, doc->getFileName(), -1,
		   doc->getNumPages(), NULL);
    }
  }
  return err;
}

void XPDFCore::loadDoc(PDFDoc *docA) {
  PDFCore::loadDoc(docA);

  // save the modification time
  if (doc->getFileName()) {
    modTime = getModTime(doc->getFileName()->getCString());
  }

  // update the parent window
  if (updateCbk) {
    (*updateCbk)(updateCbkData, doc->getFileName(), -1,
		 doc->getNumPages(), NULL);
  }
}

void XPDFCore::resizeToPage(int pg) {
  Dimension width, height;
  double width1, height1;
  Dimension topW, topH, topBorder, daW, daH;
  Dimension displayW, displayH;

  displayW = DisplayWidth(display, screenNum);
  displayH = DisplayHeight(display, screenNum);
  if (fullScreen) {
    width = displayW;
    height = displayH;
  } else {
    if (!doc || pg <= 0 || pg > doc->getNumPages()) {
      width1 = 612;
      height1 = 792;
    } else if (doc->getPageRotate(pg) == 90 ||
	       doc->getPageRotate(pg) == 270) {
      width1 = doc->getPageCropHeight(pg);
      height1 = doc->getPageCropWidth(pg);
    } else {
      width1 = doc->getPageCropWidth(pg);
      height1 = doc->getPageCropHeight(pg);
    }
    if (zoom == zoomPage || zoom == zoomWidth) {
      width = (Dimension)(width1 * 0.01 * defZoom + 0.5);
      height = (Dimension)(height1 * 0.01 * defZoom + 0.5);
    } else {
      width = (Dimension)(width1 * 0.01 * zoom + 0.5);
      height = (Dimension)(height1 * 0.01 * zoom + 0.5);
    }
    if (continuousMode) {
      height += continuousModePageSpacing;
    }
    if (width > displayW - 100) {
      width = displayW - 100;
    }
    if (height > displayH - 100) {
      height = displayH - 100;
    }
  }

  if (XtIsRealized(shell)) {
    XtVaGetValues(shell, XmNwidth, &topW, XmNheight, &topH,
		  XmNborderWidth, &topBorder, NULL);
    XtVaGetValues(drawArea, XmNwidth, &daW, XmNheight, &daH, NULL);
    XtVaSetValues(shell, XmNwidth, width + (topW - daW),
		  XmNheight, height + (topH - daH), NULL);
  } else {
    XtVaSetValues(drawArea, XmNwidth, width, XmNheight, height, NULL);
  }
}

void XPDFCore::update(int topPageA, int scrollXA, int scrollYA,
		      double zoomA, int rotateA,
		      GBool force, GBool addToHist) {
  int oldPage;

  oldPage = topPage;
  PDFCore::update(topPageA, scrollXA, scrollYA, zoomA, rotateA,
		  force, addToHist);
  linkAction = NULL;
  if (doc && topPage != oldPage) {
    if (updateCbk) {
      (*updateCbk)(updateCbkData, NULL, topPage, -1, "");
    }
  }
}

GBool XPDFCore::checkForNewFile() {
  time_t newModTime;

  if (doc->getFileName()) {
    newModTime = getModTime(doc->getFileName()->getCString());
    if (newModTime != modTime) {
      modTime = newModTime;
      return gTrue;
    }
  }
  return gFalse;
}

//------------------------------------------------------------------------
// page/position changes
//------------------------------------------------------------------------

GBool XPDFCore::gotoNextPage(int inc, GBool top) {
  if (!PDFCore::gotoNextPage(inc, top)) {
    XBell(display, 0);
    return gFalse;
  }
  return gTrue;
}

GBool XPDFCore::gotoPrevPage(int dec, GBool top, GBool bottom) {
  if (!PDFCore::gotoPrevPage(dec, top, bottom)) {
    XBell(display, 0);
    return gFalse;
  }
  return gTrue;
}

GBool XPDFCore::goForward() {
  if (!PDFCore::goForward()) {
    XBell(display, 0);
    return gFalse;
  }
  return gTrue;
}

GBool XPDFCore::goBackward() {
  if (!PDFCore::goBackward()) {
    XBell(display, 0);
    return gFalse;
  }
  return gTrue;
}

void XPDFCore::startPan(int wx, int wy) {
  panning = gTrue;
  panMX = wx;
  panMY = wy;
}

void XPDFCore::endPan(int wx, int wy) {
  panning = gFalse;
}

//------------------------------------------------------------------------
// selection
//------------------------------------------------------------------------

void XPDFCore::startSelection(int wx, int wy) {
  int pg, x, y;

  takeFocus();
  if (doc && doc->getNumPages() > 0) {
    if (selectEnabled) {
      if (cvtWindowToDev(wx, wy, &pg, &x, &y)) {
	setSelection(pg, x, y, x, y);
	setCursor(selectCursor);
	dragging = gTrue;
      }
    }
  }
}

void XPDFCore::endSelection(int wx, int wy) {
  int pg, x, y;
  GBool ok;

  if (doc && doc->getNumPages() > 0) {
    ok = cvtWindowToDev(wx, wy, &pg, &x, &y);
    if (dragging) {
      dragging = gFalse;
      setCursor(None);
      if (ok) {
	moveSelection(pg, x, y);
      }
#ifndef NO_TEXT_SELECT
      if (selectULX != selectLRX &&
	  selectULY != selectLRY) {
	if (doc->okToCopy()) {
	  copySelection();
	} else {
	  error(-1, "Copying of text from this document is not allowed.");
	}
      }
#endif
    }
  }
}

// X's copy-and-paste mechanism is brain damaged.  Xt doesn't help
// any, but doesn't make it too much worse, either.  Motif, on the
// other hand, adds significant complexity to the mess.  So here we
// blow off the Motif junk and stick to plain old Xt.  The next two
// functions (copySelection and convertSelectionCbk) implement the
// magic needed to deal with Xt's mechanism.  Note that this requires
// global variables (currentSelection and currentSelectionOwner).

void XPDFCore::copySelection() {
  int pg;
  double ulx, uly, lrx, lry;

  if (!doc->okToCopy()) {
    return;
  }
  if (getSelection(&pg, &ulx, &uly, &lrx, &lry)) {
    //~ for multithreading: need a mutex here
    if (currentSelection) {
      delete currentSelection;
    }
    currentSelection = extractText(pg, ulx, uly, lrx, lry);
    currentSelectionOwner = this;
    XtOwnSelection(drawArea, XA_PRIMARY, XtLastTimestampProcessed(display),
		   &convertSelectionCbk, NULL, NULL);
  }
}

Boolean XPDFCore::convertSelectionCbk(Widget widget, Atom *selection,
				      Atom *target, Atom *type,
				      XtPointer *value, unsigned long *length,
				      int *format) {
  Atom *array;

  // send back a list of supported conversion targets
  if (*target == targetsAtom) {
    if (!(array = (Atom *)XtMalloc(sizeof(Atom)))) {
      return False;
    }
    array[0] = XA_STRING;
    *value = (XtPointer)array;
    *type = XA_ATOM;
    *format = 32;
    *length = 1;
    return True;

  // send the selected text
  } else if (*target == XA_STRING) {
    //~ for multithreading: need a mutex here
    *value = XtNewString(currentSelection->getCString());
    *length = currentSelection->getLength();
    *type = XA_STRING;
    *format = 8; // 8-bit elements
    return True;
  }

  return False;
}

//------------------------------------------------------------------------
// hyperlinks
//------------------------------------------------------------------------

void XPDFCore::doAction(LinkAction *action) {
  LinkActionKind kind;
  LinkDest *dest;
  GString *namedDest;
  char *s;
  GString *fileName, *fileName2;
  GString *cmd;
  GString *actionName;
  Object movieAnnot, obj1, obj2;
  GString *msg;
  int i;

  switch (kind = action->getKind()) {

  // GoTo / GoToR action
  case actionGoTo:
  case actionGoToR:
    if (kind == actionGoTo) {
      dest = NULL;
      namedDest = NULL;
      if ((dest = ((LinkGoTo *)action)->getDest())) {
	dest = dest->copy();
      } else if ((namedDest = ((LinkGoTo *)action)->getNamedDest())) {
	namedDest = namedDest->copy();
      }
    } else {
      dest = NULL;
      namedDest = NULL;
      if ((dest = ((LinkGoToR *)action)->getDest())) {
	dest = dest->copy();
      } else if ((namedDest = ((LinkGoToR *)action)->getNamedDest())) {
	namedDest = namedDest->copy();
      }
      s = ((LinkGoToR *)action)->getFileName()->getCString();
      //~ translate path name for VMS (deal with '/')
      if (isAbsolutePath(s)) {
	fileName = new GString(s);
      } else {
	fileName = appendToPath(grabPath(doc->getFileName()->getCString()), s);
      }
      if (loadFile(fileName) != errNone) {
	if (dest) {
	  delete dest;
	}
	if (namedDest) {
	  delete namedDest;
	}
	delete fileName;
	return;
      }
      delete fileName;
    }
    if (namedDest) {
      dest = doc->findDest(namedDest);
      delete namedDest;
    }
    if (dest) {
      displayDest(dest, zoom, rotate, gTrue);
      delete dest;
    } else {
      if (kind == actionGoToR) {
	displayPage(1, zoom, 0, gFalse, gTrue);
      }
    }
    break;

  // Launch action
  case actionLaunch:
    fileName = ((LinkLaunch *)action)->getFileName();
    s = fileName->getCString();
    if (!strcmp(s + fileName->getLength() - 4, ".pdf") ||
	!strcmp(s + fileName->getLength() - 4, ".PDF")) {
      //~ translate path name for VMS (deal with '/')
      if (isAbsolutePath(s)) {
	fileName = fileName->copy();
      } else {
	fileName = appendToPath(grabPath(doc->getFileName()->getCString()), s);
      }
      if (loadFile(fileName) != errNone) {
	delete fileName;
	return;
      }
      delete fileName;
      displayPage(1, zoom, rotate, gFalse, gTrue);
    } else {
      fileName = fileName->copy();
      if (((LinkLaunch *)action)->getParams()) {
	fileName->append(' ');
	fileName->append(((LinkLaunch *)action)->getParams());
      }
#ifdef VMS
      fileName->insert(0, "spawn/nowait ");
#elif defined(__EMX__)
      fileName->insert(0, "start /min /n ");
#else
      fileName->append(" &");
#endif
      msg = new GString("About to execute the command:\n");
      msg->append(fileName);
      if (doQuestionDialog("Launching external application", msg)) {
	system(fileName->getCString());
      }
      delete fileName;
      delete msg;
    }
    break;

  // URI action
  case actionURI:
    if (!(cmd = globalParams->getURLCommand())) {
      error(-1, "No urlCommand defined in config file");
      break;
    }
    runCommand(cmd, ((LinkURI *)action)->getURI());
    break;

  // Named action
  case actionNamed:
    actionName = ((LinkNamed *)action)->getName();
    if (!actionName->cmp("NextPage")) {
      gotoNextPage(1, gTrue);
    } else if (!actionName->cmp("PrevPage")) {
      gotoPrevPage(1, gTrue, gFalse);
    } else if (!actionName->cmp("FirstPage")) {
      if (topPage != 1) {
	displayPage(1, zoom, rotate, gTrue, gTrue);
      }
    } else if (!actionName->cmp("LastPage")) {
      if (topPage != doc->getNumPages()) {
	displayPage(doc->getNumPages(), zoom, rotate, gTrue, gTrue);
      }
    } else if (!actionName->cmp("GoBack")) {
      goBackward();
    } else if (!actionName->cmp("GoForward")) {
      goForward();
    } else if (!actionName->cmp("Quit")) {
      if (actionCbk) {
	(*actionCbk)(actionCbkData, actionName->getCString());
      }
    } else {
      error(-1, "Unknown named action: '%s'", actionName->getCString());
    }
    break;

  // Movie action
  case actionMovie:
    if (!(cmd = globalParams->getMovieCommand())) {
      error(-1, "No movieCommand defined in config file");
      break;
    }
    if (((LinkMovie *)action)->hasAnnotRef()) {
      doc->getXRef()->fetch(((LinkMovie *)action)->getAnnotRef()->num,
			    ((LinkMovie *)action)->getAnnotRef()->gen,
			    &movieAnnot);
    } else {
      //~ need to use the correct page num here
      doc->getCatalog()->getPage(topPage)->getAnnots(&obj1);
      if (obj1.isArray()) {
	for (i = 0; i < obj1.arrayGetLength(); ++i) {
	  if (obj1.arrayGet(i, &movieAnnot)->isDict()) {
	    if (movieAnnot.dictLookup("Subtype", &obj2)->isName("Movie")) {
	      obj2.free();
	      break;
	    }
	    obj2.free();
	  }
	  movieAnnot.free();
	}
	obj1.free();
      }
    }
    if (movieAnnot.isDict()) {
      if (movieAnnot.dictLookup("Movie", &obj1)->isDict()) {
	if (obj1.dictLookup("F", &obj2)) {
	  if ((fileName = LinkAction::getFileSpecName(&obj2))) {
	    if (!isAbsolutePath(fileName->getCString())) {
	      fileName2 = appendToPath(
			      grabPath(doc->getFileName()->getCString()),
			      fileName->getCString());
	      delete fileName;
	      fileName = fileName2;
	    }
	    runCommand(cmd, fileName);
	    delete fileName;
	  }
	  obj2.free();
	}
	obj1.free();
      }
    }
    movieAnnot.free();
    break;

  // unknown action type
  case actionUnknown:
    error(-1, "Unknown link action type: '%s'",
	  ((LinkUnknown *)action)->getAction()->getCString());
    break;
  }
}

// Run a command, given a <cmdFmt> string with one '%s' in it, and an
// <arg> string to insert in place of the '%s'.
void XPDFCore::runCommand(GString *cmdFmt, GString *arg) {
  GString *cmd;
  char *s;

  if ((s = strstr(cmdFmt->getCString(), "%s"))) {
    cmd = mungeURL(arg);
    cmd->insert(0, cmdFmt->getCString(),
		s - cmdFmt->getCString());
    cmd->append(s + 2);
  } else {
    cmd = cmdFmt->copy();
  }
#ifdef VMS
  cmd->insert(0, "spawn/nowait ");
#elif defined(__EMX__)
  cmd->insert(0, "start /min /n ");
#else
  cmd->append(" &");
#endif
  system(cmd->getCString());
  delete cmd;
}

// Escape any characters in a URL which might cause problems when
// calling system().
GString *XPDFCore::mungeURL(GString *url) {
  static char *allowed = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                         "abcdefghijklmnopqrstuvwxyz"
                         "0123456789"
                         "-_.~/?:@&=+,#%";
  GString *newURL;
  char c;
  char buf[4];
  int i;

  newURL = new GString();
  for (i = 0; i < url->getLength(); ++i) {
    c = url->getChar(i);
    if (strchr(allowed, c)) {
      newURL->append(c);
    } else {
      sprintf(buf, "%%%02x", c & 0xff);
      newURL->append(buf);
    }
  }
  return newURL;
}

//------------------------------------------------------------------------
// find
//------------------------------------------------------------------------

GBool XPDFCore::find(char *s, GBool caseSensitive,
		     GBool next, GBool backward, GBool onePageOnly) {
  if (!PDFCore::find(s, caseSensitive, next, backward, onePageOnly)) {
    XBell(display, 0);
    return gFalse;
  }
#ifndef NO_TEXT_SELECT
  copySelection();
#endif
  return gTrue;
}

GBool XPDFCore::findU(Unicode *u, int len, GBool caseSensitive,
		      GBool next, GBool backward, GBool onePageOnly) {
  if (!PDFCore::findU(u, len, caseSensitive, next, backward, onePageOnly)) {
    XBell(display, 0);
    return gFalse;
  }
#ifndef NO_TEXT_SELECT
  copySelection();
#endif
  return gTrue;
}

//------------------------------------------------------------------------
// misc access
//------------------------------------------------------------------------

void XPDFCore::setBusyCursor(GBool busy) {
  setCursor(busy ? busyCursor : None);
}

void XPDFCore::takeFocus() {
  XmProcessTraversal(drawArea, XmTRAVERSE_CURRENT);
}

//------------------------------------------------------------------------
// GUI code
//------------------------------------------------------------------------

void XPDFCore::setupX(GBool installCmap, int rgbCubeSizeA) {
  XVisualInfo visualTempl;
  XVisualInfo *visualList;
  Gulong mask;
  int nVisuals;
  XColor xcolor;
  XColor *xcolors;
  int r, g, b, n, m;
  GBool ok;

  // for some reason, querying XmNvisual doesn't work (even if done
  // after the window is mapped)
  visual = DefaultVisual(display, screenNum);
  XtVaGetValues(shell, XmNcolormap, &colormap, NULL);

  // check for TrueColor visual
  //~ this should scan the list, not just look at the first one
  visualTempl.visualid = XVisualIDFromVisual(visual);
  visualList = XGetVisualInfo(display, VisualIDMask,
                              &visualTempl, &nVisuals);
  if (nVisuals < 1) {
    // this shouldn't happen
    XFree((XPointer)visualList);
    visualList = XGetVisualInfo(display, VisualNoMask, &visualTempl,
                                &nVisuals);
  }
  depth = visualList->depth;
  if (visualList->c_class == TrueColor) {
    trueColor = gTrue;
    for (mask = visualList->red_mask, rShift = 0;
         mask && !(mask & 1);
         mask >>= 1, ++rShift) ;
    for (rDiv = 8; mask; mask >>= 1, --rDiv) ;
    for (mask = visualList->green_mask, gShift = 0;
         mask && !(mask & 1);
         mask >>= 1, ++gShift) ;
    for (gDiv = 8; mask; mask >>= 1, --gDiv) ;
    for (mask = visualList->blue_mask, bShift = 0;
         mask && !(mask & 1);
         mask >>= 1, ++bShift) ;
    for (bDiv = 8; mask; mask >>= 1, --bDiv) ;
  } else {
    trueColor = gFalse;
  }
  XFree((XPointer)visualList);

  // allocate a color cube
  if (!trueColor) {

    // set colors in private colormap
    if (installCmap) {
      for (rgbCubeSize = xMaxRGBCube; rgbCubeSize >= 2; --rgbCubeSize) {
        m = rgbCubeSize * rgbCubeSize * rgbCubeSize;
        if (XAllocColorCells(display, colormap, False, NULL, 0, colors, m)) {
          break;
        }
      }
      if (rgbCubeSize >= 2) {
        m = rgbCubeSize * rgbCubeSize * rgbCubeSize;
        xcolors = (XColor *)gmallocn(m, sizeof(XColor));
        n = 0;
        for (r = 0; r < rgbCubeSize; ++r) {
          for (g = 0; g < rgbCubeSize; ++g) {
            for (b = 0; b < rgbCubeSize; ++b) {
              xcolors[n].pixel = colors[n];
              xcolors[n].red = (r * 65535) / (rgbCubeSize - 1);
              xcolors[n].green = (g * 65535) / (rgbCubeSize - 1);
              xcolors[n].blue = (b * 65535) / (rgbCubeSize - 1);
              xcolors[n].flags = DoRed | DoGreen | DoBlue;
              ++n;
            }
          }
        }
        XStoreColors(display, colormap, xcolors, m);
        gfree(xcolors);
      } else {
        rgbCubeSize = 1;
        colors[0] = BlackPixel(display, screenNum);
        colors[1] = WhitePixel(display, screenNum);
      }

    // allocate colors in shared colormap
    } else {
      if (rgbCubeSize > xMaxRGBCube) {
        rgbCubeSize = xMaxRGBCube;
      }
      ok = gFalse;
      for (rgbCubeSize = rgbCubeSizeA; rgbCubeSize >= 2; --rgbCubeSize) {
        ok = gTrue;
        n = 0;
        for (r = 0; r < rgbCubeSize && ok; ++r) {
          for (g = 0; g < rgbCubeSize && ok; ++g) {
            for (b = 0; b < rgbCubeSize && ok; ++b) {
              if (n == 0) {
                colors[n] = BlackPixel(display, screenNum);
                ++n;
              } else {
                xcolor.red = (r * 65535) / (rgbCubeSize - 1);
                xcolor.green = (g * 65535) / (rgbCubeSize - 1);
                xcolor.blue = (b * 65535) / (rgbCubeSize - 1);
                if (XAllocColor(display, colormap, &xcolor)) {
                  colors[n++] = xcolor.pixel;
                } else {
                  ok = gFalse;
                }
              }
            }
          }
        }
        if (ok) {
          break;
        }
        XFreeColors(display, colormap, &colors[1], n-1, 0);
      }
      if (!ok) {
        rgbCubeSize = 1;
        colors[0] = BlackPixel(display, screenNum);
        colors[1] = WhitePixel(display, screenNum);
      }
    }
  }
}

void XPDFCore::initWindow() {
  Arg args[20];
  int n;

  // create the cursors
  busyCursor = XCreateFontCursor(display, XC_watch);
  linkCursor = XCreateFontCursor(display, XC_hand2);
  selectCursor = XCreateFontCursor(display, XC_cross);
  currentCursor = 0;

  // create the scrolled window and scrollbars
  n = 0;
  XtSetArg(args[n], XmNscrollingPolicy, XmAPPLICATION_DEFINED); ++n;
  XtSetArg(args[n], XmNvisualPolicy, XmVARIABLE); ++n;
  scrolledWin = XmCreateScrolledWindow(parentWidget, "scroll", args, n);
  XtManageChild(scrolledWin);
  n = 0;
  XtSetArg(args[n], XmNorientation, XmHORIZONTAL); ++n;
  XtSetArg(args[n], XmNminimum, 0); ++n;
  XtSetArg(args[n], XmNmaximum, 1); ++n;
  XtSetArg(args[n], XmNsliderSize, 1); ++n;
  XtSetArg(args[n], XmNvalue, 0); ++n;
  XtSetArg(args[n], XmNincrement, 1); ++n;
  XtSetArg(args[n], XmNpageIncrement, 1); ++n;
  hScrollBar = XmCreateScrollBar(scrolledWin, "hScrollBar", args, n);
  if (!fullScreen) {
    XtManageChild(hScrollBar);
  }
  XtAddCallback(hScrollBar, XmNvalueChangedCallback,
		&hScrollChangeCbk, (XtPointer)this);
#ifndef DISABLE_SMOOTH_SCROLL
  XtAddCallback(hScrollBar, XmNdragCallback,
		&hScrollDragCbk, (XtPointer)this);
#endif
  n = 0;
  XtSetArg(args[n], XmNorientation, XmVERTICAL); ++n;
  XtSetArg(args[n], XmNminimum, 0); ++n;
  XtSetArg(args[n], XmNmaximum, 1); ++n;
  XtSetArg(args[n], XmNsliderSize, 1); ++n;
  XtSetArg(args[n], XmNvalue, 0); ++n;
  XtSetArg(args[n], XmNincrement, 1); ++n;
  XtSetArg(args[n], XmNpageIncrement, 1); ++n;
  vScrollBar = XmCreateScrollBar(scrolledWin, "vScrollBar", args, n);
  if (!fullScreen) {
    XtManageChild(vScrollBar);
  }
  XtAddCallback(vScrollBar, XmNvalueChangedCallback,
		&vScrollChangeCbk, (XtPointer)this);
#ifndef DISABLE_SMOOTH_SCROLL
  XtAddCallback(vScrollBar, XmNdragCallback,
		&vScrollDragCbk, (XtPointer)this);
#endif

  // create the drawing area
  n = 0;
  XtSetArg(args[n], XmNshadowType, XmSHADOW_IN); ++n;
  XtSetArg(args[n], XmNmarginWidth, 0); ++n;
  XtSetArg(args[n], XmNmarginHeight, 0); ++n;
  if (fullScreen) {
    XtSetArg(args[n], XmNshadowThickness, 0); ++n;
  }
  drawAreaFrame = XmCreateFrame(scrolledWin, "drawAreaFrame", args, n);
  XtManageChild(drawAreaFrame);
  n = 0;
  XtSetArg(args[n], XmNresizePolicy, XmRESIZE_ANY); ++n;
  XtSetArg(args[n], XmNwidth, 700); ++n;
  XtSetArg(args[n], XmNheight, 500); ++n;
  drawArea = XmCreateDrawingArea(drawAreaFrame, "drawArea", args, n);
  XtManageChild(drawArea);
  XtAddCallback(drawArea, XmNresizeCallback, &resizeCbk, (XtPointer)this);
  XtAddCallback(drawArea, XmNexposeCallback, &redrawCbk, (XtPointer)this);
  XtAddCallback(drawArea, XmNinputCallback, &inputCbk, (XtPointer)this);
  resizeCbk(drawArea, this, NULL);

  // set up mouse motion translations
  XtOverrideTranslations(drawArea, XtParseTranslationTable(
      "<Btn1Down>:DrawingAreaInput()\n"
      "<Btn1Up>:DrawingAreaInput()\n"
      "<Btn1Motion>:DrawingAreaInput()\n"
      "<Motion>:DrawingAreaInput()"));

  // can't create a GC until the window gets mapped
  drawAreaGC = NULL;
}

void XPDFCore::hScrollChangeCbk(Widget widget, XtPointer ptr,
			     XtPointer callData) {
  XPDFCore *core = (XPDFCore *)ptr;
  XmScrollBarCallbackStruct *data = (XmScrollBarCallbackStruct *)callData;

  core->scrollTo(data->value, core->scrollY);
}

void XPDFCore::hScrollDragCbk(Widget widget, XtPointer ptr,
			      XtPointer callData) {
  XPDFCore *core = (XPDFCore *)ptr;
  XmScrollBarCallbackStruct *data = (XmScrollBarCallbackStruct *)callData;

  core->scrollTo(data->value, core->scrollY);
}

void XPDFCore::vScrollChangeCbk(Widget widget, XtPointer ptr,
			     XtPointer callData) {
  XPDFCore *core = (XPDFCore *)ptr;
  XmScrollBarCallbackStruct *data = (XmScrollBarCallbackStruct *)callData;

  core->scrollTo(core->scrollX, data->value);
}

void XPDFCore::vScrollDragCbk(Widget widget, XtPointer ptr,
			      XtPointer callData) {
  XPDFCore *core = (XPDFCore *)ptr;
  XmScrollBarCallbackStruct *data = (XmScrollBarCallbackStruct *)callData;

  core->scrollTo(core->scrollX, data->value);
}

void XPDFCore::resizeCbk(Widget widget, XtPointer ptr, XtPointer callData) {
  XPDFCore *core = (XPDFCore *)ptr;
  XEvent event;
  Widget top;
  Window rootWin;
  int x1, y1;
  Guint w1, h1, bw1, depth1;
  Arg args[2];
  int n;
  Dimension w, h;
  int sx, sy;

  // find the top-most widget which has an associated window, and look
  // for a pending ConfigureNotify in the event queue -- if there is
  // one, and it specifies a different width or height, that means
  // we're still resizing, and we want to skip the current event
  for (top = core->parentWidget;
       XtParent(top) && XtWindow(XtParent(top));
       top = XtParent(top)) ;
  if (XCheckTypedWindowEvent(core->display, XtWindow(top),
			     ConfigureNotify, &event)) {
    XPutBackEvent(core->display, &event);
    XGetGeometry(core->display, event.xconfigure.window,
		 &rootWin, &x1, &y1, &w1, &h1, &bw1, &depth1);
    if ((Guint)event.xconfigure.width != w1 ||
	(Guint)event.xconfigure.height != h1) {
      return;
    }
  }

  n = 0;
  XtSetArg(args[n], XmNwidth, &w); ++n;
  XtSetArg(args[n], XmNheight, &h); ++n;
  XtGetValues(core->drawArea, args, n);
  core->drawAreaWidth = (int)w;
  core->drawAreaHeight = (int)h;
  if (core->zoom == zoomPage || core->zoom == zoomWidth) {
    sx = sy = -1;
  } else {
    sx = core->scrollX;
    sy = core->scrollY;
  }
  core->update(core->topPage, sx, sy, core->zoom, core->rotate, gTrue, gFalse);
}

void XPDFCore::redrawCbk(Widget widget, XtPointer ptr, XtPointer callData) {
  XPDFCore *core = (XPDFCore *)ptr;
  XmDrawingAreaCallbackStruct *data = (XmDrawingAreaCallbackStruct *)callData;
  int x, y, w, h;

  if (data->reason == XmCR_EXPOSE) {
    x = data->event->xexpose.x;
    y = data->event->xexpose.y;
    w = data->event->xexpose.width;
    h = data->event->xexpose.height;
  } else {
    x = 0;
    y = 0;
    w = core->drawAreaWidth;
    h = core->drawAreaHeight;
  }
  core->redrawWindow(x, y, w, h, gFalse);
}

void XPDFCore::inputCbk(Widget widget, XtPointer ptr, XtPointer callData) {
  XPDFCore *core = (XPDFCore *)ptr;
  XmDrawingAreaCallbackStruct *data = (XmDrawingAreaCallbackStruct *)callData;
  LinkAction *action;
  int pg, x, y;
  double xu, yu;
  char *s;
  KeySym key;
  GBool ok;

  switch (data->event->type) {
  case ButtonPress:
    if (*core->mouseCbk) {
      (*core->mouseCbk)(core->mouseCbkData, data->event);
    }
    break;
  case ButtonRelease:
    if (*core->mouseCbk) {
      (*core->mouseCbk)(core->mouseCbkData, data->event);
    }
    break;
  case MotionNotify:
    if (core->doc && core->doc->getNumPages() > 0) {
      ok = core->cvtWindowToDev(data->event->xmotion.x, data->event->xmotion.y,
				&pg, &x, &y);
      if (core->dragging) {
	if (ok) {
	  core->moveSelection(pg, x, y);
	}
      } else if (core->hyperlinksEnabled) {
	core->cvtDevToUser(pg, x, y, &xu, &yu);
	if (ok && (action = core->findLink(pg, xu, yu))) {
	  core->setCursor(core->linkCursor);
	  if (action != core->linkAction) {
	    core->linkAction = action;
	    if (core->updateCbk) {
	      s = "";
	      switch (action->getKind()) {
	      case actionGoTo:
		s = "[internal link]";
		break;
	      case actionGoToR:
		s = ((LinkGoToR *)action)->getFileName()->getCString();
		break;
	      case actionLaunch:
		s = ((LinkLaunch *)action)->getFileName()->getCString();
		break;
	      case actionURI:
		s = ((LinkURI *)action)->getURI()->getCString();
		break;
	      case actionNamed:
		s = ((LinkNamed *)action)->getName()->getCString();
		break;
	      case actionMovie:
		s = "[movie]";
		break;
	      case actionUnknown:
		s = "[unknown link]";
		break;
	      }
	      (*core->updateCbk)(core->updateCbkData, NULL, -1, -1, s);
	    }
	  }
	} else {
	  core->setCursor(None);
	  if (core->linkAction) {
	    core->linkAction = NULL;
	    if (core->updateCbk) {
	      (*core->updateCbk)(core->updateCbkData, NULL, -1, -1, "");
	    }
	  }
	}
      }
    }
    if (core->panning) {
      core->scrollTo(core->scrollX - (data->event->xmotion.x - core->panMX),
		     core->scrollY - (data->event->xmotion.y - core->panMY));
      core->panMX = data->event->xmotion.x;
      core->panMY = data->event->xmotion.y;
    }
    break;
  case KeyPress:
    if (core->keyPressCbk) {
      key = XLookupKeysym(&data->event->xkey,
			  (data->event->xkey.state & ShiftMask) ? 1 : 0);
      (*core->keyPressCbk)(core->keyPressCbkData,
			   key, data->event->xkey.state, data->event);
    }
    break;
  }
}

PDFCoreTile *XPDFCore::newTile(int xDestA, int yDestA) {
  return new XPDFCoreTile(xDestA, yDestA);
}

void XPDFCore::updateTileData(PDFCoreTile *tileA, int xSrc, int ySrc,
			      int width, int height, GBool composited) {
  XPDFCoreTile *tile = (XPDFCoreTile *)tileA;
  XImage *image;
  SplashColorPtr dataPtr, p;
  Gulong pixel;
  Guchar *ap;
  Guchar alpha, alpha1;
  int w, h, bw, x, y, r, g, b, gray;
  int *errDownR, *errDownG, *errDownB;
  int errRightR, errRightG, errRightB;
  int errDownRightR, errDownRightG, errDownRightB;
  int r0, g0, b0, re, ge, be;

  if (!tile->image) {
    w = tile->xMax - tile->xMin;
    h = tile->yMax - tile->yMin;
    image = XCreateImage(display, visual, depth, ZPixmap, 0, NULL, w, h, 8, 0);
    image->data = (char *)gmalloc(h * image->bytes_per_line);
    tile->image = image;
  } else {
    image = (XImage *)tile->image;
  }

  //~ optimize for known XImage formats
  bw = tile->bitmap->getRowSize();
  dataPtr = tile->bitmap->getDataPtr();

  if (trueColor) {
    for (y = 0; y < height; ++y) {
      p = dataPtr + (ySrc + y) * bw + xSrc * 3;
      if (!composited && tile->bitmap->getAlphaPtr()) {
	ap = tile->bitmap->getAlphaPtr() +
	       (ySrc + y) * tile->bitmap->getWidth() + xSrc;
      } else {
	ap = NULL;
      }
      for (x = 0; x < width; ++x) {
	r = splashRGB8R(p);
	g = splashRGB8G(p);
	b = splashRGB8B(p);
	if (ap) {
	  alpha = *ap++;
	  alpha1 = 255 - alpha;
	  r = div255(alpha1 * paperColor[0] + alpha * r);
	  g = div255(alpha1 * paperColor[1] + alpha * g);
	  b = div255(alpha1 * paperColor[2] + alpha * b);
	}
	r >>= rDiv;
	g >>= gDiv;
	b >>= bDiv;
	pixel = ((Gulong)r << rShift) +
	        ((Gulong)g << gShift) +
	        ((Gulong)b << bShift);
	XPutPixel(image, xSrc + x, ySrc + y, pixel);
	p += 3;
      }
    }
  } else if (rgbCubeSize == 1) {
    //~ this should really use splashModeMono, with non-clustered dithering
    for (y = 0; y < height; ++y) {
      p = dataPtr + (ySrc + y) * bw + xSrc * 3;
      if (!composited && tile->bitmap->getAlphaPtr()) {
	ap = tile->bitmap->getAlphaPtr() +
	       (ySrc + y) * tile->bitmap->getWidth() + xSrc;
      } else {
	ap = NULL;
      }
      for (x = 0; x < width; ++x) {
	r = splashRGB8R(p);
	g = splashRGB8G(p);
	b = splashRGB8B(p);
	if (ap) {
	  alpha = *ap++;
	  alpha1 = 255 - alpha;
	  r = div255(alpha1 * paperColor[0] + alpha * r);
	  g = div255(alpha1 * paperColor[1] + alpha * g);
	  b = div255(alpha1 * paperColor[2] + alpha * b);
	}
	gray = (int)(0.299 * r + 0.587 * g + 0.114 * b + 0.5);
	if (gray < 128) {
	  pixel = colors[0];
	} else {
	  pixel = colors[1];
	}
	XPutPixel(image, xSrc + x, ySrc + y, pixel);
	p += 3;
      }
    }
  } else {
    // do Floyd-Steinberg dithering on the whole bitmap
    errDownR = (int *)gmallocn(width + 2, sizeof(int));
    errDownG = (int *)gmallocn(width + 2, sizeof(int));
    errDownB = (int *)gmallocn(width + 2, sizeof(int));
    errRightR = errRightG = errRightB = 0;
    errDownRightR = errDownRightG = errDownRightB = 0;
    memset(errDownR, 0, (width + 2) * sizeof(int));
    memset(errDownG, 0, (width + 2) * sizeof(int));
    memset(errDownB, 0, (width + 2) * sizeof(int));
    for (y = 0; y < height; ++y) {
      p = dataPtr + (ySrc + y) * bw + xSrc * 3;
      if (!composited && tile->bitmap->getAlphaPtr()) {
	ap = tile->bitmap->getAlphaPtr() +
	       (ySrc + y) * tile->bitmap->getWidth() + xSrc;
      } else {
	ap = NULL;
      }
      for (x = 0; x < width; ++x) {
	r = splashRGB8R(p);
	g = splashRGB8G(p);
	b = splashRGB8B(p);
	if (ap) {
	  alpha = *ap++;
	  alpha1 = 255 - alpha;
	  r = div255(alpha1 * paperColor[0] + alpha * r);
	  g = div255(alpha1 * paperColor[1] + alpha * g);
	  b = div255(alpha1 * paperColor[2] + alpha * b);
	}
	r0 = r + errRightR + errDownR[x+1];
	g0 = g + errRightG + errDownG[x+1];
	b0 = b + errRightB + errDownB[x+1];
	if (r0 < 0) {
	  r = 0;
	} else if (r0 >= 255) {
	  r = rgbCubeSize - 1;
	} else {
	  r = div255(r0 * (rgbCubeSize - 1));
	}
	if (g0 < 0) {
	  g = 0;
	} else if (g0 >= 255) {
	  g = rgbCubeSize - 1;
	} else {
	  g = div255(g0 * (rgbCubeSize - 1));
	}
	if (b0 < 0) {
	  b = 0;
	} else if (b0 >= 255) {
	  b = rgbCubeSize - 1;
	} else {
	  b = div255(b0 * (rgbCubeSize - 1));
	}
	re = r0 - ((r << 8) - r) / (rgbCubeSize - 1);
	ge = g0 - ((g << 8) - g) / (rgbCubeSize - 1);
	be = b0 - ((b << 8) - b) / (rgbCubeSize - 1);
	errRightR = (re * 7) >> 4;
	errRightG = (ge * 7) >> 4;
	errRightB = (be * 7) >> 4;
	errDownR[x] += (re * 3) >> 4;
	errDownG[x] += (ge * 3) >> 4;
	errDownB[x] += (be * 3) >> 4;
	errDownR[x+1] = ((re * 5) >> 4) + errDownRightR;
	errDownG[x+1] = ((ge * 5) >> 4) + errDownRightG;
	errDownB[x+1] = ((be * 5) >> 4) + errDownRightB;
	errDownRightR = re >> 4;
	errDownRightG = ge >> 4;
	errDownRightB = be >> 4;
	pixel = colors[(r * rgbCubeSize + g) * rgbCubeSize + b];
	XPutPixel(image, xSrc + x, ySrc + y, pixel);
	p += 3;
      }
    }
    gfree(errDownR);
    gfree(errDownG);
    gfree(errDownB);
  }
}

void XPDFCore::redrawRect(PDFCoreTile *tileA, int xSrc, int ySrc,
			  int xDest, int yDest, int width, int height,
			  GBool composited) {
  XPDFCoreTile *tile = (XPDFCoreTile *)tileA;
  Window drawAreaWin;
  XGCValues gcValues;

  // create a GC for the drawing area
  drawAreaWin = XtWindow(drawArea);
  if (!drawAreaGC) {
    gcValues.foreground = mattePixel;
    drawAreaGC = XCreateGC(display, drawAreaWin, GCForeground, &gcValues);
  }

  // draw the document
  if (tile) {
    XPutImage(display, drawAreaWin, drawAreaGC, tile->image,
	      xSrc, ySrc, xDest, yDest, width, height);

  // draw the background
  } else {
    XFillRectangle(display, drawAreaWin, drawAreaGC,
		   xDest, yDest, width, height);
  }
}

void XPDFCore::updateScrollbars() {
  Arg args[20];
  int n;
  int maxPos;

  if (pages->getLength() > 0) {
    if (continuousMode) {
      maxPos = maxPageW;
    } else {
      maxPos = ((PDFCorePage *)pages->get(0))->w;
    }
  } else {
    maxPos = 1;
  }
  if (maxPos < drawAreaWidth) {
    maxPos = drawAreaWidth;
  }
  n = 0;
  XtSetArg(args[n], XmNvalue, scrollX); ++n;
  XtSetArg(args[n], XmNmaximum, maxPos); ++n;
  XtSetArg(args[n], XmNsliderSize, drawAreaWidth); ++n;
  XtSetArg(args[n], XmNincrement, 16); ++n;
  XtSetArg(args[n], XmNpageIncrement, drawAreaWidth); ++n;
  XtSetValues(hScrollBar, args, n);

  if (pages->getLength() > 0) {
    if (continuousMode) {
      maxPos = totalDocH;
    } else {
      maxPos = ((PDFCorePage *)pages->get(0))->h;
    }
  } else {
    maxPos = 1;
  }
  if (maxPos < drawAreaHeight) {
    maxPos = drawAreaHeight;
  }
  n = 0;
  XtSetArg(args[n], XmNvalue, scrollY); ++n;
  XtSetArg(args[n], XmNmaximum, maxPos); ++n;
  XtSetArg(args[n], XmNsliderSize, drawAreaHeight); ++n;
  XtSetArg(args[n], XmNincrement, 16); ++n;
  XtSetArg(args[n], XmNpageIncrement, drawAreaHeight); ++n;
  XtSetValues(vScrollBar, args, n);
}

void XPDFCore::setCursor(Cursor cursor) {
  Window topWin;

  if (cursor == currentCursor) {
    return;
  }
  if (!(topWin = XtWindow(shell))) {
    return;
  }
  if (cursor == None) {
    XUndefineCursor(display, topWin);
  } else {
    XDefineCursor(display, topWin, cursor);
  }
  XFlush(display);
  currentCursor = cursor;
}

GBool XPDFCore::doQuestionDialog(char *title, GString *msg) {
  return doDialog(XmDIALOG_QUESTION, gTrue, title, msg);
}

void XPDFCore::doInfoDialog(char *title, GString *msg) {
  doDialog(XmDIALOG_INFORMATION, gFalse, title, msg);
}

void XPDFCore::doErrorDialog(char *title, GString *msg) {
  doDialog(XmDIALOG_ERROR, gFalse, title, msg);
}

GBool XPDFCore::doDialog(int type, GBool hasCancel,
			 char *title, GString *msg) {
  Widget dialog, scroll, text;
  XtAppContext appContext;
  Arg args[20];
  int n;
  XmString s1, s2;
  XEvent event;

  n = 0;
  XtSetArg(args[n], XmNdialogType, type); ++n;
  XtSetArg(args[n], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL); ++n;
  s1 = XmStringCreateLocalized(title);
  XtSetArg(args[n], XmNdialogTitle, s1); ++n;
  s2 = NULL; // make gcc happy
  if (msg->getLength() <= 80) {
    s2 = XmStringCreateLocalized(msg->getCString());
    XtSetArg(args[n], XmNmessageString, s2); ++n;
  }
  dialog = XmCreateMessageDialog(drawArea, "questionDialog", args, n);
  XmStringFree(s1);
  if (msg->getLength() <= 80) {
    XmStringFree(s2);
  } else {
    n = 0;
    XtSetArg(args[n], XmNscrollingPolicy, XmAUTOMATIC); ++n;
    if (drawAreaWidth > 300) {
      XtSetArg(args[n], XmNwidth, drawAreaWidth - 100); ++n;
    }
    scroll = XmCreateScrolledWindow(dialog, "scroll", args, n);
    XtManageChild(scroll);
    n = 0;
    XtSetArg(args[n], XmNeditable, False); ++n;
    XtSetArg(args[n], XmNeditMode, XmMULTI_LINE_EDIT); ++n;
    XtSetArg(args[n], XmNvalue, msg->getCString()); ++n;
    XtSetArg(args[n], XmNshadowThickness, 0); ++n;
    text = XmCreateText(scroll, "text", args, n);
    XtManageChild(text);
  }
  XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_HELP_BUTTON));
  XtAddCallback(dialog, XmNokCallback,
		&dialogOkCbk, (XtPointer)this);
  if (hasCancel) {
    XtAddCallback(dialog, XmNcancelCallback,
		  &dialogCancelCbk, (XtPointer)this);
  } else {
    XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_CANCEL_BUTTON));
  }

  XtManageChild(dialog);

  appContext = XtWidgetToApplicationContext(dialog);
  dialogDone = 0;
  do {
    XtAppNextEvent(appContext, &event);
    XtDispatchEvent(&event);
  } while (!dialogDone);

  XtUnmanageChild(dialog);
  XtDestroyWidget(dialog);

  return dialogDone > 0;
}

void XPDFCore::dialogOkCbk(Widget widget, XtPointer ptr,
			   XtPointer callData) {
  XPDFCore *core = (XPDFCore *)ptr;

  core->dialogDone = 1;
}

void XPDFCore::dialogCancelCbk(Widget widget, XtPointer ptr,
			       XtPointer callData) {
  XPDFCore *core = (XPDFCore *)ptr;

  core->dialogDone = -1;
}

//------------------------------------------------------------------------
// password dialog
//------------------------------------------------------------------------

void XPDFCore::initPasswordDialog() {
  Widget row, label, okBtn, cancelBtn;
  Arg args[20];
  int n;
  XmString s;

  //----- dialog
  n = 0;
  s = XmStringCreateLocalized(xpdfAppName ": Password");
  XtSetArg(args[n], XmNdialogTitle, s); ++n;
  XtSetArg(args[n], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL); ++n;
  passwordDialog = XmCreateFormDialog(drawArea, "passwordDialog", args, n);
  XmStringFree(s);

  //----- message
  n = 0;
  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNtopOffset, 4); ++n;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNleftOffset, 4); ++n;
  s = XmStringCreateLocalized("This document requires a password.");
  XtSetArg(args[n], XmNlabelString, s); ++n;
  label = XmCreateLabel(passwordDialog, "msg", args, n);
  XmStringFree(s);
  XtManageChild(label);

  //----- label and password entry
  n = 0;
  XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n;
  XtSetArg(args[n], XmNtopWidget, label); ++n;
  XtSetArg(args[n], XmNtopOffset, 4); ++n;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNleftOffset, 4); ++n;
  XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNleftOffset, 4); ++n;
  XtSetArg(args[n], XmNorientation, XmHORIZONTAL); ++n;
  XtSetArg(args[n], XmNpacking, XmPACK_TIGHT); ++n;
  row = XmCreateRowColumn(passwordDialog, "row", args, n);
  XtManageChild(row);
  n = 0;
  s = XmStringCreateLocalized("Password: ");
  XtSetArg(args[n], XmNlabelString, s); ++n;
  label = XmCreateLabel(row, "label", args, n);
  XmStringFree(s);
  XtManageChild(label);
  n = 0;
  XtSetArg(args[n], XmNcolumns, 16); ++n;
  passwordText = XmCreateTextField(row, "text", args, n);
  XtManageChild(passwordText);
  XtAddCallback(passwordText, XmNmodifyVerifyCallback,
		&passwordTextVerifyCbk, this);

  //----- "Ok" and "Cancel" buttons
  n = 0;
  XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n;
  XtSetArg(args[n], XmNtopWidget, row); ++n;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNleftOffset, 4); ++n;
  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNbottomOffset, 4); ++n;
  XtSetArg(args[n], XmNnavigationType, XmEXCLUSIVE_TAB_GROUP); ++n;
  okBtn = XmCreatePushButton(passwordDialog, "Ok", args, n);
  XtManageChild(okBtn);
  XtAddCallback(okBtn, XmNactivateCallback,
		&passwordOkCbk, (XtPointer)this);
  n = 0;
  XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n;
  XtSetArg(args[n], XmNtopWidget, row); ++n;
  XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNrightOffset, 4); ++n;
  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNbottomOffset, 4); ++n;
  XtSetArg(args[n], XmNnavigationType, XmEXCLUSIVE_TAB_GROUP); ++n;
  cancelBtn = XmCreatePushButton(passwordDialog, "Cancel", args, n);
  XtManageChild(cancelBtn);
  XtAddCallback(cancelBtn, XmNactivateCallback,
		&passwordCancelCbk, (XtPointer)this);
  n = 0;
  XtSetArg(args[n], XmNdefaultButton, okBtn); ++n;
  XtSetArg(args[n], XmNcancelButton, cancelBtn); ++n;
#if XmVersion > 1001
  XtSetArg(args[n], XmNinitialFocus, passwordText); ++n;
#endif
  XtSetValues(passwordDialog, args, n);
}

void XPDFCore::passwordTextVerifyCbk(Widget widget, XtPointer ptr,
				     XtPointer callData) {
  XPDFCore *core = (XPDFCore *)ptr;
  XmTextVerifyCallbackStruct *data =
      (XmTextVerifyCallbackStruct *)callData;
  int i, n;

  i = (int)data->startPos;
  n = (int)data->endPos - i;
  if (i > core->password->getLength()) {
    i = core->password->getLength();
  }
  if (i + n > core->password->getLength()) {
    n = core->password->getLength() - i;
  }
  core->password->del(i, n);
  core->password->insert(i, data->text->ptr, data->text->length);

  for (i = 0; i < data->text->length; ++i) {
    data->text->ptr[i] = '*';
  }
  data->doit = True;
}

void XPDFCore::passwordOkCbk(Widget widget, XtPointer ptr,
			     XtPointer callData) {
  XPDFCore *core = (XPDFCore *)ptr;

  core->dialogDone = 1;
}

void XPDFCore::passwordCancelCbk(Widget widget, XtPointer ptr,
				 XtPointer callData) {
  XPDFCore *core = (XPDFCore *)ptr;

  core->dialogDone = -1;
}

GString *XPDFCore::getPassword() {
  XtAppContext appContext;
  XEvent event;

  // NB: set <password> before calling XmTextFieldSetString, because
  // SetString will trigger a call to passwordTextVerifyCbk, which
  // expects <password> to be valid
  password = new GString();
  XmTextFieldSetString(passwordText, "");
  XtManageChild(passwordDialog);

  appContext = XtWidgetToApplicationContext(passwordDialog);
  dialogDone = 0;
  do {
    XtAppNextEvent(appContext, &event);
    XtDispatchEvent(&event);
  } while (!dialogDone);
  XtUnmanageChild(passwordDialog);

  if (dialogDone < 0) {
    delete password;
    return NULL;
  }
  return password;
}
//========================================================================
//
// XPDFCore.h
//
// Copyright 2002-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef XPDFCORE_H
#define XPDFCORE_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#define Object XtObject
#include <Xm/XmAll.h>
#undef Object
#include "gtypes.h"
#include "gfile.h" // for time_t
#include "SplashTypes.h"
#include "PDFCore.h"

class GString;
class BaseStream;
class PDFDoc;
class LinkAction;

//------------------------------------------------------------------------

#define xMaxRGBCube 6		// max size of RGB color cube

//------------------------------------------------------------------------
// callbacks
//------------------------------------------------------------------------

typedef void (*XPDFUpdateCbk)(void *data, GString *fileName,
			      int pageNum, int numPages, char *linkLabel);

typedef void (*XPDFActionCbk)(void *data, char *action);

typedef void (*XPDFKeyPressCbk)(void *data, KeySym key, Guint modifiers,
				XEvent *event);

typedef void (*XPDFMouseCbk)(void *data, XEvent *event);

//------------------------------------------------------------------------
// XPDFCore
//------------------------------------------------------------------------

class XPDFCore: public PDFCore {
public:

  // Create viewer core inside <parentWidgetA>.
  XPDFCore(Widget shellA, Widget parentWidgetA,
	   SplashColorPtr paperColorA, Gulong paperPixelA,
	   Gulong mattePixelA, GBool fullScreenA, GBool reverseVideoA,
	   GBool installCmap, int rgbCubeSizeA);

  ~XPDFCore();

  //----- loadFile / displayPage / displayDest

  // Load a new file.  Returns pdfOk or error code.
  virtual int loadFile(GString *fileName, GString *ownerPassword = NULL,
		       GString *userPassword = NULL);

  // Load a new file, via a Stream instead of a file name.  Returns
  // pdfOk or error code.
  virtual int loadFile(BaseStream *stream, GString *ownerPassword = NULL,
		       GString *userPassword = NULL);

  // Load an already-created PDFDoc object.
  virtual void loadDoc(PDFDoc *docA);

  // Resize the window to fit page <pg> of the current document.
  void resizeToPage(int pg);

  // Update the display, given the specified parameters.
  virtual void update(int topPageA, int scrollXA, int scrollYA,
		      double zoomA, int rotateA,
		      GBool force, GBool addToHist);

  //----- page/position changes

  virtual GBool gotoNextPage(int inc, GBool top);
  virtual GBool gotoPrevPage(int dec, GBool top, GBool bottom);
  virtual GBool goForward();
  virtual GBool goBackward();

  //----- selection

  void startSelection(int wx, int wy);
  void endSelection(int wx, int wy);
  void copySelection();
  void startPan(int wx, int wy);
  void endPan(int wx, int wy);

  //----- hyperlinks

  void doAction(LinkAction *action);
  LinkAction *getLinkAction() { return linkAction; }
  GString *mungeURL(GString *url);

  //----- find

  virtual GBool find(char *s, GBool caseSensitive,
		     GBool next, GBool backward, GBool onePageOnly);
  virtual GBool findU(Unicode *u, int len, GBool caseSensitive,
		      GBool next, GBool backward, GBool onePageOnly);

  //----- simple modal dialogs

  GBool doQuestionDialog(char *title, GString *msg);
  void doInfoDialog(char *title, GString *msg);
  void doErrorDialog(char *title, GString *msg);

  //----- password dialog

  GString *getPassword();

  //----- misc access

  Widget getWidget() { return scrolledWin; }
  Widget getDrawAreaWidget() { return drawArea; }
  virtual void setBusyCursor(GBool busy);
  Cursor getBusyCursor() { return busyCursor; }
  void takeFocus();
  void enableHyperlinks(GBool on) { hyperlinksEnabled = on; }
  GBool getHyperlinksEnabled() { return hyperlinksEnabled; }
  void enableSelect(GBool on) { selectEnabled = on; }
  void setUpdateCbk(XPDFUpdateCbk cbk, void *data)
    { updateCbk = cbk; updateCbkData = data; }
  void setActionCbk(XPDFActionCbk cbk, void *data)
    { actionCbk = cbk; actionCbkData = data; }
  void setKeyPressCbk(XPDFKeyPressCbk cbk, void *data)
    { keyPressCbk = cbk; keyPressCbkData = data; }
  void setMouseCbk(XPDFMouseCbk cbk, void *data)
    { mouseCbk = cbk; mouseCbkData = data; }
  GBool getFullScreen() { return fullScreen; }

private:

  virtual GBool checkForNewFile();

  //----- hyperlinks
  void runCommand(GString *cmdFmt, GString *arg);

  //----- selection
  static Boolean convertSelectionCbk(Widget widget, Atom *selection,
				     Atom *target, Atom *type,
				     XtPointer *value, unsigned long *length,
				     int *format);

  //----- GUI code
  void setupX(GBool installCmap, int rgbCubeSizeA);
  void initWindow();
  static void hScrollChangeCbk(Widget widget, XtPointer ptr,
			       XtPointer callData);
  static void hScrollDragCbk(Widget widget, XtPointer ptr,
			     XtPointer callData);
  static void vScrollChangeCbk(Widget widget, XtPointer ptr,
			       XtPointer callData);
  static void vScrollDragCbk(Widget widget, XtPointer ptr,
			     XtPointer callData);
  static void resizeCbk(Widget widget, XtPointer ptr, XtPointer callData);
  static void redrawCbk(Widget widget, XtPointer ptr, XtPointer callData);
  static void inputCbk(Widget widget, XtPointer ptr, XtPointer callData);
  virtual PDFCoreTile *newTile(int xDestA, int yDestA);
  virtual void updateTileData(PDFCoreTile *tileA, int xSrc, int ySrc,
			      int width, int height, GBool composited);
  virtual void redrawRect(PDFCoreTile *tileA, int xSrc, int ySrc,
			  int xDest, int yDest, int width, int height,
			  GBool composited);
  virtual void updateScrollbars();
  void setCursor(Cursor cursor);
  GBool doDialog(int type, GBool hasCancel,
		 char *title, GString *msg);
  static void dialogOkCbk(Widget widget, XtPointer ptr,
			  XtPointer callData);
  static void dialogCancelCbk(Widget widget, XtPointer ptr,
			      XtPointer callData);
  void initPasswordDialog();
  static void passwordTextVerifyCbk(Widget widget, XtPointer ptr,
				    XtPointer callData);
  static void passwordOkCbk(Widget widget, XtPointer ptr,
			    XtPointer callData);
  static void passwordCancelCbk(Widget widget, XtPointer ptr,
				XtPointer callData);

  Gulong paperPixel;
  Gulong mattePixel;
  //~unimp: move fullScreen into PDFCore?
  GBool fullScreen;

  Display *display;
  int screenNum;
  Visual *visual;
  Colormap colormap;
  Guint depth;                  // visual depth
  GBool trueColor;              // set if using a TrueColor visual
  int rDiv, gDiv, bDiv;         // RGB right shifts (for TrueColor)
  int rShift, gShift, bShift;   // RGB left shifts (for TrueColor)
  int rgbCubeSize;              // size of color cube (for non-TrueColor)
  Gulong                        // color cube (for non-TrueColor)
    colors[xMaxRGBCube * xMaxRGBCube * xMaxRGBCube];

  Widget shell;			// top-level shell containing the widget
  Widget parentWidget;		// parent widget (not created by XPDFCore)
  Widget scrolledWin;
  Widget hScrollBar;
  Widget vScrollBar;
  Widget drawAreaFrame;
  Widget drawArea;
  Cursor busyCursor, linkCursor, selectCursor;
  Cursor currentCursor;
  GC drawAreaGC;		// GC for blitting into drawArea

  static GString *currentSelection;  // selected text
  static XPDFCore *currentSelectionOwner;
  static Atom targetsAtom;

  GBool panning;
  int panMX, panMY;

  time_t modTime;		// last modification time of PDF file

  LinkAction *linkAction;	// mouse cursor is over this link

  XPDFUpdateCbk updateCbk;
  void *updateCbkData;
  XPDFActionCbk actionCbk;
  void *actionCbkData;
  XPDFKeyPressCbk keyPressCbk;
  void *keyPressCbkData;
  XPDFMouseCbk mouseCbk;
  void *mouseCbkData;

  GBool hyperlinksEnabled;
  GBool selectEnabled;

  int dialogDone;

  Widget passwordDialog;
  Widget passwordText;
  GString *password;
};

#endif
//========================================================================
//
// XPDFTree.cc
//
// Copyright 2002-2003 Glyph & Cog, LLC
//
//========================================================================

#include <stdlib.h>
#include "gmem.h"
#include "XPDFTreeP.h"

//------------------------------------------------------------------------

#define xpdfTreeIndent 16

//------------------------------------------------------------------------

struct _XPDFTreeEntry {
  Widget widget;
  XPDFTreeEntry *children;
  XPDFTreeEntry *next;
};

//------------------------------------------------------------------------

static void classPartInitialize(WidgetClass widgetClass);
static void initialize(Widget requestWidget, Widget newWidget,
		       ArgList args, Cardinal *numArgs);
static void destroy(Widget widget);
static void destroySubtree(XPDFTreeEntry *e);
static void resize(Widget widget);
static void redisplay(Widget widget, XEvent *event, Region region);
static void redisplaySubtree(XPDFTreeWidget w, XPDFTreeEntry *e,
			     XEvent *event, Region region);
static void drawExpandedIcon(XPDFTreeWidget w, Position x, Position y);
static void drawCollapsedIcon(XPDFTreeWidget w, Position x, Position y);
static Boolean setValues(Widget oldWidget, Widget requestWidget,
			 Widget newWidget, ArgList args, Cardinal *numArgs);
static void setValuesAlmost(Widget oldWidget, Widget newWidget,
			    XtWidgetGeometry *request,
			    XtWidgetGeometry *reply);
static XtGeometryResult queryGeometry(Widget widget,
				      XtWidgetGeometry *request,
				      XtWidgetGeometry *reply);
static XtGeometryResult geometryManager(Widget widget,
					XtWidgetGeometry *request,
					XtWidgetGeometry *reply);
static void changeManaged(Widget widget);
static void initConstraint(Widget requestWidget, Widget newWidget,
			   ArgList args, Cardinal *numArgs);
static void destroyConstraint(Widget widget);
static void deleteSubtree(Widget widget);
static Boolean constraintSetValues(Widget oldWidget, Widget requestWidget,
				   Widget newWidget,
				   ArgList args, Cardinal *numArgs);
static void insertChildOnList(XPDFTreeEntry *e, XPDFTreeEntry **listHead);
static void deleteChildFromList(XPDFTreeEntry *e, XPDFTreeEntry **listHead);
static void createGC(Widget widget);
static void destroyGC(Widget widget);
static void layout(Widget widget, Widget instigator);
static int layoutSubtree(XPDFTreeWidget w, Widget instigator,
			 XPDFTreeEntry *e, Position x, Position y,
			 Boolean visible);
static void calcSize(Widget widget, Widget instigator,
		     Dimension *totalWidth,
		     Dimension *totalHeight);
static void calcSubtreeSize(XPDFTreeWidget w, Widget instigator,
			    XPDFTreeEntry *e,
			    Dimension *width, Dimension *height);
static Boolean needRelayout(Widget oldWidget, Widget newWidget);
static void click(Widget widget, XEvent *event,
		  String *params, Cardinal *numParams);
static Boolean findPosition(XPDFTreeWidget w, int x, int y,
			    XPDFTreeEntry **e, Boolean *onExpandIcon);
static Boolean findPositionInSubtree(XPDFTreeWidget w, int x, int y,
				     XPDFTreeEntry **e,
				     Boolean *onExpandIcon);

//------------------------------------------------------------------------

static XtResource resources[] = {
  { XmNmarginWidth, XmCMarginWidth, XmRHorizontalDimension,
    sizeof(Dimension), XtOffsetOf(XPDFTreeRec, tree.marginWidth),
    XmRImmediate, (XtPointer)0 },
  { XmNmarginHeight, XmCMarginHeight, XmRVerticalDimension,
    sizeof(Dimension), XtOffsetOf(XPDFTreeRec, tree.marginHeight),
    XmRImmediate, (XtPointer)0 },
  { XPDFNselectionCallback, XmCCallback, XmRCallback,
    sizeof(XtCallbackList), XtOffsetOf(XPDFTreeRec, tree.selectCallback),
    XmRImmediate, (XtPointer)NULL }
};

static XmSyntheticResource synResources[] = {
  { XmNmarginWidth, sizeof(Dimension),
    XtOffsetOf(XPDFTreeRec, tree.marginWidth),
#if XmVERSION > 1
    XmeFromHorizontalPixels, XmeToHorizontalPixels
#else
    _XmFromHorizontalPixels, _XmToHorizontalPixels
#endif
  },
  { XmNmarginHeight, sizeof(Dimension),
    XtOffsetOf(XPDFTreeRec, tree.marginHeight),
#if XmVERSION > 1
    XmeFromVerticalPixels, XmeToVerticalPixels
#else
    _XmFromVerticalPixels, _XmToVerticalPixels
#endif
  }
};

static XtResource constraints[] = {
  { XPDFNentryParent, XPDFCentryParent, XmRWidget,
    sizeof(Widget), XtOffsetOf(XPDFTreeConstraintRec, tree.entryParent),
    XmRImmediate, (XtPointer)NULL },
  { XPDFNentryExpanded, XPDFCentryExpanded, XmRBoolean,
    sizeof(Boolean), XtOffsetOf(XPDFTreeConstraintRec, tree.entryExpanded),
    XmRImmediate, (XtPointer)False },
  { XPDFNentryPosition, XPDFCentryPosition, XmRInt,
    sizeof(int), XtOffsetOf(XPDFTreeConstraintRec, tree.entryPosition),
    XmRImmediate, (XtPointer)0 }
};

static char defaultTranslations[] =
  "<Btn1Down>: XPDFTreeClick()";

static XtActionsRec actions[] = {
  { "XPDFTreeClick", click }
};

externaldef(xpdftreeclassrec) XPDFTreeClassRec xpdfTreeClassRec = {
  { // Core
    (WidgetClass)&xmManagerClassRec,		// superclass
    "XPDFTree",					// class_name
    sizeof(XPDFTreeRec),			// widget_size
    NULL,					// class_initialize
    &classPartInitialize,			// class_part_initialize
    FALSE,					// class_inited
    &initialize,				// initialize
    NULL,					// initialize_hook
    XtInheritRealize,				// realize
    actions,					// actions
    XtNumber(actions),				// num_actions
    resources,					// resources
    XtNumber(resources),			// num_resources
    NULLQUARK,					// xrm_class
    TRUE,					// compress_motion
    XtExposeCompressMaximal,			// compress_exposure
    TRUE,					// compress_enterleave
    FALSE,					// visible_interest
    &destroy,					// destroy
    &resize,					// resize
    &redisplay,					// expose
    &setValues,					// set_values
    NULL,					// set_values_hook
    &setValuesAlmost,				// set_values_almost
    NULL,					// get_values_hook
    NULL,					// accept_focus
    XtVersion,					// version
    NULL,					// callback_private
    defaultTranslations,			// tm_table
    &queryGeometry,				// query_geometry
    NULL,					// display_accelerator
    NULL					// extension
  },
  { // Composite
    &geometryManager,				// geometry_manager
    &changeManaged,				// change_managed
    XtInheritInsertChild,			// insert_child
    XtInheritDeleteChild,			// delete_child
    NULL					// extension
  },
  { // Constraint
    constraints,				// constraint_resources
    XtNumber(constraints),			// constraint_num_resources
    sizeof(XPDFTreeConstraintRec),		// constraint_size
    &initConstraint,				// constraint_initialize
    &destroyConstraint,				// constraint_destroy
    &constraintSetValues,			// constraint_set_values
    NULL					// extension
  },
  { // XmManager
    XtInheritTranslations,			// translations
#if XmVERSION > 1
    synResources,				// syn_resources
    XtNumber(synResources),			// num_syn_resources
#else
    NULL,					// syn_resources
    0,						// num_syn_resources
#endif
    NULL,					// syn_constraint_resources
    0,						// num_syn_constraint_res's
    XmInheritParentProcess,			// parent_process
    NULL					// extension
  },
  { // XPDFTree
    &createGC,					// createGC
    &destroyGC,					// destroyGC
    &layout,					// layout
    &calcSize,					// calcSize
    &needRelayout,				// needRelayout
    NULL					// extension
  }
};

externaldef(xpdftreewidgetclass) WidgetClass xpdfTreeWidgetClass =
  (WidgetClass)&xpdfTreeClassRec;

//------------------------------------------------------------------------

static void classPartInitialize(WidgetClass widgetCls) {
  XPDFTreeWidgetClass wc = (XPDFTreeWidgetClass)widgetCls;
  XPDFTreeWidgetClass sc = (XPDFTreeWidgetClass)wc->coreClass.superclass;

  // method inheritance
  if (wc->treeClass.createGC == XPDFInheritCreateGC) {
    wc->treeClass.createGC = sc->treeClass.createGC;
  }
  if (wc->treeClass.destroyGC == XPDFInheritDestroyGC) {
    wc->treeClass.destroyGC = sc->treeClass.destroyGC;
  }
  if (wc->treeClass.layout == XPDFInheritLayout) {
    wc->treeClass.layout = sc->treeClass.layout;
  }
  if (wc->treeClass.calcSize == XPDFInheritCalcSize) {
    wc->treeClass.calcSize = sc->treeClass.calcSize;
  }
  if (wc->treeClass.needRelayout == XPDFInheritNeedRelayout) {
    wc->treeClass.needRelayout = sc->treeClass.needRelayout;
  }
}

static void initialize(Widget requestWidget, Widget newWidget,
		       ArgList args, Cardinal *numArgs) {
  XPDFTreeWidget nw = (XPDFTreeWidget)newWidget;
  XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(newWidget);

  nw->tree.root = NULL;
  nw->tree.redrawY = -1;
  if (cls->treeClass.createGC) {
    (*cls->treeClass.createGC)(newWidget);
  } else {
    createGC(newWidget);
  }
}

static void destroy(Widget widget) {
  XPDFTreeWidget w = (XPDFTreeWidget)widget;
  XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(widget);

  if (w->tree.root) {
    destroySubtree(w->tree.root);
    w->tree.root = NULL;
  }
  if (cls->treeClass.destroyGC) {
    (*cls->treeClass.destroyGC)(widget);
  } else {
    destroyGC(widget);
  }
}

static void destroySubtree(XPDFTreeEntry *e) {
  if (e->children) {
    destroySubtree(e->children);
  }
  if (e->next) {
    destroySubtree(e->next);
  }
}

static void resize(Widget widget) {
  XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(widget);

  if (cls->treeClass.layout) {
    (*cls->treeClass.layout)(widget, NULL);
  } else {
    layout(widget, NULL);
  }
}

static void redisplay(Widget widget, XEvent *event, Region region) {
  XPDFTreeWidget w = (XPDFTreeWidget)widget;
  XPDFTreeEntry *e;

  if (w->tree.redrawY >= 0) {
    XClearArea(XtDisplay((Widget)w), XtWindow((Widget)w),
	       0, w->tree.redrawY, w->core.width, w->core.height, False);
    w->tree.redrawY = -1;
  }
  for (e = w->tree.root; e; e = e->next) {
    redisplaySubtree(w, e, event, region);
  }
}

static void redisplaySubtree(XPDFTreeWidget w, XPDFTreeEntry *e,
			     XEvent *event, Region region) {
  XPDFTreeConstraint c;
  Position x, y, y2;
  XPDFTreeEntry *child;

  (*XtClass(e->widget)->core_class.expose)(e->widget, event, region);
  c = XPDFTreeCPart(e->widget);
  x = e->widget->core.x;
  y = e->widget->core.y + e->widget->core.height / 2;
  if (e->children) {
    if (c->entryExpanded) {
      drawExpandedIcon(w, x - 8, y);
      y2 = y; // make gcc happy
      for (child = e->children; child; child = child->next) {
	y2 = child->widget->core.y + child->widget->core.height / 2;
	XDrawLine(XtDisplay((Widget)w), XtWindow((Widget)w), w->tree.dottedGC,
		  x - 8, y2, x + 6, y2);
	redisplaySubtree(w, child, event, region);
      }
      XDrawLine(XtDisplay((Widget)w), XtWindow((Widget)w), w->tree.dottedGC,
		x - 8, y + 2, x - 8, y2);
    } else {
      drawCollapsedIcon(w, x - 8, y);
    }
  }
}

static void drawExpandedIcon(XPDFTreeWidget w, Position x, Position y) {
  XPoint pts[4];

  pts[0].x = x - 4;    pts[0].y = y - 2;
  pts[1].x = x + 4;    pts[1].y = y - 2;
  pts[2].x = x;        pts[2].y = y + 2;
  pts[3].x = x - 4;    pts[3].y = y - 2;
  XDrawLines(XtDisplay((Widget)w), XtWindow((Widget)w), w->tree.plainGC,
	     pts, 4, CoordModeOrigin);
}

static void drawCollapsedIcon(XPDFTreeWidget w, Position x, Position y) {
  XPoint pts[4];

  pts[0].x = x - 2;    pts[0].y = y - 4;
  pts[1].x = x - 2;    pts[1].y = y + 4;
  pts[2].x = x + 2;    pts[2].y = y;
  pts[3].x = x - 2;    pts[3].y = y - 4;
  XDrawLines(XtDisplay((Widget)w), XtWindow((Widget)w), w->tree.plainGC,
	     pts, 4, CoordModeOrigin);
}

static Boolean setValues(Widget oldWidget, Widget requestWidget,
			 Widget newWidget, ArgList args, Cardinal *numArgs) {
  XPDFTreeWidget ow = (XPDFTreeWidget)oldWidget;
  XPDFTreeWidget nw = (XPDFTreeWidget)newWidget;
  XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(nw);
  Boolean relayout, redisp;

  // check to see if layout-affecting resources have changed
  if (cls->treeClass.needRelayout) {
    relayout = (*cls->treeClass.needRelayout)((Widget)ow, (Widget)nw);
  } else {
    relayout = needRelayout((Widget)ow, (Widget)nw);
  }
  redisp = False;
  if (relayout) {

    // calculate a new ideal size (reset the widget size first so
    // calcSize will compute a new one)
    if (nw->core.width == ow->core.width) {
      nw->core.width = 0;
    }
    if (nw->core.height == ow->core.height) {
      nw->core.height = 0;
    }
    if (cls->treeClass.calcSize) {
      (*cls->treeClass.calcSize)((Widget)nw, NULL,
				 &nw->core.width, &nw->core.height);
    } else {
      calcSize((Widget)nw, NULL, &nw->core.width, &nw->core.height);
    }

    // if resources have changed but size hasn't, layout manually
    // (because Xt just looks at the size)
    if (nw->core.width == ow->core.width &&
	nw->core.height == ow->core.height) {
      if (cls->treeClass.layout) {
	(*cls->treeClass.layout)((Widget)nw, NULL);
      } else {
	layout((Widget)nw, NULL);
      }
      redisp = True;
    }
  }

  return redisp;
}

static void setValuesAlmost(Widget oldWidget, Widget newWidget,
			    XtWidgetGeometry *request,
			    XtWidgetGeometry *reply) {
  XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(newWidget);

  // our parent rejected a geometry request, so accept the compromise
  // and relayout
  if (!reply->request_mode) {
    if (cls->treeClass.layout) {
      (*cls->treeClass.layout)(newWidget, NULL);
    } else {
      layout(newWidget, NULL);
    }
  }
  *request = *reply;
}

static XtGeometryResult queryGeometry(Widget widget,
				      XtWidgetGeometry *request,
				      XtWidgetGeometry *reply) {
  XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(widget);

  if (!XtIsRealized(widget)) {
    reply->width = XtWidth(widget);
    reply->height = XtHeight(widget);
  } else {
    reply->width = 0;
    reply->height = 0;
  }
  if (cls->treeClass.calcSize) {
    (*cls->treeClass.calcSize)(widget, NULL, &reply->width, &reply->height);
  } else {
    calcSize(widget, NULL, &reply->width, &reply->height);
  }
#if XmVERSION > 1
  return XmeReplyToQueryGeometry(widget, request, reply);
#else
  if ((request->request_mode & CWWidth) &&
      (request->request_mode & CWHeight) &&
      request->width == reply->width &&
      request->height == reply->height) {
    return XtGeometryYes;
  }
  if (reply->width == XtWidth(widget) &&
      reply->height == XtHeight(widget)) {
    return XtGeometryNo;
  }
  reply->request_mode = CWWidth | CWHeight;
  return XtGeometryAlmost;
#endif
}

static XtGeometryResult geometryManager(Widget widget,
					XtWidgetGeometry *request,
					XtWidgetGeometry *reply) {
  XPDFTreeWidget w = (XPDFTreeWidget)XtParent(widget);
  XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(w);
  Dimension curWidth, curHeight, curBW;
  XtWidgetGeometry parentReq;
  XtGeometryResult result;

  // deny any requests for a new position
  if ((request->request_mode & CWX) || (request->request_mode & CWY)) {
    return XtGeometryNo;
  }

  // save the current geometry
  curWidth = w->core.width;
  curHeight = w->core.height;
  curBW = w->core.border_width;

  // make the requested changes
  if (request->request_mode & CWWidth) {
    w->core.width = request->width;
  }
  if (request->request_mode & CWHeight) {
    w->core.height = request->height;
  }
  if (request->request_mode & CWBorderWidth) {
    w->core.border_width = request->border_width;
  }

  // calculate the new ideal size
  parentReq.width = 0;
  parentReq.height = 0;
  if (cls->treeClass.calcSize) {
    (*cls->treeClass.calcSize)((Widget)w, widget,
			       &parentReq.width, &reply->height);
  } else {
    calcSize((Widget)w, widget, &parentReq.width, &reply->height);
  }

  // send geometry request to our parent
  parentReq.request_mode = CWWidth | CWHeight;
  if (request->request_mode & XtCWQueryOnly) {
    parentReq.request_mode |= XtCWQueryOnly;
  }
  result = XtMakeGeometryRequest((Widget)w, &parentReq, NULL);
  if (result == XtGeometryAlmost) {
    result = XtGeometryNo;
  }

  if (result == XtGeometryNo || (request->request_mode & XtCWQueryOnly)) {
    // restore the original geometry
    w->core.width = curWidth;
    w->core.height = curHeight;
    w->core.border_width = curBW;
  } else {
    if (cls->treeClass.layout) {
      (*cls->treeClass.layout)((Widget)w, widget);
    } else {
      layout((Widget)w, widget);
    }
  }

  return result;
}

static void changeManaged(Widget widget) {
  Dimension width, height;
  XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(widget);

  // compute the ideal size
  if (!XtIsRealized(widget)) {
    width = XtWidth(widget);
    height = XtHeight(widget);
  } else {
    width = 0;
    height = 0;
  }
  if (cls->treeClass.calcSize) {
    (*cls->treeClass.calcSize)(widget, NULL, &width, &height);
  } else {
    calcSize(widget, NULL, &width, &height);
  }

  // make resize request to parent -- keep asking until we get a yes
  // or no
  while (XtMakeResizeRequest(widget, width, height, &width, &height)
	 == XtGeometryAlmost) ;

  // relayout
  if (cls->treeClass.layout) {
    (*cls->treeClass.layout)(widget, NULL);
  } else {
    layout(widget, NULL);
  }

#if XmVERSION > 1
  // update keyboard traversal
  XmeNavigChangeManaged(widget);
#else
  _XmNavigChangeManaged(widget);
#endif
}

static void initConstraint(Widget requestWidget, Widget newWidget,
			   ArgList args, Cardinal *numArgs) {
  XPDFTreeWidget w = (XPDFTreeWidget)XtParent(newWidget);
  XPDFTreeConstraint c;

  c = XPDFTreeCPart(newWidget);
  c->e = (XPDFTreeEntry *)gmalloc(sizeof(XPDFTreeEntry));
  c->e->widget = newWidget;
  c->e->children = NULL;
  c->e->next = NULL;
  if (c->entryParent) {
    insertChildOnList(c->e, &XPDFTreeCPart(c->entryParent)->e->children);
  } else {
    insertChildOnList(c->e, &w->tree.root);
  }
}

static void destroyConstraint(Widget widget) {
  deleteSubtree(widget);
}

static void deleteSubtree(Widget widget) {
  XPDFTreeWidget w = (XPDFTreeWidget)XtParent(widget);
  XPDFTreeConstraint c;

  c = XPDFTreeCPart(widget);
  if (!c->e) {
    return;
  }
  while (c->e->children) {
    deleteSubtree(c->e->children->widget);
  }
  if (c->entryParent) {
    deleteChildFromList(c->e, &XPDFTreeCPart(c->entryParent)->e->children);
  } else {
    deleteChildFromList(c->e, &w->tree.root);
  }
  gfree(c->e);
  c->e = NULL;
}

static Boolean constraintSetValues(Widget oldWidget, Widget requestWidget,
				   Widget newWidget,
				   ArgList args, Cardinal *numArgs) {
  XPDFTreeWidget w = (XPDFTreeWidget)XtParent(newWidget);
  XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass((Widget)w);
  XPDFTreeConstraint oc, nc;
  Boolean relayout;
  Dimension width, height;

  if (!XtIsManaged(newWidget)) {
    return False;
  }
  oc = XPDFTreeCPart(oldWidget);
  nc = XPDFTreeCPart(newWidget);
  relayout = False;
  if (nc->entryParent != oc->entryParent ||
      nc->entryPosition != oc->entryPosition) {
    if (oc->entryParent) {
      deleteChildFromList(oc->e, &XPDFTreeCPart(oc->entryParent)->e->children);
    } else {
      deleteChildFromList(oc->e, &w->tree.root);
    }
    if (nc->entryParent) {
      insertChildOnList(nc->e, &XPDFTreeCPart(nc->entryParent)->e->children);
    } else {
      insertChildOnList(nc->e, &w->tree.root);
    }
    relayout = True;
  } else if (nc->entryExpanded != oc->entryExpanded) {
    relayout = True;
  }

  if (relayout) {

    // calculate a new ideal size (reset the widget size first so
    // calcSize will compute a new one)
    width = 0;
    height = 0;
    if (cls->treeClass.calcSize) {
      (*cls->treeClass.calcSize)((Widget)w, NULL, &width, &height);
    } else {
      calcSize((Widget)w, NULL, &width, &height);
    }

    // make resize request to parent -- keep asking until we get a yes
    // or no
    while (XtMakeResizeRequest((Widget)w, width, height, &width, &height)
	   == XtGeometryAlmost) ;

    // relayout the widget
    if (cls->treeClass.layout) {
      (*cls->treeClass.layout)((Widget)w, NULL);
    } else {
      layout((Widget)w, NULL);
    }
  }

  return relayout;
}

static void insertChildOnList(XPDFTreeEntry *e, XPDFTreeEntry **listHead) {
  int pos;
  XPDFTreeEntry *e2;

  pos = XPDFTreeCPart(e->widget)->entryPosition;
  if (!*listHead || pos < XPDFTreeCPart((*listHead)->widget)->entryPosition) {
    e->next = *listHead;
    *listHead = e;
  } else {
    for (e2 = *listHead;
	 e2->next && pos >= XPDFTreeCPart(e2->next->widget)->entryPosition;
	 e2 = e2->next) ;
    e->next = e2->next;
    e2->next = e;
  }
}

static void deleteChildFromList(XPDFTreeEntry *e, XPDFTreeEntry **listHead) {
  XPDFTreeEntry **p;

  for (p = listHead; *p; p = &(*p)->next) {
    if (*p == e) {
      *p = e->next;
      e->next = NULL;
      return;
    }
  }
}

static void createGC(Widget widget) {
  XPDFTreeWidget w = (XPDFTreeWidget)widget;
  XGCValues gcValues;

  gcValues.foreground = w->manager.foreground;
  gcValues.line_width = 0;
  gcValues.line_style = LineSolid;
  w->tree.plainGC = XtGetGC(widget,
			    GCForeground | GCLineWidth | GCLineStyle,
			    &gcValues);

  gcValues.line_style = LineOnOffDash;
  gcValues.dashes = 1;
  gcValues.dash_offset = 0;
  w->tree.dottedGC = XtGetGC(widget,
			     GCForeground | GCLineWidth | GCLineStyle |
			         GCDashList | GCDashOffset,
			     &gcValues);
}

static void destroyGC(Widget widget) {
  XPDFTreeWidget w = (XPDFTreeWidget)widget;

  XtReleaseGC(widget, w->tree.plainGC);
  XtReleaseGC(widget, w->tree.dottedGC);
}

static void layout(Widget widget, Widget instigator) {
  XPDFTreeWidget w = (XPDFTreeWidget)widget;
  XPDFTreeEntry *e;
  Position x, y;

  x = w->tree.marginWidth + xpdfTreeIndent;
  y = w->tree.marginHeight;
  for (e = w->tree.root; e; e = e->next) {
    y = layoutSubtree(w, instigator, e, x, y, True);
  }
}

static int layoutSubtree(XPDFTreeWidget w, Widget instigator,
			 XPDFTreeEntry *e, Position x, Position y,
			 Boolean visible) {
  Widget ew;
  XPDFTreeEntry *child;
  XPDFTreeConstraint c;

  ew = e->widget;
  if (!XtIsManaged(ew)) {
    return y;
  }
  c = XPDFTreeCPart(ew);

  // place this entry
  if (ew) {
    if (visible) {
      if (ew == instigator) {
	ew->core.x = x;
	ew->core.y = y;
      } else {
#if XmVERSION > 1
	XmeConfigureObject(ew, x, y, ew->core.width, ew->core.height,
			   ew->core.border_width);
#else
	_XmConfigureObject(ew, x, y, ew->core.width, ew->core.height,
			   ew->core.border_width);
#endif
      }
      y += ew->core.height + 2 * ew->core.border_width;
    }
  }

  // place this entry's children
  x += xpdfTreeIndent;
  for (child = e->children; child; child = child->next) {
    y = layoutSubtree(w, instigator, child, x, y,
		      visible && (!c || c->entryExpanded));
  }

  return y;
}

static void calcSize(Widget widget, Widget instigator,
		     Dimension *totalWidth,
		     Dimension *totalHeight) {
  XPDFTreeWidget w = (XPDFTreeWidget)widget;
  XPDFTreeEntry *e;
  Dimension w1, h1, w2, h2;

  w1 = h1 = 0;
  for (e = w->tree.root; e; e = e->next) {
    calcSubtreeSize(w, instigator, e, &w2, &h2);
    if (w2 > w1) {
      w1 = w2;
    }
    h1 += h2;
  }
  w1 += xpdfTreeIndent + 2 * w->tree.marginWidth;
  h1 += 2 * w->tree.marginHeight;
  if (h1 == 0) {
    h1 = 1;
  }
  if (!*totalWidth) {
    *totalWidth = w1;
  }
  if (!*totalHeight) {
    *totalHeight = h1;
  }
}

static void calcSubtreeSize(XPDFTreeWidget w, Widget instigator,
			    XPDFTreeEntry *e,
			    Dimension *width, Dimension *height) {
  Widget ew;
  XPDFTreeEntry *child;
  XPDFTreeConstraint c;
  XtWidgetGeometry geom;
  Dimension w1, h1, w2, h2;
  
  ew = e->widget;
  if (!XtIsManaged(ew)) {
    *width = *height = 0;
    return;
  }
  c = XPDFTreeCPart(ew);

  // get size of this entry
  if (ew) {
    if (!XtIsManaged(ew)) {
      *width = *height = 0;
      return;
    }
    if (ew == instigator) {
      w1 = ew->core.width;
      h1 = ew->core.height;
    } else {
      XtQueryGeometry(ew, NULL, &geom);
      w1 = (geom.request_mode & CWWidth) ? geom.width : ew->core.width;
      h1 = (geom.request_mode & CWHeight) ? geom.height : ew->core.height;
    }
    h1 += 2 * ew->core.border_width;
  } else {
    // root of tree
    w1 = 0;
    h1 = 0;
  }

  // if this entry is expanded, get size of all of its children
  if (c->entryExpanded) {
    for (child = e->children; child; child = child->next) {
      calcSubtreeSize(w, instigator, child, &w2, &h2);
      w2 += xpdfTreeIndent;
      if (w2 > w1) {
	w1 = w2;
      }
      h1 += h2;
    }
  }

  *width = w1;
  *height = h1;
}

static Boolean needRelayout(Widget oldWidget, Widget newWidget) {
  XPDFTreeWidget ow = (XPDFTreeWidget)oldWidget;
  XPDFTreeWidget nw = (XPDFTreeWidget)newWidget;

  if (nw->tree.marginWidth != ow->tree.marginWidth ||
      nw->tree.marginHeight != ow->tree.marginHeight) {
    return True;
  }
  return False;
}

static void click(Widget widget, XEvent *event,
		  String *params, Cardinal *numParams) {
  XPDFTreeWidget w = (XPDFTreeWidget)widget;
  XButtonPressedEvent *bpe;
  XPDFTreeEntry *e;
  Boolean onExpandIcon;
  XPDFTreeConstraint c;
  XPDFTreeSelectCallbackStruct cbs;

  if (event->type != ButtonPress) {
    return;
  }
  bpe = (XButtonPressedEvent *)event;
  if (findPosition(w, bpe->x, bpe->y, &e, &onExpandIcon)) {
    if (onExpandIcon) {
      c = XPDFTreeCPart(e->widget);
      w->tree.redrawY = e->widget->core.y;
      XtVaSetValues(e->widget, XPDFNentryExpanded, !c->entryExpanded, NULL);
    } else {
      XmProcessTraversal(e->widget, XmTRAVERSE_CURRENT);
      XtCallActionProc(widget, "ManagerGadgetActivate", event, NULL, 0);
      cbs.reason = XmCR_ACTIVATE;
      cbs.event = event;
      cbs.selectedItem = e->widget;
      XtCallCallbackList(widget, w->tree.selectCallback, &cbs);
    }
  }
}

static Boolean findPosition(XPDFTreeWidget w, int x, int y,
			    XPDFTreeEntry **e, Boolean *onExpandIcon) {
  XPDFTreeEntry *e2;

  for (e2 = w->tree.root; e2; e2 = e2->next) {
    *e = e2;
    if (findPositionInSubtree(w, x, y, e, onExpandIcon)) {
      return True;
    }
  }
  return False;
}

// If (x,y) falls on either an expand/collapse icon or a label gadget,
// set *<e> and *<onExpandIcon> and return true.
static Boolean findPositionInSubtree(XPDFTreeWidget w, int x, int y,
				     XPDFTreeEntry **e,
				     Boolean *onExpandIcon) {
  Widget child;
  XPDFTreeConstraint c;
  XPDFTreeEntry *e2;
  int y1;

  child = (*e)->widget;
  y1 = child->core.y + child->core.height / 2;
  if (x >= child->core.x && x < child->core.x + child->core.width &&
      y >= child->core.y && y < child->core.y + child->core.height) {
    *onExpandIcon = False;
    return True;
  } else if (x >= child->core.x - 16 && x < child->core.x - 4 &&
	     y >= y1 - 6 && y < y1 + 6 &&
	     (*e)->children) {
    *onExpandIcon = True;
    return True;
  }
  c = XPDFTreeCPart(child);
  if (!c || c->entryExpanded) {
    for (e2 = (*e)->children; e2; e2 = e2->next) {
      *e = e2;
      if (findPositionInSubtree(w, x, y, e, onExpandIcon)) {
	return True;
      }
    }
  }
  return False;
}

Widget XPDFCreateTree(Widget parent, char *name,
		      ArgList argList, Cardinal numArgs) {
  return XtCreateWidget(name, xpdfTreeWidgetClass, parent, argList, numArgs);
}
//========================================================================
//
// XPDFTree.h
//
// Copyright 2002-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef XPDFTREE_H
#define XPDFTREE_H

#include <Xm/Xm.h>

extern "C" {

externalref WidgetClass xpdfTreeWidgetClass;

typedef struct _XPDFTreeClassRec *XPDFTreeWidgetClass;
typedef struct _XPDFTreeRec      *XPDFTreeWidget;

#ifndef XPDFIsTree
#define XPDFIsTree(w) XtIsSubclass(w, xpdfTreeWidgetClass)
#endif

#define XPDFNentryParent       "entryParent"
#define XPDFNentryExpanded     "entryExpanded"
#define XPDFNentryPosition     "entryPosition"
#define XPDFNselectionCallback "selectionCallback"

#define XPDFCentryParent       "EntryParent"
#define XPDFCentryExpanded     "EntryExpanded"
#define XPDFCentryPosition     "EntryPosition"

typedef struct {
  int reason;
  XEvent *event;
  Widget selectedItem;
} XPDFTreeSelectCallbackStruct;

extern Widget XPDFCreateTree(Widget parent, char *name,
			     ArgList argList, Cardinal argCount);

} // extern "C"

#endif
//========================================================================
//
// XPDFTreeP.h
//
// Copyright 2002-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef XPDFTREEP_H
#define XPDFTREEP_H

#include <Xm/ManagerP.h>
#include "XPDFTree.h"

extern "C" {

typedef void (*XPDFLayoutProc)(Widget widget, Widget instigator);
typedef void (*XPDFCalcSizeProc)(Widget widget, Widget instigator,
				 Dimension *totalWidth,
				 Dimension *totalHeight);
typedef Boolean (*XPDFNeedRelayoutProc)(Widget oldWidget, Widget newWidget);

#define XPDFInheritCreateGC ((XtWidgetProc)_XtInherit)
#define XPDFInheritDestroyGC ((XtWidgetProc)_XtInherit)
#define XPDFInheritLayout ((XPDFLayoutProc)_XtInherit)
#define XPDFInheritCalcSize ((XPDFCalcSizeProc)_XtInherit)
#define XPDFInheritNeedRelayout ((XPDFNeedRelayoutProc)_XtInherit)

typedef struct {
  XtWidgetProc createGC;
  XtWidgetProc destroyGC;
  XPDFLayoutProc layout;
  XPDFCalcSizeProc calcSize;
  XPDFNeedRelayoutProc needRelayout;
  XtPointer extension;
} XPDFTreeClassPart;

typedef struct _XPDFTreeClassRec {
  CoreClassPart coreClass;
  CompositeClassPart compositeClass;
  ConstraintClassPart constraintClass;
  XmManagerClassPart managerClass;
  XPDFTreeClassPart treeClass;
} XPDFTreeClassRec;

externalref XPDFTreeClassRec xpdfTreeClassRec;

typedef struct _XPDFTreeEntry XPDFTreeEntry;

typedef struct {
  Dimension marginWidth;
  Dimension marginHeight;
  XtCallbackList selectCallback;
  GC plainGC;
  GC dottedGC;
  XPDFTreeEntry *root;
  int redrawY;
} XPDFTreePart;

typedef struct _XPDFTreeRec {
  CorePart core;
  CompositePart composite;
  ConstraintPart constraint;
  XmManagerPart manager;
  XPDFTreePart tree;
} XPDFTreeRec;

#define XPDFTreeIndex (XmManagerIndex + 1)

typedef struct _XPDFTreeConstraintPart {
  Widget entryParent;
  Boolean entryExpanded;
  int entryPosition;
  XPDFTreeEntry *e;
} XPDFTreeConstraintPart, *XPDFTreeConstraint;

typedef struct _XPDFTreeConstraintRec {
  XmManagerConstraintPart manager;
  XPDFTreeConstraintPart tree;
} XPDFTreeConstraintRec, *XPDFTreeConstraintPtr;

#define XPDFTreeCPart(w) \
  (&((XPDFTreeConstraintPtr)(w)->core.constraints)->tree)

} // extern "C"

#endif
//========================================================================
//
// XPDFViewer.cc
//
// Copyright 2002-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <X11/keysym.h>
#include <X11/cursorfont.h>
#ifdef HAVE_X11_XPM_H
#include <X11/xpm.h>
#endif
#if defined(__sgi) && (XmVERSION <= 1)
#define Object XtObject
#include <Sgm/HPanedW.h>
#undef Object
#endif
#include "gmem.h"
#include "gfile.h"
#include "GString.h"
#include "GList.h"
#include "Error.h"
#include "GlobalParams.h"
#include "PDFDoc.h"
#include "Link.h"
#include "ErrorCodes.h"
#include "Outline.h"
#include "UnicodeMap.h"
#ifndef DISABLE_OUTLINE
#define Object XtObject
#include "XPDFTree.h"
#undef Object
#endif
#include "XPDFApp.h"
#include "XPDFViewer.h"
#include "PSOutputDev.h"
#include "config.h"

// these macro defns conflict with xpdf's Object class
#ifdef LESSTIF_VERSION
#undef XtDisplay
#undef XtScreen
#undef XtWindow
#undef XtParent
#undef XtIsRealized
#endif

#if XmVERSION <= 1
#define XmSET   True
#define XmUNSET False
#endif

// hack around old X includes which are missing these symbols
#ifndef XK_Page_Up
#define XK_Page_Up              0xFF55
#endif
#ifndef XK_Page_Down
#define XK_Page_Down            0xFF56
#endif
#ifndef XK_KP_Home
#define XK_KP_Home              0xFF95
#endif
#ifndef XK_KP_Left
#define XK_KP_Left              0xFF96
#endif
#ifndef XK_KP_Up
#define XK_KP_Up                0xFF97
#endif
#ifndef XK_KP_Right
#define XK_KP_Right             0xFF98
#endif
#ifndef XK_KP_Down
#define XK_KP_Down              0xFF99
#endif
#ifndef XK_KP_Prior
#define XK_KP_Prior             0xFF9A
#endif
#ifndef XK_KP_Page_Up
#define XK_KP_Page_Up           0xFF9A
#endif
#ifndef XK_KP_Next
#define XK_KP_Next              0xFF9B
#endif
#ifndef XK_KP_Page_Down
#define XK_KP_Page_Down         0xFF9B
#endif
#ifndef XK_KP_End
#define XK_KP_End               0xFF9C
#endif
#ifndef XK_KP_Begin
#define XK_KP_Begin             0xFF9D
#endif
#ifndef XK_KP_Insert
#define XK_KP_Insert            0xFF9E
#endif
#ifndef XK_KP_Delete
#define XK_KP_Delete            0xFF9F
#endif

//------------------------------------------------------------------------
// GUI includes
//------------------------------------------------------------------------

#include "xpdfIcon.xpm"
#include "leftArrow.xbm"
#include "leftArrowDis.xbm"
#include "dblLeftArrow.xbm"
#include "dblLeftArrowDis.xbm"
#include "rightArrow.xbm"
#include "rightArrowDis.xbm"
#include "dblRightArrow.xbm"
#include "dblRightArrowDis.xbm"
#include "backArrow.xbm"
#include "backArrowDis.xbm"
#include "forwardArrow.xbm"
#include "forwardArrowDis.xbm"
#include "find.xbm"
#include "findDis.xbm"
#include "print.xbm"
#include "printDis.xbm"
#include "about.xbm"
#include "about-text.h"

//------------------------------------------------------------------------

struct ZoomMenuInfo {
  char *label;
  double zoom;
};

static ZoomMenuInfo zoomMenuInfo[nZoomMenuItems] = {
  { "400%",      400 },
  { "200%",      200 },
  { "150%",      150 },
  { "125%",      125 },
  { "100%",      100 },
  { "50%",        50 },
  { "25%",        25 },
  { "12.5%",      12.5 },
  { "fit page",  zoomPage },
  { "fit width", zoomWidth }
};

#define maxZoomIdx   0
#define defZoomIdx   3
#define minZoomIdx   7
#define zoomPageIdx  8
#define zoomWidthIdx 9

//------------------------------------------------------------------------

#define cmdMaxArgs 2

XPDFViewerCmd XPDFViewer::cmdTab[] = {
  { "about",                   0, gFalse, gFalse, &XPDFViewer::cmdAbout },
  { "closeOutline",            0, gFalse, gFalse, &XPDFViewer::cmdCloseOutline },
  { "closeWindow",             0, gFalse, gFalse, &XPDFViewer::cmdCloseWindow },
  { "continuousMode",          0, gFalse, gFalse, &XPDFViewer::cmdContinuousMode },
  { "endPan",                  0, gTrue,  gTrue,  &XPDFViewer::cmdEndPan },
  { "endSelection",            0, gTrue,  gTrue,  &XPDFViewer::cmdEndSelection },
  { "find",                    0, gTrue,  gFalse, &XPDFViewer::cmdFind },
  { "findNext",                0, gTrue,  gFalse, &XPDFViewer::cmdFindNext },
  { "focusToDocWin",           0, gFalse, gFalse, &XPDFViewer::cmdFocusToDocWin },
  { "focusToPageNum",          0, gFalse, gFalse, &XPDFViewer::cmdFocusToPageNum },
  { "followLink",              0, gTrue,  gTrue,  &XPDFViewer::cmdFollowLink },
  { "followLinkInNewWin",      0, gTrue,  gTrue,  &XPDFViewer::cmdFollowLinkInNewWin },
  { "followLinkInNewWinNoSel", 0, gTrue,  gTrue,  &XPDFViewer::cmdFollowLinkInNewWinNoSel },
  { "followLinkNoSel",         0, gTrue,  gTrue,  &XPDFViewer::cmdFollowLinkNoSel },
  { "fullScreenMode",          0, gFalse, gFalse, &XPDFViewer::cmdFullScreenMode },
  { "goBackward",              0, gFalse, gFalse, &XPDFViewer::cmdGoBackward },
  { "goForward",               0, gFalse, gFalse, &XPDFViewer::cmdGoForward },
  { "gotoDest",                1, gTrue,  gFalse, &XPDFViewer::cmdGotoDest },
  { "gotoLastPage",            0, gTrue,  gFalse, &XPDFViewer::cmdGotoLastPage },
  { "gotoLastPageNoScroll",    0, gTrue,  gFalse, &XPDFViewer::cmdGotoLastPageNoScroll },
  { "gotoPage",                1, gTrue,  gFalse, &XPDFViewer::cmdGotoPage },
  { "gotoPageNoScroll",        1, gTrue,  gFalse, &XPDFViewer::cmdGotoPageNoScroll },
  { "nextPage",                0, gTrue,  gFalse, &XPDFViewer::cmdNextPage },
  { "nextPageNoScroll",        0, gTrue,  gFalse, &XPDFViewer::cmdNextPageNoScroll },
  { "open",                    0, gFalse, gFalse, &XPDFViewer::cmdOpen },
  { "openFile",                1, gFalse, gFalse, &XPDFViewer::cmdOpenFile },
  { "openFileAtDest",          2, gFalse, gFalse, &XPDFViewer::cmdOpenFileAtDest },
  { "openFileAtDestInNewWin",  2, gFalse, gFalse, &XPDFViewer::cmdOpenFileAtDestInNewWin },
  { "openFileAtPage",          2, gFalse, gFalse, &XPDFViewer::cmdOpenFileAtPage },
  { "openFileAtPageInNewWin",  2, gFalse, gFalse, &XPDFViewer::cmdOpenFileAtPageInNewWin },
  { "openFileInNewWin",        1, gFalse, gFalse, &XPDFViewer::cmdOpenFileInNewWin },
  { "openInNewWin",            0, gFalse, gFalse, &XPDFViewer::cmdOpenInNewWin },
  { "openOutline",             0, gFalse, gFalse, &XPDFViewer::cmdOpenOutline },
  { "pageDown",                0, gTrue,  gFalse, &XPDFViewer::cmdPageDown },
  { "pageUp",                  0, gTrue,  gFalse, &XPDFViewer::cmdPageUp },
  { "postPopupMenu",           0, gFalse, gTrue,  &XPDFViewer::cmdPostPopupMenu },
  { "prevPage",                0, gTrue,  gFalse, &XPDFViewer::cmdPrevPage },
  { "prevPageNoScroll",        0, gTrue,  gFalse, &XPDFViewer::cmdPrevPageNoScroll },
  { "print",                   0, gTrue,  gFalse, &XPDFViewer::cmdPrint },
  { "quit",                    0, gFalse, gFalse, &XPDFViewer::cmdQuit },
  { "raise",                   0, gFalse, gFalse, &XPDFViewer::cmdRaise },
  { "redraw",                  0, gTrue,  gFalse, &XPDFViewer::cmdRedraw },
  { "reload",                  0, gTrue,  gFalse, &XPDFViewer::cmdReload },
  { "run",                     1, gFalse, gFalse, &XPDFViewer::cmdRun },
  { "scrollDown",              1, gTrue,  gFalse, &XPDFViewer::cmdScrollDown },
  { "scrollDownNextPage",      1, gTrue,  gFalse, &XPDFViewer::cmdScrollDownNextPage },
  { "scrollLeft",              1, gTrue,  gFalse, &XPDFViewer::cmdScrollLeft },
  { "scrollOutlineDown",       1, gTrue,  gFalse, &XPDFViewer::cmdScrollOutlineDown },
  { "scrollOutlineUp",         1, gTrue,  gFalse, &XPDFViewer::cmdScrollOutlineUp },
  { "scrollRight",             1, gTrue,  gFalse, &XPDFViewer::cmdScrollRight },
  { "scrollToBottomEdge",      0, gTrue,  gFalse, &XPDFViewer::cmdScrollToBottomEdge },
  { "scrollToBottomRight",     0, gTrue,  gFalse, &XPDFViewer::cmdScrollToBottomRight },
  { "scrollToLeftEdge",        0, gTrue,  gFalse, &XPDFViewer::cmdScrollToLeftEdge },
  { "scrollToRightEdge",       0, gTrue,  gFalse, &XPDFViewer::cmdScrollToRightEdge },
  { "scrollToTopEdge",         0, gTrue,  gFalse, &XPDFViewer::cmdScrollToTopEdge },
  { "scrollToTopLeft",         0, gTrue,  gFalse, &XPDFViewer::cmdScrollToTopLeft },
  { "scrollUp",                1, gTrue,  gFalse, &XPDFViewer::cmdScrollUp },
  { "scrollUpPrevPage",        1, gTrue,  gFalse, &XPDFViewer::cmdScrollUpPrevPage },
  { "singlePageMode",          0, gFalse, gFalse, &XPDFViewer::cmdSinglePageMode },
  { "startPan",                0, gTrue,  gTrue,  &XPDFViewer::cmdStartPan },
  { "startSelection",          0, gTrue,  gTrue,  &XPDFViewer::cmdStartSelection },
  { "toggleContinuousMode",    0, gFalse, gFalse, &XPDFViewer::cmdToggleContinuousMode },
  { "toggleFullScreenMode",    0, gFalse, gFalse, &XPDFViewer::cmdToggleFullScreenMode },
  { "toggleOutline",           0, gFalse, gFalse, &XPDFViewer::cmdToggleOutline },
  { "windowMode",              0, gFalse, gFalse, &XPDFViewer::cmdWindowMode },
  { "zoomFitPage",             0, gFalse, gFalse, &XPDFViewer::cmdZoomFitPage },
  { "zoomFitWidth",            0, gFalse, gFalse, &XPDFViewer::cmdZoomFitWidth },
  { "zoomIn",                  0, gFalse, gFalse, &XPDFViewer::cmdZoomIn },
  { "zoomOut",                 0, gFalse, gFalse, &XPDFViewer::cmdZoomOut },
  { "zoomPercent",             1, gFalse, gFalse, &XPDFViewer::cmdZoomPercent },
  { "zoomToSelection",         0, gTrue,  gFalse, &XPDFViewer::cmdZoomToSelection }
};

#define nCmds (sizeof(cmdTab) / sizeof(XPDFViewerCmd))

//------------------------------------------------------------------------

XPDFViewer::XPDFViewer(XPDFApp *appA, GString *fileName,
		       int pageA, GString *destName, GBool fullScreen,
		       GString *ownerPassword, GString *userPassword) {
  LinkDest *dest;
  int pg;
  double z;

  app = appA;
  win = NULL;
  core = NULL;
  ok = gFalse;
#ifndef DISABLE_OUTLINE
  outlineLabels = NULL;
  outlineLabelsLength = outlineLabelsSize = 0;
  outlinePaneWidth = 175;
#endif

  // do Motif-specific initialization and create the window;
  // this also creates the core object
  initWindow(fullScreen);
  initAboutDialog();
  initFindDialog();
  initPrintDialog();
  openDialog = NULL;
  saveAsDialog = NULL;

  dest = NULL; // make gcc happy
  pg = pageA; // make gcc happy

  if (fileName) {
    if (loadFile(fileName, ownerPassword, userPassword)) {
      getPageAndDest(pageA, destName, &pg, &dest);
#ifndef DISABLE_OUTLINE
      if (outlineScroll != None &&
	  core->getDoc()->getOutline()->getItems() &&
	  core->getDoc()->getOutline()->getItems()->getLength() > 0) {
	XtVaSetValues(outlineScroll, XmNwidth, outlinePaneWidth, NULL);
      }
#endif
    } else {
      return;
    }
  }
  core->resizeToPage(pg);

  // map the window -- we do this after calling resizeToPage to avoid
  // an annoying on-screen resize
  mapWindow();

  // display the first page
  z = core->getZoom();
  if (dest) {
    displayDest(dest, z, core->getRotate(), gTrue);
    delete dest;
  } else {
    displayPage(pg, z, core->getRotate(), gTrue, gTrue);
  }

  ok = gTrue;
}

XPDFViewer::XPDFViewer(XPDFApp *appA, PDFDoc *doc, int pageA,
		       GString *destName, GBool fullScreen) {
  LinkDest *dest;
  int pg;
  double z;

  app = appA;
  win = NULL;
  core = NULL;
  ok = gFalse;
#ifndef DISABLE_OUTLINE
  outlineLabels = NULL;
  outlineLabelsLength = outlineLabelsSize = 0;
  outlinePaneWidth = 175;
#endif

  // do Motif-specific initialization and create the window;
  // this also creates the core object
  initWindow(fullScreen);
  initAboutDialog();
  initFindDialog();
  initPrintDialog();
  openDialog = NULL;
  saveAsDialog = NULL;

  dest = NULL; // make gcc happy
  pg = pageA; // make gcc happy

  if (doc) {
    core->loadDoc(doc);
    getPageAndDest(pageA, destName, &pg, &dest);
#ifndef DISABLE_OUTLINE
    if (outlineScroll != None &&
	core->getDoc()->getOutline()->getItems() &&
	core->getDoc()->getOutline()->getItems()->getLength() > 0) {
      XtVaSetValues(outlineScroll, XmNwidth, outlinePaneWidth, NULL);
    }
#endif
  }
  core->resizeToPage(pg);

  // map the window -- we do this after calling resizeToPage to avoid
  // an annoying on-screen resize
  mapWindow();

  // display the first page
  z = core->getZoom();
  if (dest) {
    displayDest(dest, z, core->getRotate(), gTrue);
    delete dest;
  } else {
    displayPage(pg, z, core->getRotate(), gTrue, gTrue);
  }

  ok = gTrue;
}

XPDFViewer::~XPDFViewer() {
  delete core;
  XmFontListFree(aboutBigFont);
  XmFontListFree(aboutVersionFont);
  XmFontListFree(aboutFixedFont);
  closeWindow();
#ifndef DISABLE_OUTLINE
  if (outlineLabels) {
    gfree(outlineLabels);
  }
#endif
}

void XPDFViewer::open(GString *fileName, int pageA, GString *destName) {
  LinkDest *dest;
  int pg;
  double z;

  if (!core->getDoc() || fileName->cmp(core->getDoc()->getFileName())) {
    if (!loadFile(fileName, NULL, NULL)) {
      return;
    }
  }
  getPageAndDest(pageA, destName, &pg, &dest);
  z = core->getZoom();
  if (dest) {
    displayDest(dest, z, core->getRotate(), gTrue);
    delete dest;
  } else {
    displayPage(pg, z, core->getRotate(), gTrue, gTrue);
  }
}

void XPDFViewer::clear() {
  char *title;
  XmString s;

  core->clear();

  // set up title, number-of-pages display
  title = app->getTitle() ? app->getTitle()->getCString()
                          : (char *)xpdfAppName;
  XtVaSetValues(win, XmNtitle, title, XmNiconName, title, NULL);
  s = XmStringCreateLocalized("");
  XtVaSetValues(pageNumText, XmNlabelString, s, NULL);
  XmStringFree(s);
  s = XmStringCreateLocalized(" of 0");
  XtVaSetValues(pageCountLabel, XmNlabelString, s, NULL);
  XmStringFree(s);

  // disable buttons
  XtVaSetValues(prevTenPageBtn, XmNsensitive, False, NULL);
  XtVaSetValues(prevPageBtn, XmNsensitive, False, NULL);
  XtVaSetValues(nextTenPageBtn, XmNsensitive, False, NULL);
  XtVaSetValues(nextPageBtn, XmNsensitive, False, NULL);

  // remove the old outline
#ifndef DISABLE_OUTLINE
  setupOutline();
#endif
}

//------------------------------------------------------------------------
// load / display
//------------------------------------------------------------------------

GBool XPDFViewer::loadFile(GString *fileName, GString *ownerPassword,
			   GString *userPassword) {
  return core->loadFile(fileName, ownerPassword, userPassword) == errNone;
}

void XPDFViewer::reloadFile() {
  int pg;

  if (!core->getDoc()) {
    return;
  }
  pg = core->getPageNum();
  loadFile(core->getDoc()->getFileName());
  if (pg > core->getDoc()->getNumPages()) {
    pg = core->getDoc()->getNumPages();
  }
  displayPage(pg, core->getZoom(), core->getRotate(), gFalse, gFalse);
}

void XPDFViewer::displayPage(int pageA, double zoomA, int rotateA,
			     GBool scrollToTop, GBool addToHist) {
  core->displayPage(pageA, zoomA, rotateA, scrollToTop, addToHist);
}

void XPDFViewer::displayDest(LinkDest *dest, double zoomA, int rotateA,
			     GBool addToHist) {
  core->displayDest(dest, zoomA, rotateA, addToHist);
}

void XPDFViewer::getPageAndDest(int pageA, GString *destName,
				int *pageOut, LinkDest **destOut) {
  Ref pageRef;

  // find the page number for a named destination
  *pageOut = pageA;
  *destOut = NULL;
  if (destName && (*destOut = core->getDoc()->findDest(destName))) {
    if ((*destOut)->isPageRef()) {
      pageRef = (*destOut)->getPageRef();
      *pageOut = core->getDoc()->findPage(pageRef.num, pageRef.gen);
    } else {
      *pageOut = (*destOut)->getPageNum();
    }
  }

  if (*pageOut <= 0) {
    *pageOut = 1;
  }
  if (*pageOut > core->getDoc()->getNumPages()) {
    *pageOut = core->getDoc()->getNumPages();
  }
}

//------------------------------------------------------------------------
// hyperlinks / actions
//------------------------------------------------------------------------

void XPDFViewer::doLink(int wx, int wy, GBool onlyIfNoSelection,
			GBool newWin) {
  XPDFViewer *newViewer;
  LinkAction *action;
  int pg, selPg;
  double xu, yu, selULX, selULY, selLRX, selLRY;

  if (core->getHyperlinksEnabled() &&
      core->cvtWindowToUser(wx, wy, &pg, &xu, &yu) &&
      !(onlyIfNoSelection &&
	core->getSelection(&selPg, &selULX, &selULY, &selLRX, &selLRY))) {
    if ((action = core->findLink(pg, xu, yu))) {
      if (newWin &&
	  core->getDoc()->getFileName() &&
	  (action->getKind() == actionGoTo ||
	   action->getKind() == actionGoToR ||
	   (action->getKind() == actionNamed &&
	    ((LinkNamed *)action)->getName()->cmp("Quit")))) {
	newViewer = app->open(core->getDoc()->getFileName());
	newViewer->core->doAction(action);
      } else {
	core->doAction(action);
      }
    }
  }
}

void XPDFViewer::actionCbk(void *data, char *action) {
  XPDFViewer *viewer = (XPDFViewer *)data;

  if (!strcmp(action, "Quit")) {
    viewer->app->quit();
  }
}

//------------------------------------------------------------------------
// keyboard/mouse input
//------------------------------------------------------------------------

void XPDFViewer::keyPressCbk(void *data, KeySym key, Guint modifiers,
			     XEvent *event) {
  XPDFViewer *viewer = (XPDFViewer *)data;
  int keyCode;
  GList *cmds;
  int i;

  if (key >= 0x20 && key <= 0xfe) {
    keyCode = (int)key;
  } else if (key == XK_Tab ||
	     key == XK_KP_Tab) {
    keyCode = xpdfKeyCodeTab;
  } else if (key == XK_Return) {
    keyCode = xpdfKeyCodeReturn;
  } else if (key == XK_KP_Enter) {
    keyCode = xpdfKeyCodeEnter;
  } else if (key == XK_BackSpace) {
    keyCode = xpdfKeyCodeBackspace;
  } else if (key == XK_Insert ||
	     key == XK_KP_Insert) {
    keyCode = xpdfKeyCodeInsert;
  } else if (key == XK_Delete ||
	     key == XK_KP_Delete) {
    keyCode = xpdfKeyCodeDelete;
  } else if (key == XK_Home ||
	     key == XK_KP_Home) {
    keyCode = xpdfKeyCodeHome;
  } else if (key == XK_End ||
	     key == XK_KP_End) {
    keyCode = xpdfKeyCodeEnd;
  } else if (key == XK_Page_Up ||
	     key == XK_KP_Page_Up) {
    keyCode = xpdfKeyCodePgUp;
  } else if (key == XK_Page_Down ||
	     key == XK_KP_Page_Down) {
    keyCode = xpdfKeyCodePgDn;
  } else if (key == XK_Left ||
	     key == XK_KP_Left) {
    keyCode = xpdfKeyCodeLeft;
  } else if (key == XK_Right ||
	     key == XK_KP_Right) {
    keyCode = xpdfKeyCodeRight;
  } else if (key == XK_Up ||
	     key == XK_KP_Up) {
    keyCode = xpdfKeyCodeUp;
  } else if (key == XK_Down ||
	     key == XK_KP_Down) {
    keyCode = xpdfKeyCodeDown;
  } else if (key >= XK_F1 && key <= XK_F35) {
    keyCode = xpdfKeyCodeF1 + (key - XK_F1);
  } else {
    return;
  }

  if ((cmds = globalParams->getKeyBinding(keyCode,
					  viewer->getModifiers(modifiers),
					  viewer->getContext(modifiers)))) {
    for (i = 0; i < cmds->getLength(); ++i) {
      viewer->execCmd((GString *)cmds->get(i), event);
    }
    deleteGList(cmds, GString);
  }
}

void XPDFViewer::mouseCbk(void *data, XEvent *event) {
  XPDFViewer *viewer = (XPDFViewer *)data;
  int keyCode;
  GList *cmds;
  int i;

  if (event->type == ButtonPress) {
    if (event->xbutton.button >= 1 && event->xbutton.button <= 7) {
      keyCode = xpdfKeyCodeMousePress1 + event->xbutton.button - 1;
    } else {
      return;
    }
  } else if (event->type == ButtonRelease) {
    if (event->xbutton.button >= 1 && event->xbutton.button <= 7) {
      keyCode = xpdfKeyCodeMouseRelease1 + event->xbutton.button - 1;
    } else {
      return;
    }
  } else {
    return;
  }

  if ((cmds = globalParams->getKeyBinding(keyCode,
					  viewer->getModifiers(
						      event->xkey.state),
					  viewer->getContext(
						      event->xkey.state)))) {
    for (i = 0; i < cmds->getLength(); ++i) {
      viewer->execCmd((GString *)cmds->get(i), event);
    }
    deleteGList(cmds, GString);
  }
}

int XPDFViewer::getModifiers(Guint modifiers) {
  int mods;

  mods = 0;
  if (modifiers & ShiftMask) {
    mods |= xpdfKeyModShift;
  }
  if (modifiers & ControlMask) {
    mods |= xpdfKeyModCtrl;
  }
  if (modifiers & Mod1Mask) {
    mods |= xpdfKeyModAlt;
  }
  return mods;
}

int XPDFViewer::getContext(Guint modifiers) {
  int context;

  context = (core->getFullScreen() ? xpdfKeyContextFullScreen
                                   : xpdfKeyContextWindow) |
            (core->getContinuousMode() ? xpdfKeyContextContinuous
                                       : xpdfKeyContextSinglePage) |
            (core->getLinkAction() ? xpdfKeyContextOverLink
                                   : xpdfKeyContextOffLink) |
            ((modifiers & Mod5Mask) ? xpdfKeyContextScrLockOn
	                            : xpdfKeyContextScrLockOff);
  return context;
}

void XPDFViewer::execCmd(GString *cmd, XEvent *event) {
  GString *name;
  GString *args[cmdMaxArgs];
  char *p0, *p1;
  int nArgs, i;
  int a, b, m, cmp;

  //----- parse the command
  name = NULL;
  nArgs = 0;
  for (i = 0; i < cmdMaxArgs; ++i) {
    args[i] = NULL;
  }
  p0 = cmd->getCString();
  for (p1 = p0; *p1 && isalnum(*p1); ++p1) ;
  if (p1 == p0) {
    goto err1;
  }
  name = new GString(p0, p1 - p0);
  if (*p1 == '(') {
    while (nArgs < cmdMaxArgs) {
      p0 = p1 + 1;
      for (p1 = p0; *p1 && *p1 != ',' && *p1 != ')'; ++p1) ;
      args[nArgs++] = new GString(p0, p1 - p0);
      if (*p1 != ',') {
	break;
      }
    }
    if (*p1 != ')') {
      goto err1;
    }
    ++p1;
  }
  if (*p1) {
    goto err1;
  }
  
  //----- find the command
  a = -1;
  b = nCmds;
  // invariant: cmdTab[a].name < name < cmdTab[b].name
  while (b - a > 1) {
    m = (a + b) / 2;
    cmp = strcmp(cmdTab[m].name, name->getCString());
    if (cmp < 0) {
      a = m;
    } else if (cmp > 0) {
      b = m;
    } else {
      a = b = m;
    }
  }
  if (cmp != 0) {
    goto err1;
  }

  //----- execute the command
  if (nArgs != cmdTab[a].nArgs ||
      (cmdTab[a].requiresEvent && !event)) {
    goto err1;
  }
  if (cmdTab[a].requiresDoc && !core->getDoc()) {
    // don't issue an error message for this -- it happens, e.g., when
    // clicking in a window with no open PDF file
    goto err2;
  }
  (this->*cmdTab[a].func)(args, nArgs, event);

  //----- clean up
  delete name;
  for (i = 0; i < nArgs; ++i) {
    if (args[i]) {
      delete args[i];
    }
  }
  return;

 err1:
  error(-1, "Invalid command syntax: '%s'", cmd->getCString());
 err2:
  if (name) {
    delete name;
  }
  for (i = 0; i < nArgs; ++i) {
    if (args[i]) {
      delete args[i];
    }
  }
}

//------------------------------------------------------------------------
// command functions
//------------------------------------------------------------------------

static int mouseX(XEvent *event) {
  switch (event->type) {
  case ButtonPress:
  case ButtonRelease:
    return event->xbutton.x;
  case KeyPress:
    return event->xkey.x;
  }
  return 0;
}

static int mouseY(XEvent *event) {
  switch (event->type) {
  case ButtonPress:
  case ButtonRelease:
    return event->xbutton.y;
  case KeyPress:
    return event->xkey.y;
  }
  return 0;
}

void XPDFViewer::cmdAbout(GString *args[], int nArgs,
			  XEvent *event) {
  XtManageChild(aboutDialog);
}

void XPDFViewer::cmdCloseOutline(GString *args[], int nArgs,
				 XEvent *event) {
#ifndef DISABLE_OUTLINE
  Dimension w;

  if (outlineScroll == None) {
    return;
  }
  XtVaGetValues(outlineScroll, XmNwidth, &w, NULL);
  if (w > 1) {
    outlinePaneWidth = w;
    // this ugly kludge is apparently the only way to resize the panes
    // within an XmPanedWindow
    XtVaSetValues(outlineScroll, XmNpaneMinimum, 1,
		  XmNpaneMaximum, 1, NULL);
    XtVaSetValues(outlineScroll, XmNpaneMinimum, 1,
		  XmNpaneMaximum, 10000, NULL);
  }
#endif
}

void XPDFViewer::cmdCloseWindow(GString *args[], int nArgs,
				XEvent *event) {
  app->close(this, gFalse);
}

void XPDFViewer::cmdContinuousMode(GString *args[], int nArgs,
				   XEvent *event) {
  Widget btn;

  if (core->getContinuousMode()) {
    return;
  }
  core->setContinuousMode(gTrue);

  btn = XtNameToWidget(popupMenu, "continuousMode");
  XtVaSetValues(btn, XmNset, XmSET, NULL);
}

void XPDFViewer::cmdEndPan(GString *args[], int nArgs,
			   XEvent *event) {
  core->endPan(mouseX(event), mouseY(event));
}

void XPDFViewer::cmdEndSelection(GString *args[], int nArgs,
				 XEvent *event) {
  core->endSelection(mouseX(event), mouseY(event));
}

void XPDFViewer::cmdFind(GString *args[], int nArgs,
			 XEvent *event) {
  mapFindDialog();
}

void XPDFViewer::cmdFindNext(GString *args[], int nArgs,
			     XEvent *event) {
  doFind(gTrue);
}

void XPDFViewer::cmdFocusToDocWin(GString *args[], int nArgs,
				  XEvent *event) {
  core->takeFocus();
}

void XPDFViewer::cmdFocusToPageNum(GString *args[], int nArgs,
				   XEvent *event) {
  XmTextFieldSetSelection(pageNumText, 0,
			  strlen(XmTextFieldGetString(pageNumText)),
			  XtLastTimestampProcessed(display));
  XmProcessTraversal(pageNumText, XmTRAVERSE_CURRENT);
}

void XPDFViewer::cmdFollowLink(GString *args[], int nArgs,
			       XEvent *event) {
  doLink(mouseX(event), mouseY(event), gFalse, gFalse);
}

void XPDFViewer::cmdFollowLinkInNewWin(GString *args[], int nArgs,
				       XEvent *event) {
  doLink(mouseX(event), mouseY(event), gFalse, gTrue);
}

void XPDFViewer::cmdFollowLinkInNewWinNoSel(GString *args[], int nArgs,
					    XEvent *event) {
  doLink(mouseX(event), mouseY(event), gTrue, gTrue);
}

void XPDFViewer::cmdFollowLinkNoSel(GString *args[], int nArgs,
				    XEvent *event) {
  doLink(mouseX(event), mouseY(event), gTrue, gFalse);
}

void XPDFViewer::cmdFullScreenMode(GString *args[], int nArgs,
				   XEvent *event) {
  PDFDoc *doc;
  XPDFViewer *viewer;
  int pg;
  Widget btn;

  if (core->getFullScreen()) {
    return;
  }
  pg = core->getPageNum();
  XtPopdown(win);
  doc = core->takeDoc(gFalse);
  viewer = app->reopen(this, doc, pg, gTrue);

  btn = XtNameToWidget(viewer->popupMenu, "fullScreen");
  XtVaSetValues(btn, XmNset, XmSET, NULL);
}

void XPDFViewer::cmdGoBackward(GString *args[], int nArgs,
			       XEvent *event) {
  core->goBackward();
}

void XPDFViewer::cmdGoForward(GString *args[], int nArgs,
			      XEvent *event) {
  core->goForward();
}

void XPDFViewer::cmdGotoDest(GString *args[], int nArgs,
			     XEvent *event) {
  int pg;
  LinkDest *dest;

  getPageAndDest(1, args[0], &pg, &dest);
  if (dest) {
    displayDest(dest, core->getZoom(), core->getRotate(), gTrue);
    delete dest;
  }
}

void XPDFViewer::cmdGotoLastPage(GString *args[], int nArgs,
				 XEvent *event) {
  displayPage(core->getDoc()->getNumPages(),
	      core->getZoom(), core->getRotate(),
	      gTrue, gTrue);
}

void XPDFViewer::cmdGotoLastPageNoScroll(GString *args[], int nArgs,
					 XEvent *event) {
  displayPage(core->getDoc()->getNumPages(),
	      core->getZoom(), core->getRotate(),
	      gFalse, gTrue);
}

void XPDFViewer::cmdGotoPage(GString *args[], int nArgs,
			     XEvent *event) {
  int pg;

  pg = atoi(args[0]->getCString());
  if (pg < 1 || pg > core->getDoc()->getNumPages()) {
    return;
  }
  displayPage(pg, core->getZoom(), core->getRotate(), gTrue, gTrue);
}

void XPDFViewer::cmdGotoPageNoScroll(GString *args[], int nArgs,
				     XEvent *event) {
  int pg;

  pg = atoi(args[0]->getCString());
  if (pg < 1 || pg > core->getDoc()->getNumPages()) {
    return;
  }
  displayPage(pg, core->getZoom(), core->getRotate(), gFalse, gTrue);
}

void XPDFViewer::cmdNextPage(GString *args[], int nArgs,
			     XEvent *event) {
  core->gotoNextPage(1, gTrue);
}

void XPDFViewer::cmdNextPageNoScroll(GString *args[], int nArgs,
				     XEvent *event) {
  core->gotoNextPage(1, gFalse);
}

void XPDFViewer::cmdOpen(GString *args[], int nArgs,
			 XEvent *event) {
  mapOpenDialog(gFalse);
}

void XPDFViewer::cmdOpenFile(GString *args[], int nArgs,
			     XEvent *event) {
  open(args[0], 1, NULL);
}

void XPDFViewer::cmdOpenFileAtDest(GString *args[], int nArgs,
				   XEvent *event) {
  open(args[0], 1, args[1]);
}

void XPDFViewer::cmdOpenFileAtDestInNewWin(GString *args[], int nArgs,
					   XEvent *event) {
  app->openAtDest(args[0], args[1]);
}

void XPDFViewer::cmdOpenFileAtPage(GString *args[], int nArgs,
				   XEvent *event) {
  open(args[0], atoi(args[1]->getCString()), NULL);
}

void XPDFViewer::cmdOpenFileAtPageInNewWin(GString *args[], int nArgs,
					   XEvent *event) {
  app->open(args[0], atoi(args[1]->getCString()));
}

void XPDFViewer::cmdOpenFileInNewWin(GString *args[], int nArgs,
				     XEvent *event) {
  app->open(args[0]);
}

void XPDFViewer::cmdOpenInNewWin(GString *args[], int nArgs,
				 XEvent *event) {
  mapOpenDialog(gTrue);
}

void XPDFViewer::cmdOpenOutline(GString *args[], int nArgs,
				XEvent *event) {
#ifndef DISABLE_OUTLINE
  Dimension w;

  if (outlineScroll == None) {
    return;
  }
  XtVaGetValues(outlineScroll, XmNwidth, &w, NULL);
  if (w == 1) {
    // this ugly kludge is apparently the only way to resize the panes
    // within an XmPanedWindow
    XtVaSetValues(outlineScroll, XmNpaneMinimum, outlinePaneWidth,
		  XmNpaneMaximum, outlinePaneWidth, NULL);
    XtVaSetValues(outlineScroll, XmNpaneMinimum, 1,
		  XmNpaneMaximum, 10000, NULL);
  }
#endif
}

void XPDFViewer::cmdPageDown(GString *args[], int nArgs,
			     XEvent *event) {
  core->scrollPageDown();
}

void XPDFViewer::cmdPageUp(GString *args[], int nArgs,
			   XEvent *event) {
  core->scrollPageUp();
}

void XPDFViewer::cmdPostPopupMenu(GString *args[], int nArgs,
				  XEvent *event) {
  XmMenuPosition(popupMenu, event->type == ButtonPress ? &event->xbutton
		                                       : (XButtonEvent *)NULL);
  XtManageChild(popupMenu);

  // this is magic (taken from DDD) - weird things happen if this
  // call isn't made (this is done in two different places, in hopes
  // of squashing this stupid bug)
  XtUngrabButton(core->getDrawAreaWidget(), AnyButton, AnyModifier);
}

void XPDFViewer::cmdPrevPage(GString *args[], int nArgs,
			     XEvent *event) {
  core->gotoPrevPage(1, gTrue, gFalse);
}

void XPDFViewer::cmdPrevPageNoScroll(GString *args[], int nArgs,
				     XEvent *event) {
  core->gotoPrevPage(1, gFalse, gFalse);
}

void XPDFViewer::cmdPrint(GString *args[], int nArgs,
			  XEvent *event) {
  XtManageChild(printDialog);
}

void XPDFViewer::cmdQuit(GString *args[], int nArgs,
			 XEvent *event) {
  app->quit();
}

void XPDFViewer::cmdRaise(GString *args[], int nArgs,
			  XEvent *event) {
  XMapRaised(display, XtWindow(win));
  XFlush(display);
}

void XPDFViewer::cmdRedraw(GString *args[], int nArgs,
			   XEvent *event) {
  displayPage(core->getPageNum(), core->getZoom(), core->getRotate(),
	      gFalse, gFalse);
}

void XPDFViewer::cmdReload(GString *args[], int nArgs,
			   XEvent *event) {
  reloadFile();
}

void XPDFViewer::cmdRun(GString *args[], int nArgs,
			XEvent *event) {
  GString *fmt, *cmd, *s;
  LinkAction *action;
  double selLRX, selLRY, selURX, selURY;
  int selPage;
  GBool gotSel;
  char buf[64];
  char *p;
  char c0, c1;
  int i;

  cmd = new GString();
  fmt = args[0];
  i = 0;
  gotSel = gFalse;
  while (i < fmt->getLength()) {
    c0 = fmt->getChar(i);
    if (c0 == '%' && i+1 < fmt->getLength()) {
      c1 = fmt->getChar(i+1);
      switch (c1) {
      case 'f':
	if (core->getDoc() && (s = core->getDoc()->getFileName())) {
	  cmd->append(s);
	}
	break;
      case 'b':
	if (core->getDoc() && (s = core->getDoc()->getFileName())) {
	  if ((p = strrchr(s->getCString(), '.'))) {
	    cmd->append(s->getCString(), p - s->getCString());
	  } else {
	    cmd->append(s);
	  }
	}
	break;
      case 'u':
	if ((action = core->getLinkAction()) &&
	    action->getKind() == actionURI) {
	  s = core->mungeURL(((LinkURI *)action)->getURI());
	  cmd->append(s);
	  delete s;
	}
	break;
      case 'x':
      case 'y':
      case 'X':
      case 'Y':
	if (!gotSel) {
	  if (!core->getSelection(&selPage, &selURX, &selURY,
				  &selLRX, &selLRY)) {
	    selPage = 0;
	    selURX = selURY = selLRX = selLRY = 0;
	  }
	  gotSel = gTrue;
	}
	sprintf(buf, "%g",
		(c1 == 'x') ? selURX :
		(c1 == 'y') ? selURY :
		(c1 == 'X') ? selLRX : selLRY);
	cmd->append(buf);
	break;
      default:
	cmd->append(c1);
	break;
      }
      i += 2;
    } else {
      cmd->append(c0);
      ++i;
    }
  }
#ifdef VMS
  cmd->insert(0, "spawn/nowait ");
#elif defined(__EMX__)
  cmd->insert(0, "start /min /n ");
#else
  cmd->append(" &");
#endif
  system(cmd->getCString());
  delete cmd;
}

void XPDFViewer::cmdScrollDown(GString *args[], int nArgs,
			       XEvent *event) {
  core->scrollDown(atoi(args[0]->getCString()));
}

void XPDFViewer::cmdScrollDownNextPage(GString *args[], int nArgs,
				       XEvent *event) {
  core->scrollDownNextPage(atoi(args[0]->getCString()));
}

void XPDFViewer::cmdScrollLeft(GString *args[], int nArgs,
			       XEvent *event) {
  core->scrollLeft(atoi(args[0]->getCString()));
}

void XPDFViewer::cmdScrollOutlineDown(GString *args[], int nArgs,
				      XEvent *event) {
#ifndef DISABLE_OUTLINE
  Widget sb;
  int val, inc, pageInc, m, slider;

  if (outlineScroll == None) {
    return;
  }
  if ((sb = XtNameToWidget(outlineScroll, "VertScrollBar"))) {
    XtVaGetValues(sb, XmNvalue, &val, XmNincrement, &inc,
		  XmNpageIncrement, &pageInc, XmNmaximum, &m,
		  XmNsliderSize, &slider, NULL);
    if ((val += inc * atoi(args[0]->getCString())) > m - slider) {
      val = m - slider;
    }
    XmScrollBarSetValues(sb, val, slider, inc, pageInc, True);
  }
#endif
}

void XPDFViewer::cmdScrollOutlineUp(GString *args[], int nArgs,
				    XEvent *event) {
#ifndef DISABLE_OUTLINE
  Widget sb;
  int val, inc, pageInc, m, slider;

  if (outlineScroll == None) {
    return;
  }
  if ((sb = XtNameToWidget(outlineScroll, "VertScrollBar"))) {
    XtVaGetValues(sb, XmNvalue, &val, XmNincrement, &inc,
		  XmNpageIncrement, &pageInc, XmNminimum, &m,
		  XmNsliderSize, &slider, NULL);
    if ((val -= inc * atoi(args[0]->getCString())) < m) {
      val = m;
    }
    XmScrollBarSetValues(sb, val, slider, inc, pageInc, True);
  }
#endif
}

void XPDFViewer::cmdScrollRight(GString *args[], int nArgs,
				XEvent *event) {
  core->scrollRight(atoi(args[0]->getCString()));
}

void XPDFViewer::cmdScrollToBottomEdge(GString *args[], int nArgs,
				       XEvent *event) {
  core->scrollToBottomEdge();
}

void XPDFViewer::cmdScrollToBottomRight(GString *args[], int nArgs,
					XEvent *event) {
  core->scrollToBottomRight();
}

void XPDFViewer::cmdScrollToLeftEdge(GString *args[], int nArgs,
				     XEvent *event) {
  core->scrollToLeftEdge();
}

void XPDFViewer::cmdScrollToRightEdge(GString *args[], int nArgs,
				      XEvent *event) {
  core->scrollToRightEdge();
}

void XPDFViewer::cmdScrollToTopEdge(GString *args[], int nArgs,
				    XEvent *event) {
  core->scrollToTopEdge();
}

void XPDFViewer::cmdScrollToTopLeft(GString *args[], int nArgs,
				    XEvent *event) {
  core->scrollToTopLeft();
}

void XPDFViewer::cmdScrollUp(GString *args[], int nArgs,
			     XEvent *event) {
  core->scrollUp(atoi(args[0]->getCString()));
}

void XPDFViewer::cmdScrollUpPrevPage(GString *args[], int nArgs,
				     XEvent *event) {
  core->scrollUpPrevPage(atoi(args[0]->getCString()));
}

void XPDFViewer::cmdSinglePageMode(GString *args[], int nArgs,
				   XEvent *event) {
  Widget btn;

  if (!core->getContinuousMode()) {
    return;
  }
  core->setContinuousMode(gFalse);

  btn = XtNameToWidget(popupMenu, "continuousMode");
  XtVaSetValues(btn, XmNset, XmUNSET, NULL);
}

void XPDFViewer::cmdStartPan(GString *args[], int nArgs,
			     XEvent *event) {
  core->startPan(mouseX(event), mouseY(event));
}

void XPDFViewer::cmdStartSelection(GString *args[], int nArgs,
				   XEvent *event) {
  core->startSelection(mouseX(event), mouseY(event));
}

void XPDFViewer::cmdToggleContinuousMode(GString *args[], int nArgs,
					 XEvent *event) {
  if (core->getContinuousMode()) {
    cmdSinglePageMode(NULL, 0, event);
  } else {
    cmdContinuousMode(NULL, 0, event);
  }
}

void XPDFViewer::cmdToggleFullScreenMode(GString *args[], int nArgs,
					 XEvent *event) {
  if (core->getFullScreen()) {
    cmdWindowMode(NULL, 0, event);
  } else {
    cmdFullScreenMode(NULL, 0, event);
  }
}

void XPDFViewer::cmdToggleOutline(GString *args[], int nArgs,
				  XEvent *event) {
#ifndef DISABLE_OUTLINE
  Dimension w;

  if (outlineScroll == None) {
    return;
  }
  XtVaGetValues(outlineScroll, XmNwidth, &w, NULL);
  if (w > 1) {
    cmdCloseOutline(NULL, 0, event);
  } else {
    cmdOpenOutline(NULL, 0, event);
  }
#endif
}

void XPDFViewer::cmdWindowMode(GString *args[], int nArgs,
			       XEvent *event) {
  PDFDoc *doc;
  XPDFViewer *viewer;
  int pg;
  Widget btn;

  if (!core->getFullScreen()) {
    return;
  }
  pg = core->getPageNum();
  XtPopdown(win);
  doc = core->takeDoc(gFalse);
  viewer = app->reopen(this, doc, pg, gFalse);

  btn = XtNameToWidget(viewer->popupMenu, "fullScreen");
  XtVaSetValues(btn, XmNset, XmUNSET, NULL);
}

void XPDFViewer::cmdZoomFitPage(GString *args[], int nArgs,
				XEvent *event) {
  if (core->getZoom() != zoomPage) {
    setZoomIdx(zoomPageIdx);
    displayPage(core->getPageNum(), zoomPage,
		core->getRotate(), gTrue, gFalse);
  }
}

void XPDFViewer::cmdZoomFitWidth(GString *args[], int nArgs,
				 XEvent *event) {
  if (core->getZoom() != zoomWidth) {
    setZoomIdx(zoomWidthIdx);
    displayPage(core->getPageNum(), zoomWidth,
		core->getRotate(), gTrue, gFalse);
  }
}

void XPDFViewer::cmdZoomIn(GString *args[], int nArgs,
			   XEvent *event) {
  int z;

  z = getZoomIdx();
  if (z <= minZoomIdx && z > maxZoomIdx) {
    --z;
    setZoomIdx(z);
    displayPage(core->getPageNum(), zoomMenuInfo[z].zoom,
		core->getRotate(), gTrue, gFalse);
  }
}

void XPDFViewer::cmdZoomOut(GString *args[], int nArgs,
			    XEvent *event) {
  int z;

  z = getZoomIdx();
  if (z < minZoomIdx && z >= maxZoomIdx) {
    ++z;
    setZoomIdx(z);
    displayPage(core->getPageNum(), zoomMenuInfo[z].zoom,
		core->getRotate(), gTrue, gFalse);
  }
}

void XPDFViewer::cmdZoomPercent(GString *args[], int nArgs,
				XEvent *event) {
  double z;

  z = atof(args[0]->getCString());
  setZoomVal(z);
  displayPage(core->getPageNum(), z, core->getRotate(), gTrue, gFalse);
}

void XPDFViewer::cmdZoomToSelection(GString *args[], int nArgs,
				    XEvent *event) {
  int pg;
  double ulx, uly, lrx, lry;

  if (core->getSelection(&pg, &ulx, &uly, &lrx, &lry)) {
    core->zoomToRect(pg, ulx, uly, lrx, lry);
  }
}

//------------------------------------------------------------------------
// GUI code: main window
//------------------------------------------------------------------------

void XPDFViewer::initWindow(GBool fullScreen) {
  Colormap colormap;
  XColor xcol;
  Atom state, val;
  Arg args[20];
  int n;
  char *title;

  display = XtDisplay(app->getAppShell());
  screenNum = XScreenNumberOfScreen(XtScreen(app->getAppShell()));

  toolBar = None;
#ifndef DISABLE_OUTLINE
  outlineScroll = None;
#endif

  // private colormap
  if (app->getInstallCmap()) {
    XtVaGetValues(app->getAppShell(), XmNcolormap, &colormap, NULL);
    // ensure that BlackPixel and WhitePixel are reserved in the
    // new colormap
    xcol.red = xcol.green = xcol.blue = 0;
    XAllocColor(display, colormap, &xcol);
    xcol.red = xcol.green = xcol.blue = 65535;
    XAllocColor(display, colormap, &xcol);
    colormap = XCopyColormapAndFree(display, colormap);
  }

  // top-level window
  n = 0;
  title = app->getTitle() ? app->getTitle()->getCString()
                          : (char *)xpdfAppName;
  XtSetArg(args[n], XmNtitle, title); ++n;
  XtSetArg(args[n], XmNiconName, title); ++n;
  XtSetArg(args[n], XmNminWidth, 100); ++n;
  XtSetArg(args[n], XmNminHeight, 100); ++n;
  XtSetArg(args[n], XmNbaseWidth, 0); ++n;
  XtSetArg(args[n], XmNbaseHeight, 0); ++n;
  XtSetArg(args[n], XmNdeleteResponse, XmDO_NOTHING); ++n;
  win = XtCreatePopupShell("win", topLevelShellWidgetClass,
			   app->getAppShell(), args, n);
  if (app->getInstallCmap()) {
    XtVaSetValues(win, XmNcolormap, colormap, NULL);
  }
  XmAddWMProtocolCallback(win, XInternAtom(display, "WM_DELETE_WINDOW", False),
			  &closeMsgCbk, this);

  // create the full-screen window
  if (fullScreen) {
    initCore(win, gTrue);

  // create the normal (non-full-screen) window
  } else {
    if (app->getGeometry()) {
      n = 0;
      XtSetArg(args[n], XmNgeometry, app->getGeometry()->getCString()); ++n;
      XtSetValues(win, args, n);
    }

    n = 0;
    form = XmCreateForm(win, "form", args, n);
    XtManageChild(form);

#ifdef DISABLE_OUTLINE
    initToolbar(form);
    n = 0;
    XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
    XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n;
    XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
    XtSetValues(toolBar, args, n);

    initCore(form, gFalse);
    n = 0;
    XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
    XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); ++n;
    XtSetArg(args[n], XmNbottomWidget, toolBar); ++n;
    XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
    XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n;
    XtSetValues(core->getWidget(), args, n);
#else
    initToolbar(form);
    n = 0;
    XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
    XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n;
    XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
    XtSetValues(toolBar, args, n);

    initPanedWin(form);
    n = 0;
    XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
    XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); ++n;
    XtSetArg(args[n], XmNbottomWidget, toolBar); ++n;
    XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
    XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n;
    XtSetValues(panedWin, args, n);

    initCore(panedWin, fullScreen);
    n = 0;
    XtSetArg(args[n], XmNpositionIndex, 1); ++n;
    XtSetArg(args[n], XmNallowResize, True); ++n;
    XtSetArg(args[n], XmNpaneMinimum, 1); ++n;
    XtSetArg(args[n], XmNpaneMaximum, 10000); ++n;
    XtSetValues(core->getWidget(), args, n);
#endif
  }

  // set the zoom menu to match the initial zoom setting
  setZoomVal(core->getZoom());

  // set traversal order
  XtVaSetValues(core->getDrawAreaWidget(),
		XmNnavigationType, XmEXCLUSIVE_TAB_GROUP, NULL);
  if (toolBar != None) {
    XtVaSetValues(backBtn, XmNnavigationType, XmEXCLUSIVE_TAB_GROUP,
		  NULL);
    XtVaSetValues(prevTenPageBtn, XmNnavigationType, XmEXCLUSIVE_TAB_GROUP,
		  NULL);
    XtVaSetValues(prevPageBtn, XmNnavigationType, XmEXCLUSIVE_TAB_GROUP,
		  NULL);
    XtVaSetValues(nextPageBtn, XmNnavigationType, XmEXCLUSIVE_TAB_GROUP,
		  NULL);
    XtVaSetValues(nextTenPageBtn, XmNnavigationType, XmEXCLUSIVE_TAB_GROUP,
		  NULL);
    XtVaSetValues(forwardBtn, XmNnavigationType, XmEXCLUSIVE_TAB_GROUP,
		  NULL);
    XtVaSetValues(pageNumText, XmNnavigationType, XmEXCLUSIVE_TAB_GROUP,
		  NULL);
    XtVaSetValues(zoomWidget, XmNnavigationType, XmEXCLUSIVE_TAB_GROUP,
		  NULL);
    XtVaSetValues(findBtn, XmNnavigationType, XmEXCLUSIVE_TAB_GROUP,
		  NULL);
    XtVaSetValues(printBtn, XmNnavigationType, XmEXCLUSIVE_TAB_GROUP,
		  NULL);
    XtVaSetValues(aboutBtn, XmNnavigationType, XmEXCLUSIVE_TAB_GROUP,
		  NULL);
    XtVaSetValues(quitBtn, XmNnavigationType, XmEXCLUSIVE_TAB_GROUP,
		  NULL);
  }

  initPopupMenu();

  if (fullScreen) {
    // Set both the old-style Motif decorations hint and the new-style
    // _NET_WM_STATE property.  This is redundant, but might be useful
    // for older window managers.  We also set the geometry to +0+0 to
    // avoid interactive placement.  (Note: we need to realize the
    // shell, so it has a Window on which to set the _NET_WM_STATE
    // property, but we don't want to map it until later, so we set
    // mappedWhenManaged to false.)
    n = 0;
    XtSetArg(args[n], XmNmappedWhenManaged, False); ++n;
    XtSetArg(args[n], XmNmwmDecorations, 0); ++n;
    XtSetArg(args[n], XmNgeometry, "+0+0"); ++n;
    XtSetValues(win, args, n);
    XtRealizeWidget(win);
    state = XInternAtom(display, "_NET_WM_STATE", False);
    val = XInternAtom(display, "_NET_WM_STATE_FULLSCREEN", False);
    XChangeProperty(display, XtWindow(win), state, XA_ATOM, 32,
		    PropModeReplace, (Guchar *)&val, 1);
  }
}

void XPDFViewer::initToolbar(Widget parent) {
  Widget label, lastBtn;
#ifndef USE_COMBO_BOX
  Widget btn;
#endif
  Arg args[20];
  int n;
  XmString s, emptyString;
  int i;

  // toolbar
  n = 0;
  toolBar = XmCreateForm(parent, "toolBar", args, n);
  XtManageChild(toolBar);

  // create an empty string -- this is used for buttons that will get
  // pixmaps later
  emptyString = XmStringCreateLocalized("");

  // page movement buttons
  n = 0;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNmarginWidth, 6); ++n;
  XtSetArg(args[n], XmNsensitive, False); ++n;
  XtSetArg(args[n], XmNlabelString, emptyString); ++n;
  backBtn = XmCreatePushButton(toolBar, "back", args, n);
  addToolTip(backBtn, "Back");
  XtManageChild(backBtn);
  XtAddCallback(backBtn, XmNactivateCallback,
		&backCbk, (XtPointer)this);
  n = 0;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n;
  XtSetArg(args[n], XmNleftWidget, backBtn); ++n;
  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNmarginWidth, 6); ++n;
  XtSetArg(args[n], XmNsensitive, False); ++n;
  XtSetArg(args[n], XmNlabelString, emptyString); ++n;
  prevTenPageBtn = XmCreatePushButton(toolBar, "prevTenPage", args, n);
  addToolTip(prevTenPageBtn, "-10 pages");
  XtManageChild(prevTenPageBtn);
  XtAddCallback(prevTenPageBtn, XmNactivateCallback,
		&prevTenPageCbk, (XtPointer)this);
  n = 0;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n;
  XtSetArg(args[n], XmNleftWidget, prevTenPageBtn); ++n;
  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNmarginWidth, 6); ++n;
  XtSetArg(args[n], XmNsensitive, False); ++n;
  XtSetArg(args[n], XmNlabelString, emptyString); ++n;
  prevPageBtn = XmCreatePushButton(toolBar, "prevPage", args, n);
  addToolTip(prevPageBtn, "Previous page");
  XtManageChild(prevPageBtn);
  XtAddCallback(prevPageBtn, XmNactivateCallback,
		&prevPageCbk, (XtPointer)this);
  n = 0;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n;
  XtSetArg(args[n], XmNleftWidget, prevPageBtn); ++n;
  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNmarginWidth, 6); ++n;
  XtSetArg(args[n], XmNsensitive, False); ++n;
  XtSetArg(args[n], XmNlabelString, emptyString); ++n;
  nextPageBtn = XmCreatePushButton(toolBar, "nextPage", args, n);
  addToolTip(nextPageBtn, "Next page");
  XtManageChild(nextPageBtn);
  XtAddCallback(nextPageBtn, XmNactivateCallback,
		&nextPageCbk, (XtPointer)this);
  n = 0;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n;
  XtSetArg(args[n], XmNleftWidget, nextPageBtn); ++n;
  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNmarginWidth, 6); ++n;
  XtSetArg(args[n], XmNsensitive, False); ++n;
  XtSetArg(args[n], XmNlabelString, emptyString); ++n;
  nextTenPageBtn = XmCreatePushButton(toolBar, "nextTenPage", args, n);
  addToolTip(nextTenPageBtn, "+10 pages");
  XtManageChild(nextTenPageBtn);
  XtAddCallback(nextTenPageBtn, XmNactivateCallback,
		&nextTenPageCbk, (XtPointer)this);
  n = 0;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n;
  XtSetArg(args[n], XmNleftWidget, nextTenPageBtn); ++n;
  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNmarginWidth, 6); ++n;
  XtSetArg(args[n], XmNsensitive, False); ++n;
  XtSetArg(args[n], XmNlabelString, emptyString); ++n;
  forwardBtn = XmCreatePushButton(toolBar, "forward", args, n);
  addToolTip(forwardBtn, "Forward");
  XtManageChild(forwardBtn);
  XtAddCallback(forwardBtn, XmNactivateCallback,
		&forwardCbk, (XtPointer)this);

  // page number display
  n = 0;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n;
  XtSetArg(args[n], XmNleftWidget, forwardBtn); ++n;
  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
  s = XmStringCreateLocalized("Page ");
  XtSetArg(args[n], XmNlabelString, s); ++n;
  label = XmCreateLabel(toolBar, "pageLabel", args, n);
  XmStringFree(s);
  XtManageChild(label);
  n = 0;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n;
  XtSetArg(args[n], XmNleftWidget, label); ++n;
  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNmarginWidth, 3); ++n;
  XtSetArg(args[n], XmNmarginHeight, 3); ++n;
  XtSetArg(args[n], XmNcolumns, 5); ++n;
  pageNumText = XmCreateTextField(toolBar, "pageNum", args, n);
  XtManageChild(pageNumText);
  XtAddCallback(pageNumText, XmNactivateCallback,
		&pageNumCbk, (XtPointer)this);
  n = 0;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n;
  XtSetArg(args[n], XmNleftWidget, pageNumText); ++n;
  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
  s = XmStringCreateLocalized(" of 00000");
  XtSetArg(args[n], XmNlabelString, s); ++n;
  XtSetArg(args[n], XmNalignment, XmALIGNMENT_BEGINNING); ++n;
  XtSetArg(args[n], XmNrecomputeSize, False); ++n;
  pageCountLabel = XmCreateLabel(toolBar, "pageCountLabel", args, n);
  XmStringFree(s);
  XtManageChild(pageCountLabel);
  s = XmStringCreateLocalized(" of 0");
  XtVaSetValues(pageCountLabel, XmNlabelString, s, NULL);
  XmStringFree(s);

  // zoom menu
#if USE_COMBO_BOX
  XmString st[nZoomMenuItems];
  n = 0;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n;
  XtSetArg(args[n], XmNleftWidget, pageCountLabel); ++n;
  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNmarginWidth, 0); ++n;
  XtSetArg(args[n], XmNmarginHeight, 0); ++n;
  XtSetArg(args[n], XmNcomboBoxType, XmDROP_DOWN_COMBO_BOX); ++n;
  XtSetArg(args[n], XmNpositionMode, XmONE_BASED); ++n;
  XtSetArg(args[n], XmNcolumns, 7); ++n;
  for (i = 0; i < nZoomMenuItems; ++i) {
    st[i] = XmStringCreateLocalized(zoomMenuInfo[i].label);
  }
  XtSetArg(args[n], XmNitems, st); ++n;
  XtSetArg(args[n], XmNitemCount, nZoomMenuItems); ++n;
  zoomComboBox = XmCreateComboBox(toolBar, "zoomComboBox", args, n);
  for (i = 0; i < nZoomMenuItems; ++i) {
    XmStringFree(st[i]);
  }
  addToolTip(zoomComboBox, "Zoom");
  XtAddCallback(zoomComboBox, XmNselectionCallback,
		&zoomComboBoxCbk, (XtPointer)this);
  XtManageChild(zoomComboBox);
  zoomWidget = zoomComboBox;
#else
  Widget menuPane;
  char buf[16];
  n = 0;
  menuPane = XmCreatePulldownMenu(toolBar, "zoomMenuPane", args, n);
  for (i = 0; i < nZoomMenuItems; ++i) {
    n = 0;
    s = XmStringCreateLocalized(zoomMenuInfo[i].label);
    XtSetArg(args[n], XmNlabelString, s); ++n;
    XtSetArg(args[n], XmNuserData, (XtPointer)i); ++n;
    sprintf(buf, "zoom%d", i);
    btn = XmCreatePushButton(menuPane, buf, args, n);
    XmStringFree(s);
    XtManageChild(btn);
    XtAddCallback(btn, XmNactivateCallback,
		  &zoomMenuCbk, (XtPointer)this);
    zoomMenuBtns[i] = btn;
  }
  n = 0;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n;
  XtSetArg(args[n], XmNleftWidget, pageCountLabel); ++n;
  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNmarginWidth, 0); ++n;
  XtSetArg(args[n], XmNmarginHeight, 0); ++n;
  XtSetArg(args[n], XmNsubMenuId, menuPane); ++n;
  zoomMenu = XmCreateOptionMenu(toolBar, "zoomMenu", args, n);
  addToolTip(zoomMenu, "Zoom");
  XtManageChild(zoomMenu);
  zoomWidget = zoomMenu;
#endif

  // find/print/about buttons
  n = 0;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n;
  XtSetArg(args[n], XmNleftWidget, zoomWidget); ++n;
  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNmarginWidth, 6); ++n;
  XtSetArg(args[n], XmNlabelString, emptyString); ++n;
  findBtn = XmCreatePushButton(toolBar, "find", args, n);
  addToolTip(findBtn, "Find");
  XtManageChild(findBtn);
  XtAddCallback(findBtn, XmNactivateCallback,
		&findCbk, (XtPointer)this);
  n = 0;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n;
  XtSetArg(args[n], XmNleftWidget, findBtn); ++n;
  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNmarginWidth, 6); ++n;
  XtSetArg(args[n], XmNlabelString, emptyString); ++n;
  printBtn = XmCreatePushButton(toolBar, "print", args, n);
  addToolTip(printBtn, "Print");
  XtManageChild(printBtn);
  XtAddCallback(printBtn, XmNactivateCallback,
		&printCbk, (XtPointer)this);
  n = 0;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n;
  XtSetArg(args[n], XmNleftWidget, printBtn); ++n;
  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNmarginWidth, 6); ++n;
  XtSetArg(args[n], XmNlabelString, emptyString); ++n;
  aboutBtn = XmCreatePushButton(toolBar, "about", args, n);
  addToolTip(aboutBtn, "About / help");
  XtManageChild(aboutBtn);
  XtAddCallback(aboutBtn, XmNactivateCallback,
		&aboutCbk, (XtPointer)this);
  lastBtn = aboutBtn;

  // quit button
  n = 0;
  XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNmarginWidth, 6); ++n;
  s = XmStringCreateLocalized("Quit");
  XtSetArg(args[n], XmNlabelString, s); ++n;
  quitBtn = XmCreatePushButton(toolBar, "quit", args, n);
  XmStringFree(s);
  XtManageChild(quitBtn);
  XtAddCallback(quitBtn, XmNactivateCallback,
		&quitCbk, (XtPointer)this);

  // link label
  n = 0;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n;
  XtSetArg(args[n], XmNleftWidget, lastBtn); ++n;
  XtSetArg(args[n], XmNrightAttachment, XmATTACH_WIDGET); ++n;
  XtSetArg(args[n], XmNrightWidget, quitBtn); ++n;
  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
  s = XmStringCreateLocalized("");
  XtSetArg(args[n], XmNlabelString, s); ++n;
  XtSetArg(args[n], XmNrecomputeSize, True); ++n;
  XtSetArg(args[n], XmNalignment, XmALIGNMENT_BEGINNING); ++n;
  linkLabel = XmCreateLabel(toolBar, "linkLabel", args, n);
  XmStringFree(s);
  XtManageChild(linkLabel);

  XmStringFree(emptyString);
}

#ifndef DISABLE_OUTLINE
void XPDFViewer::initPanedWin(Widget parent) {
  Widget clipWin;
  Arg args[20];
  int n;

  // paned window
  n = 0;
  XtSetArg(args[n], XmNorientation, XmHORIZONTAL); ++n;
#if defined(__sgi) && (XmVERSION <= 1)
  panedWin = SgCreateHorzPanedWindow(parent, "panedWin", args, n);
#else
  panedWin = XmCreatePanedWindow(parent, "panedWin", args, n);
#endif
  XtManageChild(panedWin);

  // scrolled window for outline container
  n = 0;
  XtSetArg(args[n], XmNpositionIndex, 0); ++n;
  XtSetArg(args[n], XmNallowResize, True); ++n;
  XtSetArg(args[n], XmNpaneMinimum, 1); ++n;
  XtSetArg(args[n], XmNpaneMaximum, 10000); ++n;
#if !(defined(__sgi) && (XmVERSION <= 1))
  XtSetArg(args[n], XmNwidth, 1); ++n;
#endif
  XtSetArg(args[n], XmNscrollingPolicy, XmAUTOMATIC); ++n;
  outlineScroll = XmCreateScrolledWindow(panedWin, "outlineScroll", args, n);
  XtManageChild(outlineScroll);
  XtVaGetValues(outlineScroll, XmNclipWindow, &clipWin, NULL);
  XtVaSetValues(clipWin, XmNbackground, app->getPaperPixel(), NULL);

  // outline tree
  n = 0;
  XtSetArg(args[n], XmNbackground, app->getPaperPixel()); ++n;
  outlineTree = XPDFCreateTree(outlineScroll, "outlineTree", args, n);
  XtManageChild(outlineTree);
  XtAddCallback(outlineTree, XPDFNselectionCallback, &outlineSelectCbk,
		(XtPointer)this);
}
#endif

void XPDFViewer::initCore(Widget parent, GBool fullScreen) {
  core = new XPDFCore(win, parent,
		      app->getPaperRGB(), app->getPaperPixel(),
		      app->getMattePixel(fullScreen),
		      fullScreen, app->getReverseVideo(),
		      app->getInstallCmap(), app->getRGBCubeSize());
  core->setUpdateCbk(&updateCbk, this);
  core->setActionCbk(&actionCbk, this);
  core->setKeyPressCbk(&keyPressCbk, this);
  core->setMouseCbk(&mouseCbk, this);
}

void XPDFViewer::initPopupMenu() {
  Widget btn;
  Arg args[20];
  int n;
  XmString s, s2;

  n = 0;
#if XmVersion < 1002
  // older versions of Motif need this, newer ones choke on it,
  // sometimes not displaying the menu at all, maybe depending on the
  // state of the NumLock key (taken from DDD)
  XtSetArg(args[n], XmNmenuPost, "<Btn3Down>"); ++n;
#endif
  popupMenu = XmCreatePopupMenu(core->getDrawAreaWidget(), "popupMenu",
				args, n);
  n = 0;
  s = XmStringCreateLocalized("Open...");
  XtSetArg(args[n], XmNlabelString, s); ++n;
  s2 = XmStringCreateLocalized("O");
  XtSetArg(args[n], XmNacceleratorText, s2); ++n;
  btn = XmCreatePushButton(popupMenu, "open", args, n);
  XmStringFree(s);
  XmStringFree(s2);
  XtManageChild(btn);
  XtAddCallback(btn, XmNactivateCallback,
		&openCbk, (XtPointer)this);
  n = 0;
  s = XmStringCreateLocalized("Open in new window...");
  XtSetArg(args[n], XmNlabelString, s); ++n;
  btn = XmCreatePushButton(popupMenu, "openInNewWindow", args, n);
  XmStringFree(s);
  XtManageChild(btn);
  XtAddCallback(btn, XmNactivateCallback,
		&openInNewWindowCbk, (XtPointer)this);
  n = 0;
  s = XmStringCreateLocalized("Reload");
  XtSetArg(args[n], XmNlabelString, s); ++n;
  s2 = XmStringCreateLocalized("R");
  XtSetArg(args[n], XmNacceleratorText, s2); ++n;
  btn = XmCreatePushButton(popupMenu, "reload", args, n);
  XmStringFree(s);
  XmStringFree(s2);
  XtManageChild(btn);
  XtAddCallback(btn, XmNactivateCallback,
		&reloadCbk, (XtPointer)this);
  n = 0;
  s = XmStringCreateLocalized("Save as...");
  XtSetArg(args[n], XmNlabelString, s); ++n;
  btn = XmCreatePushButton(popupMenu, "saveAs", args, n);
  XmStringFree(s);
  XtManageChild(btn);
  XtAddCallback(btn, XmNactivateCallback,
		&saveAsCbk, (XtPointer)this);
  n = 0;
  btn = XmCreateSeparator(popupMenu, "sep1", args, n);
  XtManageChild(btn);
  n = 0;
  s = XmStringCreateLocalized("Continuous view");
  XtSetArg(args[n], XmNlabelString, s); ++n;
  XtSetArg(args[n], XmNindicatorType, XmN_OF_MANY); ++n;
  XtSetArg(args[n], XmNvisibleWhenOff, True); ++n;
  XtSetArg(args[n], XmNset, core->getContinuousMode() ? XmSET : XmUNSET); ++n;
  btn = XmCreateToggleButton(popupMenu, "continuousMode", args, n);
  XmStringFree(s);
  XtManageChild(btn);
  XtAddCallback(btn, XmNvalueChangedCallback,
		&continuousModeToggleCbk, (XtPointer)this);
  n = 0;
  s = XmStringCreateLocalized("Full screen");
  XtSetArg(args[n], XmNlabelString, s); ++n;
  XtSetArg(args[n], XmNindicatorType, XmN_OF_MANY); ++n;
  XtSetArg(args[n], XmNvisibleWhenOff, True); ++n;
  XtSetArg(args[n], XmNset, core->getFullScreen() ? XmSET : XmUNSET); ++n;
  btn = XmCreateToggleButton(popupMenu, "fullScreen", args, n);
  XmStringFree(s);
  XtManageChild(btn);
  XtAddCallback(btn, XmNvalueChangedCallback,
		&fullScreenToggleCbk, (XtPointer)this);
  n = 0;
  s = XmStringCreateLocalized("Rotate counterclockwise");
  XtSetArg(args[n], XmNlabelString, s); ++n;
  btn = XmCreatePushButton(popupMenu, "rotateCCW", args, n);
  XmStringFree(s);
  XtManageChild(btn);
  XtAddCallback(btn, XmNactivateCallback,
		&rotateCCWCbk, (XtPointer)this);
  n = 0;
  s = XmStringCreateLocalized("Rotate clockwise");
  XtSetArg(args[n], XmNlabelString, s); ++n;
  btn = XmCreatePushButton(popupMenu, "rotateCW", args, n);
  XmStringFree(s);
  XtManageChild(btn);
  XtAddCallback(btn, XmNactivateCallback,
		&rotateCWCbk, (XtPointer)this);
  n = 0;
  s = XmStringCreateLocalized("Zoom to selection");
  XtSetArg(args[n], XmNlabelString, s); ++n;
  btn = XmCreatePushButton(popupMenu, "zoomToSelection", args, n);
  XmStringFree(s);
  XtManageChild(btn);
  XtAddCallback(btn, XmNactivateCallback,
		&zoomToSelectionCbk, (XtPointer)this);
  n = 0;
  btn = XmCreateSeparator(popupMenu, "sep2", args, n);
  XtManageChild(btn);
  n = 0;
  s = XmStringCreateLocalized("Close");
  XtSetArg(args[n], XmNlabelString, s); ++n;
  s2 = XmStringCreateLocalized("Ctrl+W");
  XtSetArg(args[n], XmNacceleratorText, s2); ++n;
  btn = XmCreatePushButton(popupMenu, "close", args, n);
  XmStringFree(s);
  XmStringFree(s2);
  XtManageChild(btn);
  XtAddCallback(btn, XmNactivateCallback,
		&closeCbk, (XtPointer)this);
  n = 0;
  s = XmStringCreateLocalized("Quit");
  XtSetArg(args[n], XmNlabelString, s); ++n;
  s2 = XmStringCreateLocalized("Q");
  XtSetArg(args[n], XmNacceleratorText, s2); ++n;
  btn = XmCreatePushButton(popupMenu, "quit", args, n);
  XmStringFree(s);
  XmStringFree(s2);
  XtManageChild(btn);
  XtAddCallback(btn, XmNactivateCallback,
		&quitCbk, (XtPointer)this);

  // this is magic (taken from DDD) - weird things happen if this
  // call isn't made
  XtUngrabButton(core->getDrawAreaWidget(), AnyButton, AnyModifier);
}

void XPDFViewer::addToolTip(Widget widget, char *text) {
#ifdef XmNtoolTipString
  XmString s;
  Cardinal n, i;
  WidgetList children;

  if (XtIsComposite(widget)) {
    XtVaGetValues(widget, XmNnumChildren, &n, XmNchildren, &children, NULL);
    for (i = 0; i < n; ++i) {
      addToolTip(children[i], text);
    }
  } else {
    s = XmStringCreateLocalized(text);
    XtVaSetValues(widget, XmNtoolTipString, s, NULL);
    XmStringFree(s);
  }
#endif
}

void XPDFViewer::mapWindow() {
#ifdef HAVE_X11_XPM_H
  Pixmap iconPixmap;
#endif
  int depth;
  Pixel fg, bg, arm;

  // show the window
  XtPopup(win, XtGrabNone);
  core->takeFocus();

  // create the icon
#ifdef HAVE_X11_XPM_H
  if (XpmCreatePixmapFromData(display, XtWindow(win), xpdfIcon,
			      &iconPixmap, NULL, NULL) == XpmSuccess) {
    XtVaSetValues(win, XmNiconPixmap, iconPixmap, NULL);
  }
#endif

  // set button bitmaps (must be done after the window is mapped)
  if (toolBar != None) {
    XtVaGetValues(backBtn, XmNdepth, &depth,
		  XmNforeground, &fg, XmNbackground, &bg,
		  XmNarmColor, &arm, NULL);
    XtVaSetValues(backBtn, XmNlabelType, XmPIXMAP,
		  XmNlabelPixmap,
		  XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
					      (char *)backArrow_bits,
					      backArrow_width,
					      backArrow_height,
					      fg, bg, depth),
		  XmNarmPixmap,
		  XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
					      (char *)backArrow_bits,
					      backArrow_width,
					      backArrow_height,
					      fg, arm, depth),
		  XmNlabelInsensitivePixmap,
		  XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
					      (char *)backArrowDis_bits,
					      backArrowDis_width,
					      backArrowDis_height,
					      fg, bg, depth),
		  NULL);
    XtVaSetValues(prevTenPageBtn, XmNlabelType, XmPIXMAP,
		  XmNlabelPixmap,
		  XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
					      (char *)dblLeftArrow_bits,
					      dblLeftArrow_width,
					      dblLeftArrow_height,
					      fg, bg, depth),
		  XmNarmPixmap,
		  XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
					      (char *)dblLeftArrow_bits,
					      dblLeftArrow_width,
					      dblLeftArrow_height,
					      fg, arm, depth),
		  XmNlabelInsensitivePixmap,
		  XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
					      (char *)dblLeftArrowDis_bits,
					      dblLeftArrowDis_width,
					      dblLeftArrowDis_height,
					      fg, bg, depth),
		  NULL);
    XtVaSetValues(prevPageBtn, XmNlabelType, XmPIXMAP,
		  XmNlabelPixmap,
		  XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
					      (char *)leftArrow_bits,
					      leftArrow_width,
					      leftArrow_height,
					      fg, bg, depth),
		  XmNarmPixmap,
		  XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
					      (char *)leftArrow_bits,
					      leftArrow_width,
					      leftArrow_height,
					      fg, arm, depth),
		  XmNlabelInsensitivePixmap,
		  XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
					      (char *)leftArrowDis_bits,
					      leftArrowDis_width,
					      leftArrowDis_height,
					      fg, bg, depth),
		  NULL);
    XtVaSetValues(nextPageBtn, XmNlabelType, XmPIXMAP,
		  XmNlabelPixmap,
		  XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
					      (char *)rightArrow_bits,
					      rightArrow_width,
					      rightArrow_height,
					      fg, bg, depth),
		  XmNarmPixmap,
		  XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
					      (char *)rightArrow_bits,
					      rightArrow_width,
					      rightArrow_height,
					      fg, arm, depth),
		  XmNlabelInsensitivePixmap,
		  XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
					      (char *)rightArrowDis_bits,
					      rightArrowDis_width,
					      rightArrowDis_height,
					      fg, bg, depth),
		  NULL);
    XtVaSetValues(nextTenPageBtn, XmNlabelType, XmPIXMAP,
		  XmNlabelPixmap,
		  XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
					      (char *)dblRightArrow_bits,
					      dblRightArrow_width,
					      dblRightArrow_height,
					      fg, bg, depth),
		  XmNarmPixmap,
		  XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
					      (char *)dblRightArrow_bits,
					      dblRightArrow_width,
					      dblRightArrow_height,
					      fg, arm, depth),
		  XmNlabelInsensitivePixmap,
		  XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
					      (char *)dblRightArrowDis_bits,
					      dblRightArrowDis_width,
					      dblRightArrowDis_height,
					      fg, bg, depth),
		  NULL);
    XtVaSetValues(forwardBtn, XmNlabelType, XmPIXMAP,
		  XmNlabelPixmap,
		  XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
					      (char *)forwardArrow_bits,
					      forwardArrow_width,
					      forwardArrow_height,
					      fg, bg, depth),
		  XmNarmPixmap,
		  XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
					      (char *)forwardArrow_bits,
					      forwardArrow_width,
					      forwardArrow_height,
					      fg, arm, depth),
		  XmNlabelInsensitivePixmap,
		  XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
					      (char *)forwardArrowDis_bits,
					      forwardArrowDis_width,
					      forwardArrowDis_height,
					      fg, bg, depth),
		  NULL);
    XtVaSetValues(findBtn, XmNlabelType, XmPIXMAP,
		  XmNlabelPixmap,
		  XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
					      (char *)find_bits,
					      find_width,
					      find_height,
					      fg, bg, depth),
		  XmNarmPixmap,
		  XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
					      (char *)find_bits,
					      find_width,
					      find_height,
					      fg, arm, depth),
		  XmNlabelInsensitivePixmap,
		  XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
					      (char *)findDis_bits,
					      findDis_width,
					      findDis_height,
					      fg, bg, depth),
		  NULL);
    XtVaSetValues(printBtn, XmNlabelType, XmPIXMAP,
		  XmNlabelPixmap,
		  XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
					      (char *)print_bits,
					      print_width,
					      print_height,
					      fg, bg, depth),
		  XmNarmPixmap,
		  XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
					      (char *)print_bits,
					      print_width,
					      print_height,
					      fg, arm, depth),
		  XmNlabelInsensitivePixmap,
		  XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
					      (char *)printDis_bits,
					      printDis_width,
					      printDis_height,
					      fg, bg, depth),
		  NULL);
    XtVaSetValues(aboutBtn, XmNlabelType, XmPIXMAP,
		  XmNlabelPixmap,
		  XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
					      (char *)about_bits,
					      about_width,
					      about_height,
					      fg, bg, depth),
		  XmNarmPixmap,
		  XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
					      (char *)about_bits,
					      about_width,
					      about_height,
					      fg, arm, depth),
		  NULL);
  }
}

void XPDFViewer::closeWindow() {
  XtPopdown(win);
  XtDestroyWidget(win);
}

int XPDFViewer::getZoomIdx() {
  int i;

  for (i = 0; i < nZoomMenuItems; ++i) {
    if (core->getZoom() == zoomMenuInfo[i].zoom) {
      return i;
    }
  }
  return -1;
}

void XPDFViewer::setZoomIdx(int idx) {
  if (toolBar == None) {
    return;
  }
#if USE_COMBO_BOX
  XtVaSetValues(zoomComboBox, XmNselectedPosition, idx + 1, NULL);
#else
  XtVaSetValues(zoomMenu, XmNmenuHistory, zoomMenuBtns[idx], NULL);
#endif
}

void XPDFViewer::setZoomVal(double z) {
  if (toolBar == None) {
    return;
  }

#if USE_COMBO_BOX
  char buf[32];
  XmString s;
  int i;

  for (i = 0; i < nZoomMenuItems; ++i) {
    if (z == zoomMenuInfo[i].zoom) {
      XtVaSetValues(zoomComboBox, XmNselectedPosition, i + 1, NULL);
      return;
    }
  }
  sprintf(buf, "%d%%", (int)z);
  s = XmStringCreateLocalized(buf);
  XtVaSetValues(zoomComboBox, XmNselectedItem, s, NULL);
  XmStringFree(s);
#else
  int i;

  for (i = 0; i < nZoomMenuItems; ++i) {
    if (z == zoomMenuInfo[i].zoom) {
      XtVaSetValues(zoomMenu, XmNmenuHistory, zoomMenuBtns[i], NULL);
      return;
    }
  }
  for (i = maxZoomIdx; i < minZoomIdx; ++i) {
    if (z > zoomMenuInfo[i].zoom) {
      break;
    }
  }
  XtVaSetValues(zoomMenu, XmNmenuHistory, zoomMenuBtns[i], NULL);
#endif
}

void XPDFViewer::prevPageCbk(Widget widget, XtPointer ptr,
			     XtPointer callData) {
  XPDFViewer *viewer = (XPDFViewer *)ptr;

  viewer->core->gotoPrevPage(1, gTrue, gFalse);
  viewer->core->takeFocus();
}

void XPDFViewer::prevTenPageCbk(Widget widget, XtPointer ptr,
				XtPointer callData) {
  XPDFViewer *viewer = (XPDFViewer *)ptr;

  viewer->core->gotoPrevPage(10, gTrue, gFalse);
  viewer->core->takeFocus();
}

void XPDFViewer::nextPageCbk(Widget widget, XtPointer ptr,
			     XtPointer callData) {
  XPDFViewer *viewer = (XPDFViewer *)ptr;

  viewer->core->gotoNextPage(1, gTrue);
  viewer->core->takeFocus();
}

void XPDFViewer::nextTenPageCbk(Widget widget, XtPointer ptr,
				XtPointer callData) {
  XPDFViewer *viewer = (XPDFViewer *)ptr;

  viewer->core->gotoNextPage(10, gTrue);
  viewer->core->takeFocus();
}

void XPDFViewer::backCbk(Widget widget, XtPointer ptr,
			 XtPointer callData) {
  XPDFViewer *viewer = (XPDFViewer *)ptr;

  viewer->core->goBackward();
  viewer->core->takeFocus();
}

void XPDFViewer::forwardCbk(Widget widget, XtPointer ptr,
			    XtPointer callData) {
  XPDFViewer *viewer = (XPDFViewer *)ptr;

  viewer->core->goForward();
  viewer->core->takeFocus();
}

#if USE_COMBO_BOX

void XPDFViewer::zoomComboBoxCbk(Widget widget, XtPointer ptr,
				 XtPointer callData) {
  XPDFViewer *viewer = (XPDFViewer *)ptr;
  XmComboBoxCallbackStruct *data = (XmComboBoxCallbackStruct *)callData;
  double z;
  char *s;
  XmStringContext context;
  XmStringCharSet charSet;
  XmStringDirection dir;
  Boolean sep;

  z = viewer->core->getZoom();
  if (data->item_position == 0) {
    XmStringInitContext(&context, data->item_or_text);
    if (XmStringGetNextSegment(context, &s, &charSet, &dir, &sep)) {
      z = atof(s);
      if (z <= 1) {
	z = defZoom;
      }
      XtFree(charSet);
      XtFree(s);
    }
    XmStringFreeContext(context);
  } else {
    z = zoomMenuInfo[data->item_position - 1].zoom;
  }
  // only redraw if this was triggered by an event; otherwise
  // the caller is responsible for doing the redraw
  if (z != viewer->core->getZoom() && data->event) {
    viewer->displayPage(viewer->core->getPageNum(), z,
			viewer->core->getRotate(), gTrue, gFalse);
  }
  viewer->core->takeFocus();
}

#else // USE_COMBO_BOX

void XPDFViewer::zoomMenuCbk(Widget widget, XtPointer ptr,
			     XtPointer callData) {
  XPDFViewer *viewer = (XPDFViewer *)ptr;
  XmPushButtonCallbackStruct *data = (XmPushButtonCallbackStruct *)callData;
  XtPointer userData;
  double z;

  XtVaGetValues(widget, XmNuserData, &userData, NULL);
  z = zoomMenuInfo[(long)userData].zoom;
  // only redraw if this was triggered by an event; otherwise
  // the caller is responsible for doing the redraw
  if (z != viewer->core->getZoom() && data->event) {
    viewer->displayPage(viewer->core->getPageNum(), z,
			viewer->core->getRotate(), gTrue, gFalse);
  }
  viewer->core->takeFocus();
}

#endif // USE_COMBO_BOX

void XPDFViewer::findCbk(Widget widget, XtPointer ptr,
			 XtPointer callData) {
  XPDFViewer *viewer = (XPDFViewer *)ptr;

  if (!viewer->core->getDoc()) {
    return;
  }
  viewer->mapFindDialog();
}

void XPDFViewer::printCbk(Widget widget, XtPointer ptr,
			  XtPointer callData) {
  XPDFViewer *viewer = (XPDFViewer *)ptr;

  if (!viewer->core->getDoc()) {
    return;
  }
  XtManageChild(viewer->printDialog);
}

void XPDFViewer::aboutCbk(Widget widget, XtPointer ptr,
			  XtPointer callData) {
  XPDFViewer *viewer = (XPDFViewer *)ptr;

  XtManageChild(viewer->aboutDialog);
}

void XPDFViewer::quitCbk(Widget widget, XtPointer ptr,
			 XtPointer callData) {
  XPDFViewer *viewer = (XPDFViewer *)ptr;

  viewer->app->quit();
}

void XPDFViewer::openCbk(Widget widget, XtPointer ptr,
			 XtPointer callData) {
  XPDFViewer *viewer = (XPDFViewer *)ptr;

  viewer->mapOpenDialog(gFalse);
}

void XPDFViewer::openInNewWindowCbk(Widget widget, XtPointer ptr,
				    XtPointer callData) {
  XPDFViewer *viewer = (XPDFViewer *)ptr;

  viewer->mapOpenDialog(gTrue);
}

void XPDFViewer::reloadCbk(Widget widget, XtPointer ptr,
			 XtPointer callData) {
  XPDFViewer *viewer = (XPDFViewer *)ptr;

  viewer->reloadFile();
}

void XPDFViewer::saveAsCbk(Widget widget, XtPointer ptr,
			   XtPointer callData) {
  XPDFViewer *viewer = (XPDFViewer *)ptr;

  if (!viewer->core->getDoc()) {
    return;
  }
  viewer->mapSaveAsDialog();
}

void XPDFViewer::continuousModeToggleCbk(Widget widget, XtPointer ptr,
					 XtPointer callData) {
  XPDFViewer *viewer = (XPDFViewer *)ptr;
  XmToggleButtonCallbackStruct *data =
      (XmToggleButtonCallbackStruct *)callData;

  viewer->core->setContinuousMode(data->set == XmSET);
}

void XPDFViewer::fullScreenToggleCbk(Widget widget, XtPointer ptr,
				     XtPointer callData) {
  XPDFViewer *viewer = (XPDFViewer *)ptr;
  XmToggleButtonCallbackStruct *data =
      (XmToggleButtonCallbackStruct *)callData;

  if (data->set == XmSET) {
    viewer->cmdFullScreenMode(NULL, 0, NULL);
  } else {
    viewer->cmdWindowMode(NULL, 0, NULL);
  }
}

void XPDFViewer::rotateCCWCbk(Widget widget, XtPointer ptr,
			      XtPointer callData) {
  XPDFViewer *viewer = (XPDFViewer *)ptr;
  int r;

  r = viewer->core->getRotate();
  r = (r == 0) ? 270 : r - 90;
  viewer->displayPage(viewer->core->getPageNum(), viewer->core->getZoom(),
		      r, gTrue, gFalse);
}

void XPDFViewer::rotateCWCbk(Widget widget, XtPointer ptr,
			     XtPointer callData) {
  XPDFViewer *viewer = (XPDFViewer *)ptr;
  int r;

  r = viewer->core->getRotate();
  r = (r == 270) ? 0 : r + 90;
  viewer->displayPage(viewer->core->getPageNum(), viewer->core->getZoom(),
		      r, gTrue, gFalse);
}

void XPDFViewer::zoomToSelectionCbk(Widget widget, XtPointer ptr,
				    XtPointer callData) {
  XPDFViewer *viewer = (XPDFViewer *)ptr;
  int pg;
  double ulx, uly, lrx, lry;

  if (viewer->core->getSelection(&pg, &ulx, &uly, &lrx, &lry)) {
    viewer->core->zoomToRect(pg, ulx, uly, lrx, lry);
  }
}

void XPDFViewer::closeCbk(Widget widget, XtPointer ptr,
			  XtPointer callData) {
  XPDFViewer *viewer = (XPDFViewer *)ptr;

  viewer->app->close(viewer, gFalse);
}

void XPDFViewer::closeMsgCbk(Widget widget, XtPointer ptr,
			     XtPointer callData) {
  XPDFViewer *viewer = (XPDFViewer *)ptr;

  viewer->app->close(viewer, gTrue);
}

void XPDFViewer::pageNumCbk(Widget widget, XtPointer ptr,
			    XtPointer callData) {
  XPDFViewer *viewer = (XPDFViewer *)ptr;
  char *s, *p;
  int pg;
  char buf[20];

  if (!viewer->core->getDoc()) {
    goto err;
  }
  s = XmTextFieldGetString(viewer->pageNumText);
  for (p = s; *p; ++p) {
    if (!isdigit(*p)) {
      goto err;
    }
  }
  pg = atoi(s);
  if (pg < 1 || pg > viewer->core->getDoc()->getNumPages()) {
    goto err;
  }
  viewer->displayPage(pg, viewer->core->getZoom(),
		      viewer->core->getRotate(), gFalse, gTrue);
  viewer->core->takeFocus();
  return;

 err:
  XBell(viewer->display, 0);
  sprintf(buf, "%d", viewer->core->getPageNum());
  XmTextFieldSetString(viewer->pageNumText, buf);
}

void XPDFViewer::updateCbk(void *data, GString *fileName,
			   int pageNum, int numPages, char *linkString) {
  XPDFViewer *viewer = (XPDFViewer *)data;
  GString *title;
  char buf[20];
  XmString s;

  if (fileName) {
    if (!(title = viewer->app->getTitle())) {
      title = (new GString(xpdfAppName))->append(": ")->append(fileName);
    }
    XtVaSetValues(viewer->win, XmNtitle, title->getCString(),
		  XmNiconName, title->getCString(), NULL);
    if (!viewer->app->getTitle()) {
      delete title;
    }
#ifndef DISABLE_OUTLINE
    viewer->setupOutline();
#endif
    viewer->setupPrintDialog();
  }

  if (viewer->toolBar != None) {
    if (pageNum >= 0) {
      s = XmStringCreateLocalized("");
      XtVaSetValues(viewer->linkLabel, XmNlabelString, s, NULL);
      XmStringFree(s);
      sprintf(buf, "%d", pageNum);
      XmTextFieldSetString(viewer->pageNumText, buf);
      XtVaSetValues(viewer->prevTenPageBtn, XmNsensitive,
		    pageNum > 1, NULL);
      XtVaSetValues(viewer->prevPageBtn, XmNsensitive,
		    pageNum > 1, NULL);
      XtVaSetValues(viewer->nextTenPageBtn, XmNsensitive,
		    pageNum < viewer->core->getDoc()->getNumPages(), NULL);
      XtVaSetValues(viewer->nextPageBtn, XmNsensitive,
		    pageNum < viewer->core->getDoc()->getNumPages(), NULL);
      XtVaSetValues(viewer->backBtn, XmNsensitive,
		    viewer->core->canGoBack(), NULL);
      XtVaSetValues(viewer->forwardBtn, XmNsensitive,
		    viewer->core->canGoForward(), NULL);
    }

    if (numPages >= 0) {
      sprintf(buf, " of %d", numPages);
      s = XmStringCreateLocalized(buf);
      XtVaSetValues(viewer->pageCountLabel, XmNlabelString, s, NULL);
      XmStringFree(s);
    }

    if (linkString) {
      s = XmStringCreateLocalized(linkString);
      XtVaSetValues(viewer->linkLabel, XmNlabelString, s, NULL);
      XmStringFree(s);
    }
  }
}


//------------------------------------------------------------------------
// GUI code: outline
//------------------------------------------------------------------------

#ifndef DISABLE_OUTLINE

void XPDFViewer::setupOutline() {
  GList *items;
  UnicodeMap *uMap;
  GString *enc;
  int i;

  if (outlineScroll == None) {
    return;
  }

  // unmanage and destroy the old labels
  if (outlineLabels) {
    XtUnmanageChildren(outlineLabels, outlineLabelsLength);
    for (i = 0; i < outlineLabelsLength; ++i) {
      XtDestroyWidget(outlineLabels[i]);
    }
    gfree(outlineLabels);
    outlineLabels = NULL;
    outlineLabelsLength = outlineLabelsSize = 0;
  }

  if (core->getDoc()) {

    // create the new labels
    items = core->getDoc()->getOutline()->getItems();
    if (items && items->getLength() > 0) {
      enc = new GString("Latin1");
      uMap = globalParams->getUnicodeMap(enc);
      delete enc;
      setupOutlineItems(items, NULL, uMap);
      uMap->decRefCnt();
    }

    // manage the new labels
    XtManageChildren(outlineLabels, outlineLabelsLength);
  }
}

void XPDFViewer::setupOutlineItems(GList *items, Widget parent,
				   UnicodeMap *uMap) {
  OutlineItem *item;
  GList *kids;
  Widget label;
  Arg args[20];
  GString *title;
  char buf[8];
  XmString s;
  int i, j, n;

  for (i = 0; i < items->getLength(); ++i) {
    item = (OutlineItem *)items->get(i);
    title = new GString();
    for (j = 0; j < item->getTitleLength(); ++j) {
      n = uMap->mapUnicode(item->getTitle()[j], buf, sizeof(buf));
      title->append(buf, n);
    }
    n = 0;
    XtSetArg(args[n], XPDFNentryPosition, i); ++n;
    if (parent) {
      XtSetArg(args[n], XPDFNentryParent, parent); ++n;
    }
    XtSetArg(args[n], XPDFNentryExpanded, item->isOpen()); ++n;
    s = XmStringCreateLocalized(title->getCString());
    delete title;
    XtSetArg(args[n], XmNlabelString, s); ++n;
    XtSetArg(args[n], XmNuserData, item); ++n;
    XtSetArg(args[n], XmNmarginWidth, 0); ++n;
    XtSetArg(args[n], XmNmarginHeight, 2); ++n;
    XtSetArg(args[n], XmNshadowThickness, 0); ++n;
    XtSetArg(args[n], XmNforeground,
	     app->getReverseVideo() ? WhitePixel(display, screenNum)
	                            : BlackPixel(display, screenNum)); ++n;
    XtSetArg(args[n], XmNbackground, app->getPaperPixel()); ++n;
    label = XmCreateLabelGadget(outlineTree, "label", args, n);
    XmStringFree(s);
    if (outlineLabelsLength == outlineLabelsSize) {
      outlineLabelsSize += 64;
      outlineLabels = (Widget *)greallocn(outlineLabels,
					  outlineLabelsSize, sizeof(Widget *));
    }
    outlineLabels[outlineLabelsLength++] = label;
    item->open();
    if ((kids = item->getKids())) {
      setupOutlineItems(kids, label, uMap);
    }
  }
}

void XPDFViewer::outlineSelectCbk(Widget widget, XtPointer ptr,
				  XtPointer callData) {
  XPDFViewer *viewer = (XPDFViewer *)ptr;
  XPDFTreeSelectCallbackStruct *data =
      (XPDFTreeSelectCallbackStruct *)callData;
  OutlineItem *item;

  XtVaGetValues(data->selectedItem, XmNuserData, &item, NULL);
  if (item) {
    if (item->getAction()) {
      viewer->core->doAction(item->getAction());
    }
  }
  viewer->core->takeFocus();
}

#endif // !DISABLE_OUTLINE

//------------------------------------------------------------------------
// GUI code: "about" dialog
//------------------------------------------------------------------------

void XPDFViewer::initAboutDialog() {
  Widget scrolledWin, col, label, sep, closeBtn;
  Arg args[20];
  int n, i;
  XmString s;
  char buf[20];

  //----- dialog
  n = 0;
  s = XmStringCreateLocalized(xpdfAppName ": About");
  XtSetArg(args[n], XmNdialogTitle, s); ++n;
  XtSetArg(args[n], XmNwidth, 450); ++n;
  XtSetArg(args[n], XmNheight, 300); ++n;
  aboutDialog = XmCreateFormDialog(win, "aboutDialog", args, n);
  XmStringFree(s);

  //----- "close" button
  n = 0;
  XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNrightOffset, 4); ++n;
  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNbottomOffset, 4); ++n;
  closeBtn = XmCreatePushButton(aboutDialog, "Close", args, n);
  XtManageChild(closeBtn);
  n = 0;
  XtSetArg(args[n], XmNdefaultButton, closeBtn); ++n;
  XtSetArg(args[n], XmNcancelButton, closeBtn); ++n;
  XtSetValues(aboutDialog, args, n);

  //----- scrolled window and RowColumn
  n = 0;
  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); ++n;
  XtSetArg(args[n], XmNbottomWidget, closeBtn); ++n;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNscrollingPolicy, XmAUTOMATIC); ++n;
  scrolledWin = XmCreateScrolledWindow(aboutDialog, "scrolledWin", args, n);
  XtManageChild(scrolledWin);
  n = 0;
  XtSetArg(args[n], XmNorientation, XmVERTICAL); ++n;
  XtSetArg(args[n], XmNpacking, XmPACK_TIGHT); ++n;
  col = XmCreateRowColumn(scrolledWin, "col", args, n);
  XtManageChild(col);

  //----- fonts
  aboutBigFont =
    createFontList("-*-times-bold-i-normal--20-*-*-*-*-*-iso8859-1");
  aboutVersionFont =
    createFontList("-*-times-medium-r-normal--16-*-*-*-*-*-iso8859-1");
  aboutFixedFont =
    createFontList("-*-courier-medium-r-normal--12-*-*-*-*-*-iso8859-1");

  //----- heading
  n = 0;
  s = XmStringCreateLocalized("Xpdf");
  XtSetArg(args[n], XmNlabelString, s); ++n;
  XtSetArg(args[n], XmNfontList, aboutBigFont); ++n;
  label = XmCreateLabel(col, "h0", args, n);
  XmStringFree(s);
  XtManageChild(label);
  n = 0;
  s = XmStringCreateLocalized("Version " xpdfVersion);
  XtSetArg(args[n], XmNlabelString, s); ++n;
  XtSetArg(args[n], XmNfontList, aboutVersionFont); ++n;
  label = XmCreateLabel(col, "h1", args, n);
  XmStringFree(s);
  XtManageChild(label);
  n = 0;
  s = XmStringCreateLocalized(xpdfCopyright);
  XtSetArg(args[n], XmNlabelString, s); ++n;
  XtSetArg(args[n], XmNfontList, aboutVersionFont); ++n;
  label = XmCreateLabel(col, "h2", args, n);
  XmStringFree(s);
  XtManageChild(label);
  n = 0;
  s = XmStringCreateLocalized(" ");
  XtSetArg(args[n], XmNlabelString, s); ++n;
  XtSetArg(args[n], XmNfontList, aboutVersionFont); ++n;
  label = XmCreateLabel(col, "h3", args, n);
  XmStringFree(s);
  XtManageChild(label);
  n = 0;
  XtSetArg(args[n], XmNorientation, XmHORIZONTAL); ++n;
  sep = XmCreateSeparator(col, "sep", args, n);
  XtManageChild(sep);
  n = 0;
  s = XmStringCreateLocalized(" ");
  XtSetArg(args[n], XmNlabelString, s); ++n;
  XtSetArg(args[n], XmNfontList, aboutVersionFont); ++n;
  label = XmCreateLabel(col, "h4", args, n);
  XmStringFree(s);
  XtManageChild(label);
  n = 0;

  //----- text
  for (i = 0; aboutWinText[i]; ++i) {
    n = 0;
    s = XmStringCreateLocalized(aboutWinText[i]);
    XtSetArg(args[n], XmNlabelString, s); ++n;
    XtSetArg(args[n], XmNfontList, aboutFixedFont); ++n;
    sprintf(buf, "t%d", i);
    label = XmCreateLabel(col, buf, args, n);
    XtManageChild(label);
    XmStringFree(s);
  }
}

//------------------------------------------------------------------------
// GUI code: "open" dialog
//------------------------------------------------------------------------

void XPDFViewer::initOpenDialog() {
  Arg args[20];
  int n;
  XmString s1, s2, s3;
  GString *dir;

  n = 0;
  s1 = XmStringCreateLocalized("Open");
  XtSetArg(args[n], XmNokLabelString, s1); ++n;
  s2 = XmStringCreateLocalized("*.[Pp][Dd][Ff]");
  XtSetArg(args[n], XmNpattern, s2); ++n;
  s3 = XmStringCreateLocalized(xpdfAppName ": Open");
  XtSetArg(args[n], XmNdialogTitle, s3); ++n;
  XtSetArg(args[n], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL); ++n;
  XtSetArg(args[n], XmNautoUnmanage, True); ++n;
  openDialog = XmCreateFileSelectionDialog(win, "openDialog", args, n);
  XmStringFree(s1);
  XmStringFree(s2);
  XmStringFree(s3);
  XtUnmanageChild(XmFileSelectionBoxGetChild(openDialog,
					     XmDIALOG_HELP_BUTTON));
  XtAddCallback(openDialog, XmNokCallback,
		&openOkCbk, (XtPointer)this);

  if (core->getDoc() && core->getDoc()->getFileName()) {
    dir = makePathAbsolute(grabPath(
	      core->getDoc()->getFileName()->getCString()));
    s1 = XmStringCreateLocalized(dir->getCString());
    XtVaSetValues(openDialog, XmNdirectory, s1, NULL);
    XmStringFree(s1);
    delete dir;
  }
}

void XPDFViewer::mapOpenDialog(GBool openInNewWindowA) {
  if (!openDialog) {
    initOpenDialog();
  }
  openInNewWindow = openInNewWindowA;
  XmFileSelectionDoSearch(openDialog, NULL);
  XtManageChild(openDialog);
}

void XPDFViewer::openOkCbk(Widget widget, XtPointer ptr,
			   XtPointer callData) {
  XPDFViewer *viewer = (XPDFViewer *)ptr;
  XmFileSelectionBoxCallbackStruct *data =
    (XmFileSelectionBoxCallbackStruct *)callData;
  char *fileName;
  XmStringContext context;
  XmStringCharSet charSet;
  XmStringDirection dir;
  Boolean sep;
  GString *fileNameStr;

  XmStringInitContext(&context, data->value);
  if (XmStringGetNextSegment(context, &fileName, &charSet, &dir, &sep)) {
    fileNameStr = new GString(fileName);
    if (viewer->openInNewWindow) {
      viewer->app->open(fileNameStr);
    } else {
      if (viewer->loadFile(fileNameStr)) {
	viewer->displayPage(1, viewer->core->getZoom(),
			    viewer->core->getRotate(), gTrue, gTrue);
      }
    }
    delete fileNameStr;
    XtFree(charSet);
    XtFree(fileName);
  }
  XmStringFreeContext(context);
}

//------------------------------------------------------------------------
// GUI code: "find" dialog
//------------------------------------------------------------------------

void XPDFViewer::initFindDialog() {
  Widget form1, label, okBtn, closeBtn;
  Arg args[20];
  int n;
  XmString s;

  //----- dialog
  n = 0;
  s = XmStringCreateLocalized(xpdfAppName ": Find");
  XtSetArg(args[n], XmNdialogTitle, s); ++n;
  XtSetArg(args[n], XmNnavigationType, XmNONE); ++n;
  XtSetArg(args[n], XmNautoUnmanage, False); ++n;
  findDialog = XmCreateFormDialog(win, "findDialog", args, n);
  XmStringFree(s);

  //----- "find" and "close" buttons
  n = 0;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNleftOffset, 4); ++n;
  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNbottomOffset, 4); ++n;
  XtSetArg(args[n], XmNnavigationType, XmEXCLUSIVE_TAB_GROUP); ++n;
  okBtn = XmCreatePushButton(findDialog, "Find", args, n);
  XtManageChild(okBtn);
  XtAddCallback(okBtn, XmNactivateCallback,
		&findFindCbk, (XtPointer)this);
  n = 0;
  XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNrightOffset, 4); ++n;
  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNbottomOffset, 4); ++n;
  XtSetArg(args[n], XmNnavigationType, XmEXCLUSIVE_TAB_GROUP); ++n;
  closeBtn = XmCreatePushButton(findDialog, "Close", args, n);
  XtManageChild(closeBtn);
  XtAddCallback(closeBtn, XmNactivateCallback,
		&findCloseCbk, (XtPointer)this);

  //----- checkboxes
  n = 0;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNleftOffset, 4); ++n;
  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); ++n;
  XtSetArg(args[n], XmNbottomWidget, okBtn); ++n;
  XtSetArg(args[n], XmNindicatorType, XmN_OF_MANY); ++n;
#if XmVERSION <= 1
  XtSetArg(args[n], XmNindicatorOn, True); ++n;
#else
  XtSetArg(args[n], XmNindicatorOn, XmINDICATOR_FILL); ++n;
#endif
  XtSetArg(args[n], XmNset, XmUNSET); ++n;
  s = XmStringCreateLocalized("Search backward");
  XtSetArg(args[n], XmNlabelString, s); ++n;
  findBackwardToggle = XmCreateToggleButton(findDialog, "backward", args, n);
  XmStringFree(s);
  XtManageChild(findBackwardToggle);
  n = 0;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n;
  XtSetArg(args[n], XmNleftWidget, findBackwardToggle); ++n;
  XtSetArg(args[n], XmNleftOffset, 16); ++n;
  XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNrightOffset, 4); ++n;
  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); ++n;
  XtSetArg(args[n], XmNbottomWidget, okBtn); ++n;
  XtSetArg(args[n], XmNindicatorType, XmN_OF_MANY); ++n;
#if XmVERSION <= 1
  XtSetArg(args[n], XmNindicatorOn, True); ++n;
#else
  XtSetArg(args[n], XmNindicatorOn, XmINDICATOR_FILL); ++n;
#endif
  XtSetArg(args[n], XmNset, XmUNSET); ++n;
  s = XmStringCreateLocalized("Match case");
  XtSetArg(args[n], XmNlabelString, s); ++n;
  findCaseSensitiveToggle =
      XmCreateToggleButton(findDialog, "matchCase", args, n);
  XmStringFree(s);
  XtManageChild(findCaseSensitiveToggle);

  //----- search string entry
  n = 0;
  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNtopOffset, 4); ++n;
  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); ++n;
  XtSetArg(args[n], XmNbottomWidget, findBackwardToggle); ++n;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNleftOffset, 2); ++n;
  XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNrightOffset, 2); ++n;
  form1 = XmCreateForm(findDialog, "form", args, n);
  XtManageChild(form1);
  n = 0;
  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
  s = XmStringCreateLocalized("Find text: ");
  XtSetArg(args[n], XmNlabelString, s); ++n;
  label = XmCreateLabel(form1, "label", args, n);
  XmStringFree(s);
  XtManageChild(label);
  n = 0;
  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n;
  XtSetArg(args[n], XmNleftWidget, label); ++n;
  XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n;
  findText = XmCreateTextField(form1, "text", args, n);
  XtManageChild(findText);
#ifdef LESSTIF_VERSION
  XtAddCallback(findText, XmNactivateCallback,
		&findFindCbk, (XtPointer)this);
#endif

  //----- dialog parameters
  n = 0;
  XtSetArg(args[n], XmNdefaultButton, okBtn); ++n;
  XtSetArg(args[n], XmNcancelButton, closeBtn); ++n;
#if XmVersion > 1001
  XtSetArg(args[n], XmNinitialFocus, findText); ++n;
#endif
  XtSetValues(findDialog, args, n);
}

void XPDFViewer::findFindCbk(Widget widget, XtPointer ptr,
			     XtPointer callData) {
  XPDFViewer *viewer = (XPDFViewer *)ptr;

  viewer->doFind(gFalse);
}

void XPDFViewer::mapFindDialog() {
  XmTextFieldSetSelection(findText, 0, XmTextFieldGetLastPosition(findText),
			  XtLastTimestampProcessed(display));
  XmTextFieldSetInsertionPosition(findText, 0);
  XtManageChild(findDialog);
}

void XPDFViewer::doFind(GBool next) {
  if (XtWindow(findDialog)) {
    XDefineCursor(display, XtWindow(findDialog), core->getBusyCursor());
  }
  core->find(XmTextFieldGetString(findText),
	     XmToggleButtonGetState(findCaseSensitiveToggle),
	     next,
	     XmToggleButtonGetState(findBackwardToggle),
	     gFalse);
  if (XtWindow(findDialog)) {
    XUndefineCursor(display, XtWindow(findDialog));
  }
}

void XPDFViewer::findCloseCbk(Widget widget, XtPointer ptr,
			      XtPointer callData) {
  XPDFViewer *viewer = (XPDFViewer *)ptr;

  XtUnmanageChild(viewer->findDialog);
}

//------------------------------------------------------------------------
// GUI code: "save as" dialog
//------------------------------------------------------------------------

void XPDFViewer::initSaveAsDialog() {
  Arg args[20];
  int n;
  XmString s1, s2, s3;
  GString *dir;

  n = 0;
  s1 = XmStringCreateLocalized("Save");
  XtSetArg(args[n], XmNokLabelString, s1); ++n;
  s2 = XmStringCreateLocalized("*.[Pp][Dd][Ff]");
  XtSetArg(args[n], XmNpattern, s2); ++n;
  s3 = XmStringCreateLocalized(xpdfAppName ": Save as");
  XtSetArg(args[n], XmNdialogTitle, s3); ++n;
  XtSetArg(args[n], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL); ++n;
  XtSetArg(args[n], XmNautoUnmanage, True); ++n;
  saveAsDialog = XmCreateFileSelectionDialog(win, "saveAsDialog", args, n);
  XmStringFree(s1);
  XmStringFree(s2);
  XmStringFree(s3);
  XtUnmanageChild(XmFileSelectionBoxGetChild(saveAsDialog,
					     XmDIALOG_HELP_BUTTON));
  XtAddCallback(saveAsDialog, XmNokCallback,
		&saveAsOkCbk, (XtPointer)this);

  if (core->getDoc() && core->getDoc()->getFileName()) {
    dir = makePathAbsolute(grabPath(
	      core->getDoc()->getFileName()->getCString()));
    s1 = XmStringCreateLocalized(dir->getCString());
    XtVaSetValues(saveAsDialog, XmNdirectory, s1, NULL);
    XmStringFree(s1);
    delete dir;
  }
}

void XPDFViewer::mapSaveAsDialog() {
  if (!saveAsDialog) {
    initSaveAsDialog();
  }
  XmFileSelectionDoSearch(saveAsDialog, NULL);
  XtManageChild(saveAsDialog);
}

void XPDFViewer::saveAsOkCbk(Widget widget, XtPointer ptr,
			     XtPointer callData) {
  XPDFViewer *viewer = (XPDFViewer *)ptr;
  XmFileSelectionBoxCallbackStruct *data =
    (XmFileSelectionBoxCallbackStruct *)callData;
  char *fileName;
  GString *fileNameStr;
  XmStringContext context;
  XmStringCharSet charSet;
  XmStringDirection dir;
  Boolean sep;

  XmStringInitContext(&context, data->value);
  if (XmStringGetNextSegment(context, &fileName, &charSet, &dir, &sep)) {
    fileNameStr = new GString(fileName);
    viewer->core->getDoc()->saveAs(fileNameStr);
    delete fileNameStr;
    XtFree(charSet);
    XtFree(fileName);
  }
  XmStringFreeContext(context);
}

//------------------------------------------------------------------------
// GUI code: "print" dialog
//------------------------------------------------------------------------

void XPDFViewer::initPrintDialog() {
  Widget sep1, sep2, row, label1, label2, okBtn, cancelBtn;
  Arg args[20];
  int n;
  XmString s;
  GString *psFileName;

  //----- dialog
  n = 0;
  s = XmStringCreateLocalized(xpdfAppName ": Print");
  XtSetArg(args[n], XmNdialogTitle, s); ++n;
  XtSetArg(args[n], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL); ++n;
  printDialog = XmCreateFormDialog(win, "printDialog", args, n);
  XmStringFree(s);

  //----- "print with command"
  n = 0;
  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNtopOffset, 4); ++n;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNindicatorType, XmONE_OF_MANY); ++n;
  XtSetArg(args[n], XmNset, XmSET); ++n;
  s = XmStringCreateLocalized("Print with command:");
  XtSetArg(args[n], XmNlabelString, s); ++n;
  printWithCmdBtn = XmCreateToggleButton(printDialog, "printWithCmd", args, n);
  XmStringFree(s);
  XtManageChild(printWithCmdBtn);
  XtAddCallback(printWithCmdBtn, XmNvalueChangedCallback,
		&printWithCmdBtnCbk, (XtPointer)this);
  n = 0;
  XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n;
  XtSetArg(args[n], XmNtopWidget, printWithCmdBtn); ++n;
  XtSetArg(args[n], XmNtopOffset, 2); ++n;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNleftOffset, 16); ++n;
  XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNrightOffset, 4); ++n;
  XtSetArg(args[n], XmNcolumns, 40); ++n;
  printCmdText = XmCreateTextField(printDialog, "printCmd", args, n);
  XtManageChild(printCmdText);

  //----- "print to file"
  n = 0;
  XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n;
  XtSetArg(args[n], XmNtopWidget, printCmdText); ++n;
  XtSetArg(args[n], XmNtopOffset, 4); ++n;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNindicatorType, XmONE_OF_MANY); ++n;
  XtSetArg(args[n], XmNset, XmUNSET); ++n;
  s = XmStringCreateLocalized("Print to file:");
  XtSetArg(args[n], XmNlabelString, s); ++n;
  printToFileBtn = XmCreateToggleButton(printDialog, "printToFile", args, n);
  XmStringFree(s);
  XtManageChild(printToFileBtn);
  XtAddCallback(printToFileBtn, XmNvalueChangedCallback,
		&printToFileBtnCbk, (XtPointer)this);
  n = 0;
  XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n;
  XtSetArg(args[n], XmNtopWidget, printToFileBtn); ++n;
  XtSetArg(args[n], XmNtopOffset, 2); ++n;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNleftOffset, 16); ++n;
  XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNrightOffset, 4); ++n;
  XtSetArg(args[n], XmNcolumns, 40); ++n;
  XtSetArg(args[n], XmNsensitive, False); ++n;
  printFileText = XmCreateTextField(printDialog, "printFile", args, n);
  XtManageChild(printFileText);

  //----- separator
  n = 0;
  XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n;
  XtSetArg(args[n], XmNtopWidget, printFileText); ++n;
  XtSetArg(args[n], XmNtopOffset, 8); ++n;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNleftOffset, 8); ++n;
  XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNrightOffset, 8); ++n;
  XtSetArg(args[n], XmNorientation, XmHORIZONTAL); ++n;
  sep1 = XmCreateSeparator(printDialog, "sep1", args, n);
  XtManageChild(sep1);

  //----- page range
  n = 0;
  XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n;
  XtSetArg(args[n], XmNtopWidget, sep1); ++n;
  XtSetArg(args[n], XmNtopOffset, 8); ++n;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNleftOffset, 4); ++n;
  XtSetArg(args[n], XmNorientation, XmHORIZONTAL); ++n;
  XtSetArg(args[n], XmNpacking, XmPACK_TIGHT); ++n;
  row = XmCreateRowColumn(printDialog, "row", args, n);
  XtManageChild(row);
  n = 0;
  s = XmStringCreateLocalized("Pages:");
  XtSetArg(args[n], XmNlabelString, s); ++n;
  label1 = XmCreateLabel(row, "label1", args, n);
  XmStringFree(s);
  XtManageChild(label1);
  n = 0;
  XtSetArg(args[n], XmNcolumns, 5); ++n;
  printFirstPage = XmCreateTextField(row, "printFirstPage", args, n);
  XtManageChild(printFirstPage);
  n = 0;
  s = XmStringCreateLocalized("to");
  XtSetArg(args[n], XmNlabelString, s); ++n;
  label2 = XmCreateLabel(row, "label2", args, n);
  XmStringFree(s);
  XtManageChild(label2);
  n = 0;
  XtSetArg(args[n], XmNcolumns, 5); ++n;
  printLastPage = XmCreateTextField(row, "printLastPage", args, n);
  XtManageChild(printLastPage);

  //----- separator
  n = 0;
  XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n;
  XtSetArg(args[n], XmNtopWidget, row); ++n;
  XtSetArg(args[n], XmNtopOffset, 8); ++n;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNleftOffset, 8); ++n;
  XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNrightOffset, 8); ++n;
  XtSetArg(args[n], XmNorientation, XmHORIZONTAL); ++n;
  sep2 = XmCreateSeparator(printDialog, "sep2", args, n);
  XtManageChild(sep2);

  //----- "print" and "cancel" buttons
  n = 0;
  XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n;
  XtSetArg(args[n], XmNtopWidget, sep2); ++n;
  XtSetArg(args[n], XmNtopOffset, 8); ++n;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNleftOffset, 4); ++n;
  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNbottomOffset, 4); ++n;
  okBtn = XmCreatePushButton(printDialog, "Print", args, n);
  XtManageChild(okBtn);
  XtAddCallback(okBtn, XmNactivateCallback,
		&printPrintCbk, (XtPointer)this);
  n = 0;
  XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n;
  XtSetArg(args[n], XmNtopWidget, sep2); ++n;
  XtSetArg(args[n], XmNtopOffset, 8); ++n;
  XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNrightOffset, 4); ++n;
  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
  XtSetArg(args[n], XmNbottomOffset, 4); ++n;
  cancelBtn = XmCreatePushButton(printDialog, "Cancel", args, n);
  XtManageChild(cancelBtn);
  n = 0;
  XtSetArg(args[n], XmNdefaultButton, okBtn); ++n;
  XtSetArg(args[n], XmNcancelButton, cancelBtn); ++n;
  XtSetValues(printDialog, args, n);

  //----- initial values
  if ((psFileName = globalParams->getPSFile())) {
    if (psFileName->getChar(0) == '|') {
      XmTextFieldSetString(printCmdText,
			   psFileName->getCString() + 1);
    } else {
      XmTextFieldSetString(printFileText, psFileName->getCString());
    }
    delete psFileName;
  }
}

void XPDFViewer::setupPrintDialog() {
  PDFDoc *doc;
  char buf[20];
  GString *pdfFileName, *psFileName, *psFileName2;
  char *p;

  doc = core->getDoc();
  psFileName = globalParams->getPSFile();
  if (!psFileName || psFileName->getChar(0) == '|') {
    pdfFileName = doc->getFileName();
    p = pdfFileName->getCString() + pdfFileName->getLength() - 4;
    if (!strcmp(p, ".pdf") || !strcmp(p, ".PDF")) {
      psFileName2 = new GString(pdfFileName->getCString(),
				pdfFileName->getLength() - 4);
    } else {
      psFileName2 = pdfFileName->copy();
    }
    psFileName2->append(".ps");
    XmTextFieldSetString(printFileText, psFileName2->getCString());
    delete psFileName2;
  }
  if (psFileName && psFileName->getChar(0) == '|') {
    XmToggleButtonSetState(printWithCmdBtn, True, False);
    XmToggleButtonSetState(printToFileBtn, False, False);
    XtVaSetValues(printCmdText, XmNsensitive, True, NULL);
    XtVaSetValues(printFileText, XmNsensitive, False, NULL);
  } else {
    XmToggleButtonSetState(printWithCmdBtn, False, False);
    XmToggleButtonSetState(printToFileBtn, True, False);
    XtVaSetValues(printCmdText, XmNsensitive, False, NULL);
    XtVaSetValues(printFileText, XmNsensitive, True, NULL);
  }
  if (psFileName) {
    delete psFileName;
  }

  sprintf(buf, "%d", doc->getNumPages());
  XmTextFieldSetString(printFirstPage, "1");
  XmTextFieldSetString(printLastPage, buf);
}

void XPDFViewer::printWithCmdBtnCbk(Widget widget, XtPointer ptr,
				    XtPointer callData) {
  XPDFViewer *viewer = (XPDFViewer *)ptr;
  XmToggleButtonCallbackStruct *data =
      (XmToggleButtonCallbackStruct *)callData;

  if (data->set != XmSET) {
    XmToggleButtonSetState(viewer->printWithCmdBtn, True, False);
  }
  XmToggleButtonSetState(viewer->printToFileBtn, False, False);
  XtVaSetValues(viewer->printCmdText, XmNsensitive, True, NULL);
  XtVaSetValues(viewer->printFileText, XmNsensitive, False, NULL);
}

void XPDFViewer::printToFileBtnCbk(Widget widget, XtPointer ptr,
				   XtPointer callData) {
  XPDFViewer *viewer = (XPDFViewer *)ptr;
  XmToggleButtonCallbackStruct *data =
      (XmToggleButtonCallbackStruct *)callData;

  if (data->set != XmSET) {
    XmToggleButtonSetState(viewer->printToFileBtn, True, False);
  }
  XmToggleButtonSetState(viewer->printWithCmdBtn, False, False);
  XtVaSetValues(viewer->printFileText, XmNsensitive, True, NULL);
  XtVaSetValues(viewer->printCmdText, XmNsensitive, False, NULL);
}

void XPDFViewer::printPrintCbk(Widget widget, XtPointer ptr,
			       XtPointer callData) {
  XPDFViewer *viewer = (XPDFViewer *)ptr;
  unsigned char withCmd;
  GString *psFileName;
  int firstPage, lastPage;
  PDFDoc *doc;
  PSOutputDev *psOut;

  doc = viewer->core->getDoc();
  if (!doc->okToPrint()) {
    error(-1, "Printing this document is not allowed.");
    return;
  }

  viewer->core->setBusyCursor(gTrue);

  XtVaGetValues(viewer->printWithCmdBtn, XmNset, &withCmd, NULL);
  if (withCmd) {
    psFileName = new GString(XmTextFieldGetString(viewer->printCmdText));
    psFileName->insert(0, '|');
  } else {
    psFileName = new GString(XmTextFieldGetString(viewer->printFileText));
  }

  firstPage = atoi(XmTextFieldGetString(viewer->printFirstPage));
  lastPage = atoi(XmTextFieldGetString(viewer->printLastPage));
  if (firstPage < 1) {
    firstPage = 1;
  } else if (firstPage > doc->getNumPages()) {
    firstPage = doc->getNumPages();
  }
  if (lastPage < firstPage) {
    lastPage = firstPage;
  } else if (lastPage > doc->getNumPages()) {
    lastPage = doc->getNumPages();
  }

  psOut = new PSOutputDev(psFileName->getCString(), doc->getXRef(),
			  doc->getCatalog(), firstPage, lastPage,
			  psModePS);
  if (psOut->isOk()) {
    doc->displayPages(psOut, firstPage, lastPage, 72, 72,
		      0, gTrue, globalParams->getPSCrop(), gTrue);
  }
  delete psOut;
  delete psFileName;

  viewer->core->setBusyCursor(gFalse);
}

//------------------------------------------------------------------------
// Motif support
//------------------------------------------------------------------------

XmFontList XPDFViewer::createFontList(char *xlfd) {
  XmFontList fontList;

#if XmVersion <= 1001

  XFontStruct *font;
  String params;
  Cardinal nParams;

  font = XLoadQueryFont(display, xlfd);
  if (font) {
    fontList = XmFontListCreate(font, XmSTRING_DEFAULT_CHARSET);
  } else {
    params = (String)xlfd;
    nParams = 1;
    XtAppWarningMsg(app->getAppContext(),
		    "noSuchFont", "CvtStringToXmFontList",
		    "XtToolkitError", "No such font: %s",
		    &params, &nParams);
    fontList = NULL;
  }

#else

  XmFontListEntry entry;

  entry = XmFontListEntryLoad(display, xlfd,
			      XmFONT_IS_FONT, XmFONTLIST_DEFAULT_TAG);
  fontList = XmFontListAppendEntry(NULL, entry);
  XmFontListEntryFree(&entry);

#endif

  return fontList;
}
//========================================================================
//
// XPDFViewer.h
//
// Copyright 2002-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef XPDFVIEWER_H
#define XPDFVIEWER_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#define Object XtObject
#include <Xm/XmAll.h>
#undef Object
#include "gtypes.h"
#include "XPDFCore.h"

#if (XmVERSION <= 1) && !defined(__sgi)
#define DISABLE_OUTLINE
#endif

#if (XmVERSION >= 2 && !defined(LESSTIF_VERSION))
#  define USE_COMBO_BOX 1
#else
#  undef USE_COMBO_BOX
#endif

class GString;
class GList;
class UnicodeMap;
class LinkDest;
class XPDFApp;
class XPDFViewer;

//------------------------------------------------------------------------

// NB: this must match the defn of zoomMenuBtnInfo in XPDFViewer.cc
#define nZoomMenuItems 10

//------------------------------------------------------------------------

struct XPDFViewerCmd {
  char *name;
  int nArgs;
  GBool requiresDoc;
  GBool requiresEvent;
  void (XPDFViewer::*func)(GString *args[], int nArgs, XEvent *event);
};

//------------------------------------------------------------------------
// XPDFViewer
//------------------------------------------------------------------------

class XPDFViewer {
public:

  XPDFViewer(XPDFApp *appA, GString *fileName,
	     int pageA, GString *destName, GBool fullScreen,
	     GString *ownerPassword, GString *userPassword);
  XPDFViewer(XPDFApp *appA, PDFDoc *doc, int pageA,
	     GString *destName, GBool fullScreen);
  GBool isOk() { return ok; }
  ~XPDFViewer();

  void open(GString *fileName, int pageA, GString *destName);
  void clear();
  void reloadFile();

  void execCmd(GString *cmd, XEvent *event);

  Widget getWindow() { return win; }

private:

  //----- load / display
  GBool loadFile(GString *fileName, GString *ownerPassword = NULL,
		 GString *userPassword = NULL);
  void displayPage(int pageA, double zoomA, int rotateA,
                   GBool scrollToTop, GBool addToHist);
  void displayDest(LinkDest *dest, double zoomA, int rotateA,
		   GBool addToHist);
  void getPageAndDest(int pageA, GString *destName,
		      int *pageOut, LinkDest **destOut);

  //----- hyperlinks / actions
  void doLink(int wx, int wy, GBool onlyIfNoSelection, GBool newWin);
  static void actionCbk(void *data, char *action);

  //----- keyboard/mouse input
  static void keyPressCbk(void *data, KeySym key, Guint modifiers,
			  XEvent *event);
  static void mouseCbk(void *data, XEvent *event);
  int getModifiers(Guint modifiers);
  int getContext(Guint modifiers);

  //----- command functions
  void cmdAbout(GString *args[], int nArgs, XEvent *event);
  void cmdCloseOutline(GString *args[], int nArgs, XEvent *event);
  void cmdCloseWindow(GString *args[], int nArgs, XEvent *event);
  void cmdContinuousMode(GString *args[], int nArgs, XEvent *event);
  void cmdEndPan(GString *args[], int nArgs, XEvent *event);
  void cmdEndSelection(GString *args[], int nArgs, XEvent *event);
  void cmdFind(GString *args[], int nArgs, XEvent *event);
  void cmdFindNext(GString *args[], int nArgs, XEvent *event);
  void cmdFocusToDocWin(GString *args[], int nArgs, XEvent *event);
  void cmdFocusToPageNum(GString *args[], int nArgs, XEvent *event);
  void cmdFollowLink(GString *args[], int nArgs, XEvent *event);
  void cmdFollowLinkInNewWin(GString *args[], int nArgs, XEvent *event);
  void cmdFollowLinkInNewWinNoSel(GString *args[], int nArgs, XEvent *event);
  void cmdFollowLinkNoSel(GString *args[], int nArgs, XEvent *event);
  void cmdFullScreenMode(GString *args[], int nArgs, XEvent *event);
  void cmdGoBackward(GString *args[], int nArgs, XEvent *event);
  void cmdGoForward(GString *args[], int nArgs, XEvent *event);
  void cmdGotoDest(GString *args[], int nArgs, XEvent *event);
  void cmdGotoLastPage(GString *args[], int nArgs, XEvent *event);
  void cmdGotoLastPageNoScroll(GString *args[], int nArgs, XEvent *event);
  void cmdGotoPage(GString *args[], int nArgs, XEvent *event);
  void cmdGotoPageNoScroll(GString *args[], int nArgs, XEvent *event);
  void cmdNextPage(GString *args[], int nArgs, XEvent *event);
  void cmdNextPageNoScroll(GString *args[], int nArgs, XEvent *event);
  void cmdOpen(GString *args[], int nArgs, XEvent *event);
  void cmdOpenFile(GString *args[], int nArgs, XEvent *event);
  void cmdOpenFileAtDest(GString *args[], int nArgs, XEvent *event);
  void cmdOpenFileAtDestInNewWin(GString *args[], int nArgs, XEvent *event);
  void cmdOpenFileAtPage(GString *args[], int nArgs, XEvent *event);
  void cmdOpenFileAtPageInNewWin(GString *args[], int nArgs, XEvent *event);
  void cmdOpenFileInNewWin(GString *args[], int nArgs, XEvent *event);
  void cmdOpenInNewWin(GString *args[], int nArgs, XEvent *event);
  void cmdOpenOutline(GString *args[], int nArgs, XEvent *event);
  void cmdPageDown(GString *args[], int nArgs, XEvent *event);
  void cmdPageUp(GString *args[], int nArgs, XEvent *event);
  void cmdPostPopupMenu(GString *args[], int nArgs, XEvent *event);
  void cmdPrevPage(GString *args[], int nArgs, XEvent *event);
  void cmdPrevPageNoScroll(GString *args[], int nArgs, XEvent *event);
  void cmdPrint(GString *args[], int nArgs, XEvent *event);
  void cmdQuit(GString *args[], int nArgs, XEvent *event);
  void cmdRaise(GString *args[], int nArgs, XEvent *event);
  void cmdRedraw(GString *args[], int nArgs, XEvent *event);
  void cmdReload(GString *args[], int nArgs, XEvent *event);
  void cmdRun(GString *args[], int nArgs, XEvent *event);
  void cmdScrollDown(GString *args[], int nArgs, XEvent *event);
  void cmdScrollDownNextPage(GString *args[], int nArgs, XEvent *event);
  void cmdScrollLeft(GString *args[], int nArgs, XEvent *event);
  void cmdScrollOutlineDown(GString *args[], int nArgs, XEvent *event);
  void cmdScrollOutlineUp(GString *args[], int nArgs, XEvent *event);
  void cmdScrollRight(GString *args[], int nArgs, XEvent *event);
  void cmdScrollToBottomEdge(GString *args[], int nArgs, XEvent *event);
  void cmdScrollToBottomRight(GString *args[], int nArgs, XEvent *event);
  void cmdScrollToLeftEdge(GString *args[], int nArgs, XEvent *event);
  void cmdScrollToRightEdge(GString *args[], int nArgs, XEvent *event);
  void cmdScrollToTopEdge(GString *args[], int nArgs, XEvent *event);
  void cmdScrollToTopLeft(GString *args[], int nArgs, XEvent *event);
  void cmdScrollUp(GString *args[], int nArgs, XEvent *event);
  void cmdScrollUpPrevPage(GString *args[], int nArgs, XEvent *event);
  void cmdSinglePageMode(GString *args[], int nArgs, XEvent *event);
  void cmdStartPan(GString *args[], int nArgs, XEvent *event);
  void cmdStartSelection(GString *args[], int nArgs, XEvent *event);
  void cmdToggleContinuousMode(GString *args[], int nArgs, XEvent *event);
  void cmdToggleFullScreenMode(GString *args[], int nArgs, XEvent *event);
  void cmdToggleOutline(GString *args[], int nArgs, XEvent *event);
  void cmdWindowMode(GString *args[], int nArgs, XEvent *event);
  void cmdZoomFitPage(GString *args[], int nArgs, XEvent *event);
  void cmdZoomFitWidth(GString *args[], int nArgs, XEvent *event);
  void cmdZoomIn(GString *args[], int nArgs, XEvent *event);
  void cmdZoomOut(GString *args[], int nArgs, XEvent *event);
  void cmdZoomPercent(GString *args[], int nArgs, XEvent *event);
  void cmdZoomToSelection(GString *args[], int nArgs, XEvent *event);

  //----- GUI code: main window
  void initWindow(GBool fullScreen);
  void initToolbar(Widget parent);
#ifndef DISABLE_OUTLINE
  void initPanedWin(Widget parent);
#endif
  void initCore(Widget parent, GBool fullScreen);
  void initPopupMenu();
  void addToolTip(Widget widget, char *text);
  void mapWindow();
  void closeWindow();
  int getZoomIdx();
  void setZoomIdx(int idx);
  void setZoomVal(double z);
  static void prevPageCbk(Widget widget, XtPointer ptr,
			  XtPointer callData);
  static void prevTenPageCbk(Widget widget, XtPointer ptr,
			     XtPointer callData);
  static void nextPageCbk(Widget widget, XtPointer ptr,
			  XtPointer callData);
  static void nextTenPageCbk(Widget widget, XtPointer ptr,
			     XtPointer callData);
  static void backCbk(Widget widget, XtPointer ptr,
		      XtPointer callData);
  static void forwardCbk(Widget widget, XtPointer ptr,
			 XtPointer callData);
#if USE_COMBO_BOX
  static void zoomComboBoxCbk(Widget widget, XtPointer ptr,
			      XtPointer callData);
#else
  static void zoomMenuCbk(Widget widget, XtPointer ptr,
			  XtPointer callData);
#endif
  static void findCbk(Widget widget, XtPointer ptr,
		      XtPointer callData);
  static void printCbk(Widget widget, XtPointer ptr,
		       XtPointer callData);
  static void aboutCbk(Widget widget, XtPointer ptr,
		       XtPointer callData);
  static void quitCbk(Widget widget, XtPointer ptr,
		      XtPointer callData);
  static void openCbk(Widget widget, XtPointer ptr,
		      XtPointer callData);
  static void openInNewWindowCbk(Widget widget, XtPointer ptr,
				 XtPointer callData);
  static void reloadCbk(Widget widget, XtPointer ptr,
			XtPointer callData);
  static void saveAsCbk(Widget widget, XtPointer ptr,
			XtPointer callData);
  static void continuousModeToggleCbk(Widget widget, XtPointer ptr,
				      XtPointer callData);
  static void fullScreenToggleCbk(Widget widget, XtPointer ptr,
				  XtPointer callData);
  static void rotateCCWCbk(Widget widget, XtPointer ptr,
			   XtPointer callData);
  static void rotateCWCbk(Widget widget, XtPointer ptr,
			  XtPointer callData);
  static void zoomToSelectionCbk(Widget widget, XtPointer ptr,
				 XtPointer callData);
  static void closeCbk(Widget widget, XtPointer ptr,
		       XtPointer callData);
  static void closeMsgCbk(Widget widget, XtPointer ptr,
			  XtPointer callData);
  static void pageNumCbk(Widget widget, XtPointer ptr,
			 XtPointer callData);
  static void updateCbk(void *data, GString *fileName,
			int pageNum, int numPages, char *linkString);

  //----- GUI code: outline
#ifndef DISABLE_OUTLINE
  void setupOutline();
  void setupOutlineItems(GList *items, Widget parent, UnicodeMap *uMap);
  static void outlineSelectCbk(Widget widget, XtPointer ptr,
			       XtPointer callData);
#endif

  //----- GUI code: "about" dialog
  void initAboutDialog();

  //----- GUI code: "open" dialog
  void initOpenDialog();
  void mapOpenDialog(GBool openInNewWindowA);
  static void openOkCbk(Widget widget, XtPointer ptr,
			XtPointer callData);

  //----- GUI code: "find" dialog
  void initFindDialog();
  static void findFindCbk(Widget widget, XtPointer ptr,
			  XtPointer callData);
  void mapFindDialog();
  void doFind(GBool next);
  static void findCloseCbk(Widget widget, XtPointer ptr,
			   XtPointer callData);

  //----- GUI code: "save as" dialog
  void initSaveAsDialog();
  void mapSaveAsDialog();
  static void saveAsOkCbk(Widget widget, XtPointer ptr,
			  XtPointer callData);

  //----- GUI code: "print" dialog
  void initPrintDialog();
  void setupPrintDialog();
  static void printWithCmdBtnCbk(Widget widget, XtPointer ptr,
				 XtPointer callData);
  static void printToFileBtnCbk(Widget widget, XtPointer ptr,
				XtPointer callData);
  static void printPrintCbk(Widget widget, XtPointer ptr,
			    XtPointer callData);

  //----- Motif support
  XmFontList createFontList(char *xlfd);

  static XPDFViewerCmd cmdTab[];

  XPDFApp *app;
  GBool ok;

  Display *display;
  int screenNum;
  Widget win;			// top-level window
  Widget form;
  Widget panedWin;
#ifndef DISABLE_OUTLINE
  Widget outlineScroll;
  Widget outlineTree;
  Widget *outlineLabels;
  int outlineLabelsLength;
  int outlineLabelsSize;
  Dimension outlinePaneWidth;
#endif
  XPDFCore *core;
  Widget toolBar;
  Widget backBtn;
  Widget prevTenPageBtn;
  Widget prevPageBtn;
  Widget nextPageBtn;
  Widget nextTenPageBtn;
  Widget forwardBtn;
  Widget pageNumText;
  Widget pageCountLabel;
#if USE_COMBO_BOX
  Widget zoomComboBox;
#else
  Widget zoomMenu;
  Widget zoomMenuBtns[nZoomMenuItems];
#endif
  Widget zoomWidget;
  Widget findBtn;
  Widget printBtn;
  Widget aboutBtn;
  Widget linkLabel;
  Widget quitBtn;
  Widget popupMenu;

  Widget aboutDialog;
  XmFontList aboutBigFont, aboutVersionFont, aboutFixedFont;

  Widget openDialog;
  GBool openInNewWindow;

  Widget findDialog;
  Widget findText;
  Widget findBackwardToggle;
  Widget findCaseSensitiveToggle;

  Widget saveAsDialog;

  Widget printDialog;
  Widget printWithCmdBtn;
  Widget printToFileBtn;
  Widget printCmdText;
  Widget printFileText;
  Widget printFirstPage;
  Widget printLastPage;
};

#endif
//========================================================================
//
// XRef.cc
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif

#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <ctype.h>
#include "gmem.h"
#include "Object.h"
#include "Stream.h"
#include "Lexer.h"
#include "Parser.h"
#include "Dict.h"
#include "Error.h"
#include "ErrorCodes.h"
#include "XRef.h"

//------------------------------------------------------------------------

#define xrefSearchSize 1024	// read this many bytes at end of file
				//   to look for 'startxref'

//------------------------------------------------------------------------
// Permission bits
//------------------------------------------------------------------------

#define permPrint    (1<<2)
#define permChange   (1<<3)
#define permCopy     (1<<4)
#define permNotes    (1<<5)
#define defPermFlags 0xfffc

//------------------------------------------------------------------------
// ObjectStream
//------------------------------------------------------------------------

class ObjectStream {
public:

  // Create an object stream, using object number <objStrNum>,
  // generation 0.
  ObjectStream(XRef *xref, int objStrNumA);

  ~ObjectStream();

  // Return the object number of this object stream.
  int getObjStrNum() { return objStrNum; }

  // Get the <objIdx>th object from this stream, which should be
  // object number <objNum>, generation 0.
  Object *getObject(int objIdx, int objNum, Object *obj);

private:

  int objStrNum;		// object number of the object stream
  int nObjects;			// number of objects in the stream
  Object *objs;			// the objects (length = nObjects)
  int *objNums;			// the object numbers (length = nObjects)
};

ObjectStream::ObjectStream(XRef *xref, int objStrNumA) {
  Stream *str;
  Parser *parser;
  int *offsets;
  Object objStr, obj1, obj2;
  int first, i;

  objStrNum = objStrNumA;
  nObjects = 0;
  objs = NULL;
  objNums = NULL;

  if (!xref->fetch(objStrNum, 0, &objStr)->isStream()) {
    goto err1;
  }

  if (!objStr.streamGetDict()->lookup("N", &obj1)->isInt()) {
    obj1.free();
    goto err1;
  }
  nObjects = obj1.getInt();
  obj1.free();
  if (nObjects <= 0) {
    goto err1;
  }

  if (!objStr.streamGetDict()->lookup("First", &obj1)->isInt()) {
    obj1.free();
    goto err1;
  }
  first = obj1.getInt();
  obj1.free();
  if (first < 0) {
    goto err1;
  }

  objs = new Object[nObjects];
  objNums = (int *)gmallocn(nObjects, sizeof(int));
  offsets = (int *)gmallocn(nObjects, sizeof(int));

  // parse the header: object numbers and offsets
  objStr.streamReset();
  obj1.initNull();
  str = new EmbedStream(objStr.getStream(), &obj1, gTrue, first);
  parser = new Parser(xref, new Lexer(xref, str), gFalse);
  for (i = 0; i < nObjects; ++i) {
    parser->getObj(&obj1);
    parser->getObj(&obj2);
    if (!obj1.isInt() || !obj2.isInt()) {
      obj1.free();
      obj2.free();
      delete parser;
      gfree(offsets);
      goto err1;
    }
    objNums[i] = obj1.getInt();
    offsets[i] = obj2.getInt();
    obj1.free();
    obj2.free();
    if (objNums[i] < 0 || offsets[i] < 0 ||
	(i > 0 && offsets[i] < offsets[i-1])) {
      delete parser;
      gfree(offsets);
      goto err1;
    }
  }
  while (str->getChar() != EOF) ;
  delete parser;

  // skip to the first object - this shouldn't be necessary because
  // the First key is supposed to be equal to offsets[0], but just in
  // case...
  for (i = first; i < offsets[0]; ++i) {
    objStr.getStream()->getChar();
  }

  // parse the objects
  for (i = 0; i < nObjects; ++i) {
    obj1.initNull();
    if (i == nObjects - 1) {
      str = new EmbedStream(objStr.getStream(), &obj1, gFalse, 0);
    } else {
      str = new EmbedStream(objStr.getStream(), &obj1, gTrue,
			    offsets[i+1] - offsets[i]);
    }
    parser = new Parser(xref, new Lexer(xref, str), gFalse);
    parser->getObj(&objs[i]);
    while (str->getChar() != EOF) ;
    delete parser;
  }

  gfree(offsets);

 err1:
  objStr.free();
  return;
}

ObjectStream::~ObjectStream() {
  int i;

  if (objs) {
    for (i = 0; i < nObjects; ++i) {
      objs[i].free();
    }
    delete[] objs;
  }
  gfree(objNums);
}

Object *ObjectStream::getObject(int objIdx, int objNum, Object *obj) {
  if (objIdx < 0 || objIdx >= nObjects || objNum != objNums[objIdx]) {
    return obj->initNull();
  }
  return objs[objIdx].copy(obj);
}

//------------------------------------------------------------------------
// XRef
//------------------------------------------------------------------------

XRef::XRef(BaseStream *strA) {
  Guint pos;
  Object obj;

  ok = gTrue;
  errCode = errNone;
  size = 0;
  entries = NULL;
  streamEnds = NULL;
  streamEndsLen = 0;
  objStr = NULL;

  encrypted = gFalse;
  permFlags = defPermFlags;
  ownerPasswordOk = gFalse;

  // read the trailer
  str = strA;
  start = str->getStart();
  pos = getStartXref();

  // if there was a problem with the 'startxref' position, try to
  // reconstruct the xref table
  if (pos == 0) {
    if (!(ok = constructXRef())) {
      errCode = errDamaged;
      return;
    }

  // read the xref table
  } else {
    while (readXRef(&pos)) ;

    // if there was a problem with the xref table,
    // try to reconstruct it
    if (!ok) {
      if (!(ok = constructXRef())) {
	errCode = errDamaged;
	return;
      }
    }
  }

  // get the root dictionary (catalog) object
  trailerDict.dictLookupNF("Root", &obj);
  if (obj.isRef()) {
    rootNum = obj.getRefNum();
    rootGen = obj.getRefGen();
    obj.free();
  } else {
    obj.free();
    if (!(ok = constructXRef())) {
      errCode = errDamaged;
      return;
    }
  }

  // now set the trailer dictionary's xref pointer so we can fetch
  // indirect objects from it
  trailerDict.getDict()->setXRef(this);
}

XRef::~XRef() {
  gfree(entries);
  trailerDict.free();
  if (streamEnds) {
    gfree(streamEnds);
  }
  if (objStr) {
    delete objStr;
  }
}

// Read the 'startxref' position.
Guint XRef::getStartXref() {
  char buf[xrefSearchSize+1];
  char *p;
  int c, n, i;

  // read last xrefSearchSize bytes
  str->setPos(xrefSearchSize, -1);
  for (n = 0; n < xrefSearchSize; ++n) {
    if ((c = str->getChar()) == EOF) {
      break;
    }
    buf[n] = c;
  }
  buf[n] = '\0';

  // find startxref
  for (i = n - 9; i >= 0; --i) {
    if (!strncmp(&buf[i], "startxref", 9)) {
      break;
    }
  }
  if (i < 0) {
    return 0;
  }
  for (p = &buf[i+9]; isspace(*p); ++p) ;
  lastXRefPos = strToUnsigned(p);

  return lastXRefPos;
}

// Read one xref table section.  Also reads the associated trailer
// dictionary, and returns the prev pointer (if any).
GBool XRef::readXRef(Guint *pos) {
  Parser *parser;
  Object obj;
  GBool more;

  // start up a parser, parse one token
  obj.initNull();
  parser = new Parser(NULL,
	     new Lexer(NULL,
	       str->makeSubStream(start + *pos, gFalse, 0, &obj)),
	     gTrue);
  parser->getObj(&obj);

  // parse an old-style xref table
  if (obj.isCmd("xref")) {
    obj.free();
    more = readXRefTable(parser, pos);

  // parse an xref stream
  } else if (obj.isInt()) {
    obj.free();
    if (!parser->getObj(&obj)->isInt()) {
      goto err1;
    }
    obj.free();
    if (!parser->getObj(&obj)->isCmd("obj")) {
      goto err1;
    }
    obj.free();
    if (!parser->getObj(&obj)->isStream()) {
      goto err1;
    }
    more = readXRefStream(obj.getStream(), pos);
    obj.free();

  } else {
    goto err1;
  }

  delete parser;
  return more;

 err1:
  obj.free();
  delete parser;
  ok = gFalse;
  return gFalse;
}

GBool XRef::readXRefTable(Parser *parser, Guint *pos) {
  XRefEntry entry;
  GBool more;
  Object obj, obj2;
  Guint pos2;
  int first, n, newSize, i;

  while (1) {
    parser->getObj(&obj);
    if (obj.isCmd("trailer")) {
      obj.free();
      break;
    }
    if (!obj.isInt()) {
      goto err1;
    }
    first = obj.getInt();
    obj.free();
    if (!parser->getObj(&obj)->isInt()) {
      goto err1;
    }
    n = obj.getInt();
    obj.free();
    if (first < 0 || n < 0 || first + n < 0) {
      goto err1;
    }
    if (first + n > size) {
      for (newSize = size ? 2 * size : 1024;
	   first + n > newSize && newSize > 0;
	   newSize <<= 1) ;
      if (newSize < 0) {
	goto err1;
      }
      entries = (XRefEntry *)greallocn(entries, newSize, sizeof(XRefEntry));
      for (i = size; i < newSize; ++i) {
	entries[i].offset = 0xffffffff;
	entries[i].type = xrefEntryFree;
      }
      size = newSize;
    }
    for (i = first; i < first + n; ++i) {
      if (!parser->getObj(&obj)->isInt()) {
	goto err1;
      }
      entry.offset = (Guint)obj.getInt();
      obj.free();
      if (!parser->getObj(&obj)->isInt()) {
	goto err1;
      }
      entry.gen = obj.getInt();
      obj.free();
      parser->getObj(&obj);
      if (obj.isCmd("n")) {
	entry.type = xrefEntryUncompressed;
      } else if (obj.isCmd("f")) {
	entry.type = xrefEntryFree;
      } else {
	goto err1;
      }
      obj.free();
      if (entries[i].offset == 0xffffffff) {
	entries[i] = entry;
	// PDF files of patents from the IBM Intellectual Property
	// Network have a bug: the xref table claims to start at 1
	// instead of 0.
	if (i == 1 && first == 1 &&
	    entries[1].offset == 0 && entries[1].gen == 65535 &&
	    entries[1].type == xrefEntryFree) {
	  i = first = 0;
	  entries[0] = entries[1];
	  entries[1].offset = 0xffffffff;
	}
      }
    }
  }

  // read the trailer dictionary
  if (!parser->getObj(&obj)->isDict()) {
    goto err1;
  }

  // get the 'Prev' pointer
  obj.getDict()->lookupNF("Prev", &obj2);
  if (obj2.isInt()) {
    *pos = (Guint)obj2.getInt();
    more = gTrue;
  } else if (obj2.isRef()) {
    // certain buggy PDF generators generate "/Prev NNN 0 R" instead
    // of "/Prev NNN"
    *pos = (Guint)obj2.getRefNum();
    more = gTrue;
  } else {
    more = gFalse;
  }
  obj2.free();

  // save the first trailer dictionary
  if (trailerDict.isNone()) {
    obj.copy(&trailerDict);
  }

  // check for an 'XRefStm' key
  if (obj.getDict()->lookup("XRefStm", &obj2)->isInt()) {
    pos2 = (Guint)obj2.getInt();
    readXRef(&pos2);
    if (!ok) {
      obj2.free();
      goto err1;
    }
  }
  obj2.free();

  obj.free();
  return more;

 err1:
  obj.free();
  ok = gFalse;
  return gFalse;
}

GBool XRef::readXRefStream(Stream *xrefStr, Guint *pos) {
  Dict *dict;
  int w[3];
  GBool more;
  Object obj, obj2, idx;
  int newSize, first, n, i;

  dict = xrefStr->getDict();

  if (!dict->lookupNF("Size", &obj)->isInt()) {
    goto err1;
  }
  newSize = obj.getInt();
  obj.free();
  if (newSize < 0) {
    goto err1;
  }
  if (newSize > size) {
    entries = (XRefEntry *)greallocn(entries, newSize, sizeof(XRefEntry));
    for (i = size; i < newSize; ++i) {
      entries[i].offset = 0xffffffff;
      entries[i].type = xrefEntryFree;
    }
    size = newSize;
  }

  if (!dict->lookupNF("W", &obj)->isArray() ||
      obj.arrayGetLength() < 3) {
    goto err1;
  }
  for (i = 0; i < 3; ++i) {
    if (!obj.arrayGet(i, &obj2)->isInt()) {
      obj2.free();
      goto err1;
    }
    w[i] = obj2.getInt();
    obj2.free();
    if (w[i] < 0 || w[i] > 4) {
      goto err1;
    }
  }
  obj.free();

  xrefStr->reset();
  dict->lookupNF("Index", &idx);
  if (idx.isArray()) {
    for (i = 0; i+1 < idx.arrayGetLength(); i += 2) {
      if (!idx.arrayGet(i, &obj)->isInt()) {
	idx.free();
	goto err1;
      }
      first = obj.getInt();
      obj.free();
      if (!idx.arrayGet(i+1, &obj)->isInt()) {
	idx.free();
	goto err1;
      }
      n = obj.getInt();
      obj.free();
      if (first < 0 || n < 0 ||
	  !readXRefStreamSection(xrefStr, w, first, n)) {
	idx.free();
	goto err0;
      }
    }
  } else {
    if (!readXRefStreamSection(xrefStr, w, 0, newSize)) {
      idx.free();
      goto err0;
    }
  }
  idx.free();

  dict->lookupNF("Prev", &obj);
  if (obj.isInt()) {
    *pos = (Guint)obj.getInt();
    more = gTrue;
  } else {
    more = gFalse;
  }
  obj.free();
  if (trailerDict.isNone()) {
    trailerDict.initDict(dict);
  }

  return more;

 err1:
  obj.free();
 err0:
  ok = gFalse;
  return gFalse;
}

GBool XRef::readXRefStreamSection(Stream *xrefStr, int *w, int first, int n) {
  Guint offset;
  int type, gen, c, newSize, i, j;

  if (first + n < 0) {
    return gFalse;
  }
  if (first + n > size) {
    for (newSize = size ? 2 * size : 1024;
	 first + n > newSize && newSize > 0;
	 newSize <<= 1) ;
    if (newSize < 0) {
      return gFalse;
    }
    entries = (XRefEntry *)greallocn(entries, newSize, sizeof(XRefEntry));
    for (i = size; i < newSize; ++i) {
      entries[i].offset = 0xffffffff;
      entries[i].type = xrefEntryFree;
    }
    size = newSize;
  }
  for (i = first; i < first + n; ++i) {
    if (w[0] == 0) {
      type = 1;
    } else {
      for (type = 0, j = 0; j < w[0]; ++j) {
	if ((c = xrefStr->getChar()) == EOF) {
	  return gFalse;
	}
	type = (type << 8) + c;
      }
    }
    for (offset = 0, j = 0; j < w[1]; ++j) {
      if ((c = xrefStr->getChar()) == EOF) {
	return gFalse;
      }
      offset = (offset << 8) + c;
    }
    for (gen = 0, j = 0; j < w[2]; ++j) {
      if ((c = xrefStr->getChar()) == EOF) {
	return gFalse;
      }
      gen = (gen << 8) + c;
    }
    if (entries[i].offset == 0xffffffff) {
      switch (type) {
      case 0:
	entries[i].offset = offset;
	entries[i].gen = gen;
	entries[i].type = xrefEntryFree;
	break;
      case 1:
	entries[i].offset = offset;
	entries[i].gen = gen;
	entries[i].type = xrefEntryUncompressed;
	break;
      case 2:
	entries[i].offset = offset;
	entries[i].gen = gen;
	entries[i].type = xrefEntryCompressed;
	break;
      default:
	return gFalse;
      }
    }
  }

  return gTrue;
}

// Attempt to construct an xref table for a damaged file.
GBool XRef::constructXRef() {
  Parser *parser;
  Object newTrailerDict, obj;
  char buf[256];
  Guint pos;
  int num, gen;
  int newSize;
  int streamEndsSize;
  char *p;
  int i;
  GBool gotRoot;

  gfree(entries);
  size = 0;
  entries = NULL;

  error(-1, "PDF file is damaged - attempting to reconstruct xref table...");
  gotRoot = gFalse;
  streamEndsLen = streamEndsSize = 0;

  str->reset();
  while (1) {
    pos = str->getPos();
    if (!str->getLine(buf, 256)) {
      break;
    }
    p = buf;

    // skip whitespace
    while (*p && Lexer::isSpace(*p & 0xff)) ++p;

    // got trailer dictionary
    if (!strncmp(p, "trailer", 7)) {
      obj.initNull();
      parser = new Parser(NULL,
		 new Lexer(NULL,
		   str->makeSubStream(pos + 7, gFalse, 0, &obj)),
		 gFalse);
      parser->getObj(&newTrailerDict);
      if (newTrailerDict.isDict()) {
	newTrailerDict.dictLookupNF("Root", &obj);
	if (obj.isRef()) {
	  rootNum = obj.getRefNum();
	  rootGen = obj.getRefGen();
	  if (!trailerDict.isNone()) {
	    trailerDict.free();
	  }
	  newTrailerDict.copy(&trailerDict);
	  gotRoot = gTrue;
	}
	obj.free();
      }
      newTrailerDict.free();
      delete parser;

    // look for object
    } else if (isdigit(*p)) {
      num = atoi(p);
      if (num > 0) {
	do {
	  ++p;
	} while (*p && isdigit(*p));
	if (isspace(*p)) {
	  do {
	    ++p;
	  } while (*p && isspace(*p));
	  if (isdigit(*p)) {
	    gen = atoi(p);
	    do {
	      ++p;
	    } while (*p && isdigit(*p));
	    if (isspace(*p)) {
	      do {
		++p;
	      } while (*p && isspace(*p));
	      if (!strncmp(p, "obj", 3)) {
		if (num >= size) {
		  newSize = (num + 1 + 255) & ~255;
		  if (newSize < 0) {
		    error(-1, "Bad object number");
		    return gFalse;
		  }
		  entries = (XRefEntry *)
		      greallocn(entries, newSize, sizeof(XRefEntry));
		  for (i = size; i < newSize; ++i) {
		    entries[i].offset = 0xffffffff;
		    entries[i].type = xrefEntryFree;
		  }
		  size = newSize;
		}
		if (entries[num].type == xrefEntryFree ||
		    gen >= entries[num].gen) {
		  entries[num].offset = pos - start;
		  entries[num].gen = gen;
		  entries[num].type = xrefEntryUncompressed;
		}
	      }
	    }
	  }
	}
      }

    } else if (!strncmp(p, "endstream", 9)) {
      if (streamEndsLen == streamEndsSize) {
	streamEndsSize += 64;
	streamEnds = (Guint *)greallocn(streamEnds,
					streamEndsSize, sizeof(int));
      }
      streamEnds[streamEndsLen++] = pos;
    }
  }

  if (gotRoot)
    return gTrue;

  error(-1, "Couldn't find trailer dictionary");
  return gFalse;
}

void XRef::setEncryption(int permFlagsA, GBool ownerPasswordOkA,
			 Guchar *fileKeyA, int keyLengthA, int encVersionA,
			 CryptAlgorithm encAlgorithmA) {
  int i;

  encrypted = gTrue;
  permFlags = permFlagsA;
  ownerPasswordOk = ownerPasswordOkA;
  if (keyLengthA <= 16) {
    keyLength = keyLengthA;
  } else {
    keyLength = 16;
  }
  for (i = 0; i < keyLength; ++i) {
    fileKey[i] = fileKeyA[i];
  }
  encVersion = encVersionA;
  encAlgorithm = encAlgorithmA;
}

GBool XRef::okToPrint(GBool ignoreOwnerPW) {
  return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permPrint);
}

GBool XRef::okToChange(GBool ignoreOwnerPW) {
  return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permChange);
}

GBool XRef::okToCopy(GBool ignoreOwnerPW) {
  return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permCopy);
}

GBool XRef::okToAddNotes(GBool ignoreOwnerPW) {
  return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permNotes);
}

Object *XRef::fetch(int num, int gen, Object *obj) {
  XRefEntry *e;
  Parser *parser;
  Object obj1, obj2, obj3;

  // check for bogus ref - this can happen in corrupted PDF files
  if (num < 0 || num >= size) {
    goto err;
  }

  e = &entries[num];
  switch (e->type) {

  case xrefEntryUncompressed:
    if (e->gen != gen) {
      goto err;
    }
    obj1.initNull();
    parser = new Parser(this,
	       new Lexer(this,
		 str->makeSubStream(start + e->offset, gFalse, 0, &obj1)),
	       gTrue);
    parser->getObj(&obj1);
    parser->getObj(&obj2);
    parser->getObj(&obj3);
    if (!obj1.isInt() || obj1.getInt() != num ||
	!obj2.isInt() || obj2.getInt() != gen ||
	!obj3.isCmd("obj")) {
      obj1.free();
      obj2.free();
      obj3.free();
      delete parser;
      goto err;
    }
    parser->getObj(obj, encrypted ? fileKey : (Guchar *)NULL,
		   encAlgorithm, keyLength, num, gen);
    obj1.free();
    obj2.free();
    obj3.free();
    delete parser;
    break;

  case xrefEntryCompressed:
    if (gen != 0) {
      goto err;
    }
    if (!objStr || objStr->getObjStrNum() != (int)e->offset) {
      if (objStr) {
	delete objStr;
      }
      objStr = new ObjectStream(this, e->offset);
    }
    objStr->getObject(e->gen, num, obj);
    break;

  default:
    goto err;
  }

  return obj;

 err:
  return obj->initNull();
}

Object *XRef::getDocInfo(Object *obj) {
  return trailerDict.dictLookup("Info", obj);
}

// Added for the pdftex project.
Object *XRef::getDocInfoNF(Object *obj) {
  return trailerDict.dictLookupNF("Info", obj);
}

GBool XRef::getStreamEnd(Guint streamStart, Guint *streamEnd) {
  int a, b, m;

  if (streamEndsLen == 0 ||
      streamStart > streamEnds[streamEndsLen - 1]) {
    return gFalse;
  }

  a = -1;
  b = streamEndsLen - 1;
  // invariant: streamEnds[a] < streamStart <= streamEnds[b]
  while (b - a > 1) {
    m = (a + b) / 2;
    if (streamStart <= streamEnds[m]) {
      b = m;
    } else {
      a = m;
    }
  }
  *streamEnd = streamEnds[b];
  return gTrue;
}

Guint XRef::strToUnsigned(char *s) {
  Guint x;
  char *p;
  int i;

  x = 0;
  for (p = s, i = 0; *p && isdigit(*p) && i < 10; ++p, ++i) {
    x = 10 * x + (*p - '0');
  }
  return x;
}
//========================================================================
//
// XRef.h
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef XREF_H
#define XREF_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include "gtypes.h"
#include "Object.h"

class Dict;
class Stream;
class Parser;
class ObjectStream;

//------------------------------------------------------------------------
// XRef
//------------------------------------------------------------------------

enum XRefEntryType {
  xrefEntryFree,
  xrefEntryUncompressed,
  xrefEntryCompressed
};

struct XRefEntry {
  Guint offset;
  int gen;
  XRefEntryType type;
};

class XRef {
public:

  // Constructor.  Read xref table from stream.
  XRef(BaseStream *strA);

  // Destructor.
  ~XRef();

  // Is xref table valid?
  GBool isOk() { return ok; }

  // Get the error code (if isOk() returns false).
  int getErrorCode() { return errCode; }

  // Set the encryption parameters.
  void setEncryption(int permFlagsA, GBool ownerPasswordOkA,
		     Guchar *fileKeyA, int keyLengthA, int encVersionA,
		     CryptAlgorithm encAlgorithmA);

  // Is the file encrypted?
  GBool isEncrypted() { return encrypted; }

  // Check various permissions.
  GBool okToPrint(GBool ignoreOwnerPW = gFalse);
  GBool okToChange(GBool ignoreOwnerPW = gFalse);
  GBool okToCopy(GBool ignoreOwnerPW = gFalse);
  GBool okToAddNotes(GBool ignoreOwnerPW = gFalse);

  // Get catalog object.
  Object *getCatalog(Object *obj) { return fetch(rootNum, rootGen, obj); }

  // Fetch an indirect reference.
  Object *fetch(int num, int gen, Object *obj);

  // Return the document's Info dictionary (if any).
  Object *getDocInfo(Object *obj);
  Object *getDocInfoNF(Object *obj);

  // Return the number of objects in the xref table.
  int getNumObjects() { return size; }

  // Return the offset of the last xref table.
  Guint getLastXRefPos() { return lastXRefPos; }

  // Return the catalog object reference.
  int getRootNum() { return rootNum; }
  int getRootGen() { return rootGen; }

  // Get end position for a stream in a damaged file.
  // Returns false if unknown or file is not damaged.
  GBool getStreamEnd(Guint streamStart, Guint *streamEnd);

  // Direct access.
  int getSize() { return size; }
  XRefEntry *getEntry(int i) { return &entries[i]; }
  Object *getTrailerDict() { return &trailerDict; }

private:

  BaseStream *str;		// input stream
  Guint start;			// offset in file (to allow for garbage
				//   at beginning of file)
  XRefEntry *entries;		// xref entries
  int size;			// size of <entries> array
  int rootNum, rootGen;		// catalog dict
  GBool ok;			// true if xref table is valid
  int errCode;			// error code (if <ok> is false)
  Object trailerDict;		// trailer dictionary
  Guint lastXRefPos;		// offset of last xref table
  Guint *streamEnds;		// 'endstream' positions - only used in
				//   damaged files
  int streamEndsLen;		// number of valid entries in streamEnds
  ObjectStream *objStr;		// cached object stream
  GBool encrypted;		// true if file is encrypted
  int permFlags;		// permission bits
  GBool ownerPasswordOk;	// true if owner password is correct
  Guchar fileKey[16];		// file decryption key
  int keyLength;		// length of key, in bytes
  int encVersion;		// encryption version
  CryptAlgorithm encAlgorithm;	// encryption algorithm

  Guint getStartXref();
  GBool readXRef(Guint *pos);
  GBool readXRefTable(Parser *parser, Guint *pos);
  GBool readXRefStreamSection(Stream *xrefStr, int *w, int first, int n);
  GBool readXRefStream(Stream *xrefStr, Guint *pos);
  GBool constructXRef();
  Guint strToUnsigned(char *s);
};

#endif
//========================================================================
//
// XpdfPluginAPI.cc
//
// Copyright 2004 Glyph & Cog, LLC
//
//========================================================================

#include "aconf.h"

#ifdef ENABLE_PLUGINS

#include "gmem.h"
#include "GlobalParams.h"
#include "Object.h"
#include "PDFDoc.h"
#ifdef WIN32
#include "WinPDFCore.h"
#else
#include "XPDFCore.h"
#endif
#include "XpdfPluginAPI.h"

//------------------------------------------------------------------------

//~ This should use a pool of Objects; change xpdfFreeObj to match.
static Object *allocObj() {
  return (Object *)gmalloc(sizeof(Object));
}

//------------------------------------------------------------------------
// Document access functions
//------------------------------------------------------------------------

XpdfObject _xpdfGetInfoDict(XpdfDoc doc) {
  Object *obj;

  obj = allocObj();
  return (XpdfObject)((PDFDoc *)doc)->getDocInfo(obj);
}

XpdfObject _xpdfGetCatalog(XpdfDoc doc) {
  Object *obj;

  obj = allocObj();
  return (XpdfObject)((PDFDoc *)doc)->getXRef()->getCatalog(obj);
}

#ifdef _WIN32

HWND _xpdfWin32GetWindow(XpdfDoc doc) {
  WinPDFCore *core;

  if (!(core = (WinPDFCore *)((PDFDoc *)doc)->getGUIData())) {
    return NULL;
  }
  return core->getDrawFrame();
}

#else

Widget _xpdfXGetWindow(XpdfDoc doc) {
  XPDFCore *core;

  if (!(core = (XPDFCore *)((PDFDoc *)doc)->getGUIData())) {
    return NULL;
  }
  return core->getWidget();
}

#endif

//------------------------------------------------------------------------
// Object access functions.
//------------------------------------------------------------------------

XpdfBool _xpdfObjIsBool(XpdfObject obj) {
  return (XpdfBool)((Object *)obj)->isBool();
}

XpdfBool _xpdfObjIsInt(XpdfObject obj) {
  return (XpdfBool)((Object *)obj)->isInt();
}

XpdfBool _xpdfObjIsReal(XpdfObject obj) {
  return (XpdfBool)((Object *)obj)->isReal();
}

XpdfBool _xpdfObjIsNumber(XpdfObject obj) {
  return (XpdfBool)((Object *)obj)->isNum();
}

XpdfBool _xpdfObjIsString(XpdfObject obj) {
  return (XpdfBool)((Object *)obj)->isString();
}

XpdfBool _xpdfObjIsName(XpdfObject obj) {
  return (XpdfBool)((Object *)obj)->isName();
}

XpdfBool _xpdfObjIsNull(XpdfObject obj) {
  return (XpdfBool)((Object *)obj)->isNull();
}

XpdfBool _xpdfObjIsArray(XpdfObject obj) {
  return (XpdfBool)((Object *)obj)->isArray();
}

XpdfBool _xpdfObjIsDict(XpdfObject obj) {
  return (XpdfBool)((Object *)obj)->isDict();
}

XpdfBool _xpdfObjIsStream(XpdfObject obj) {
  return (XpdfBool)((Object *)obj)->isStream();
}

XpdfBool _xpdfObjIsRef(XpdfObject obj) {
  return (XpdfBool)((Object *)obj)->isRef();
}

XpdfBool _xpdfBoolValue(XpdfObject obj) {
  return (XpdfBool)((Object *)obj)->getBool();
}

int _xpdfIntValue(XpdfObject obj) {
  if (!((Object *)obj)->isInt()) {
    return 0;
  }
  return ((Object *)obj)->getInt();
}

double _xpdfRealValue(XpdfObject obj) {
  if (!((Object *)obj)->isReal()) {
    return 0;
  }
  return ((Object *)obj)->getReal();
}

double _xpdfNumberValue(XpdfObject obj) {
  if (!((Object *)obj)->isNum()) {
    return 0;
  }
  return ((Object *)obj)->getNum();
}

int _xpdfStringLength(XpdfObject obj) {
  if (!((Object *)obj)->isString()) {
    return 0;
  }
  return ((Object *)obj)->getString()->getLength();
}

char *_xpdfStringValue(XpdfObject obj) {
  if (!((Object *)obj)->isString()) {
    return 0;
  }
  return ((Object *)obj)->getString()->getCString();
}

char *_xpdfNameValue(XpdfObject obj) {
  if (!((Object *)obj)->isName()) {
    return NULL;
  }
  return ((Object *)obj)->getName();
}

int _xpdfArrayLength(XpdfObject obj) {
  if (!((Object *)obj)->isArray()) {
    return 0;
  }
  return ((Object *)obj)->arrayGetLength();
}

XpdfObject _xpdfArrayGet(XpdfObject obj, int idx) {
  Object *elem;

  elem = allocObj();
  if (!((Object *)obj)->isArray()) {
    return (XpdfObject)elem->initNull();
  }
  return (XpdfObject)((Object *)obj)->arrayGet(idx, elem);
}

XpdfObject _xpdfDictGet(XpdfObject obj, char *key) {
  Object *elem;

  elem = allocObj();
  if (!((Object *)obj)->isDict()) {
    return (XpdfObject)elem->initNull();
  }
  return (XpdfObject)((Object *)obj)->dictLookup(key, elem);
}

void _xpdfFreeObj(XpdfObject obj) {
  ((Object *)obj)->free();
  gfree(obj);
}

//------------------------------------------------------------------------
// Memory allocation functions
//------------------------------------------------------------------------

void *_xpdfMalloc(int size) {
  return gmalloc(size);
}

void *_xpdfRealloc(void *p, int size) {
  return grealloc(p, size);
}

void _xpdfFree(void *p) {
  gfree(p);
}

//------------------------------------------------------------------------
// Security handlers
//------------------------------------------------------------------------

void _xpdfRegisterSecurityHandler(XpdfSecurityHandler *handler) {
  if (handler->version <= xpdfPluginAPIVersion) {
    globalParams->addSecurityHandler(handler);
  }
}

//------------------------------------------------------------------------

XpdfPluginVecTable xpdfPluginVecTable = {
  xpdfPluginAPIVersion,
  &_xpdfGetInfoDict,
  &_xpdfGetCatalog,
#ifdef _WIN32
  &_xpdfWin32GetWindow,
#else
  &_xpdfXGetWindow,
#endif
  &_xpdfObjIsBool,
  &_xpdfObjIsInt,
  &_xpdfObjIsReal,
  &_xpdfObjIsString,
  &_xpdfObjIsName,
  &_xpdfObjIsNull,
  &_xpdfObjIsArray,
  &_xpdfObjIsDict,
  &_xpdfObjIsStream,
  &_xpdfObjIsRef,
  &_xpdfBoolValue,
  &_xpdfIntValue,
  &_xpdfRealValue,
  &_xpdfStringLength,
  &_xpdfStringValue,
  &_xpdfNameValue,
  &_xpdfArrayLength,
  &_xpdfArrayGet,
  &_xpdfDictGet,
  &_xpdfFreeObj,
  &_xpdfMalloc,
  &_xpdfRealloc,
  &_xpdfFree,
  &_xpdfRegisterSecurityHandler,
};

#endif // ENABLE_PLUGINS
/*
 * XpdfPluginAPI.h
 *
 * Copyright 2004 Glyph & Cog, LLC
 */

#ifndef XPDFPLUGINAPI_H
#define XPDFPLUGINAPI_H

#ifdef _WIN32
#include <windows.h>
#else
#define Object XtObject
#include <X11/Intrinsic.h>
#undef Object
#endif

#ifdef __cplusplus
extern "C" {
#endif

/*------------------------------------------------------------------------
 * Macros
 *------------------------------------------------------------------------*/

/*
 * The current API version.
 */
#define xpdfPluginAPIVersion 1

#ifdef _WIN32
#  ifdef __cplusplus
#    define PLUGINFUNC(retType) extern "C" __declspec(dllexport) retType
#  else
#    define PLUGINFUNC(retType) extern __declspec(dllexport) retType
#  endif
#else
#  ifdef __cplusplus
#    define PLUGINFUNC(retType) extern "C" retType
#  else
#    define PLUGINFUNC(retType) extern retType
#  endif
#endif

/*------------------------------------------------------------------------
 * Plugin setup/cleanup
 *------------------------------------------------------------------------*/

/*
 * All plugins are required to implement two functions:
 *
 * -- Initialize the plugin.  Returns non-zero if successful.
 * PLUGINFUNC(XpdfBool) xpdfInitPlugin(void);
 *
 * -- Free the plugin.
 * PLUGINFUNC(void) xpdfFreePlugin(void);
 */

/*------------------------------------------------------------------------
 * Types
 *------------------------------------------------------------------------*/

/*
 * Standard C boolean -- zero = false, non-zero = true.
 */
typedef int XpdfBool;
#define xpdfTrue  1
#define xpdfFalse 0

/*
 * PDF document handle.
 */
typedef struct _XpdfDoc *XpdfDoc;

/*
 * PDF object handle.
 */
typedef struct _XpdfObject *XpdfObject;

/*
 * Document access permissions.  Any of these can be bitwise 'or'ed
 * together.  If xpdfPermissionOpen is not included, the document
 * cannot be opened at all, and the other bits are ignored.
 */
typedef unsigned int XpdfPermission;
#define xpdfPermissionOpen     (1 << 0)
#define xpdfPermissionPrint    (1 << 2)
#define xpdfPermissionChange   (1 << 3)
#define xpdfPermissionCopy     (1 << 4)
#define xpdfPermissionNotes    (1 << 5)

/*------------------------------------------------------------------------
 * Security handler
 *------------------------------------------------------------------------*/

/*
 * XpdfSecurityHandler - a security handler plugin should create one
 * of these and pass it to xpdfRegisterSecurityHandler.
 */
#ifdef __cplusplus
struct XpdfSecurityHandler {
#else
typedef struct {
#endif

  /*
   * Version of the security handler spec (this document) -- use
   * xpdfPluginAPIVersion.
   */
  int version;

  /*
   * Security handler name.
   */
  char *name;

  /*
   * Any global data the security handler needs.  XpdfViewer will pass
   * this pointer to all handler functions as the <handlerData>
   * argument.
   */
  void *handlerData;

  /*
   * Allocate and initialize data for a new document.  XpdfViewer will
   * pass the returned pointer to all other handler functions as the
   * <docData> argument.  Returns non-zero if successful.
   */
  XpdfBool (*newDoc)(void *handlerData, XpdfDoc doc,
		     XpdfObject encryptDict, void **docData);

  /*
   * Free the data allocated by newDoc.
   */
  void (*freeDoc)(void *handlerData, void *docData);

  /*
   * Construct authorization data based on the supplied owner and user
   * passwords (either or both of which may be NULL).  This function
   * is called in "batch" mode, i.e., if the password was supplied on
   * the command line or via an Xpdf library API.  It should not
   * generate any user interaction (e.g., a password dialog).  It is
   * not required to support this function: the makeAuthData function
   * pointer can be set to NULL.  Returns non-zero if successful.
   */
  XpdfBool (*makeAuthData)(void *handlerData, void *docData,
			   char *ownerPassword, char *userPassword,
			   void **authData);

  /*
   * Request any needed information (e.g., a password) from the user,
   * and construct an authorization data object.  Returns non-zero if
   * successful.
   */
  XpdfBool (*getAuthData)(void *handlerData, void *docData,
			  void **authData);

  /*
   * Free the data allocated by getAuthData.
   */
  void (*freeAuthData)(void *handlerData, void *docData,
		       void *authData);

  /*
   * Request permission to access the document.  This returns all
   * permissions granted by authData.
   */
  XpdfPermission (*authorize)(void *handlerData, void *docData,
			      void *authData);

  /*
   * Get the decryption key and algorithm version associated with the
   * document.  Returns non-zero if successful.
   */
  XpdfBool (*getKey)(void *handlerData, void *docData,
		     char **key, int *keyLen, int *cryptVersion);

  /*
   * Free the data allocated by getKey.
   */
  void (*freeKey)(void *handlerData, void *docData,
		  char *key, int keyLen);

#ifdef __cplusplus
};
#else
} XpdfSecurityHandler;
#endif

/*------------------------------------------------------------------------*/

typedef struct {
  int version;

/*------------------------------------------------------------------------
 * Document access functions
 *------------------------------------------------------------------------*/

/*
 * Get a document's info dictionary.  (The returned object must be
 * freed with xpdfFreeObj.)
 */
XpdfObject (*_xpdfGetInfoDict)(XpdfDoc doc);

/*
 * Get a document's catalog ("root") dictionary.  (The returned object
 * must be freed with xpdfFreeObj.)
 */
XpdfObject (*_xpdfGetCatalog)(XpdfDoc doc);

#ifdef _WIN32

/*
 * Get the handle for the viewer window associated with the specified
 * document.  [Win32 only]
 */
HWND (*_xpdfWin32GetWindow)(XpdfDoc doc);

#else

/*
 * Get the Motif widget for the viewer window associated with the
 * specified document.  [X only]
 */
Widget (*_xpdfXGetWindow)(XpdfDoc doc);

#endif

/*------------------------------------------------------------------------
 * Object access functions
 *------------------------------------------------------------------------*/

/*
 * Check an object's type.
 */
XpdfBool (*_xpdfObjIsBool)(XpdfObject obj);
XpdfBool (*_xpdfObjIsInt)(XpdfObject obj);
XpdfBool (*_xpdfObjIsReal)(XpdfObject obj);
XpdfBool (*_xpdfObjIsString)(XpdfObject obj);
XpdfBool (*_xpdfObjIsName)(XpdfObject obj);
XpdfBool (*_xpdfObjIsNull)(XpdfObject obj);
XpdfBool (*_xpdfObjIsArray)(XpdfObject obj);
XpdfBool (*_xpdfObjIsDict)(XpdfObject obj);
XpdfBool (*_xpdfObjIsStream)(XpdfObject obj);
XpdfBool (*_xpdfObjIsRef)(XpdfObject obj);

/*
 * Value access.
 * (Objects returned by xpdfArrayGet and xpdfDictGet must be freed
 * with xpdfFreeObj.)
 */
XpdfBool (*_xpdfBoolValue)(XpdfObject obj);
int (*_xpdfIntValue)(XpdfObject obj);
double (*_xpdfRealValue)(XpdfObject obj);
int (*_xpdfStringLength)(XpdfObject obj);
char *(*_xpdfStringValue)(XpdfObject obj);
char *(*_xpdfNameValue)(XpdfObject obj);
int (*_xpdfArrayLength)(XpdfObject obj);
XpdfObject (*_xpdfArrayGet)(XpdfObject obj, int idx);
XpdfObject (*_xpdfDictGet)(XpdfObject obj, char *key);

/*
 * Object destruction.  NB: *all* objects must be freed after use.
 */
void (*_xpdfFreeObj)(XpdfObject obj);

/*------------------------------------------------------------------------
 * Memory allocation functions
 *------------------------------------------------------------------------*/

void *(*_xpdfMalloc)(int size);
void *(*_xpdfRealloc)(void *p, int size);
void (*_xpdfFree)(void *p);

/*------------------------------------------------------------------------
 * Security handler functions
 *------------------------------------------------------------------------*/

/*
 * Register a new security handler.
 */
void (*_xpdfRegisterSecurityHandler)(XpdfSecurityHandler *handler);

/*------------------------------------------------------------------------*/

} XpdfPluginVecTable;

#ifdef _WIN32

extern __declspec(dllexport) XpdfPluginVecTable xpdfPluginVecTable;

#define xpdfPluginSetup \
  extern __declspec(dllexport) \
  XpdfPluginVecTable xpdfPluginVecTable = {xpdfPluginAPIVersion};

#else

extern XpdfPluginVecTable xpdfPluginVecTable;

#define xpdfPluginSetup \
  XpdfPluginVecTable xpdfPluginVecTable = {xpdfPluginAPIVersion};

#endif

#define xpdfGetInfoDict (*xpdfPluginVecTable._xpdfGetInfoDict)
#define xpdfGetCatalog (*xpdfPluginVecTable._xpdfGetCatalog)
#ifdef _WIN32
#define xpdfWin32GetWindow (*xpdfPluginVecTable._xpdfWin32GetWindow)
#else
#define xpdfXGetWindow (*xpdfPluginVecTable._xpdfXGetWindow)
#endif
#define xpdfObjIsBool (*xpdfPluginVecTable._xpdfObjIsBool)
#define xpdfObjIsInt (*xpdfPluginVecTable._xpdfObjIsInt)
#define xpdfObjIsReal (*xpdfPluginVecTable._xpdfObjIsReal)
#define xpdfObjIsString (*xpdfPluginVecTable._xpdfObjIsString)
#define xpdfObjIsName (*xpdfPluginVecTable._xpdfObjIsName)
#define xpdfObjIsNull (*xpdfPluginVecTable._xpdfObjIsNull)
#define xpdfObjIsArray (*xpdfPluginVecTable._xpdfObjIsArray)
#define xpdfObjIsDict (*xpdfPluginVecTable._xpdfObjIsDict)
#define xpdfObjIsStream (*xpdfPluginVecTable._xpdfObjIsStream)
#define xpdfObjIsRef (*xpdfPluginVecTable._xpdfObjIsRef)
#define xpdfBoolValue (*xpdfPluginVecTable._xpdfBoolValue)
#define xpdfIntValue (*xpdfPluginVecTable._xpdfIntValue)
#define xpdfRealValue (*xpdfPluginVecTable._xpdfRealValue)
#define xpdfStringLength (*xpdfPluginVecTable._xpdfStringLength)
#define xpdfStringValue (*xpdfPluginVecTable._xpdfStringValue)
#define xpdfNameValue (*xpdfPluginVecTable._xpdfNameValue)
#define xpdfArrayLength (*xpdfPluginVecTable._xpdfArrayLength)
#define xpdfArrayGet (*xpdfPluginVecTable._xpdfArrayGet)
#define xpdfDictGet (*xpdfPluginVecTable._xpdfDictGet)
#define xpdfFreeObj (*xpdfPluginVecTable._xpdfFreeObj)
#define xpdfMalloc (*xpdfPluginVecTable._xpdfMalloc)
#define xpdfRealloc (*xpdfPluginVecTable._xpdfRealloc)
#define xpdfFree (*xpdfPluginVecTable._xpdfFree)
#define xpdfRegisterSecurityHandler (*xpdfPluginVecTable._xpdfRegisterSecurityHandler)

#ifdef __cplusplus
}
#endif

#endif
//========================================================================
//
// about-text.h
//
// Copyright 2002-2007 Glyph & Cog, LLC
//
//========================================================================

static char *aboutWinText[] = {
  "http://www.foolabs.com/xpdf/",
  "derekn@foolabs.com",
  " ",
  "Licensed under the GNU General Public License (GPL).",
  "See the 'COPYING' file for details.",
  " ",
  "Supports PDF version " supportedPDFVersionStr ".",
  " ",
  "The PDF data structures, operators, and specification",
  "are copyright 1985-2006 Adobe Systems Inc.",
  " ",
  "Mouse bindings:",
  "  button 1: select text / follow link",
  "  button 2: pan window",
  "  button 3: menu",
  " ",
  "Key bindings:",
  "  o              = open file",
  "  r              = reload",
  "  f / ctrl-F     = find text",
  "  ctrl-G         = find next",
  "  ctrl-P         = print",
  "  n              = next page",
  "  p              = previous page",
  "  <PgDn> = <space>                = scroll down",
  "  <PgUp> = <backspace> = <delete> = scroll up",
  "  v              = forward (history path)",
  "  b              = backward (history path)",
  "  0 / + / -      = zoom zero / in / out",
  "  z / w          = zoom page / page width",
  "  alt-F          = toggle full-screen mode",
  "  ctrl-L         = redraw",
  "  q              = quit",
  "  <home> / <end> = top / bottom of page",
  "  <arrows>       = scroll",
  " ",
  "For more information, please read the xpdf(1) man page.",
  NULL
};
#define about_width 10
#define about_height 15
static unsigned char about_bits[] = {
   0x78, 0x00, 0xfc, 0x00, 0xce, 0x01, 0x86, 0x01, 0x80, 0x01, 0x80, 0x01,
   0xc0, 0x01, 0xe0, 0x00, 0x70, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00,
   0x00, 0x00, 0x30, 0x00, 0x30, 0x00};
#define backArrow_width 16
#define backArrow_height 15
static unsigned char backArrow_bits[] = {
   0x80, 0x00, 0xc0, 0x00, 0xe0, 0x00, 0xf0, 0x00, 0xf8, 0x00, 0xfc, 0xcc,
   0xfe, 0xcc, 0xff, 0xcc, 0xfe, 0xcc, 0xfc, 0xcc, 0xf8, 0xcc, 0xf0, 0x00,
   0xe0, 0x00, 0xc0, 0x00, 0x80, 0x00};
#define backArrowDis_width 16
#define backArrowDis_height 15
static unsigned char backArrowDis_bits[] = {
   0x80, 0x00, 0x40, 0x00, 0xa0, 0x00, 0x50, 0x00, 0xa8, 0x00, 0x54, 0x44,
   0xaa, 0x88, 0x55, 0x44, 0xaa, 0x88, 0x54, 0x44, 0xa8, 0x88, 0x50, 0x00,
   0xa0, 0x00, 0x40, 0x00, 0x80, 0x00};
//========================================================================
//
// config.h
//
// Copyright 1996-2007 Glyph & Cog, LLC
//
//========================================================================

#ifndef CONFIG_H
#define CONFIG_H

//------------------------------------------------------------------------
// version
//------------------------------------------------------------------------

// xpdf version
#define xpdfVersion          "3.02"
#define xpdfVersionNum       3.02
#define xpdfMajorVersion     3
#define xpdfMinorVersion     2
#define xpdfUpdateVersion    0
#define xpdfMajorVersionStr  "3"
#define xpdfMinorVersionStr  "2"
#define xpdfUpdateVersionStr "0"

// supported PDF version
#define supportedPDFVersionStr "1.7"
#define supportedPDFVersionNum 1.7

// copyright notice
#define xpdfCopyright "Copyright 1996-2007 Glyph & Cog, LLC"

// Windows resource file stuff
#define winxpdfVersion "WinXpdf 3.02"
#define xpdfCopyrightAmp "Copyright 1996-2007 Glyph && Cog, LLC"

//------------------------------------------------------------------------
// paper size
//------------------------------------------------------------------------

// default paper size (in points) for PostScript output
#ifdef A4_PAPER
#define defPaperWidth  595    // ISO A4 (210x297 mm)
#define defPaperHeight 842
#else
#define defPaperWidth  612    // American letter (8.5x11")
#define defPaperHeight 792
#endif

//------------------------------------------------------------------------
// config file (xpdfrc) path
//------------------------------------------------------------------------

// user config file name, relative to the user's home directory
#if defined(VMS) || (defined(WIN32) && !defined(__CYGWIN32__))
#define xpdfUserConfigFile "xpdfrc"
#else
#define xpdfUserConfigFile ".xpdfrc"
#endif

// system config file name (set via the configure script)
#ifdef SYSTEM_XPDFRC
#define xpdfSysConfigFile SYSTEM_XPDFRC
#else
// under Windows, we get the directory with the executable and then
// append this file name
#define xpdfSysConfigFile "xpdfrc"
#endif

//------------------------------------------------------------------------
// X-related constants
//------------------------------------------------------------------------

// default maximum size of color cube to allocate
#define defaultRGBCube 5

// number of fonts (combined t1lib, FreeType, X server) to cache
#define xOutFontCacheSize 64

// number of Type 3 fonts to cache
#define xOutT3FontCacheSize 8

//------------------------------------------------------------------------
// popen
//------------------------------------------------------------------------

#if defined(_MSC_VER) || defined(__BORLANDC__)
#define popen _popen
#define pclose _pclose
#endif

#if defined(VMS) || defined(VMCMS) || defined(DOS) || defined(OS2) || defined(__EMX__) || defined(WIN32) || defined(__DJGPP__) || defined(MACOS)
#define POPEN_READ_MODE "rb"
#else
#define POPEN_READ_MODE "r"
#endif

//------------------------------------------------------------------------
// Win32 stuff
//------------------------------------------------------------------------

#ifdef CDECL
#undef CDECL
#endif

#if defined(_MSC_VER) || defined(__BORLANDC__)
#define CDECL __cdecl
#else
#define CDECL
#endif

#endif
#define dblLeftArrow_width 16
#define dblLeftArrow_height 15
static unsigned char dblLeftArrow_bits[] = {
   0x80, 0x80, 0xc0, 0xc0, 0xe0, 0xe0, 0xf0, 0xf0, 0xf8, 0xf8, 0xfc, 0xfc,
   0xfe, 0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xfc, 0xfc, 0xf8, 0xf8, 0xf0, 0xf0,
   0xe0, 0xe0, 0xc0, 0xc0, 0x80, 0x80};
#define dblLeftArrowDis_width 16
#define dblLeftArrowDis_height 15
static unsigned char dblLeftArrowDis_bits[] = {
   0x80, 0x80, 0x40, 0x40, 0xa0, 0xa0, 0x50, 0x50, 0xa8, 0xa8, 0x54, 0x54,
   0xaa, 0xaa, 0x55, 0x55, 0xaa, 0xaa, 0x54, 0x54, 0xa8, 0xa8, 0x50, 0x50,
   0xa0, 0xa0, 0x40, 0x40, 0x80, 0x80};
#define dblRightArrow_width 16
#define dblRightArrow_height 15
static unsigned char dblRightArrow_bits[] = {
   0x01, 0x01, 0x03, 0x03, 0x07, 0x07, 0x0f, 0x0f, 0x1f, 0x1f, 0x3f, 0x3f,
   0x7f, 0x7f, 0xff, 0xff, 0x7f, 0x7f, 0x3f, 0x3f, 0x1f, 0x1f, 0x0f, 0x0f,
   0x07, 0x07, 0x03, 0x03, 0x01, 0x01};
#define dblRightArrowDis_width 16
#define dblRightArrowDis_height 15
static unsigned char dblRightArrowDis_bits[] = {
   0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x0a, 0x0a, 0x15, 0x15, 0x2a, 0x2a,
   0x55, 0x55, 0xaa, 0xaa, 0x55, 0x55, 0x2a, 0x2a, 0x15, 0x15, 0x0a, 0x0a,
   0x05, 0x05, 0x02, 0x02, 0x01, 0x01};
#define find_width 15
#define find_height 15
static unsigned char find_bits[] = {
   0x38, 0x0e, 0x28, 0x0a, 0x2e, 0x3a, 0xfe, 0x3f, 0x7f, 0x7f, 0x61, 0x43,
   0x61, 0x43, 0x61, 0x43, 0x61, 0x43, 0xe1, 0x43, 0x21, 0x42, 0x21, 0x42,
   0x21, 0x42, 0x21, 0x42, 0x3f, 0x7e};
#define findDis_width 15
#define findDis_height 15
static unsigned char findDis_bits[] = {
   0x10, 0x04, 0x28, 0x0a, 0x04, 0x10, 0xaa, 0x2a, 0x55, 0x55, 0x20, 0x02,
   0x41, 0x41, 0x20, 0x02, 0x41, 0x41, 0xa0, 0x02, 0x01, 0x40, 0x20, 0x02,
   0x01, 0x40, 0x20, 0x02, 0x15, 0x54};
#define forwardArrow_width 16
#define forwardArrow_height 15
static unsigned char forwardArrow_bits[] = {
   0x00, 0x01, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0f, 0x00, 0x1f, 0x33, 0x3f,
   0x33, 0x7f, 0x33, 0xff, 0x33, 0x7f, 0x33, 0x3f, 0x00, 0x1f, 0x00, 0x0f,
   0x00, 0x07, 0x00, 0x03, 0x00, 0x01};
#define forwardArrowDis_width 16
#define forwardArrowDis_height 15
static unsigned char forwardArrowDis_bits[] = {
   0x00, 0x01, 0x00, 0x02, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x15, 0x22, 0x2a,
   0x11, 0x55, 0x22, 0xaa, 0x11, 0x55, 0x22, 0x2a, 0x00, 0x15, 0x00, 0x0a,
   0x00, 0x05, 0x00, 0x02, 0x00, 0x01};
#define leftArrow_width 8
#define leftArrow_height 15
static unsigned char leftArrow_bits[] = {
   0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff, 0xfe, 0xfc, 0xf8, 0xf0,
   0xe0, 0xc0, 0x80};
#define leftArrowDis_width 8
#define leftArrowDis_height 15
static unsigned char leftArrowDis_bits[] = {
   0x80, 0x40, 0xa0, 0x50, 0xa8, 0x54, 0xaa, 0x55, 0xaa, 0x54, 0xa8, 0x50,
   0xa0, 0x40, 0x80};
//========================================================================
//
// pdffonts.cc
//
// Copyright 2001-2007 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <math.h>
#include "parseargs.h"
#include "GString.h"
#include "gmem.h"
#include "GlobalParams.h"
#include "Error.h"
#include "Object.h"
#include "Dict.h"
#include "GfxFont.h"
#include "Annot.h"
#include "PDFDoc.h"
#include "config.h"

// NB: this must match the definition of GfxFontType in GfxFont.h.
static char *fontTypeNames[] = {
  "unknown",
  "Type 1",
  "Type 1C",
  "Type 1C (OT)",
  "Type 3",
  "TrueType",
  "TrueType (OT)",
  "CID Type 0",
  "CID Type 0C",
  "CID Type 0C (OT)",
  "CID TrueType",
  "CID TrueType (OT)"
};

static void scanFonts(Dict *resDict, PDFDoc *doc);
static void scanFont(GfxFont *font, PDFDoc *doc);

static int firstPage = 1;
static int lastPage = 0;
static char ownerPassword[33] = "\001";
static char userPassword[33] = "\001";
static char cfgFileName[256] = "";
static GBool printVersion = gFalse;
static GBool printHelp = gFalse;

static ArgDesc argDesc[] = {
  {"-f",      argInt,      &firstPage,     0,
   "first page to examine"},
  {"-l",      argInt,      &lastPage,      0,
   "last page to examine"},
  {"-opw",    argString,   ownerPassword,  sizeof(ownerPassword),
   "owner password (for encrypted files)"},
  {"-upw",    argString,   userPassword,   sizeof(userPassword),
   "user password (for encrypted files)"},
  {"-cfg",        argString,      cfgFileName,    sizeof(cfgFileName),
   "configuration file to use in place of .xpdfrc"},
  {"-v",      argFlag,     &printVersion,  0,
   "print copyright and version info"},
  {"-h",      argFlag,     &printHelp,     0,
   "print usage information"},
  {"-help",   argFlag,     &printHelp,     0,
   "print usage information"},
  {"--help",  argFlag,     &printHelp,     0,
   "print usage information"},
  {"-?",      argFlag,     &printHelp,     0,
   "print usage information"},
  {NULL}
};

static Ref *fonts;
static int fontsLen;
static int fontsSize;

int main(int argc, char *argv[]) {
  PDFDoc *doc;
  GString *fileName;
  GString *ownerPW, *userPW;
  GBool ok;
  Page *page;
  Dict *resDict;
  Annots *annots;
  Object obj1, obj2;
  int pg, i;
  int exitCode;

  exitCode = 99;

  // parse args
  ok = parseArgs(argDesc, &argc, argv);
  if (!ok || argc != 2 || printVersion || printHelp) {
    fprintf(stderr, "pdffonts version %s\n", xpdfVersion);
    fprintf(stderr, "%s\n", xpdfCopyright);
    if (!printVersion) {
      printUsage("pdffonts", "<PDF-file>", argDesc);
    }
    goto err0;
  }
  fileName = new GString(argv[1]);

  // read config file
  globalParams = new GlobalParams(cfgFileName);

  // open PDF file
  if (ownerPassword[0] != '\001') {
    ownerPW = new GString(ownerPassword);
  } else {
    ownerPW = NULL;
  }
  if (userPassword[0] != '\001') {
    userPW = new GString(userPassword);
  } else {
    userPW = NULL;
  }
  doc = new PDFDoc(fileName, ownerPW, userPW);
  if (userPW) {
    delete userPW;
  }
  if (ownerPW) {
    delete ownerPW;
  }
  if (!doc->isOk()) {
    exitCode = 1;
    goto err1;
  }

  // get page range
  if (firstPage < 1) {
    firstPage = 1;
  }
  if (lastPage < 1 || lastPage > doc->getNumPages()) {
    lastPage = doc->getNumPages();
  }

  // scan the fonts
  printf("name                                 type              emb sub uni object ID\n");
  printf("------------------------------------ ----------------- --- --- --- ---------\n");
  fonts = NULL;
  fontsLen = fontsSize = 0;
  for (pg = firstPage; pg <= lastPage; ++pg) {
    page = doc->getCatalog()->getPage(pg);
    if ((resDict = page->getResourceDict())) {
      scanFonts(resDict, doc);
    }
    annots = new Annots(doc->getXRef(), doc->getCatalog(),
			page->getAnnots(&obj1));
    obj1.free();
    for (i = 0; i < annots->getNumAnnots(); ++i) {
      if (annots->getAnnot(i)->getAppearance(&obj1)->isStream()) {
	obj1.streamGetDict()->lookup("Resources", &obj2);
	if (obj2.isDict()) {
	  scanFonts(obj2.getDict(), doc);
	}
	obj2.free();
      }
      obj1.free();
    }
    delete annots;
  }

  exitCode = 0;

  // clean up
  gfree(fonts);
 err1:
  delete doc;
  delete globalParams;
 err0:

  // check for memory leaks
  Object::memCheck(stderr);
  gMemReport(stderr);

  return exitCode;
}

static void scanFonts(Dict *resDict, PDFDoc *doc) {
  Object obj1, obj2, xObjDict, xObj, resObj;
  Ref r;
  GfxFontDict *gfxFontDict;
  GfxFont *font;
  int i;

  // scan the fonts in this resource dictionary
  gfxFontDict = NULL;
  resDict->lookupNF("Font", &obj1);
  if (obj1.isRef()) {
    obj1.fetch(doc->getXRef(), &obj2);
    if (obj2.isDict()) {
      r = obj1.getRef();
      gfxFontDict = new GfxFontDict(doc->getXRef(), &r, obj2.getDict());
    }
    obj2.free();
  } else if (obj1.isDict()) {
    gfxFontDict = new GfxFontDict(doc->getXRef(), NULL, obj1.getDict());
  }
  if (gfxFontDict) {
    for (i = 0; i < gfxFontDict->getNumFonts(); ++i) {
      if ((font = gfxFontDict->getFont(i))) {
	scanFont(font, doc);
      }
    }
    delete gfxFontDict;
  }
  obj1.free();

  // recursively scan any resource dictionaries in objects in this
  // resource dictionary
  resDict->lookup("XObject", &xObjDict);
  if (xObjDict.isDict()) {
    for (i = 0; i < xObjDict.dictGetLength(); ++i) {
      xObjDict.dictGetVal(i, &xObj);
      if (xObj.isStream()) {
	xObj.streamGetDict()->lookup("Resources", &resObj);
	if (resObj.isDict()) {
	  scanFonts(resObj.getDict(), doc);
	}
	resObj.free();
      }
      xObj.free();
    }
  }
  xObjDict.free();
}

static void scanFont(GfxFont *font, PDFDoc *doc) {
  Ref fontRef, embRef;
  Object fontObj, toUnicodeObj;
  GString *name;
  GBool emb, subset, hasToUnicode;
  int i;

  fontRef = *font->getID();

  // check for an already-seen font
  for (i = 0; i < fontsLen; ++i) {
    if (fontRef.num == fonts[i].num && fontRef.gen == fonts[i].gen) {
      return;
    }
  }

  // font name
  name = font->getOrigName();

  // check for an embedded font
  if (font->getType() == fontType3) {
    emb = gTrue;
  } else {
    emb = font->getEmbeddedFontID(&embRef);
  }

  // look for a ToUnicode map
  hasToUnicode = gFalse;
  if (doc->getXRef()->fetch(fontRef.num, fontRef.gen, &fontObj)->isDict()) {
    hasToUnicode = fontObj.dictLookup("ToUnicode", &toUnicodeObj)->isStream();
    toUnicodeObj.free();
  }
  fontObj.free();

  // check for a font subset name: capital letters followed by a '+'
  // sign
  subset = gFalse;
  if (name) {
    for (i = 0; i < name->getLength(); ++i) {
      if (name->getChar(i) < 'A' || name->getChar(i) > 'Z') {
	break;
      }
    }
    subset = i > 0 && i < name->getLength() && name->getChar(i) == '+';
  }

  // print the font info
  printf("%-36s %-17s %-3s %-3s %-3s",
	 name ? name->getCString() : "[none]",
	 fontTypeNames[font->getType()],
	 emb ? "yes" : "no",
	 subset ? "yes" : "no",
	 hasToUnicode ? "yes" : "no");
  if (fontRef.gen >= 100000) {
    printf(" [none]\n");
  } else {
    printf(" %6d %2d\n", fontRef.num, fontRef.gen);
  }

  // add this font to the list
  if (fontsLen == fontsSize) {
    fontsSize += 32;
    fonts = (Ref *)greallocn(fonts, fontsSize, sizeof(Ref));
  }
  fonts[fontsLen++] = *font->getID();
}
//========================================================================
//
// pdfimages.cc
//
// Copyright 1998-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include "parseargs.h"
#include "GString.h"
#include "gmem.h"
#include "GlobalParams.h"
#include "Object.h"
#include "Stream.h"
#include "Array.h"
#include "Dict.h"
#include "XRef.h"
#include "Catalog.h"
#include "Page.h"
#include "PDFDoc.h"
#include "ImageOutputDev.h"
#include "Error.h"
#include "config.h"

static int firstPage = 1;
static int lastPage = 0;
static GBool dumpJPEG = gFalse;
static char ownerPassword[33] = "\001";
static char userPassword[33] = "\001";
static GBool quiet = gFalse;
static char cfgFileName[256] = "";
static GBool printVersion = gFalse;
static GBool printHelp = gFalse;

static ArgDesc argDesc[] = {
  {"-f",      argInt,      &firstPage,     0,
   "first page to convert"},
  {"-l",      argInt,      &lastPage,      0,
   "last page to convert"},
  {"-j",      argFlag,     &dumpJPEG,      0,
   "write JPEG images as JPEG files"},
  {"-opw",    argString,   ownerPassword,  sizeof(ownerPassword),
   "owner password (for encrypted files)"},
  {"-upw",    argString,   userPassword,   sizeof(userPassword),
   "user password (for encrypted files)"},
  {"-q",      argFlag,     &quiet,         0,
   "don't print any messages or errors"},
  {"-cfg",        argString,      cfgFileName,    sizeof(cfgFileName),
   "configuration file to use in place of .xpdfrc"},
  {"-v",      argFlag,     &printVersion,  0,
   "print copyright and version info"},
  {"-h",      argFlag,     &printHelp,     0,
   "print usage information"},
  {"-help",   argFlag,     &printHelp,     0,
   "print usage information"},
  {"--help",  argFlag,     &printHelp,     0,
   "print usage information"},
  {"-?",      argFlag,     &printHelp,     0,
   "print usage information"},
  {NULL}
};

int main(int argc, char *argv[]) {
  PDFDoc *doc;
  GString *fileName;
  char *imgRoot;
  GString *ownerPW, *userPW;
  ImageOutputDev *imgOut;
  GBool ok;
  int exitCode;

  exitCode = 99;

  // parse args
  ok = parseArgs(argDesc, &argc, argv);
  if (!ok || argc != 3 || printVersion || printHelp) {
    fprintf(stderr, "pdfimages version %s\n", xpdfVersion);
    fprintf(stderr, "%s\n", xpdfCopyright);
    if (!printVersion) {
      printUsage("pdfimages", "<PDF-file> <image-root>", argDesc);
    }
    goto err0;
  }
  fileName = new GString(argv[1]);
  imgRoot = argv[2];

  // read config file
  globalParams = new GlobalParams(cfgFileName);
  if (quiet) {
    globalParams->setErrQuiet(quiet);
  }

  // open PDF file
  if (ownerPassword[0] != '\001') {
    ownerPW = new GString(ownerPassword);
  } else {
    ownerPW = NULL;
  }
  if (userPassword[0] != '\001') {
    userPW = new GString(userPassword);
  } else {
    userPW = NULL;
  }
  doc = new PDFDoc(fileName, ownerPW, userPW);
  if (userPW) {
    delete userPW;
  }
  if (ownerPW) {
    delete ownerPW;
  }
  if (!doc->isOk()) {
    exitCode = 1;
    goto err1;
  }

  // check for copy permission
  if (!doc->okToCopy()) {
    error(-1, "Copying of images from this document is not allowed.");
    exitCode = 3;
    goto err1;
  }

  // get page range
  if (firstPage < 1)
    firstPage = 1;
  if (lastPage < 1 || lastPage > doc->getNumPages())
    lastPage = doc->getNumPages();

  // write image files
  imgOut = new ImageOutputDev(imgRoot, dumpJPEG);
  if (imgOut->isOk()) {
    doc->displayPages(imgOut, firstPage, lastPage, 72, 72, 0,
		      gFalse, gTrue, gFalse);
  }
  delete imgOut;

  exitCode = 0;

  // clean up
 err1:
  delete doc;
  delete globalParams;
 err0:

  // check for memory leaks
  Object::memCheck(stderr);
  gMemReport(stderr);

  return exitCode;
}
//========================================================================
//
// pdfinfo.cc
//
// Copyright 1998-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <time.h>
#include <math.h>
#include "parseargs.h"
#include "GString.h"
#include "gmem.h"
#include "GlobalParams.h"
#include "Object.h"
#include "Stream.h"
#include "Array.h"
#include "Dict.h"
#include "XRef.h"
#include "Catalog.h"
#include "Page.h"
#include "PDFDoc.h"
#include "CharTypes.h"
#include "UnicodeMap.h"
#include "PDFDocEncoding.h"
#include "Error.h"
#include "config.h"

static void printInfoString(Dict *infoDict, char *key, char *text,
			    UnicodeMap *uMap);
static void printInfoDate(Dict *infoDict, char *key, char *text);
static void printBox(char *text, PDFRectangle *box);

static int firstPage = 1;
static int lastPage = 0;
static GBool printBoxes = gFalse;
static GBool printMetadata = gFalse;
static char textEncName[128] = "";
static char ownerPassword[33] = "\001";
static char userPassword[33] = "\001";
static char cfgFileName[256] = "";
static GBool printVersion = gFalse;
static GBool printHelp = gFalse;

static ArgDesc argDesc[] = {
  {"-f",      argInt,      &firstPage,        0,
   "first page to convert"},
  {"-l",      argInt,      &lastPage,         0,
   "last page to convert"},
  {"-box",    argFlag,     &printBoxes,       0,
   "print the page bounding boxes"},
  {"-meta",   argFlag,     &printMetadata,    0,
   "print the document metadata (XML)"},
  {"-enc",    argString,   textEncName,    sizeof(textEncName),
   "output text encoding name"},
  {"-opw",    argString,   ownerPassword,  sizeof(ownerPassword),
   "owner password (for encrypted files)"},
  {"-upw",    argString,   userPassword,   sizeof(userPassword),
   "user password (for encrypted files)"},
  {"-cfg",        argString,      cfgFileName,    sizeof(cfgFileName),
   "configuration file to use in place of .xpdfrc"},
  {"-v",      argFlag,     &printVersion,  0,
   "print copyright and version info"},
  {"-h",      argFlag,     &printHelp,     0,
   "print usage information"},
  {"-help",   argFlag,     &printHelp,     0,
   "print usage information"},
  {"--help",  argFlag,     &printHelp,     0,
   "print usage information"},
  {"-?",      argFlag,     &printHelp,     0,
   "print usage information"},
  {NULL}
};

int main(int argc, char *argv[]) {
  PDFDoc *doc;
  GString *fileName;
  GString *ownerPW, *userPW;
  UnicodeMap *uMap;
  Page *page;
  Object info;
  char buf[256];
  double w, h, wISO, hISO;
  FILE *f;
  GString *metadata;
  GBool ok;
  int exitCode;
  int pg, i;
  GBool multiPage;

  exitCode = 99;

  // parse args
  ok = parseArgs(argDesc, &argc, argv);
  if (!ok || argc != 2 || printVersion || printHelp) {
    fprintf(stderr, "pdfinfo version %s\n", xpdfVersion);
    fprintf(stderr, "%s\n", xpdfCopyright);
    if (!printVersion) {
      printUsage("pdfinfo", "<PDF-file>", argDesc);
    }
    goto err0;
  }
  fileName = new GString(argv[1]);

  // read config file
  globalParams = new GlobalParams(cfgFileName);
  if (textEncName[0]) {
    globalParams->setTextEncoding(textEncName);
  }

  // get mapping to output encoding
  if (!(uMap = globalParams->getTextEncoding())) {
    error(-1, "Couldn't get text encoding");
    delete fileName;
    goto err1;
  }

  // open PDF file
  if (ownerPassword[0] != '\001') {
    ownerPW = new GString(ownerPassword);
  } else {
    ownerPW = NULL;
  }
  if (userPassword[0] != '\001') {
    userPW = new GString(userPassword);
  } else {
    userPW = NULL;
  }
  doc = new PDFDoc(fileName, ownerPW, userPW);
  if (userPW) {
    delete userPW;
  }
  if (ownerPW) {
    delete ownerPW;
  }
  if (!doc->isOk()) {
    exitCode = 1;
    goto err2;
  }

  // get page range
  if (firstPage < 1) {
    firstPage = 1;
  }
  if (lastPage == 0) {
    multiPage = gFalse;
    lastPage = 1;
  } else {
    multiPage = gTrue;
  }
  if (lastPage < 1 || lastPage > doc->getNumPages()) {
    lastPage = doc->getNumPages();
  }

  // print doc info
  doc->getDocInfo(&info);
  if (info.isDict()) {
    printInfoString(info.getDict(), "Title",        "Title:          ", uMap);
    printInfoString(info.getDict(), "Subject",      "Subject:        ", uMap);
    printInfoString(info.getDict(), "Keywords",     "Keywords:       ", uMap);
    printInfoString(info.getDict(), "Author",       "Author:         ", uMap);
    printInfoString(info.getDict(), "Creator",      "Creator:        ", uMap);
    printInfoString(info.getDict(), "Producer",     "Producer:       ", uMap);
    printInfoDate(info.getDict(),   "CreationDate", "CreationDate:   ");
    printInfoDate(info.getDict(),   "ModDate",      "ModDate:        ");
  }
  info.free();

  // print tagging info
  printf("Tagged:         %s\n",
	 doc->getStructTreeRoot()->isDict() ? "yes" : "no");

  // print page count
  printf("Pages:          %d\n", doc->getNumPages());

  // print encryption info
  printf("Encrypted:      ");
  if (doc->isEncrypted()) {
    printf("yes (print:%s copy:%s change:%s addNotes:%s)\n",
	   doc->okToPrint(gTrue) ? "yes" : "no",
	   doc->okToCopy(gTrue) ? "yes" : "no",
	   doc->okToChange(gTrue) ? "yes" : "no",
	   doc->okToAddNotes(gTrue) ? "yes" : "no");
  } else {
    printf("no\n");
  }

  // print page size
  for (pg = firstPage; pg <= lastPage; ++pg) {
    w = doc->getPageCropWidth(pg);
    h = doc->getPageCropHeight(pg);
    if (multiPage) {
      printf("Page %4d size: %g x %g pts", pg, w, h);
    } else {
      printf("Page size:      %g x %g pts", w, h);
    }
    if ((fabs(w - 612) < 0.1 && fabs(h - 792) < 0.1) ||
	(fabs(w - 792) < 0.1 && fabs(h - 612) < 0.1)) {
      printf(" (letter)");
    } else {
      hISO = sqrt(sqrt(2.0)) * 7200 / 2.54;
      wISO = hISO / sqrt(2.0);
      for (i = 0; i <= 6; ++i) {
	if ((fabs(w - wISO) < 1 && fabs(h - hISO) < 1) ||
	    (fabs(w - hISO) < 1 && fabs(h - wISO) < 1)) {
	  printf(" (A%d)", i);
	  break;
	}
	hISO = wISO;
	wISO /= sqrt(2.0);
      }
    }
    printf("\n");
  } 

  // print the boxes
  if (printBoxes) {
    if (multiPage) {
      for (pg = firstPage; pg <= lastPage; ++pg) {
	page = doc->getCatalog()->getPage(pg);
	sprintf(buf, "Page %4d MediaBox: ", pg);
	printBox(buf, page->getMediaBox());
	sprintf(buf, "Page %4d CropBox:  ", pg);
	printBox(buf, page->getCropBox());
	sprintf(buf, "Page %4d BleedBox: ", pg);
	printBox(buf, page->getBleedBox());
	sprintf(buf, "Page %4d TrimBox:  ", pg);
	printBox(buf, page->getTrimBox());
	sprintf(buf, "Page %4d ArtBox:   ", pg);
	printBox(buf, page->getArtBox());
      }
    } else {
      page = doc->getCatalog()->getPage(firstPage);
      printBox("MediaBox:       ", page->getMediaBox());
      printBox("CropBox:        ", page->getCropBox());
      printBox("BleedBox:       ", page->getBleedBox());
      printBox("TrimBox:        ", page->getTrimBox());
      printBox("ArtBox:         ", page->getArtBox());
    }
  }

  // print file size
#ifdef VMS
  f = fopen(fileName->getCString(), "rb", "ctx=stm");
#else
  f = fopen(fileName->getCString(), "rb");
#endif
  if (f) {
#if HAVE_FSEEKO
    fseeko(f, 0, SEEK_END);
    printf("File size:      %u bytes\n", (Guint)ftello(f));
#elif HAVE_FSEEK64
    fseek64(f, 0, SEEK_END);
    printf("File size:      %u bytes\n", (Guint)ftell64(f));
#else
    fseek(f, 0, SEEK_END);
    printf("File size:      %d bytes\n", (int)ftell(f));
#endif
    fclose(f);
  }

  // print linearization info
  printf("Optimized:      %s\n", doc->isLinearized() ? "yes" : "no");

  // print PDF version
  printf("PDF version:    %.1f\n", doc->getPDFVersion());

  // print the metadata
  if (printMetadata && (metadata = doc->readMetadata())) {
    fputs("Metadata:\n", stdout);
    fputs(metadata->getCString(), stdout);
    fputc('\n', stdout);
    delete metadata;
  }

  exitCode = 0;

  // clean up
 err2:
  uMap->decRefCnt();
  delete doc;
 err1:
  delete globalParams;
 err0:

  // check for memory leaks
  Object::memCheck(stderr);
  gMemReport(stderr);

  return exitCode;
}

static void printInfoString(Dict *infoDict, char *key, char *text,
			    UnicodeMap *uMap) {
  Object obj;
  GString *s1;
  GBool isUnicode;
  Unicode u;
  char buf[8];
  int i, n;

  if (infoDict->lookup(key, &obj)->isString()) {
    fputs(text, stdout);
    s1 = obj.getString();
    if ((s1->getChar(0) & 0xff) == 0xfe &&
	(s1->getChar(1) & 0xff) == 0xff) {
      isUnicode = gTrue;
      i = 2;
    } else {
      isUnicode = gFalse;
      i = 0;
    }
    while (i < obj.getString()->getLength()) {
      if (isUnicode) {
	u = ((s1->getChar(i) & 0xff) << 8) |
	    (s1->getChar(i+1) & 0xff);
	i += 2;
      } else {
	u = pdfDocEncoding[s1->getChar(i) & 0xff];
	++i;
      }
      n = uMap->mapUnicode(u, buf, sizeof(buf));
      fwrite(buf, 1, n, stdout);
    }
    fputc('\n', stdout);
  }
  obj.free();
}

static void printInfoDate(Dict *infoDict, char *key, char *text) {
  Object obj;
  char *s;
  int year, mon, day, hour, min, sec, n;
  struct tm tmStruct;
  char buf[256];

  if (infoDict->lookup(key, &obj)->isString()) {
    fputs(text, stdout);
    s = obj.getString()->getCString();
    if (s[0] == 'D' && s[1] == ':') {
      s += 2;
    }
    if ((n = sscanf(s, "%4d%2d%2d%2d%2d%2d",
		    &year, &mon, &day, &hour, &min, &sec)) >= 1) {
      switch (n) {
      case 1: mon = 1;
      case 2: day = 1;
      case 3: hour = 0;
      case 4: min = 0;
      case 5: sec = 0;
      }
      tmStruct.tm_year = year - 1900;
      tmStruct.tm_mon = mon - 1;
      tmStruct.tm_mday = day;
      tmStruct.tm_hour = hour;
      tmStruct.tm_min = min;
      tmStruct.tm_sec = sec;
      tmStruct.tm_wday = -1;
      tmStruct.tm_yday = -1;
      tmStruct.tm_isdst = -1;
      // compute the tm_wday and tm_yday fields
      if (mktime(&tmStruct) != (time_t)-1 &&
	  strftime(buf, sizeof(buf), "%c", &tmStruct)) {
	fputs(buf, stdout);
      } else {
	fputs(s, stdout);
      }
    } else {
      fputs(s, stdout);
    }



    fputc('\n', stdout);
  }
  obj.free();
}

static void printBox(char *text, PDFRectangle *box) {
  printf("%s%8.2f %8.2f %8.2f %8.2f\n",
	 text, box->x1, box->y1, box->x2, box->y2);
}
//========================================================================
//
// pdftoppm.cc
//
// Copyright 2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>
#include <stdio.h>
#include "parseargs.h"
#include "gmem.h"
#include "GString.h"
#include "GlobalParams.h"
#include "Object.h"
#include "PDFDoc.h"
#include "SplashBitmap.h"
#include "Splash.h"
#include "SplashOutputDev.h"
#include "config.h"

static int firstPage = 1;
static int lastPage = 0;
static int resolution = 150;
static GBool mono = gFalse;
static GBool gray = gFalse;
static char enableT1libStr[16] = "";
static char enableFreeTypeStr[16] = "";
static char antialiasStr[16] = "";
static char vectorAntialiasStr[16] = "";
static char ownerPassword[33] = "";
static char userPassword[33] = "";
static GBool quiet = gFalse;
static char cfgFileName[256] = "";
static GBool printVersion = gFalse;
static GBool printHelp = gFalse;

static ArgDesc argDesc[] = {
  {"-f",      argInt,      &firstPage,     0,
   "first page to print"},
  {"-l",      argInt,      &lastPage,      0,
   "last page to print"},
  {"-r",      argInt,      &resolution,    0,
   "resolution, in DPI (default is 150)"},
  {"-mono",   argFlag,     &mono,          0,
   "generate a monochrome PBM file"},
  {"-gray",   argFlag,     &gray,          0,
   "generate a grayscale PGM file"},
#if HAVE_T1LIB_H
  {"-t1lib",      argString,      enableT1libStr, sizeof(enableT1libStr),
   "enable t1lib font rasterizer: yes, no"},
#endif
#if HAVE_FREETYPE_FREETYPE_H | HAVE_FREETYPE_H
  {"-freetype",   argString,      enableFreeTypeStr, sizeof(enableFreeTypeStr),
   "enable FreeType font rasterizer: yes, no"},
#endif
  {"-aa",         argString,      antialiasStr,   sizeof(antialiasStr),
   "enable font anti-aliasing: yes, no"},
  {"-aaVector",   argString,      vectorAntialiasStr, sizeof(vectorAntialiasStr),
   "enable vector anti-aliasing: yes, no"},
  {"-opw",    argString,   ownerPassword,  sizeof(ownerPassword),
   "owner password (for encrypted files)"},
  {"-upw",    argString,   userPassword,   sizeof(userPassword),
   "user password (for encrypted files)"},
  {"-q",      argFlag,     &quiet,         0,
   "don't print any messages or errors"},
  {"-cfg",        argString,      cfgFileName,    sizeof(cfgFileName),
   "configuration file to use in place of .xpdfrc"},
  {"-v",      argFlag,     &printVersion,  0,
   "print copyright and version info"},
  {"-h",      argFlag,     &printHelp,     0,
   "print usage information"},
  {"-help",   argFlag,     &printHelp,     0,
   "print usage information"},
  {"--help",  argFlag,     &printHelp,     0,
   "print usage information"},
  {"-?",      argFlag,     &printHelp,     0,
   "print usage information"},
  {NULL}
};

int main(int argc, char *argv[]) {
  PDFDoc *doc;
  GString *fileName;
  char *ppmRoot;
  char ppmFile[512];
  GString *ownerPW, *userPW;
  SplashColor paperColor;
  SplashOutputDev *splashOut;
  GBool ok;
  int exitCode;
  int pg;

  exitCode = 99;

  // parse args
  ok = parseArgs(argDesc, &argc, argv);
  if (mono && gray) {
    ok = gFalse;
  }
  if (!ok || argc != 3 || printVersion || printHelp) {
    fprintf(stderr, "pdftoppm version %s\n", xpdfVersion);
    fprintf(stderr, "%s\n", xpdfCopyright);
    if (!printVersion) {
      printUsage("pdftoppm", "<PDF-file> <PPM-root>", argDesc);
    }
    goto err0;
  }
  fileName = new GString(argv[1]);
  ppmRoot = argv[2];

  // read config file
  globalParams = new GlobalParams(cfgFileName);
  globalParams->setupBaseFonts(NULL);
  if (enableT1libStr[0]) {
    if (!globalParams->setEnableT1lib(enableT1libStr)) {
      fprintf(stderr, "Bad '-t1lib' value on command line\n");
    }
  }
  if (enableFreeTypeStr[0]) {
    if (!globalParams->setEnableFreeType(enableFreeTypeStr)) {
      fprintf(stderr, "Bad '-freetype' value on command line\n");
    }
  }
  if (antialiasStr[0]) {
    if (!globalParams->setAntialias(antialiasStr)) {
      fprintf(stderr, "Bad '-aa' value on command line\n");
    }
  }
  if (vectorAntialiasStr[0]) {
    if (!globalParams->setVectorAntialias(vectorAntialiasStr)) {
      fprintf(stderr, "Bad '-aaVector' value on command line\n");
    }
  }
  if (quiet) {
    globalParams->setErrQuiet(quiet);
  }

  // open PDF file
  if (ownerPassword[0]) {
    ownerPW = new GString(ownerPassword);
  } else {
    ownerPW = NULL;
  }
  if (userPassword[0]) {
    userPW = new GString(userPassword);
  } else {
    userPW = NULL;
  }
  doc = new PDFDoc(fileName, ownerPW, userPW);
  if (userPW) {
    delete userPW;
  }
  if (ownerPW) {
    delete ownerPW;
  }
  if (!doc->isOk()) {
    exitCode = 1;
    goto err1;
  }

  // get page range
  if (firstPage < 1)
    firstPage = 1;
  if (lastPage < 1 || lastPage > doc->getNumPages())
    lastPage = doc->getNumPages();

  // write PPM files
  if (mono) {
    paperColor[0] = 0xff;
    splashOut = new SplashOutputDev(splashModeMono1, 1, gFalse, paperColor);
  } else if (gray) {
    paperColor[0] = 0xff;
    splashOut = new SplashOutputDev(splashModeMono8, 1, gFalse, paperColor);
  } else {
    paperColor[0] = paperColor[1] = paperColor[2] = 0xff;
    splashOut = new SplashOutputDev(splashModeRGB8, 1, gFalse, paperColor);
  }
  splashOut->startDoc(doc->getXRef());
  for (pg = firstPage; pg <= lastPage; ++pg) {
    doc->displayPage(splashOut, pg, resolution, resolution, 0,
		     gFalse, gTrue, gFalse);
    sprintf(ppmFile, "%.*s-%06d.%s",
	    (int)sizeof(ppmFile) - 32, ppmRoot, pg,
	    mono ? "pbm" : gray ? "pgm" : "ppm");
    splashOut->getBitmap()->writePNMFile(ppmFile);
  }
  delete splashOut;

  exitCode = 0;

  // clean up
 err1:
  delete doc;
  delete globalParams;
 err0:

  // check for memory leaks
  Object::memCheck(stderr);
  gMemReport(stderr);

  return exitCode;
}
//========================================================================
//
// pdftops.cc
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include "parseargs.h"
#include "GString.h"
#include "gmem.h"
#include "GlobalParams.h"
#include "Object.h"
#include "Stream.h"
#include "Array.h"
#include "Dict.h"
#include "XRef.h"
#include "Catalog.h"
#include "Page.h"
#include "PDFDoc.h"
#include "PSOutputDev.h"
#include "Error.h"
#include "config.h"

static int firstPage = 1;
static int lastPage = 0;
static GBool level1 = gFalse;
static GBool level1Sep = gFalse;
static GBool level2 = gFalse;
static GBool level2Sep = gFalse;
static GBool level3 = gFalse;
static GBool level3Sep = gFalse;
static GBool doEPS = gFalse;
static GBool doForm = gFalse;
#if OPI_SUPPORT
static GBool doOPI = gFalse;
#endif
static GBool noEmbedT1Fonts = gFalse;
static GBool noEmbedTTFonts = gFalse;
static GBool noEmbedCIDPSFonts = gFalse;
static GBool noEmbedCIDTTFonts = gFalse;
static GBool preload = gFalse;
static char paperSize[15] = "";
static int paperWidth = 0;
static int paperHeight = 0;
static GBool noCrop = gFalse;
static GBool expand = gFalse;
static GBool noShrink = gFalse;
static GBool noCenter = gFalse;
static GBool pageCrop = gFalse;
static GBool duplex = gFalse;
static char ownerPassword[33] = "\001";
static char userPassword[33] = "\001";
static GBool quiet = gFalse;
static char cfgFileName[256] = "";
static GBool printVersion = gFalse;
static GBool printHelp = gFalse;

static ArgDesc argDesc[] = {
  {"-f",          argInt,      &firstPage,      0,
   "first page to print"},
  {"-l",          argInt,      &lastPage,       0,
   "last page to print"},
  {"-level1",     argFlag,     &level1,         0,
   "generate Level 1 PostScript"},
  {"-level1sep",  argFlag,     &level1Sep,      0,
   "generate Level 1 separable PostScript"},
  {"-level2",     argFlag,     &level2,         0,
   "generate Level 2 PostScript"},
  {"-level2sep",  argFlag,     &level2Sep,      0,
   "generate Level 2 separable PostScript"},
  {"-level3",     argFlag,     &level3,         0,
   "generate Level 3 PostScript"},
  {"-level3sep",  argFlag,     &level3Sep,      0,
   "generate Level 3 separable PostScript"},
  {"-eps",        argFlag,     &doEPS,          0,
   "generate Encapsulated PostScript (EPS)"},
  {"-form",       argFlag,     &doForm,         0,
   "generate a PostScript form"},
#if OPI_SUPPORT
  {"-opi",        argFlag,     &doOPI,          0,
   "generate OPI comments"},
#endif
  {"-noembt1",    argFlag,     &noEmbedT1Fonts, 0,
   "don't embed Type 1 fonts"},
  {"-noembtt",    argFlag,     &noEmbedTTFonts, 0,
   "don't embed TrueType fonts"},
  {"-noembcidps", argFlag,     &noEmbedCIDPSFonts, 0,
   "don't embed CID PostScript fonts"},
  {"-noembcidtt", argFlag,     &noEmbedCIDTTFonts,  0,
   "don't embed CID TrueType fonts"},
  {"-preload",    argFlag,     &preload,        0,
   "preload images and forms"},
  {"-paper",      argString,   paperSize,       sizeof(paperSize),
   "paper size (letter, legal, A4, A3, match)"},
  {"-paperw",     argInt,      &paperWidth,     0,
   "paper width, in points"},
  {"-paperh",     argInt,      &paperHeight,    0,
   "paper height, in points"},
  {"-nocrop",     argFlag,     &noCrop,         0,
   "don't crop pages to CropBox"},
  {"-expand",     argFlag,     &expand,         0,
   "expand pages smaller than the paper size"},
  {"-noshrink",   argFlag,     &noShrink,       0,
   "don't shrink pages larger than the paper size"},
  {"-nocenter",   argFlag,     &noCenter,       0,
   "don't center pages smaller than the paper size"},
  {"-pagecrop",   argFlag,     &pageCrop,       0,
   "treat the CropBox as the page size"},
  {"-duplex",     argFlag,     &duplex,         0,
   "enable duplex printing"},
  {"-opw",        argString,   ownerPassword,   sizeof(ownerPassword),
   "owner password (for encrypted files)"},
  {"-upw",        argString,   userPassword,    sizeof(userPassword),
   "user password (for encrypted files)"},
  {"-q",          argFlag,     &quiet,          0,
   "don't print any messages or errors"},
  {"-cfg",        argString,      cfgFileName,    sizeof(cfgFileName),
   "configuration file to use in place of .xpdfrc"},
  {"-v",          argFlag,     &printVersion,   0,
   "print copyright and version info"},
  {"-h",          argFlag,     &printHelp,      0,
   "print usage information"},
  {"-help",       argFlag,     &printHelp,      0,
   "print usage information"},
  {"--help",      argFlag,     &printHelp,      0,
   "print usage information"},
  {"-?",          argFlag,     &printHelp,      0,
   "print usage information"},
  {NULL}
};

int main(int argc, char *argv[]) {
  PDFDoc *doc;
  GString *fileName;
  GString *psFileName;
  PSLevel level;
  PSOutMode mode;
  GString *ownerPW, *userPW;
  PSOutputDev *psOut;
  GBool ok;
  char *p;
  int exitCode;

  exitCode = 99;

  // parse args
  ok = parseArgs(argDesc, &argc, argv);
  if (!ok || argc < 2 || argc > 3 || printVersion || printHelp) {
    fprintf(stderr, "pdftops version %s\n", xpdfVersion);
    fprintf(stderr, "%s\n", xpdfCopyright);
    if (!printVersion) {
      printUsage("pdftops", "<PDF-file> [<PS-file>]", argDesc);
    }
    exit(1);
  }
  if ((level1 ? 1 : 0) +
      (level1Sep ? 1 : 0) +
      (level2 ? 1 : 0) +
      (level2Sep ? 1 : 0) +
      (level3 ? 1 : 0) +
      (level3Sep ? 1 : 0) > 1) {
    fprintf(stderr, "Error: use only one of the 'level' options.\n");
    exit(1);
  }
  if (doEPS && doForm) {
    fprintf(stderr, "Error: use only one of -eps and -form\n");
    exit(1);
  }
  if (level1) {
    level = psLevel1;
  } else if (level1Sep) {
    level = psLevel1Sep;
  } else if (level2Sep) {
    level = psLevel2Sep;
  } else if (level3) {
    level = psLevel3;
  } else if (level3Sep) {
    level = psLevel3Sep;
  } else {
    level = psLevel2;
  }
  if (doForm && level < psLevel2) {
    fprintf(stderr, "Error: forms are only available with Level 2 output.\n");
    exit(1);
  }
  mode = doEPS ? psModeEPS
               : doForm ? psModeForm
                        : psModePS;
  fileName = new GString(argv[1]);

  // read config file
  globalParams = new GlobalParams(cfgFileName);
#if HAVE_SPLASH
  globalParams->setupBaseFonts(NULL);
#endif
  if (paperSize[0]) {
    if (!globalParams->setPSPaperSize(paperSize)) {
      fprintf(stderr, "Invalid paper size\n");
      delete fileName;
      goto err0;
    }
  } else {
    if (paperWidth) {
      globalParams->setPSPaperWidth(paperWidth);
    }
    if (paperHeight) {
      globalParams->setPSPaperHeight(paperHeight);
    }
  }
  if (noCrop) {
    globalParams->setPSCrop(gFalse);
  }
  if (expand) {
    globalParams->setPSExpandSmaller(gTrue);
  }
  if (noShrink) {
    globalParams->setPSShrinkLarger(gFalse);
  }
  if (noCenter) {
    globalParams->setPSCenter(gFalse);
  }
  if (duplex) {
    globalParams->setPSDuplex(duplex);
  }
  if (level1 || level1Sep || level2 || level2Sep || level3 || level3Sep) {
    globalParams->setPSLevel(level);
  }
  if (noEmbedT1Fonts) {
    globalParams->setPSEmbedType1(!noEmbedT1Fonts);
  }
  if (noEmbedTTFonts) {
    globalParams->setPSEmbedTrueType(!noEmbedTTFonts);
  }
  if (noEmbedCIDPSFonts) {
    globalParams->setPSEmbedCIDPostScript(!noEmbedCIDPSFonts);
  }
  if (noEmbedCIDTTFonts) {
    globalParams->setPSEmbedCIDTrueType(!noEmbedCIDTTFonts);
  }
  if (preload) {
    globalParams->setPSPreload(preload);
  }
#if OPI_SUPPORT
  if (doOPI) {
    globalParams->setPSOPI(doOPI);
  }
#endif
  if (quiet) {
    globalParams->setErrQuiet(quiet);
  }

  // open PDF file
  if (ownerPassword[0] != '\001') {
    ownerPW = new GString(ownerPassword);
  } else {
    ownerPW = NULL;
  }
  if (userPassword[0] != '\001') {
    userPW = new GString(userPassword);
  } else {
    userPW = NULL;
  }
  doc = new PDFDoc(fileName, ownerPW, userPW);
  if (userPW) {
    delete userPW;
  }
  if (ownerPW) {
    delete ownerPW;
  }
  if (!doc->isOk()) {
    exitCode = 1;
    goto err1;
  }

  // check for print permission
  if (!doc->okToPrint()) {
    error(-1, "Printing this document is not allowed.");
    exitCode = 3;
    goto err1;
  }

  // construct PostScript file name
  if (argc == 3) {
    psFileName = new GString(argv[2]);
  } else {
    p = fileName->getCString() + fileName->getLength() - 4;
    if (!strcmp(p, ".pdf") || !strcmp(p, ".PDF")) {
      psFileName = new GString(fileName->getCString(),
			       fileName->getLength() - 4);
    } else {
      psFileName = fileName->copy();
    }
    psFileName->append(doEPS ? ".eps" : ".ps");
  }

  // get page range
  if (firstPage < 1) {
    firstPage = 1;
  }
  if (lastPage < 1 || lastPage > doc->getNumPages()) {
    lastPage = doc->getNumPages();
  }

  // check for multi-page EPS or form
  if ((doEPS || doForm) && firstPage != lastPage) {
    error(-1, "EPS and form files can only contain one page.");
    goto err2;
  }

  // write PostScript file
  psOut = new PSOutputDev(psFileName->getCString(), doc->getXRef(),
			  doc->getCatalog(), firstPage, lastPage, mode);
  if (psOut->isOk()) {
    doc->displayPages(psOut, firstPage, lastPage, 72, 72,
		      0, !pageCrop, globalParams->getPSCrop(), gTrue);
  } else {
    delete psOut;
    exitCode = 2;
    goto err2;
  }
  delete psOut;

  exitCode = 0;

  // clean up
 err2:
  delete psFileName;
 err1:
  delete doc;
 err0:
  delete globalParams;

  // check for memory leaks
  Object::memCheck(stderr);
  gMemReport(stderr);

  return exitCode;
}
//========================================================================
//
// pdftotext.cc
//
// Copyright 1997-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include "parseargs.h"
#include "GString.h"
#include "gmem.h"
#include "GlobalParams.h"
#include "Object.h"
#include "Stream.h"
#include "Array.h"
#include "Dict.h"
#include "XRef.h"
#include "Catalog.h"
#include "Page.h"
#include "PDFDoc.h"
#include "TextOutputDev.h"
#include "CharTypes.h"
#include "UnicodeMap.h"
#include "Error.h"
#include "config.h"

static void printInfoString(FILE *f, Dict *infoDict, char *key,
			    char *text1, char *text2, UnicodeMap *uMap);
static void printInfoDate(FILE *f, Dict *infoDict, char *key, char *fmt);

static int firstPage = 1;
static int lastPage = 0;
static GBool physLayout = gFalse;
static GBool rawOrder = gFalse;
static GBool htmlMeta = gFalse;
static char textEncName[128] = "";
static char textEOL[16] = "";
static GBool noPageBreaks = gFalse;
static char ownerPassword[33] = "\001";
static char userPassword[33] = "\001";
static GBool quiet = gFalse;
static char cfgFileName[256] = "";
static GBool printVersion = gFalse;
static GBool printHelp = gFalse;

static ArgDesc argDesc[] = {
  {"-f",       argInt,      &firstPage,     0,
   "first page to convert"},
  {"-l",       argInt,      &lastPage,      0,
   "last page to convert"},
  {"-layout",  argFlag,     &physLayout,    0,
   "maintain original physical layout"},
  {"-raw",     argFlag,     &rawOrder,      0,
   "keep strings in content stream order"},
  {"-htmlmeta", argFlag,   &htmlMeta,       0,
   "generate a simple HTML file, including the meta information"},
  {"-enc",     argString,   textEncName,    sizeof(textEncName),
   "output text encoding name"},
  {"-eol",     argString,   textEOL,        sizeof(textEOL),
   "output end-of-line convention (unix, dos, or mac)"},
  {"-nopgbrk", argFlag,     &noPageBreaks,  0,
   "don't insert page breaks between pages"},
  {"-opw",     argString,   ownerPassword,  sizeof(ownerPassword),
   "owner password (for encrypted files)"},
  {"-upw",     argString,   userPassword,   sizeof(userPassword),
   "user password (for encrypted files)"},
  {"-q",       argFlag,     &quiet,         0,
   "don't print any messages or errors"},
  {"-cfg",     argString,   cfgFileName,    sizeof(cfgFileName),
   "configuration file to use in place of .xpdfrc"},
  {"-v",       argFlag,     &printVersion,  0,
   "print copyright and version info"},
  {"-h",       argFlag,     &printHelp,     0,
   "print usage information"},
  {"-help",    argFlag,     &printHelp,     0,
   "print usage information"},
  {"--help",   argFlag,     &printHelp,     0,
   "print usage information"},
  {"-?",       argFlag,     &printHelp,     0,
   "print usage information"},
  {NULL}
};

int main(int argc, char *argv[]) {
  PDFDoc *doc;
  GString *fileName;
  GString *textFileName;
  GString *ownerPW, *userPW;
  TextOutputDev *textOut;
  FILE *f;
  UnicodeMap *uMap;
  Object info;
  GBool ok;
  char *p;
  int exitCode;

  exitCode = 99;

  // parse args
  ok = parseArgs(argDesc, &argc, argv);
  if (!ok || argc < 2 || argc > 3 || printVersion || printHelp) {
    fprintf(stderr, "pdftotext version %s\n", xpdfVersion);
    fprintf(stderr, "%s\n", xpdfCopyright);
    if (!printVersion) {
      printUsage("pdftotext", "<PDF-file> [<text-file>]", argDesc);
    }
    goto err0;
  }
  fileName = new GString(argv[1]);

  // read config file
  globalParams = new GlobalParams(cfgFileName);
  if (textEncName[0]) {
    globalParams->setTextEncoding(textEncName);
  }
  if (textEOL[0]) {
    if (!globalParams->setTextEOL(textEOL)) {
      fprintf(stderr, "Bad '-eol' value on command line\n");
    }
  }
  if (noPageBreaks) {
    globalParams->setTextPageBreaks(gFalse);
  }
  if (quiet) {
    globalParams->setErrQuiet(quiet);
  }

  // get mapping to output encoding
  if (!(uMap = globalParams->getTextEncoding())) {
    error(-1, "Couldn't get text encoding");
    delete fileName;
    goto err1;
  }

  // open PDF file
  if (ownerPassword[0] != '\001') {
    ownerPW = new GString(ownerPassword);
  } else {
    ownerPW = NULL;
  }
  if (userPassword[0] != '\001') {
    userPW = new GString(userPassword);
  } else {
    userPW = NULL;
  }
  doc = new PDFDoc(fileName, ownerPW, userPW);
  if (userPW) {
    delete userPW;
  }
  if (ownerPW) {
    delete ownerPW;
  }
  if (!doc->isOk()) {
    exitCode = 1;
    goto err2;
  }

  // check for copy permission
  if (!doc->okToCopy()) {
    error(-1, "Copying of text from this document is not allowed.");
    exitCode = 3;
    goto err2;
  }

  // construct text file name
  if (argc == 3) {
    textFileName = new GString(argv[2]);
  } else {
    p = fileName->getCString() + fileName->getLength() - 4;
    if (!strcmp(p, ".pdf") || !strcmp(p, ".PDF")) {
      textFileName = new GString(fileName->getCString(),
				 fileName->getLength() - 4);
    } else {
      textFileName = fileName->copy();
    }
    textFileName->append(htmlMeta ? ".html" : ".txt");
  }

  // get page range
  if (firstPage < 1) {
    firstPage = 1;
  }
  if (lastPage < 1 || lastPage > doc->getNumPages()) {
    lastPage = doc->getNumPages();
  }

  // write HTML header
  if (htmlMeta) {
    if (!textFileName->cmp("-")) {
      f = stdout;
    } else {
      if (!(f = fopen(textFileName->getCString(), "wb"))) {
	error(-1, "Couldn't open text file '%s'", textFileName->getCString());
	exitCode = 2;
	goto err3;
      }
    }
    fputs("<html>\n", f);
    fputs("<head>\n", f);
    doc->getDocInfo(&info);
    if (info.isDict()) {
      printInfoString(f, info.getDict(), "Title", "<title>", "</title>\n",
		      uMap);
      printInfoString(f, info.getDict(), "Subject",
		      "<meta name=\"Subject\" content=\"", "\">\n", uMap);
      printInfoString(f, info.getDict(), "Keywords",
		      "<meta name=\"Keywords\" content=\"", "\">\n", uMap);
      printInfoString(f, info.getDict(), "Author",
		      "<meta name=\"Author\" content=\"", "\">\n", uMap);
      printInfoString(f, info.getDict(), "Creator",
		      "<meta name=\"Creator\" content=\"", "\">\n", uMap);
      printInfoString(f, info.getDict(), "Producer",
		      "<meta name=\"Producer\" content=\"", "\">\n", uMap);
      printInfoDate(f, info.getDict(), "CreationDate",
		    "<meta name=\"CreationDate\" content=\"%s\">\n");
      printInfoDate(f, info.getDict(), "LastModifiedDate",
		    "<meta name=\"ModDate\" content=\"%s\">\n");
    }
    info.free();
    fputs("</head>\n", f);
    fputs("<body>\n", f);
    fputs("<pre>\n", f);
    if (f != stdout) {
      fclose(f);
    }
  }

  // write text file
  textOut = new TextOutputDev(textFileName->getCString(),
			      physLayout, rawOrder, htmlMeta);
  if (textOut->isOk()) {
    doc->displayPages(textOut, firstPage, lastPage, 72, 72, 0,
		      gFalse, gTrue, gFalse);
  } else {
    delete textOut;
    exitCode = 2;
    goto err3;
  }
  delete textOut;

  // write end of HTML file
  if (htmlMeta) {
    if (!textFileName->cmp("-")) {
      f = stdout;
    } else {
      if (!(f = fopen(textFileName->getCString(), "ab"))) {
	error(-1, "Couldn't open text file '%s'", textFileName->getCString());
	exitCode = 2;
	goto err3;
      }
    }
    fputs("</pre>\n", f);
    fputs("</body>\n", f);
    fputs("</html>\n", f);
    if (f != stdout) {
      fclose(f);
    }
  }

  exitCode = 0;

  // clean up
 err3:
  delete textFileName;
 err2:
  delete doc;
  uMap->decRefCnt();
 err1:
  delete globalParams;
 err0:

  // check for memory leaks
  Object::memCheck(stderr);
  gMemReport(stderr);

  return exitCode;
}

static void printInfoString(FILE *f, Dict *infoDict, char *key,
			    char *text1, char *text2, UnicodeMap *uMap) {
  Object obj;
  GString *s1;
  GBool isUnicode;
  Unicode u;
  char buf[8];
  int i, n;

  if (infoDict->lookup(key, &obj)->isString()) {
    fputs(text1, f);
    s1 = obj.getString();
    if ((s1->getChar(0) & 0xff) == 0xfe &&
	(s1->getChar(1) & 0xff) == 0xff) {
      isUnicode = gTrue;
      i = 2;
    } else {
      isUnicode = gFalse;
      i = 0;
    }
    while (i < obj.getString()->getLength()) {
      if (isUnicode) {
	u = ((s1->getChar(i) & 0xff) << 8) |
	    (s1->getChar(i+1) & 0xff);
	i += 2;
      } else {
	u = s1->getChar(i) & 0xff;
	++i;
      }
      n = uMap->mapUnicode(u, buf, sizeof(buf));
      fwrite(buf, 1, n, f);
    }
    fputs(text2, f);
  }
  obj.free();
}

static void printInfoDate(FILE *f, Dict *infoDict, char *key, char *fmt) {
  Object obj;
  char *s;

  if (infoDict->lookup(key, &obj)->isString()) {
    s = obj.getString()->getCString();
    if (s[0] == 'D' && s[1] == ':') {
      s += 2;
    }
    fprintf(f, fmt, s);
  }
  obj.free();
}
#define print_width 15
#define print_height 15
static unsigned char print_bits[] = {
   0xf0, 0x7f, 0x10, 0x40, 0x10, 0x40, 0xc8, 0x23, 0x08, 0x20, 0x68, 0x23,
   0x04, 0x10, 0x34, 0x10, 0x04, 0x10, 0xff, 0x7f, 0x55, 0x55, 0xab, 0x6a,
   0x55, 0x55, 0xab, 0x6a, 0xfe, 0x3f};
#define printDis_width 15
#define printDis_height 15
static unsigned char printDis_bits[] = {
   0xa0, 0x2a, 0x10, 0x40, 0x00, 0x00, 0x40, 0x01, 0x08, 0x20, 0x40, 0x01,
   0x00, 0x00, 0x14, 0x10, 0x00, 0x00, 0x55, 0x55, 0x00, 0x00, 0x01, 0x40,
   0x00, 0x00, 0x01, 0x40, 0xaa, 0x2a};
#define rightArrow_width 8
#define rightArrow_height 15
static unsigned char rightArrow_bits[] = {
   0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff, 0x7f, 0x3f, 0x1f, 0x0f,
   0x07, 0x03, 0x01};
#define rightArrowDis_width 8
#define rightArrowDis_height 15
static unsigned char rightArrowDis_bits[] = {
   0x01, 0x02, 0x05, 0x0a, 0x15, 0x2a, 0x55, 0xaa, 0x55, 0x2a, 0x15, 0x0a,
   0x05, 0x02, 0x01};
$!========================================================================
$!
$! Xpdf compile script for VMS.
$!
$! Written by Patrick Moreau, Martin P.J. Zinser.
$!
$! Copyright 1996-2003 Glyph & Cog, LLC
$!
$!========================================================================
$!
$ i = 0
$ j = 0
$ APPS = "XPDF,PDFTOPS,PDFTOTEXT,PDFINFO,PDFTOPBM,PDFIMAGES,PDFFONTS"
$ if f$search("COMMON.OLB").eqs."" then lib/create common.olb
$!
$ COMMON_OBJS = "Annot.obj,Array.obj,BuiltinFont.obj," + - 
                "BuiltinFontTables.obj,Catalog.obj,CharCodeToUnicode.obj," + - 
                "CMap.obj,Decrypt.obj,Dict.obj,Error.obj," + -
                "FontEncodingTables.obj,FontFile.obj," + -
                "Function.obj,Gfx.obj,GfxFont.obj,GfxState.obj,"+ - 
                "GlobalParams.obj,JArithmeticDecoder.obj,JBIG2Stream.obj,"+ -
                "Lexer.obj,Link.obj,NameToCharCode.obj,Object.obj,"+ -
                "Outline.obj,OutputDev.obj,Page.obj,Parser.obj,PDFdoc.obj," + -
                "PDFDocEncoding.obj,PSTokenizer.obj,Stream.obj," + -
                "UnicodeMap.obj,UnicodeTypeTable.obj,XRef.obj"
$ COMMON_LIBS = "[]common.olb/lib,[-.goo]libgoo.olb/lib"
$!
$ XPDF_OBJS = "xpdf.obj,FTFont.obj,PSOutputDev.obj," + -
              "SFont.obj,T1Font.obj,TextOutputDev.obj,TTFont.obj," + -
              "XOutputDev.obj,XPDFApp.obj,XPDFCore.obj,XPDFTree.obj," + -
              "XPDFViewer.obj,XPixmapOutputDev.obj"
$ XPDF_LIBS = ""
$!
$ PDFTOPS_OBJS   = "pdftops.obj,PSOutputDev.obj" 
$ PDFTOPS_LIBS   = ""
$!
$ PDFTOTEXT_OBJS = "pdftotext.obj,TextOutputDev.obj"
$ PDFTOTEXT_LIBS = ""
$!
$ PDFINFO_OBJS   =  "pdfinfo.obj"
$ PDFINFO_LIBS   = ""
$!
$ PDFTOPBM_OBJS  = "pdftopbm.obj,FTFont.obj,PBMOutputDev.obj,SFont.obj," + -
                   "T1Font.obj,TextOutputDev.obj,TTFont.obj,XOutputDev.obj"
$ PDFTOPBM_LIBS  = ""
$!
$ PDFIMAGES_OBJS = "pdfimages.obj,ImageOutputDev.obj"
$ PDFIMAGES_LIBS = ""
$!
$ PDFFONTS_OBJS  = "pdffonts.obj"
$ PDFFONTS_LIBS  = ""
$!
$COMPILE_CXX_LOOP:
$ file = f$element(i, ",",COMMON_OBJS)
$ if file .eqs. "," then goto BUILD_APPS
$ i = i + 1
$ name = f$parse(file,,,"NAME")
$ call make 'file "CXXCOMP ''name'.cc" - 
       'name'.cc
$ call make common.olb "lib/replace common.olb ''name'.obj" - 
       'name'.obj
$ goto COMPILE_CXX_LOOP
$!
$BUILD_APPS:
$ curr_app = f$element(j,",",APPS)
$ if curr_app .eqs. "," then exit
$ j = j + 1
$ i = 0
$COMPILE_APP:
$ file = f$element(i,",",'curr_app'_OBJS)
$ if file .eqs. "," then goto LINK_APP
$ i = i + 1
$ name = f$parse(file,,,"NAME")
$ call make 'file "CXXCOMP ''name'.cc" - 
       'name'.cc
$ goto COMPILE_APP
$LINK_APP:
$ if 'curr_app'_LIBS .nes. "" 
$ then 
$   LIBS = 'curr_app'_LIBS + "," + COMMON_LIBS
$ else
$   LIBS = COMMON_LIBS 
$ endif
$ OBJS = 'curr_app'_OBJS
$ write sys$output "Linking ''curr_app'..."
$ xpdf_link/exe='curr_app'.exe 'OBJS','libs',[-]xpdf.opt/opt
$!  
$ goto BUILD_APPS
$ exit
$!
$MAKE: SUBROUTINE   !SUBROUTINE TO CHECK DEPENDENCIES
$ V = 'F$Verify(0)
$! P1 = What we are trying to make
$! P2 = Command to make it
$! P3 - P8  What it depends on
$
$ If F$Search(P1) .Eqs. "" Then Goto Makeit
$ Time = F$CvTime(F$File(P1,"RDT"))
$arg=3
$Loop:
$       Argument = P'arg
$       If Argument .Eqs. "" Then Goto Exit
$       El=0
$Loop2:
$       File = F$Element(El," ",Argument)
$       If File .Eqs. " " Then Goto Endl
$       AFile = ""
$Loop3:
$       OFile = AFile
$       AFile = F$Search(File)
$       If AFile .Eqs. "" .Or. AFile .Eqs. OFile Then Goto NextEl
$       If F$CvTime(F$File(AFile,"RDT")) .Ges. Time Then Goto Makeit
$       Goto Loop3
$NextEL:
$       El = El + 1
$       Goto Loop2
$EndL:
$ arg=arg+1
$ If arg .Le. 8 Then Goto Loop
$ Goto Exit
$
$Makeit:
$ VV=F$VERIFY(0)
$ write sys$output P2
$ 'P2
$ VV='F$Verify(VV)
$Exit:
$ If V Then Set Verify
$ENDSUBROUTINE
//========================================================================
//
// xpdf.cc
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#include <aconf.h>
#include "gtypes.h"
#include "GString.h"
#include "parseargs.h"
#include "gfile.h"
#include "gmem.h"
#include "GlobalParams.h"
#include "Object.h"
#include "XPDFApp.h"
#include "config.h"

//------------------------------------------------------------------------
// command line options
//------------------------------------------------------------------------

static GBool contView = gFalse;
static char enableT1libStr[16] = "";
static char enableFreeTypeStr[16] = "";
static char antialiasStr[16] = "";
static char vectorAntialiasStr[16] = "";
static char psFileArg[256];
static char paperSize[15] = "";
static int paperWidth = 0;
static int paperHeight = 0;
static GBool level1 = gFalse;
static char textEncName[128] = "";
static char textEOL[16] = "";
static char ownerPassword[33] = "\001";
static char userPassword[33] = "\001";
static GBool fullScreen = gFalse;
static char remoteName[100] = "xpdf_";
static char remoteCmd[512] = "";
static GBool doRemoteReload = gFalse;
static GBool doRemoteRaise = gFalse;
static GBool doRemoteQuit = gFalse;
static GBool printCommands = gFalse;
static GBool quiet = gFalse;
static char cfgFileName[256] = "";
static GBool printVersion = gFalse;
static GBool printHelp = gFalse;

static ArgDesc argDesc[] = {
  {"-g",          argStringDummy, NULL,           0,
   "initial window geometry"},
  {"-geometry",   argStringDummy, NULL,           0,
   "initial window geometry"},
  {"-title",      argStringDummy, NULL,           0,
   "window title"},
  {"-cmap",       argFlagDummy,   NULL,           0,
   "install a private colormap"},
  {"-rgb",        argIntDummy,    NULL,           0,
   "biggest RGB cube to allocate (default is 5)"},
  {"-rv",         argFlagDummy,   NULL,           0,
   "reverse video"},
  {"-papercolor", argStringDummy, NULL,           0,
   "color of paper background"},
  {"-z",          argStringDummy, NULL,           0,
   "initial zoom level (percent, 'page', 'width')"},
  {"-cont",       argFlag,        &contView,      0,
   "start in continuous view mode" },
#if HAVE_T1LIB_H
  {"-t1lib",      argString,      enableT1libStr, sizeof(enableT1libStr),
   "enable t1lib font rasterizer: yes, no"},
#endif
#if HAVE_FREETYPE_FREETYPE_H | HAVE_FREETYPE_H
  {"-freetype",   argString,      enableFreeTypeStr, sizeof(enableFreeTypeStr),
   "enable FreeType font rasterizer: yes, no"},
#endif
  {"-aa",         argString,      antialiasStr,   sizeof(antialiasStr),
   "enable font anti-aliasing: yes, no"},
  {"-aaVector",   argString,      vectorAntialiasStr, sizeof(vectorAntialiasStr),
   "enable vector anti-aliasing: yes, no"},
  {"-ps",         argString,      psFileArg,      sizeof(psFileArg),
   "default PostScript file name or command"},
  {"-paper",      argString,      paperSize,      sizeof(paperSize),
   "paper size (letter, legal, A4, A3, match)"},
  {"-paperw",     argInt,         &paperWidth,    0,
   "paper width, in points"},
  {"-paperh",     argInt,         &paperHeight,   0,
   "paper height, in points"},
  {"-level1",     argFlag,        &level1,        0,
   "generate Level 1 PostScript"},
  {"-enc",    argString,   textEncName,    sizeof(textEncName),
   "output text encoding name"},
  {"-eol",    argString,   textEOL,        sizeof(textEOL),
   "output end-of-line convention (unix, dos, or mac)"},
  {"-opw",        argString,      ownerPassword,  sizeof(ownerPassword),
   "owner password (for encrypted files)"},
  {"-upw",        argString,      userPassword,   sizeof(userPassword),
   "user password (for encrypted files)"},
  {"-fullscreen", argFlag,        &fullScreen,    0,
   "run in full-screen (presentation) mode"},
  {"-remote",     argString,      remoteName + 5, sizeof(remoteName) - 5,
   "start/contact xpdf remote server with specified name"},
  {"-exec",       argString,      remoteCmd,      sizeof(remoteCmd),
   "execute command on xpdf remote server (with -remote only)"},
  {"-reload",     argFlag,        &doRemoteReload, 0,
   "reload xpdf remove server window (with -remote only)"},
  {"-raise",      argFlag,        &doRemoteRaise, 0,
   "raise xpdf remote server window (with -remote only)"},
  {"-quit",       argFlag,        &doRemoteQuit,  0,
   "kill xpdf remote server (with -remote only)"},
  {"-cmd",        argFlag,        &printCommands, 0,
   "print commands as they're executed"},
  {"-q",          argFlag,        &quiet,         0,
   "don't print any messages or errors"},
  {"-cfg",        argString,      cfgFileName,    sizeof(cfgFileName),
   "configuration file to use in place of .xpdfrc"},
  {"-v",          argFlag,        &printVersion,  0,
   "print copyright and version info"},
  {"-h",          argFlag,        &printHelp,     0,
   "print usage information"},
  {"-help",       argFlag,        &printHelp,     0,
   "print usage information"},
  {"--help",  argFlag,     &printHelp,     0,
   "print usage information"},
  {"-?",      argFlag,     &printHelp,     0,
   "print usage information"},
  {NULL}
};

//------------------------------------------------------------------------

int main(int argc, char *argv[]) {
  XPDFApp *app;
  GString *fileName;
  int pg;
  GString *destName;
  GString *userPasswordStr, *ownerPasswordStr;
  GBool ok;
  int exitCode;

  exitCode = 0;
  userPasswordStr = ownerPasswordStr = NULL;

  // parse args
  ok = parseArgs(argDesc, &argc, argv);
  if (!ok || printVersion || printHelp) {
    fprintf(stderr, "xpdf version %s\n", xpdfVersion);
    fprintf(stderr, "%s\n", xpdfCopyright);
    if (!printVersion) {
      printUsage("xpdf", "[<PDF-file> [<page> | +<dest>]]", argDesc);
    }
    exitCode = 99;
    goto done0;
  }

  // read config file
  globalParams = new GlobalParams(cfgFileName);
  globalParams->setupBaseFonts(NULL);
  if (contView) {
    globalParams->setContinuousView(contView);
  }
  if (psFileArg[0]) {
    globalParams->setPSFile(psFileArg);
  }
  if (paperSize[0]) {
    if (!globalParams->setPSPaperSize(paperSize)) {
      fprintf(stderr, "Invalid paper size\n");
    }
  } else {
    if (paperWidth) {
      globalParams->setPSPaperWidth(paperWidth);
    }
    if (paperHeight) {
      globalParams->setPSPaperHeight(paperHeight);
    }
  }
  if (level1) {
    globalParams->setPSLevel(psLevel1);
  }
  if (textEncName[0]) {
    globalParams->setTextEncoding(textEncName);
  }
  if (textEOL[0]) {
    if (!globalParams->setTextEOL(textEOL)) {
      fprintf(stderr, "Bad '-eol' value on command line\n");
    }
  }
  if (enableT1libStr[0]) {
    if (!globalParams->setEnableT1lib(enableT1libStr)) {
      fprintf(stderr, "Bad '-t1lib' value on command line\n");
    }
  }
  if (enableFreeTypeStr[0]) {
    if (!globalParams->setEnableFreeType(enableFreeTypeStr)) {
      fprintf(stderr, "Bad '-freetype' value on command line\n");
    }
  }
  if (antialiasStr[0]) {
    if (!globalParams->setAntialias(antialiasStr)) {
      fprintf(stderr, "Bad '-aa' value on command line\n");
    }
  }
  if (vectorAntialiasStr[0]) {
    if (!globalParams->setVectorAntialias(vectorAntialiasStr)) {
      fprintf(stderr, "Bad '-aaVector' value on command line\n");
    }
  }
  if (printCommands) {
    globalParams->setPrintCommands(printCommands);
  }
  if (quiet) {
    globalParams->setErrQuiet(quiet);
  }

  // create the XPDFApp object
  app = new XPDFApp(&argc, argv);

  // the initialZoom parameter can be set in either the config file or
  // as an X resource (or command line arg)
  if (app->getInitialZoom()) {
    globalParams->setInitialZoom(app->getInitialZoom()->getCString());
  }

  // check command line
  ok = ok && argc >= 1 && argc <= 3;
  if (remoteCmd[0]) {
    ok = ok && remoteName[5] && !doRemoteReload && !doRemoteRaise &&
         !doRemoteQuit && argc == 1;
  }
  if (doRemoteReload) {
    ok = ok && remoteName[5] && !doRemoteQuit && argc == 1;
  }
  if (doRemoteRaise) {
    ok = ok && remoteName[5] && !doRemoteQuit;
  }
  if (doRemoteQuit) {
    ok = ok && remoteName[5] && argc == 1;
  }
  if (!ok || printVersion || printHelp) {
    fprintf(stderr, "xpdf version %s\n", xpdfVersion);
    fprintf(stderr, "%s\n", xpdfCopyright);
    if (!printVersion) {
      printUsage("xpdf", "[<PDF-file> [<page> | +<dest>]]", argDesc);
    }
    exitCode = 99;
    goto done1;
  }
  if (argc >= 2) {
    fileName = new GString(argv[1]);
  } else {
    fileName = NULL;
  }
  pg = 1;
  destName = NULL;
  if (argc == 3) {
    if (argv[2][0] == '+') {
      destName = new GString(&argv[2][1]);
    } else {
      pg = atoi(argv[2]);
      if (pg < 0) {
	fprintf(stderr, "Invalid page number (%d)\n", pg);
	exitCode = 99;
	goto done2;
      }
    }
  }

  // handle remote server stuff
  if (remoteName[5]) {
    app->setRemoteName(remoteName);
    if (app->remoteServerRunning()) {
      if (fileName) {
	if (destName) {
	  app->remoteOpenAtDest(fileName, destName, doRemoteRaise);
	} else {
	  app->remoteOpen(fileName, pg, doRemoteRaise);
	}
      } else if (remoteCmd[0]) {
	app->remoteExec(remoteCmd);
      } else if (doRemoteReload) {
	app->remoteReload(doRemoteRaise);
      } else if (doRemoteRaise) {
	app->remoteRaise();
      } else if (doRemoteQuit) {
	app->remoteQuit();
      }
      goto done2;
    }
    if (doRemoteQuit) {
      goto done2;
    }
  }

  // set options
  app->setFullScreen(fullScreen);

  // check for password string(s)
  ownerPasswordStr = ownerPassword[0] != '\001' ? new GString(ownerPassword)
                                                : (GString *)NULL;
  userPasswordStr = userPassword[0] != '\001' ? new GString(userPassword)
                                              : (GString *)NULL;

  // open the file and run the main loop
  if (destName) {
    if (!app->openAtDest(fileName, destName,
			 ownerPasswordStr, userPasswordStr)) {
      exitCode = 1;
      goto done2;
    }
  } else {
    if (!app->open(fileName, pg, ownerPasswordStr, userPasswordStr)) {
      exitCode = 1;
      goto done2;
    }
  }
  app->run();

  exitCode = 0;

  // clean up
 done2:
  if (userPasswordStr) {
    delete userPasswordStr;
  }
  if (ownerPasswordStr) {
    delete ownerPasswordStr;
  }
  if (destName) {
    delete destName;
  }
  if (fileName) {
    delete fileName;
  }
 done1:
  delete app;
  delete globalParams;

  // check for memory leaks
 done0:
  Object::memCheck(stderr);
  gMemReport(stderr);

  return exitCode;
}
/* XPM */
static char *xpdfIcon[] = {
/* width height num_colors chars_per_pixel */
"    48    48        7            1",
/* colors */
". c #000000",
"# c #a00000",
"a c #a0a0a0",
"b c #c0c0c0",
"c c #e00000",
"d c #e0e0e0",
"e c #ffffff",
/* pixels */
"................................................",
"................................................",
"................................................",
"................................................",
"#ccccccc#................................#ccc#..",
".#ccccccc#..............................#ccc#...",
"..#ccccccc#............................#ccc#....",
"...#ccccccc#................bbba.....abbbc#.....",
"....#ccccccc#...............beea....debbed......",
".....#ccccccc#...............ee....bebccee......",
"......#ccccccc#..............ee....eecc#bb......",
".......#ccccccc#............aee...#eec#.........",
"........#ccccccc#...........bed..#beb#..........",
".........#ccccccc#..........beb.#cbeb...........",
"..........#ccccccc#.........beb#ccbeb...........",
"...........#ccccccc#........bebcccbeb...........",
"............#ccccccc#.......eebcc#dea...........",
".............#ccccccc#......eecc#.ee............",
".........ae...#ccccccc#....#eec#..ee............",
"........aeeaeeeebcccccabeeeaee#.beeeeee.........",
".......aeeeea..debcccaed##ceee....ee............",
"......addee.....eeccaedccccbed...beb............",
"......a.bee.....ee#cbeaccccbeb...beb............",
"........beb.....ee.#eecccccbeb...beb............",
"........beb.....ee..eecccccbeb...beb............",
"........beb.....ee..eeccccceeb...dea............",
"........deb....aeb.#eeccccceec#..ee.............",
"........eea....dea#cee##ccceecc#.ee.............",
"........eee...dea#ccbeda#cdeeccc#ee.............",
"........eeaeeeba#ccc#beeeeaeeecceeee............",
"........ee.....#ccc#......#ccccccc#.............",
".......bee....#ccc#........#ccccccc#............",
".......beb...#ccc#..........#ccccccc#...........",
".......beb..#ccc#............#ccccccc#..........",
".......beb.#ccc#..............#ccccccc#.........",
".......deb#ccc#................#ccccccc#........",
".......eeaccc#..................#ccccccc#.......",
".......eeccc#....................#ccccccc#......",
".......eecc#......................#ccccccc#.....",
"......beeb#........................#ccccccc#....",
".....#bbbb..........................#ccccccc#...",
"....#ccc#............................#ccccccc#..",
"...#ccc#..............................#ccccccc#.",
"..#ccc#................................#ccccccc.",
"................................................",
"................................................",
"................................................",
"................................................"
};