summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Cloos <cloos@freedesktop.org>2011-08-30 03:35:26 -0700
committerJames Cloos <cloos@freedesktop.org>2011-08-30 03:35:26 -0700
commitf2a102d2e974ad0b6f7bd548b04c6fc39601d173 (patch)
treedd14e07964656a7fbf47a7f813f5f7ac7a93a9d2
xpdf-3.02xpdf-3.02
-rw-r--r--ANNOUNCE34
-rw-r--r--CHANGES1762
-rw-r--r--COPYING339
-rw-r--r--INSTALL248
-rw-r--r--Makefile.in126
-rw-r--r--README405
-rw-r--r--aclocal.m4274
-rw-r--r--aconf-dj.h77
-rw-r--r--aconf-win32.h120
-rw-r--r--aconf.h.in115
-rw-r--r--aconf2.h38
-rwxr-xr-xconfigure11805
-rw-r--r--configure.in353
-rw-r--r--dj_make.bat81
-rw-r--r--doc/pdffonts.1142
-rw-r--r--doc/pdffonts.cat112
-rw-r--r--doc/pdffonts.hlp122
-rw-r--r--doc/pdfimages.1102
-rw-r--r--doc/pdfimages.cat92
-rw-r--r--doc/pdfimages.hlp101
-rw-r--r--doc/pdfinfo.1158
-rw-r--r--doc/pdfinfo.cat119
-rw-r--r--doc/pdfinfo.hlp129
-rw-r--r--doc/pdftoppm.1119
-rw-r--r--doc/pdftoppm.cat108
-rw-r--r--doc/pdftoppm.hlp118
-rw-r--r--doc/pdftops.1235
-rw-r--r--doc/pdftops.cat229
-rw-r--r--doc/pdftops.hlp239
-rw-r--r--doc/pdftotext.1137
-rw-r--r--doc/pdftotext.cat120
-rw-r--r--doc/pdftotext.hlp133
-rw-r--r--doc/sample-xpdfrc91
-rw-r--r--doc/xpdf.1863
-rw-r--r--doc/xpdf.cat809
-rw-r--r--doc/xpdf.hlp823
-rw-r--r--doc/xpdfrc.5617
-rw-r--r--doc/xpdfrc.cat614
-rw-r--r--doc/xpdfrc.hlp619
-rw-r--r--fofi/FoFiBase.cc156
-rw-r--r--fofi/FoFiBase.h57
-rw-r--r--fofi/FoFiEncodings.cc994
-rw-r--r--fofi/FoFiEncodings.h36
-rw-r--r--fofi/FoFiTrueType.cc2027
-rw-r--r--fofi/FoFiTrueType.h174
-rw-r--r--fofi/FoFiType1.cc252
-rw-r--r--fofi/FoFiType1.h59
-rw-r--r--fofi/FoFiType1C.cc2603
-rw-r--r--fofi/FoFiType1C.h233
-rw-r--r--fofi/Makefile.dep0
-rw-r--r--fofi/Makefile.in69
-rw-r--r--fofi/vms_make.com0
-rw-r--r--goo/FixedPoint.cc118
-rw-r--r--goo/FixedPoint.h155
-rw-r--r--goo/GHash.cc380
-rw-r--r--goo/GHash.h78
-rw-r--r--goo/GList.cc97
-rw-r--r--goo/GList.h96
-rw-r--r--goo/GMutex.h49
-rw-r--r--goo/GString.cc718
-rw-r--r--goo/GString.h136
-rw-r--r--goo/Makefile.dep0
-rw-r--r--goo/Makefile.in71
-rw-r--r--goo/gfile.cc731
-rw-r--r--goo/gfile.h138
-rw-r--r--goo/gmem.cc264
-rw-r--r--goo/gmem.h79
-rw-r--r--goo/gmempp.cc32
-rw-r--r--goo/gtypes.h29
-rw-r--r--goo/parseargs.c190
-rw-r--r--goo/parseargs.h71
-rw-r--r--goo/vms_directory.c214
-rw-r--r--goo/vms_dirent.h67
-rw-r--r--goo/vms_make.com82
-rw-r--r--goo/vms_sys_dirent.h54
-rw-r--r--goo/vms_unix_time.h102
-rw-r--r--goo/vms_unix_times.c42
-rw-r--r--goo/vms_unlink.c22
-rwxr-xr-xinstall-sh250
-rw-r--r--misc/hello.pdfbin0 -> 917 bytes
-rw-r--r--misc/xpdf.dt28
-rw-r--r--ms_make.bat118
-rw-r--r--splash/Makefile.dep0
-rw-r--r--splash/Makefile.in99
-rw-r--r--splash/Splash.cc3310
-rw-r--r--splash/Splash.h293
-rw-r--r--splash/SplashBitmap.cc188
-rw-r--r--splash/SplashBitmap.h61
-rw-r--r--splash/SplashClip.cc382
-rw-r--r--splash/SplashClip.h107
-rw-r--r--splash/SplashErrorCodes.h32
-rw-r--r--splash/SplashFTFont.cc357
-rw-r--r--splash/SplashFTFont.h58
-rw-r--r--splash/SplashFTFontEngine.cc179
-rw-r--r--splash/SplashFTFontEngine.h65
-rw-r--r--splash/SplashFTFontFile.cc114
-rw-r--r--splash/SplashFTFontFile.h73
-rw-r--r--splash/SplashFont.cc176
-rw-r--r--splash/SplashFont.h104
-rw-r--r--splash/SplashFontEngine.cc317
-rw-r--r--splash/SplashFontEngine.h91
-rw-r--r--splash/SplashFontFile.cc55
-rw-r--r--splash/SplashFontFile.h60
-rw-r--r--splash/SplashFontFileID.cc23
-rw-r--r--splash/SplashFontFileID.h30
-rw-r--r--splash/SplashGlyphBitmap.h26
-rw-r--r--splash/SplashMath.h89
-rw-r--r--splash/SplashPath.cc184
-rw-r--r--splash/SplashPath.h121
-rw-r--r--splash/SplashPattern.cc40
-rw-r--r--splash/SplashPattern.h65
-rw-r--r--splash/SplashScreen.cc383
-rw-r--r--splash/SplashScreen.h56
-rw-r--r--splash/SplashState.cc165
-rw-r--r--splash/SplashState.h103
-rw-r--r--splash/SplashT1Font.cc287
-rw-r--r--splash/SplashT1Font.h57
-rw-r--r--splash/SplashT1FontEngine.cc124
-rw-r--r--splash/SplashT1FontEngine.h53
-rw-r--r--splash/SplashT1FontFile.cc97
-rw-r--r--splash/SplashT1FontFile.h58
-rw-r--r--splash/SplashTypes.h132
-rw-r--r--splash/SplashXPath.cc438
-rw-r--r--splash/SplashXPath.h100
-rw-r--r--splash/SplashXPathScanner.cc428
-rw-r--r--splash/SplashXPathScanner.h87
-rw-r--r--splash/vms_make.com0
-rw-r--r--vms_make.com736
-rw-r--r--xpdf/Annot.cc1556
-rw-r--r--xpdf/Annot.h142
-rw-r--r--xpdf/Array.cc73
-rw-r--r--xpdf/Array.h58
-rw-r--r--xpdf/BuiltinFont.cc65
-rw-r--r--xpdf/BuiltinFont.h57
-rw-r--r--xpdf/BuiltinFontTables.cc4284
-rw-r--r--xpdf/BuiltinFontTables.h23
-rw-r--r--xpdf/CMap.cc408
-rw-r--r--xpdf/CMap.h102
-rw-r--r--xpdf/Catalog.cc374
-rw-r--r--xpdf/Catalog.h97
-rw-r--r--xpdf/CharCodeToUnicode.cc540
-rw-r--r--xpdf/CharCodeToUnicode.h117
-rw-r--r--xpdf/CharTypes.h24
-rw-r--r--xpdf/CompactFontTables.h464
-rw-r--r--xpdf/CoreOutputDev.cc61
-rw-r--r--xpdf/CoreOutputDev.h61
-rw-r--r--xpdf/Decrypt.cc776
-rw-r--r--xpdf/Decrypt.h95
-rw-r--r--xpdf/Dict.cc95
-rw-r--r--xpdf/Dict.h77
-rw-r--r--xpdf/Error.cc38
-rw-r--r--xpdf/Error.h23
-rw-r--r--xpdf/ErrorCodes.h36
-rw-r--r--xpdf/FontEncodingTables.cc1824
-rw-r--r--xpdf/FontEncodingTables.h20
-rw-r--r--xpdf/Function.cc1573
-rw-r--r--xpdf/Function.h229
-rw-r--r--xpdf/Gfx.cc4181
-rw-r--r--xpdf/Gfx.h312
-rw-r--r--xpdf/GfxFont.cc1568
-rw-r--r--xpdf/GfxFont.h320
-rw-r--r--xpdf/GfxState.cc4137
-rw-r--r--xpdf/GfxState.h1244
-rw-r--r--xpdf/GlobalParams.cc2908
-rw-r--r--xpdf/GlobalParams.h463
-rw-r--r--xpdf/ImageOutputDev.cc195
-rw-r--r--xpdf/ImageOutputDev.h76
-rw-r--r--xpdf/JArithmeticDecoder.cc322
-rw-r--r--xpdf/JArithmeticDecoder.h109
-rw-r--r--xpdf/JBIG2Stream.cc3413
-rw-r--r--xpdf/JBIG2Stream.h145
-rw-r--r--xpdf/JPXStream.cc3144
-rw-r--r--xpdf/JPXStream.h351
-rw-r--r--xpdf/Lexer.cc485
-rw-r--r--xpdf/Lexer.h80
-rw-r--r--xpdf/Link.cc784
-rw-r--r--xpdf/Link.h369
-rw-r--r--xpdf/Makefile.dep0
-rw-r--r--xpdf/Makefile.in254
-rw-r--r--xpdf/NameToCharCode.cc116
-rw-r--r--xpdf/NameToCharCode.h42
-rw-r--r--xpdf/NameToUnicodeTable.h1097
-rw-r--r--xpdf/Object.cc231
-rw-r--r--xpdf/Object.h303
-rw-r--r--xpdf/Outline.cc151
-rw-r--r--xpdf/Outline.h76
-rw-r--r--xpdf/OutputDev.cc131
-rw-r--r--xpdf/OutputDev.h250
-rw-r--r--xpdf/PDFCore.cc2044
-rw-r--r--xpdf/PDFCore.h321
-rw-r--r--xpdf/PDFDoc.cc402
-rw-r--r--xpdf/PDFDoc.h183
-rw-r--r--xpdf/PDFDocEncoding.cc44
-rw-r--r--xpdf/PDFDocEncoding.h16
-rw-r--r--xpdf/PSOutputDev.cc6222
-rw-r--r--xpdf/PSOutputDev.h395
-rw-r--r--xpdf/PSTokenizer.cc135
-rw-r--r--xpdf/PSTokenizer.h41
-rw-r--r--xpdf/Page.cc441
-rw-r--r--xpdf/Page.h187
-rw-r--r--xpdf/Parser.cc227
-rw-r--r--xpdf/Parser.h59
-rw-r--r--xpdf/PreScanOutputDev.cc257
-rw-r--r--xpdf/PreScanOutputDev.h130
-rw-r--r--xpdf/SecurityHandler.cc390
-rw-r--r--xpdf/SecurityHandler.h160
-rw-r--r--xpdf/SplashOutputDev.cc2845
-rw-r--r--xpdf/SplashOutputDev.h247
-rw-r--r--xpdf/Stream-CCITT.h459
-rw-r--r--xpdf/Stream.cc4627
-rw-r--r--xpdf/Stream.h858
-rw-r--r--xpdf/TextOutputDev.cc4090
-rw-r--r--xpdf/TextOutputDev.h661
-rw-r--r--xpdf/UTF8.h56
-rw-r--r--xpdf/UnicodeMap.cc293
-rw-r--r--xpdf/UnicodeMap.h123
-rw-r--r--xpdf/UnicodeMapTables.h361
-rw-r--r--xpdf/UnicodeTypeTable.cc949
-rw-r--r--xpdf/UnicodeTypeTable.h20
-rw-r--r--xpdf/XPDFApp.cc447
-rw-r--r--xpdf/XPDFApp.h114
-rw-r--r--xpdf/XPDFCore.cc1655
-rw-r--r--xpdf/XPDFCore.h251
-rw-r--r--xpdf/XPDFTree.cc931
-rw-r--r--xpdf/XPDFTree.h45
-rw-r--r--xpdf/XPDFTreeP.h87
-rw-r--r--xpdf/XPDFViewer.cc3488
-rw-r--r--xpdf/XPDFViewer.h352
-rw-r--r--xpdf/XRef.cc896
-rw-r--r--xpdf/XRef.h133
-rw-r--r--xpdf/XpdfPluginAPI.cc262
-rw-r--r--xpdf/XpdfPluginAPI.h341
-rw-r--r--xpdf/about-text.h48
-rw-r--r--xpdf/about.xbm6
-rw-r--r--xpdf/backArrow.xbm6
-rw-r--r--xpdf/backArrowDis.xbm6
-rw-r--r--xpdf/config.h112
-rw-r--r--xpdf/dblLeftArrow.xbm6
-rw-r--r--xpdf/dblLeftArrowDis.xbm6
-rw-r--r--xpdf/dblRightArrow.xbm6
-rw-r--r--xpdf/dblRightArrowDis.xbm6
-rw-r--r--xpdf/find.xbm6
-rw-r--r--xpdf/findDis.xbm6
-rw-r--r--xpdf/forwardArrow.xbm6
-rw-r--r--xpdf/forwardArrowDis.xbm6
-rw-r--r--xpdf/leftArrow.xbm5
-rw-r--r--xpdf/leftArrowDis.xbm5
-rw-r--r--xpdf/pdffonts.cc298
-rw-r--r--xpdf/pdfimages.cc155
-rw-r--r--xpdf/pdfinfo.cc387
-rw-r--r--xpdf/pdftoppm.cc203
-rw-r--r--xpdf/pdftops.cc344
-rw-r--r--xpdf/pdftotext.cc333
-rw-r--r--xpdf/print.xbm6
-rw-r--r--xpdf/printDis.xbm6
-rw-r--r--xpdf/rightArrow.xbm5
-rw-r--r--xpdf/rightArrowDis.xbm5
-rw-r--r--xpdf/vms_make.com129
-rw-r--r--xpdf/xpdf.cc344
-rw-r--r--xpdf/xpdfIcon.xpm62
260 files changed, 123109 insertions, 0 deletions
diff --git a/ANNOUNCE b/ANNOUNCE
new file mode 100644
index 0000000..1184040
--- /dev/null
+++ b/ANNOUNCE
@@ -0,0 +1,34 @@
+Subject: ANNOUNCE: Xpdf 3.02 - a PDF viewer for X
+
+
+Glyph & Cog, LLC is pleased to announce a new version of Xpdf, the
+open source Portable Document Format (PDF) viewer for X. The Xpdf
+project also includes a PDF text extractor, PDF-to-PostScript
+converter, and various other utilities.
+
+Xpdf runs under the X Window System on Unix, VMS, and OS/2. The non-X
+components (pdftops, pdftotext, etc.) also run on Win32 systems and
+should run on pretty much any system with a decent C++ compiler.
+
+Major changes:
+* Anti-aliased vector graphics, including stroke adjustment.
+* Support for PDF 1.6 and PDF 1.7, including AES decryption and
+ OpenType fonts.
+* User-configurable key/mouse bindings.
+* Improved full-screen mode, with the ability to toggle on the fly.
+
+See the `CHANGES' file for a complete list of changes.
+
+Source (C++ and C) is available, and it should be fairly easy to
+compile for UNIX, VMS, OS/2, and Win32.
+
+More information, source code, and precompiled binaries are on the
+xpdf web page and ftp site:
+
+ http://www.foolabs.com/xpdf/
+ ftp://ftp.foolabs.com/pub/xpdf/
+
+For information on commercial licensing and consulting, please see the
+Glyph & Cog web site:
+
+ http://www.glyphandcog.com/
diff --git a/CHANGES b/CHANGES
new file mode 100644
index 0000000..0378f0d
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,1762 @@
+0.2 (95-dec-12)
+---------------
+First public release.
+
+
+0.3 (96-jan-13)
+---------------
+LZW patent workaround.
+Implemented inline images.
+Fixed (mostly) disjoint polygon fills.
+Added remote server stuff.
+Added page number on command line.
+Fixed problem with font encodings which caused character misalignment.
+Fixed inverted CCITT decoding and inverted image mask drawing.
+Now compiles under gcc 2.7.x (ignore those stupid 'unused parameter'
+ warnings).
+Many minor bug fixes and optimizations.
+
+
+0.4 (96-apr-24)
+---------------
+Implemented DCT filter.
+Implemented PostScript output; wrote pdftops program.
+Implemented links.
+Implemented font rotation -- I was wrong: X11R6 servers *do* support
+ font rotation (by specifying a matrix in place of a size).
+Added bindings for Home/End, Page Up/Down, arrow keys.
+Added initialZoom resource and -z option.
+Added geometry resource and -g option.
+Fixed image size off-by-one bug.
+Fixed bug where page content is reference to an array of streams.
+Cleaned up uninitialized variables which were causing random problems
+ on various platforms.
+Manually skip whitespace before calling atoi() for startxref.
+Replaced calls to XrmCombineFileDatabase() with calls to
+ XrmGetFileDatabase() and XrmMergeDatabases() so it will work under
+ older versions of X.
+Fixed problem with reading multiple xref tables in updated PDF files.
+Check for encryption and print appropriate error message.
+Rudimentary dithering of images.
+Fixed bug in CCITTFax filter (pass mode followed by horizontal mode).
+Optimized drawImage() and drawImageMask().
+Changed several things to ease porting:
+ - changed '__inline' to 'inline' (to adhere to the ANSI standard)
+ - surrounded interface/implementation pragmas with #ifdef _GNUC__
+ - got rid of empty array initializer lists
+ - moved Operator type definition from Gfx.cc to Gfx.h
+ - renamed String, uint, etc.
+ - ability to uncompress to file (NO_POPEN and USE_GZIP flags)
+ - added definitions of XK_Page_Up/Down and XPointer for old versions
+ of X
+For VMS port:
+ - use correct Xdefaults name for VMS, get rid of ltkGetHomeDir()
+ - added '#include <stddef.h>' before all X includes
+ - renamed files with multiple periods in their names
+Fixed window resizing infinite oscillation bug.
+Fixed problem with string-type (as opposed to stream-type) indexed
+ color space lookup tables (which are used in inline images).
+If an X font is not found, try smaller and then larger sizes (this is
+ useful for old or broken X servers which can't scale bitmap fonts).
+Added -rgb (color cube size) option.
+Various minor bug fixes.
+
+
+0.5 (96-may-23)
+---------------
+Fixed bug in LTKWindow which broke the remote server mode.
+Fixed PostScript output:
+ - doesn't seg fault if file is unwritable.
+ - incorrect DSC comment - need colon in '%%Page:'.
+ - use 'imagemask' command for masks.
+ - output filters in the correct order.
+ - Stream::isBinary() checks the next-to-bottom, not top, stream.
+ - if page width > height, rotate it 90 degrees.
+ - if page is larger than paper size, scale it down.
+Set default MediaBox to 8.5" x 11" to deal with non-compliant PDF
+ files which don't specify a MediaBox.
+Added DEBUG_MEM stuff in gmem.c and gmempp.cc.
+Fixed memory leaks:
+ - LTKWindow didn't delete the LTKBox.
+ - LinkAction needs a virtual destructor.
+Use $(RANLIB) variable in goo/Makefile and ltk/Makefile.
+Allocate image data after calling XCreateImage, using
+ image->bytes_per_line -- works in 24-bit mode now.
+DCTStream rounds width of rowBuf lines up to the next multiple of
+ mcuWidth, so last MCU doesn't run off end of buffer.
+Increase size of block (from 255 to 1024 bytes) read at end of file to
+ search for 'startxref'.
+Skip past garbage at start of file, look for '%PDF'.
+Moved more compiler options out of Makefiles into Makefile.config.
+Top-level Makefile uses '$(MAKE)' instead of 'make' for making
+ subdirectories.
+Space/PageDown/Next and Backspace/PageUp/Previous now moves to
+ next/previous page if already scrolled to bottom/top of current
+ page.
+
+
+0.5a (96-jul-09)
+----------------
+[not a public release]
+For PDF 1.2 (a.k.a. Amber, a.k.a. Acrobat 3) support:
+ - look for trailer after first xref instead of at end of file.
+Deal with font subsets by converting character names of the form
+ 'Cnnnn' to the appropriate character from the standard encoding.
+Extract encoding from embedded Type 1 fonts.
+Kludge to fill one-pixel thick polygons.
+Changed X font encoding to use endash for hyphen (gets rid of too-long
+ hyphens).
+Handle Resources key in Pages dictionaries (needed for pstoedit
+ output).
+Fix comment handling in Parser (needed for pstoedit output).
+Move Bezier curve conversion from GfxState to XOutputDev; look at
+ flatness parameter in GfxState.
+Change all of the path functions in XOutputDev (stroke, fill, clip) to
+ use the same path transformation/conversion function.
+Rewrote PostScript output driver as a subclass of OutputDev; removed
+ duplicated code (ps_ functions) from Gfx.
+Fixed bug in xref code with small (< 1024 bytes) PDF files.
+Implemented BX/EX operators.
+Added PDFDoc class.
+
+
+0.6 (96-nov-12)
+---------------
+Add support for PostScript output to stdout (-) and to a command
+ (|lpr); added -ps option and psFile resource.
+Decryption is implemented but not included in the distribution due to
+ legal restrictions: the decryption algorithm is a trade secret of
+ RSA, Inc., and the U.S.A. still has bogus export controls on
+ cryptography software.
+Added .xpdfrc config file:
+ - Added fontmap parameter: user can map PDF font names to X fonts.
+ - Added fontpath parameter: search for Type 1 font if encoding is
+ not in PDF file.
+Incremental display: display is updated after every 200 commands.
+Added forward and backward by-10-page buttons.
+Links:
+ - Implement links with "Launch" actions that point to PDF files.
+ - Draw borders around links.
+ - Handle links with named destinations.
+ - GoToR links specify a page number instead of a page reference.
+Optimizations:
+ - Rewrote Stream to use buffering, and added lookChar() functions;
+ rewrote Lexer to take advantage of this.
+ - Use hash tables for name->code mapping in font encodings.
+ - Made XOutputDev::doCurve() iterative, changed /2 to *0.5, and
+ changed the flatness test.
+Added file name to window title.
+Implemented RunLength filter.
+Implemented forms.
+Convert ObjType to an enum.
+Changed isDict("Pages") to isDict() (in Catalog.cc) to deal with
+ incorrect PDF files.
+Changed color selection so that very pale colors don't map to white.
+Fixed bug in CCITTFax filter (multiple make-up codes).
+In GString::clear(): need to set length to 0 before calling resize().
+Base initial window size on first displayed page, not page 1; deal
+ correctly with rotated pages.
+Added ltkGetIntResource() and LTKApp::getIntResource().
+PostScript output fixes:
+ - Escape backslashes in strings.
+ - When doing ASCII85 encoding, keep both chars of EOF marker ('~>')
+ on same line.
+ - Add extra line '%-EOD-' after image data streams; call wrapper
+ functions for image and imagemask which look for this line -- this
+ should fix the 'too much data in stream' bug.
+ - Font tags can be reused for different fonts on different pages --
+ so use font object reference (number/generation) instead.
+Initialize character widths to zero (this caused crashes on OSF/1).
+Handle image masks which go outside of pixmap.
+Makefile.config changes:
+ - Remove -o in C++ compile rule.
+ - Add $(AR) variable.
+Code which read char widths from font dictionary read all but the last
+ width.
+Add 'return 0;' to main() in xpdf and pdftops.
+Allow fonts to use StandardEncoding.
+Convert man pages to VMS help files.
+
+0.7 (97-may-28)
+---------------
+Implemented FlateDecode filter (for PDF 1.2).
+Basic xref table reconstruction for damaged files
+New pdftotext program converts PDF to plain text.
+Implemented menus in LTK; added a menu to xpdf.
+Added open and save functions; allow xpdf to start without any PDF
+ file.
+Implemented text find.
+Implemented text select/copy.
+Change mouse cursor when it's over a link.
+Embed Type 1 fonts in PostScript output.
+Moved rotate functions to menu; added quit to menu.
+Fixed stroke color bug in PostScript output (was using fill color
+ instead of stroke color; this sometimes caused lines to be missing
+ (white) in PostScript output).
+Support Launch-type links -- pops up a dialog before executing
+ anything. Expects the A (action) dictionary to contain a Unix
+ dictionary with F (file) and P (paremeter) keys just like the Win
+ dictionary.
+A moveto op all by itself should just be discarded, instead of
+ generating a subpath with one point (this was causing seg faults).
+Balanced parentheses in strings don't need to be escaped.
+Tj operator in PostScript prolog didn't check for zero when dividing
+ by length of string.
+Implemented selection in LTK; TextIn widgets support dragging/copy/
+ paste.
+Handle font subsets that use hex character codes.
+Added icon pixmap; added the XPMLIB and NO_XPM variables to
+ Makefile.config.
+Fixed subtle bug in use of horizontal scaling parameter (it affects
+ only the width of drawn characters, not positioning done in text
+ space).
+Memory testing (with DEBUG_MEM):
+ - gmalloc now fills memory blocks with garbage to catch unitialized
+ fields.
+ - gfree fills memory blocks with garbage to catch uses of freed
+ blocks.
+Handle image masks which go off the pixmap on the top and/or left.
+Fixed inline functions which had two return statements (to make the
+ HP, SCO, and other cfront-based compilers happy).
+Fixed bug which caused seg faults when following a link to a different
+ file (info in LinkGoto object was used after link was deleted by
+ loadFile).
+If page content is an array of streams, the streams are concatenated;
+ objects and commands can span multiple streams.
+If file open fails, try lower-casing and upper-casing the file name.
+Commands should end when lexer sees a '/' character.
+GString::append(char *, int) was broken.
+Changed LTKScrollingCanvas redraw to be more efficient: copy as much
+ as possible from window before copying from off-screen pixmap.
+Ignore gs (set extended graphics state) operator.
+Handle colorspaces (CalGray/RGB are treated as DeviceGray/RGB; the
+ weird colorspaces are not yet implemented).
+Named destinations (for links) can be strings as well as names; deal
+ with the names tree in the catalog.
+Clip to the page CropBox.
+Added '-q' to gzip options (to suppress warnings, in case user has -v
+ in GZIP env var).
+Added 'include Makefile.config' to top-level Makefile.
+Added INSTALL variable to Makefile.config; used in top-level
+ Makefile.
+Always initialize LinkDest left/bottom/top/right/zoom fields (bogus
+ floating point values were causing crashes on Alpha).
+Added Makefile.config options for Digital Unix (DEC compilers), HP-UX
+ (HP compilers), SCO Unix, and Evans & Sutherland ES/OS.
+Added flag to set stream mode in fopen call for VMS.
+Rewrote Link module.
+Pages with no contents shouldn't cause an error message.
+In PostScript output: pdfImM needs to set fill color before doing
+ imagemask.
+If font doesn't specify character widths, use widths from built-in
+ font, based on font flags.
+Fixed LTK scrollbar to delay before repeating and to control the
+ period between repeats.
+Removed window/widget copy() methods (they were untested and unused).
+Unknown filter types produce a single error message instead of a
+ stream of errors.
+Added a dummy target in top-level Makefile so making individual
+ executables (e.g., 'make pdftops') should now work.
+Added optional xpdf-flip.ltk with buttons on right side instead of
+ bottom of window.
+
+0.7a (98-feb-22)
+----------------
+Moved find command from menu to toolbar button ('f' key still works).
+Support TrueColor visuals.
+Added a -cmap option and a installCmap resource to install a private
+ colormap.
+Mouse button 2 pans the window.
+Selecting a URI link now executes a configurable command (from the
+ urlCommand resource).
+Added a "link info" display which shows the URL or file for the link
+ under the mouse.
+Don't draw (or convert to PostScript) text drawn in render modes 3 and
+ 7 -- this is invisible text, used by Acrobat Capture; this text is
+ still passed to the TextPage object so that selection works.
+Recognize (and quietly ignore) marked content operators (BMC, BDC,
+ EMC, MP, DP).
+Recognize new color-setting operators (scn, SCN).
+Added A4_PAPER option.
+Embed external Type 1 font files (this currently only works with PFA
+ files).
+Added "-level1" option (in xpdf and pdftops) to generate Level 1
+ PostScript.
+Setup autoconf -- replaced Makefile.config. Added SELECT_TAKES_INT
+ flag, and use configure to autodetect (for HP-UX).
+Fixed appendToPath() to behave reasonably when appending ".." to root
+ directory.
+Fixed array size in FlateStream::compHuffmanCodes() (was causing xpdf
+ to crash under OSF/1).
+ASCII85Stream, ASCIIHexStream, and DCTStream didn't check for EOF and
+ could run past the end of the stream in damaged files.
+Handle hex escapes (#xx) in names. Still allow the name /# for
+ backward-compatibility.
+Check for NULL characters in encoding array in GfxFont.cc (was calling
+ strcmp() with NULL which crashed under Solaris).
+PageAttrs::PageAttrs() didn't initialize crop box boundaries.
+Changed uses of lookup() to lookupNF() in XRef.cc.
+Fixed type checking of operators which take a variable number of
+ args.
+Gfx::buildImageStream() doesn't need to check for parser (since I got
+ rid of the bogus array-of-command thing).
+XOutputFont matches on font reference instead of font tag (similar to
+ PSOutputDev fix).
+Fixed bug in position calculation for multi-char substitutions in
+ XOutputDev.
+Cleaned up local variables which hid class variables.
+Optimized variable length decoding in CCITTFaxStream.
+Set link border width to zero if Border dictionary entry is missing.
+Throw away zero-length strings in TextOutputDev -- they don't have
+ valid xMin/xMax values.
+Swapped order of XLIBS and XPMLIB in xpdf/Makefile.
+Deleted 'LTKApp::' in function declaration in LTKApp.h.
+Changed '(XKeyEvent *)&event' to '&event.xkey' in LTKApp.cc.
+Check that the link rectangle coordinates are in the correct order,
+ and swap if necessary.
+TextOutputDev didn't set text to NULL, which caused pdftotext to
+ segfault if it couldn't open it's output file.
+Fixed a hash table search bug in GfxFontEncoding::getCharCode().
+Cleaned up colorspace code: rewrote GfxColorSpace and added
+ GfxImageColorMap; cleaned up PSOutputDev::doImage.
+Handle named colorspaces in images.
+Correctly set the default color after a colorspace change.
+Old setcolor operators now set the colorspace.
+Fixed bug with uncompressed blocks in FlateStream.
+Fixed bug with fixed Huffman code table in FlateStream.
+Added hash table of X windows (for LTKWindow and LTKWidget) to LTKApp
+ and replaced calls to XQueryTree with hash table searches -- this
+ avoids a roundtrip to the server for each event and also fixes the
+ problem where XQueryTree crashed if the window no longer existed
+ (with leftover events from a destroyed window). (Thanks to Yair
+ Lenga for the suggestion.)
+Create a new GC for selection -- xor black and white (instead of LTK
+ foreground and background).
+Fixed crash with blank lines in .xpdfrc.
+Allow spaces in font descriptors in fontmap lines in .xpdfrc.
+Check for bogus object number in XRef::fetch().
+Use MacRomanEncoding for TrueType fonts that don't specify an
+ encoding.
+Certain PDF generators apparently don't include FontDescriptors for
+ Arial, TimesNewRoman, and CourierNew -- set GfxFont flags
+ appropriately.
+Fixed a bug in width guessing in GfxFont -- sans serif and serif were
+ swapped.
+Rewrote XRef::readXRef() to avoid using a parser to read the xref
+ entries.
+Added NO_TEXT_SELECT option.
+Ignore APPn/COM/etc. markers in DCT streams.
+Replaced select() with XMultiplexInput() in LTKApp.cc for VMS.
+Handle WM_DELETE_WINDOW protocol -- if you ask the window manager to
+ delete the xpdf window, xpdf will exit cleanly; other
+ windows/dialogs are simply closed.
+Optimized DCT decoder; switched to integer arithmetic.
+The "/Type /Annots" field in an annotation dictionary is optional.
+Check for null nameTree in Catalog::findDest().
+In XOutputDev, search user font map before default font map.
+Added "normal" SETWIDTH parameter to all font descriptors in
+ XOutputDev (some systems have a narrow-width Helvetica font).
+Added FOPEN_READ_BIN and FOPEN_WRITE_BIN to support Win32.
+Added a hack which allows better font substitution for some Type 3
+ fonts. Also allow character names of the form /nn and /nnn.
+Added <strings.h> and <bstring.h> to LTKApp.cc (needed by AIX and IRIX
+ for bzero() declaration for FD_ZERO).
+
+0.80 (98-nov-27)
+----------------
+Support for some Japanese fonts (Type 0 fonts using the Adobe-Japan1-2
+ character collection, horizontal only).
+Added pdfinfo application.
+Added pdftopbm application.
+Added pdfimages application.
+Added -papercolor option and .paperColor resource.
+Fixed divide-by-zero problem in XOutputDev Type 3 font matrix kludge.
+Font subset char names can be 'Bxx' as well as 'Cxx' and 'Gxx'.
+Fixed bug in color space conversion in DCTStream filter (YCC->RGB was
+ correct, YCCK->CMYK was broken).
+Added XRef::getDocInfo() and PDFDoc::getDocInfo() to support pdfinfo.
+Optimized GfxImageColorMap.
+Lexer::getStream(), getPos(), and setPos() check for null stream.
+Decryption code now does strings as well as streams.
+ASCII85 decoder rounds short tuples up instead of down.
+CropBox and MediaBox can be non-integers.
+PostScript output:
+ - Use a rectangle operator.
+ - Call setpagedevice with page size.
+ - Insert %%PageOrientation comments.
+ - Add paper size flags (-paperw and -paperh) to xpdf and pdftops.
+ - If HAVE_POPEN is not defined, and user tries to print to '|...',
+ the PSOutputDev destructor tried to write to the PS file.
+ - Added support for forms (pdftops -form).
+Removed error messages for empty paths in stroke, fill, etc. operators.
+Don't allow flatnesses less than 1 in XOutputDev (this speeds up
+ rendering a little bit when there are lots of tiny curves).
+Moved the font subset character name guessing from GfxFont to
+ XOutputDev and TextOutputDev - now these files print correctly.
+Cast argument to XFree() to XPointer; add XPointer definition where
+ necessary (portability fixes).
+Various minor VMS fixes.
+Changes to configure script and Makefiles:
+ - Print a warning if X is missing.
+ - Use C++ when checking select() argument type (HP-UX).
+ - Use 0 instead of NULL when checking select().
+ - Default to gcc instead of c++.
+ - Get rid of AC_C_INLINE -- this is meant for C, not C++.
+ - Changed -USE_GZIP to -DUSE_GZIP.
+ - Added ability to compile ouside of the source tree.
+ - Added .cc.o rule to {goo,ltk,xpdf}/Makefile.in.
+ - Added @LIBS@ to XLIBS in xpdf/Makefile.in.
+ - In top-level Makefile.in: added '-' to clean commands; added
+ distclean rule.
+ - Create install directories.
+ - Use INSTALL_DATA (instead of INSTALL) for man pages.
+ - Changed xpdf-ltk.h rule to avoid leaving an empty file when
+ ltkbuild fails.
+ - Change things so that by default, ltkbuild is not built and
+ xpdf-ltk.h is not rebuilt.
+ - Use AM_PROG_CC_STDC to add compiler flags for ANSI C.
+ - Modify autoconf's builtin macros to check for xlC.
+ - Use Steve Robbins' smr_CHECK_LIB to test for pixmap library (Xpm)
+ -- this should fix the problems on systems that have the library
+ but not the include file.
+ - Added better test for sys/select.h, sys/bsdtypes.h, strings.h,
+ bstring.h.
+ - New VMS make scripts from Martin P.J. Zinser.
+ - Moved dependences into Makefile.in -- this gets rid of problems
+ with 'cc -MM' (which is gcc-specific) and 'include Makefile.dep'
+ (which isn't supported by all make implementations). Also changed
+ all non-system include files to '#include "..."' (from '<...>').
+Tweaked the TextOutputDev heuristics slightly.
+Modify Gfx to use a stack of resources -- this is necessary for Form
+ XObjects, which can define their own local resources; also modified
+ PSOutputDev to dump fonts used by forms.
+Look for excessively large MediaBox (compared to CropBox) and shrink
+ it to CropBox.
+Minor fix to scrolling when dragging a selection.
+Various patches for pdftex and Win32 support.
+Deal with Separation colorspaces by using their alternate colorspace
+ entry.
+Added PBMOutputDev for pdftopbm application.
+Added ImageOutputDev for pdfimages application.
+Separated XOutputDev into LTKOutputDev + XOutputDev.
+Added support for 1-D and mixed 1-D/2-D (Group 3) decoding to
+ CCITTDecode filter.
+Added resetImage(), getImagePixel(), and skipImageLine() to Stream
+ class; used these in XOutputDev, PSOutputDev, and ImageOutputDev.
+Implemented predictor for LZW and Flate filters.
+In pdfImM1 in PSOutputDev prolog: div should be idiv.
+Changed output from printUsage() function in parseargs to look nicer.
+
+0.90 (99-aug-02)
+----------------
+Added Type 1/1C font rendering, using t1lib.
+Added "backward" and "forward" buttons.
+Added fit-page and fit-page-width zoom factors; replaced zoom-in and
+ zoom-out buttons with a zoom popup menu.
+Type 1C fonts are converted to Type 1 and embedded in PostScript.
+Support vertical Japanese text.
+Added Japanese text support (EUC-JP) to pdftotext.
+Bumped PDF version to 1.3. Added stub functions for ri and sh
+ operators. (But there are still some missing 1.3 features.)
+Added -raw option to pdftotext.
+Minor changes to allow compiling under MS Visual C++ 5.0.
+Top-level makefile: changed 'mkdir -p' to '-mkdir -p'.
+Configure script: added X_CFLAGS to smr_CHECK_LIB(Xpm).
+Added Xpm_CFLAGS to xpdf/Makefile.in (this is needed to get the -I for
+ the xpm includes).
+Rewrote code that handles font encodings; added support for Type 1C
+ fonts.
+In the setpagedevice dictionary in PostScript output - added a
+ /Policies entry which tells the PS interpreter to scale the page to
+ fit the available paper.
+Changed PageUp behavior slightly: move to bottom (instead of top) of
+ previous page.
+TextPage used character's dx (width + char space) instead of just its
+ width.
+Read base URI from document Catalog (for URI-type links).
+Minor change to configure script to avoid using 'unset'.
+Fixed bugs in CropBox inheritance.
+Fixed a bug in resource stack handling for form objects.
+Display forms even if they have a missing/incorrect FormType.
+Fixed a bug in stream predictors -- the predictor parameters (width,
+ comps, bits) don't need to match the actual image parameters.
+ Completely rearranged the predictor code.
+Fixed PostScript output to correctly handle stream predictors.
+Don't segfault on empty (zero-page) documents.
+Added the xpdf.viKeys feature.
+Added the ffi and ffl ligatures to XOutputDev and TextOutputDev.
+Pdftotext and pdfimages now check okToCopy().
+Added a '-q' flag to all programs (except pdfinfo) to suppress
+ messages and errors.
+Deal with DeviceN colorspaces by using their alternate colorspace
+ entry.
+Change PostScript output so setpagedevice is only called once, at the
+ very beginning of the document (to avoid problems on duplex
+ printers).
+Changes to configure script and makefiles for DOS/DJGPP.
+FontEncoding::getCharCode() looked for (code>0) instead of (code>=0).
+Added keypad arrow keys, etc. to xpdf.
+Minor changes to gfile.{h,cpp} (<windows.h>) to compile under VC++.
+Fixed CCITTFaxStream to correctly handle all parameters.
+Modifications to gfile.{h,cc} for Acorn.
+Some minor changes for OS/2.
+Added 'SHELL = /bin/sh' to Makefiles.
+Compare file version to pdfVersionNum+0.0001 to avoid floating point
+ precision problems.
+Added LDFLAGS to Makefiles.
+Removed strip entirely from configure and Makefiles.
+Fixed a bug in choosing the correct DCTStream color transform.
+
+0.91 (2000-aug-14)
+------------------
+Added TrueType font rendering, using FreeType.
+Support for Chinese fonts (Type 0 fonts using the Adobe-GB1-2
+ character collection).
+Decryption code is included with the main distribution (now that the
+ US export regulations are a little bit less irrational).
+Added (very basic) support for generating PostScript with Japanese
+ fonts -- only tested with ghostscript so far.
+Added support for generating EPS files (pdftops -eps).
+Much-improved image filtering in xpdf (for downsampling and for
+ transforms other than 0/90/180/270-degree rotations).
+Implemented a basic full-screen (presentation) mode (xpdf
+ -fullscreen). (There is currently no way to switch between window
+ and full-screen modes on the fly -- this will be fixed in a later
+ release.)
+Added "reload" menu item.
+Do a better job with anti-aliased Type 1 fonts on non-white
+ backgrounds.
+Handle Lab color spaces.
+Handle non-null user passwords.
+Avoid security hole with tmpnam()/fopen() -- added openTempFile() in
+ goo/gfile.cc. [Thanks to Joseph S. Myers for pointing this out.]
+Filter out quote marks (' and ") in URLs before running urlCommand to
+ avoid a potential security hole. [Thanks to Frank Doepper for
+ pointing this out.]
+Fixed TrueColor detection to look at the complete list of available
+ visuals, not just the default visual.
+In gfile.h, changed NAMLEN(dirent) macro to NAMLEN(d).
+Removed copyright character from start-up banners.
+In the open and save dialogs, if the open/save button is pressed with
+ no file name, the dialog is not canceled.
+Added Xpm_CFLAGS to ltk/Makefile.
+XOutputDev::updateLineAttrs was using dashLength before it was
+ defined.
+In top-level Makefile.in, use INSTALL_PROGRAM instead of INSTALL.
+In man page, recommend -q instead of -err in .mailcap.
+Changes for GNOME / Bonobo support:
+ - Separated Stream classes into BaseStream and FilterStream trees.
+ - Got rid of all FileStream-specific stuff.
+ - Added a PDFDoc constructor that takes a BaseStream* instead of a
+ file name.
+Allow newlines inside strings (Photoshop does this).
+Don't require whitespace between tokens in consecutive content
+ streams (for pages that specify an array of streams).
+Look at MissingWidth when constructing font character widths array.
+Fixed a bug that caused incorrect PostScript output for images that
+ use 8-bit indexed color spaces with < 256 colors in the palette.
+Handle case where embedded font file is bad (this was seg faulting).
+Minor changes for Windows/pdftex.
+Work around a bug in PDF files from the IBM patent server.
+Fixed bugs in PostScript form generation: use pdfStartPage instead of
+ pdfSetup; problem with inline images.
+Minor bug fix in FlateStream::loadFixedCodes().
+Added %%DocumentMedia and %%PageMedia comments to the PostScript so
+ that gsview (a Windows frontend for ghostscript) gets the right
+ paper size.
+Draw AcroForm fields that have appearance annotations.
+Bounds check gray, CMYK, and RGB values (in GfxColor).
+Moved the link border drawing code into Page (from PDFDoc).
+Minor modifications for pdftohtml.
+PSOutputDev: use the Type 3 font scaling kludge from XOutputDev.
+Separation color spaces were handled incorrectly in images.
+Fixed a bug with form bounding boxes.
+Modified the t1lib support -- replace libt1x code with my own code.
+ Type 1 and TrueType fonts are now handled similarly, and clipping
+ works on Type 1 fonts.
+Don't print copyright banner (xpdf); add -v switch to get copyright
+ and version info (all apps); get rid of -err switch (xpdf).
+Automatically reload the PDF file if it has been changed, i.e., if the
+ modification time is different.
+Fixed a memory (malloc size) bug in CCITTFaxStream.
+Fixed two bugs in FontEncoding::hash() -- handle zero-length character
+ names (which were found in a (buggy?) PDF file), and handle
+ character names with high-bit-set characters (use unsigned ints).
+Added PDFDoc::isLinearized() and corresponding code in pdfinfo.cc.
+Handle files with an incorrect page count in the Pages dictionary
+ (FOP, from the Apache project produces a page count of 0).
+Handle TrueType equivalents to the Base14 fonts (Arial, TimesNewRoman,
+ CourierNew) -- Adobe's tools use these names without embedding the
+ fonts.
+Tweaked the Type 3 font sizing kludge.
+Changed pdfimages (ImageOutputDev) so it doesn't output JPEG files for
+ 4-component color spaces, since these seem to confuse most image
+ viewers.
+Added support for generating OPI comments (pdftops -opi).
+In XOutputDev::drawImage() and drawImageMask(), check for images that
+ are completely off-page.
+Use the provided alternate or a default (DeviceGray/RGB/CMYK) color
+ space for ICCBased color spaces.
+Incorporated MacOS-specific code from Leonard Rosenthol.
+Configure script switches to C++ for the strings.h/bstring.h test.
+Gfx::opRestore() calls clearPath() to handle (apparently) buggy PDF
+ files produced by FreeHand.
+The /Type field in most dictionaries is optional (PDF 1.3 change).
+Move printCommands variable definition into Gfx.cc.
+If page is smaller than paper, center the PostScript output.
+Fix a minor bug in the SELECT_TAKES_INT detection in the configure
+ script.
+TextOutputDev filters out control characters.
+Changed enough occurrences of 'char *' to 'const char *' to keep gcc
+ 2.95 from barfing.
+Support for Latin-2 and Latin-5 in pdftotext (however, this will only
+ work if the PDF file contains correct font encodings, which seems to
+ be rare).
+TextOutputDev converts "eightoldstyle" to "eight", etc.
+Don't use the return value from sprintf() -- most systems return the
+ length, but some return the string.
+Minor fixes for SunOS 4.
+Configure script looks for both select() and fd_set in sys/select.h.
+Configure script checks for gethostbyname() in -lbsd (for LynxOS).
+Fix missing closepath bug in PostScript output.
+Change PostScript portrait/landscape mode selection so it only uses
+ landscape if the page width is greater than the paper width.
+Tweaked the VMS code in makePathAbsolute().
+
+0.91a (2000-oct-11)
+-------------------
+Implemented separable CMYK PostScript output (the -level1sep switch to
+ pdftops).
+Implemented Pattern color spaces with tiling patterns (polygon fills
+ only).
+Implemented Stamp annotations.
+Implemented Named link actions.
+Fixed a really dumb bug in the TrueColor code in SFont (which affects
+ both Type 1 and TrueType font rendering on 16-bit displays).
+Rewrote the GfxColorSpace / GfxColor code.
+Switched from djgppcfg to dj_make.bat (from Michael Richmond).
+Bug in the Type 1 encoding parser -- couldn't handle lines of the form
+ 'dup NNN/name put' (with no space between the code and the name).
+Fixed the mkstemp() test in configure.in -- switched from
+ AC_TRY_COMPILE to AC_TRY_LINK and added <unistd.h>.
+Added DESTDIR to top-level Makefile.in.
+Fixed an incorrect OPI comment in PSOutputDev.
+Minor tweak to the CCITTFax code to avoid writing past the end of an
+ array on an invalid data stream.
+Xpdf crashed if the user selected 'reload' when no document was
+ loaded.
+Look for character names of the form "xx" (two hex digits with no
+ leading alphabetic char) and 'cNNN' (decimal digits with a leading
+ alphabetic char that happens to be a hex digit).
+FlateStream didn't correctly handle zero-length streams.
+Xref reconstruction didn't handle the case where the opening "<<"
+ immediately followed "trailer" with no intervening whitespace.
+Fix the %%DocumentSuppliedResources comment in EPS output.
+Scale annotations to fit their rectangles.
+Added Stream::close() to handle cases (e.g., patterns) where a Stream
+ object is used multiple times before it is deleted.
+Added the topLevel arg to Gfx::go() so it doesn't call out->dump() for
+ every pattern element (and form).
+Rearranged the GfxResources class.
+Clean up white space handling in Lexer.
+Make the dpi parameter to PDFDoc::displayPage etc. a double - this
+ avoids margin gaps with fit-page and fit-width.
+Fix a rounding problem in xpdf.cc that was causing the window to
+ sometimes be one pixel too small.
+Fixed a minor bug in dealing with Base-14 TrueType font names.
+Fixed Lab -> RGB color space conversion.
+Added support for opacity values (from PDF 1.4) to GfxState and
+ OutputDev. [Thanks to Leonard Rosenthol.]
+Implemented type 2 functions; rearranged the Function class
+ hierarchy.
+
+0.91b (2000-oct-29)
+-------------------
+Print a warning about Type 3 fonts (XOutputDev, PSOutputDev).
+Added the scroll lock behavior to 'n' and 'p' keys in xpdf.
+Change FileStream buffer size to a #define'd constant.
+Renamed Pattern to GfxPattern to avoid clashes with Windows and MacOS
+ types.
+Added CNS (Big5) Chinese font support (CHINESE_CNS_SUPPORT); renamed
+ CHINESE_SUPPORT to CHINESE_GB_SUPPORT.
+
+0.91c (2000-nov-19)
+-------------------
+Fix an endianness problem in the Type 1 font code which resulted in an
+ incorrect display with "-t1lib plain" on big-endian systems.
+CCITTFax stream decoder will skip over extra zero bits at end of line,
+ even if EncodedByteAlign flag wasn't set.
+Added Big5 support to pdftotext (with CHINESE_CNS_SUPPORT enabled).
+Fixed a typo in the CNS/Big5 encoding translation table.
+Change the form code in PSOutputDev to store images in arrays of
+ strings.
+The xref reconstruction (for damaged files) now also looks for
+ 'endstream' tags, and the parser uses this information when setting
+ up stream objects.
+In pdfinfo, convert Unicode chars in the 00xx range into 8-bit chars;
+ print a warning if there are any other Unicode chars.
+
+0.92 (2000-dec-03)
+------------------
+Fixed %%BeginResource comment (for xpdf procset) in PostScript
+ output.
+Added "-title" switch and "xpdf.title" resource to set the window
+ title.
+Check for <freetype.h> in addition to <freetype/freetype.h>.
+Upgraded the configure script to smr_macros 0.2.4 - this should fix a
+ bug where configure wasn't correctly finding t1lib.
+
+0.92a (2000-dec-17)
+-------------------
+Added 'extern "C" { ... }' in various places for ANSI C++ compliance.
+Tweaked the code that figures out DPI for fit-to-page and fit-to-width
+ modes.
+Fixed the image transformation code in XOutputDev -- no more missing
+ lines.
+Implemented color key image masking in XOutputDev.
+
+0.92b (2001-jan-07)
+-------------------
+Fixed a bug in the error-checking code in the Separation/DeviceN color
+ space parsing functions. [Thanks to Lidia Mirkin.]
+Added wheel mouse support (mouse buttons 4 and 5). [Thanks to
+ Thorsten Schreiner.]
+Added preliminary support for FreeType 2 (disabled by default).
+
+0.92c (2001-jun-04)
+-------------------
+Fixed a bug in the new image transformation code.
+Look for character names of the form "<letter><digit><digit>", instead
+ of looking for names beginning with a few specific letters.
+T1FontFile::T1FontFile wasn't initializing vars, and ~T1FontFile
+ wasn't checking before calling T1_DeleteFont -- this caused crashes
+ if it tried to open a nonexistent font file.
+Catalog::Catalog didn't set baseURI to NULL early enough.
+Tweak the check for strings.h in the configure script.
+Yet another fix for the image rotation code in XOutputDev --
+ off-by-one problem when upsampling.
+Handle Type 1/1C encodings when using FreeType 2.
+Allow FreeType2 to render user-supplied Type 1 base fonts.
+Opening a new file from full-screen mode tried to scroll.
+Fixed a bug in GfxFont constructor (missing check for NULL base font
+ name).
+Don't crash if a Type 1 font's FontBBox is non-integer.
+Pdfinfo prints page size.
+Tweak for the alpha hack in T1Font/TTFont: sample the middle pixel
+ instead of the top-left pixel.
+Automatically activate the text input widget in the find window.
+Changed a Japanese char code mapping in XOutputDev and TextOutputDev:
+ period was being incorrectly mapped to small circle (end-of-sentence
+ character).
+Add the 0/+/-/z/w key bindings to control the zoom setting.
+Fixed ImageOutputDev (pdfimages) to correctly handle inline image
+ masks.
+Extract ascent/descent info from font descriptor.
+
+0.92d (2001-jun-26)
+-------------------
+Embed TrueType fonts in PostScript output. (Added a "-noembtt" flag
+ to pdftops.)
+Extract encoding from TrueType fonts.
+Moved Function classes to a separate file (Function.h/cc).
+Implemented multi-dimensional sampled Functions.
+Implemented Type 4 (PostScript calculator) Functions.
+For Type 0 fonts, FontDescriptor is in descendant font, not parent.
+ [Thanks to Lidia Mirkin.]
+Added the "-htmlmeta" option to pdftotext.
+In TextOutputDev, when computing the number of blank lines to insert,
+ do a sanity check on the result.
+If both FlateDecode and some other filter (e.g., DCTDecode) were
+ applied to an image stream, getPSFilter() crashed instead of just
+ returning NULL.
+Handle the /Identity function.
+
+0.92e (2001-aug-23)
+-------------------
+Widths in font dict should override built-in font widths.
+Changed "rotate left/right" menu items to "rotate
+ clockwise/counterclockwise".
+The link parsing code choked if the Border array was incorrect (too
+ short).
+Modified PSOutputDev to output CMYK for fill/stroke colors.
+
+0.93 (2001-oct-25)
+------------------
+Implement PDF 1.4 (128-bit) decryption.
+Bump supported PDF version number to 1.4.
+Text output for Simplified Chinese. [Thanks to Cheung Siu Fai.]
+Read an app-defaults file for Xpdf.
+Read a system-wide config file (<prefix>/etc/xpdfrc) if ~/.xpdfrc
+ doesn't exist.
+Accept and verify owner password; if correct, allow all actions.
+Added a "-level2sep" option to pdftops to generate Level 2 separable
+ PostScript. The PostScript separation convention operators are used
+ to handle custom (spot) colors. [Thanks to Thomas Freitag for help
+ on this.]
+Add support for FreeType 2 to the configure script. Warning: this
+ requires FT 2.0.5 or newer.
+Fixed the bounding rectangle overlap test in the disconnected subpath
+ fill hack in XOutputDev.
+Stupid typo in font name table in PSOutputDev.
+Changing the zoom setting with a keyboard shortcut didn't update the
+ displayed setting.
+Modified the mouse wheel support and added the second wheel (mouse
+ buttons 6 and 7). [Thanks to Michal Pasternak.]
+Character and word spacing is affected by horizontal scaling (display
+ and PS output). [Thanks to Eddy Ng.]
+Rotation specified by the text matrix, character spacing, and
+ horizontal scaling interacted incorrectly (display and PS output).
+Some broken Type 1/1C fonts have a zero BBox -- kludge around this by
+ assuming a largeish BBox.
+Handle PDF files with an incorrect (too small) xref table size.
+Allow "-?" and "--help" as aliases for "-h" (all apps).
+Correctly handle unescaped parens in strings in Lexer.
+Fixed a bug in LTK where a menu got posted multiple times if you right
+ clicked while a page was being rendered.
+Removed a comma inside a string in configure.in.
+Kludge around broken PDF files that use char 32 but encode it as
+ .notdef instead of space.
+Clean up various compiler warnings: use constructor args like "fooA"
+ if there is a field named "foo". Everything now compiles cleanly
+ under gcc 2.91.66, 2.95.2, and 3.0.1.
+Page objects now read all of the page rectangles (MediaBox, CropBox,
+ BleedBox, TrimBox, ArtBox), as requested by the pdfTeX folks.
+ Added a new PDFRectangle struct to hold these.
+Use XOutputDev's Type 3 font size hack in TextOutputDev too, so it
+ does a little better job of extracting text in Type 3 fonts.
+Modify pdfimages to write one-bit images as PBM files.
+Work around a bug in cygwin's implementation of fseek.
+
+0.93a (2001-nov-21)
+-------------------
+Implemented the sh (shaded fill) operator for the axial shading type.
+Minor fixes to avoid compiler warnings.
+Cleaned up global variables -- moved many into instance vars and
+ function args.
+Minor fixes for OS/2.
+Fix the system config file path for VMS.
+Fix an uninitialized var in XOutputDev that caused crashes on Alphas.
+Don't incrementally update the display in full-screen mode.
+For Type 1/1C fonts, use the FontBBox from the PDF FontDescriptor
+ (instead of the one in the font file) if present -- this avoids
+ problems with fonts that have non-standard FontMatrixes.
+Add the Euro character to WinAnsiEncoding.
+Track the bounding box of the clip region to make rendering patterns
+ more efficient.
+Fix openTempFile() for Win32.
+
+0.93b (2001-dec-11)
+-------------------
+Added a duplex option to PSOutputDev and a -duplex switch to pdftops.
+Added XRef::PDFgetDocInfoNF() for pdftex project.
+Updated the VMS build script.
+
+0.93c (2001-dec-12)
+-------------------
+Completely rewrote the code that handles font encodings:
+ - everything is Unicode-based
+ - 16-bit fonts are handled much more cleanly
+ - text output encoding can be set more flexibly
+New .xpdfrc config files.
+
+1.00 (2002-feb-01)
+------------------
+More work on the font encoding rewrite:
+ - use the ToUnicode font dict entry
+ - pdfinfo and pdftotext (with '-htmlmeta') convert info strings to
+ the selected text encoding
+Added key bindings for forward ('v') and backward ('b').
+Added the pdffonts program which lists the fonts used in a PDF file.
+Fixed several problems in the TrueType font embedding code (for
+ PostScript output).
+Accept named destination on command line.
+Added several new items to pdfinfo: file size, PDF version, tagged
+ (yes or no), XML metadata (with the -meta option).
+Pdftops didn't get the portrait/landscape setting correct for PDF
+ files with rotated pages.
+The TrueTypeFontFile class (including the Type 42 converter) now
+ understands cmap format 6.
+Improved the "about" window -- mention the GPL, add a list of key
+ bindings.
+Added Zcaron and zcaron characters to WinAnsiEncoding.
+The '0' keyboard shortcut didn't update the zoom popup menu.
+Handle the complete list of alternate names for the Base14 fonts.
+Fixed substitute font scaling in XOutputDev - scale only the width,
+ not the height.
+Implemented stitching (type 3) functions.
+Handle the case of moveto/closepath/clip, which defines an empty
+ clipping region.
+Move dependences into separate Makefile.dep files; get rid of the
+ distdepend target.
+Move all of the configure-script-generated -D options out of the
+ Makefiles and into a top-level .h file (aconf.h).
+Cleaned up the FreeType 1/2 detection code in the configure script.
+Pdfinfo prints dates in a more readable format.
+Fixed a bug in the Paeth image predictor.
+Handle annotations with multiple states.
+Another workaround for buggy X servers: clip points that are way out
+ of bounds.
+Added libpaper support (for Debian).
+Generate PostScript DSC resource comments for PS (not just EPS)
+ files.
+The save and restore (q/Q) operators shouldn't save/restore the path.
+Performance optimization: disable pattern drawing in TextOutputDev.
+
+1.00a (2002-feb-25)
+-------------------
+Added an optimized special case for one-bit images in XOutputDev.
+Implemented CID TrueType font embedding; added a psEmbedCIDTrueType
+ option.
+The initialZoom X resource was broken.
+The reverse MacRoman encoding should return 32 for "space" (not 202,
+ which is an alternate encoding).
+Tweaks to the FreeType 2 support: only disable hinting if the bytecode
+ interpreter is disabled (i.e., disable autohinting but not bytecode
+ hinting); add some padding to the glyph cache for CJK fonts.
+Added level3 and level3Sep options for the psLevel setting and
+ corresponding -level3 and -level3Sep options to pdftops.
+Added a -level2 option to pdftops for consistency.
+Avoid a divide by zero in pdftotext. [Thanks to William Bader.]
+Added a Greek language support package. [Thanks to Alexandros
+ Diamantidis and Maria Adaloglou.]
+Don't bother trying to extract a "builtin" encoding from a TrueType
+ font.
+Accept either a page number or a page reference in a link
+ destination.
+Update the fontFixedWidth flag in GfxFont after reading the char
+ widths (used by the Acorn RiscOS port).
+Removed yet another (illegal but not caught by gcc) class specified
+ from a .h file.
+Avoid using snprintf - it's not available everywhere.
+Improved the CMYK->RGB transform.
+Use mkstemps where available.
+
+1.01 (2002-may-20)
+------------------
+Implemented Type 3 fonts.
+Implemented PostScript CID font embedding; added a
+ psEmbedCIDPostScriptFonts option.
+Implemented PostScript 16-bit font substitution; added psNamedFont16
+ and psFont16 options.
+Moved the initialZoom setting from X resources to the xpdfrc file.
+Implemented the radial shading type in the sh (shaded fill) operator.
+ [Thanks to Mike Sweet.]
+Added an 'include' command to the xpdfrc format.
+Added the displayNamedCIDFontX option so different fonts can be used
+ within one character collection.
+Added a simple reverse video mode (-rv switch, xpdf.reverseVideo
+ resource).
+Implemented stroked text in XOutputDev (with t1lib and FreeType2).
+ [Thanks to Leonard Rosenthol.]
+Implemented stroked text in PSOutputDev.
+Added a built-in Unicode map for UCS-2.
+New key binding in xpdf: 'g' activates the page number text field.
+PSOutputDev will now embed external TrueType fonts in addition to
+ external Type 1 fonts.
+The psEmbedType1Fonts and psEmbedTrueTypeFonts options were missing
+ the "Fonts" suffix.
+Documentation in xpdf.1 for -freetype option was wrong.
+Added the Big5ascii Unicode map to the Chinese-traditional support
+ package (maps 7-bit ASCII straight through). [Thanks to Lawrence
+ Lai.]
+Modified the EUC-CN and EUC-JP encodings to pass 7-bit ASCII straight
+ through. [Thanks to Lawrence Lai.]
+Avoid a divide by zero in XOutputDev. [Thanks to Simon Burge.]
+Remove old code in openTempFile that removed an extension from the
+ name returned by tmpnam.
+Tweak the scrolling behavior when switching pages. [Thanks to Case
+ Jones.]
+In the code that guesses character names (for font subsets), also
+ handle names of the form 'ABnnn'. [Thanks to Colin Granville.]
+Fix the transform code for annotations.
+Improved the CMap file parser to handle more general PostScript
+ lexical conventions.
+Added '-enc' option to pdfinfo.
+Added the small caps and oldstyle numbers from Adobe's Unicode
+ corporate use area to the Latin1 and ASCII7 Unicode maps.
+The code in TextOutputDev that guesses Type 3 font size could generate
+ a zero size, which resulted in div-by-zero errors.
+Various tools (including Adobe's) occasionally embed Type 1 fonts but
+ label them Type 1C - so check for a '%!' at the start.
+Some tools embed Type 1C fonts with an extra whitespace char at the
+ beginning - just skip over it.
+Fixed a typo in the Simplified Chinese add-to-xpdfrc file.
+Updates to dj_make.bat and the djgpp build instructions.
+Added a Turkish language support package.
+Updated VMS build scripts. [Thanks to Martin Zinser.]
+Modify the incremental display update code to redraw less often if
+ most of the commands are vector graphics, as opposed to text and
+ images.
+Tweak the Type 1 font bbox code to look at the bboxes in both the PDF
+ font object and the embedded font file.
+Fixed the ETenms-B5-H CMap file (for traditional Chinese) to map the
+ Latin characters to their proportional versions.
+Added an optional displayCIDFontX entry for one of the Arphic TrueType
+ fonts in the traditional Chinese 'add-to-xpdfrc' file.
+Remove leading '-' on include statements in Makefiles.
+Added psASCIIHex parameter.
+Added the GBK Unicode map to the simplified Chinese language pack.
+Pdftotext now opens the text file in binary mode to avoid Microsoft's
+ annoying automatic end-of-line translation stuff.
+Added an executeCommand function in goo/gfile.cc. [Thanks to Mikhail
+ Kruk.]
+The %ALDImagePosition OPI comment was wrong if the page was scaled to
+ a different paper size.
+The OPI code was saving the default transform matrix before calling
+ setpagedevice, which can change the matrix.
+Fixed a crash when an inline image dictionary contains garbage.
+Upgraded to autoconf 2.53.
+Use unsigned int file offsets, to allow access to PDF files in the 2-4
+ GB size range; use fseek64/ftell64 if available.
+Fixed two floating point exception cases that came up with broken PDF
+ files.
+Avoid a crash when printing an error message regarding an unnamed
+ font.
+Default link border width should be 1. [Thanks to Michael Pfeiffer.]
+Minor tweak to build with FreeType 2.1.0.
+Handle "weird" characters in PostScript font names.
+PSOutputDev now handles PostScript XObjects.
+Added several more page attributes for the pdftex project.
+Transferred the copyright to Glyph & Cog, LLC.
+
+2.00 (2002-nov-04)
+------------------
+Switched to the Motif toolkit.
+Support multiple open documents (in separate windows).
+Added document outlines to the viewer.
+Modified the text extraction (placement) algorithm.
+Implemented the JBIG2 decoder.
+Added a Latin2 language support package.
+Added support for movie annotations.
+Switched back to native LZW decompression code.
+Text extraction from Type 3 fonts was (partly) broken.
+The owner password checking code was missing a step in the case of
+ 128-bit encryption.
+Added the 'printCommands' option to the xpdfrc file.
+Added key binding for '?' to bring up the about/help dialog.
+In TextOutputDev, ignore any text that's outside the page bounding
+ box.
+Text extraction throws away "tiny" characters after the first 20000
+ per page, to avoid really slow runtimes with PDF files that use
+ special fonts to do shading or cross-hatching; added the
+ 'textKeepTinyChars' option to disable this behavior.
+Text extraction discards duplicated text (fake boldface, shadow
+ effects).
+Added ctrl-F as a key binding for find.
+Added a "find next" function, bound to ctrl-G.
+Added ctrl-P as a key binding for print.
+Modified the DCT decoder to handle progressive and non-interleaved
+ JPEG streams.
+Added key bindings for ctrl-Home and ctrl-End.
+Allow the initialZoom setting to be made in either the xpdfrc file or
+ as an X resource.
+Added a Hebrew language support package. [Thanks to Roy Arav.]
+The "make distclean" target now creates (empty) Makefile.dep files in
+ the three subdirectories.
+Initialize XRef::ownerPasswordOk.
+Correctly handle stroking of Type 3 fonts in PSOutputDev.
+Generate correct PostScript for fonts with "weird" character names
+ (e.g., "(").
+Generate correct PostScript for images using Indexed color spaces with
+ DeviceN base color spaces.
+Added lowercase Roman numerals to ISO-2022-CN.unicodeMap (simplified
+ Chinese support package).
+Tweak the image scaling code to better handle flipped (top-bottom
+ and/or left-right) images.
+Generate correct PostScript code for inline images and images in Type
+ 3 fonts which are too large for a single PS string.
+Correctly handle indexed color spaces whose base color spaces have
+ component ranges other than [0,1].
+Optimized the DCT decoder.
+Fixed mistakes in the list of key bindings in the about/help dialog.
+Optimized the Flate decoder.
+Add literal names for punctuation and digits to the Unicode name
+ table.
+Cygwin's popen wants mode "r", not "rb".
+Fixed a bug in the Type 4 function parser (the "if" operator wasn't
+ parsed correctly).
+Fix a bug in PS output for TrueType fonts with no PDF encoding.
+Make the bbox size in FTFont more liberal (to avoid problems with
+ fonts that have incorrect bboxes).
+Reverse the colors in PBM files generated by pdfimages, so the common
+ case (an image mask filled with black) comes out correct.
+Add fseeko/ftello support which is basically identical to
+ fseek64/ftell64. [Thanks to Nassib Nassar.]
+Modified column assignment in text extractor to account for characters
+ that convert to multiple characters in the output encoding.
+Fix TrueType fonts which have an incorrect cmap table length.
+Work around a pragma bug in the version of gcc that ships with MacOS X
+ 10.2. [Thanks to Frank Siegert and Andrew Stone.]
+Fix a problem that was causing an infinite loop when a damaged content
+ stream contains an 'ID' command inside a dictionary.
+Handle the case where systempapername() returns NULL (libpaper
+ support).
+Handle fonts which are defined directly in the font resource
+ dictionary rather than as separate objects.
+Track process colors in Level 1 separable PostScript.
+Pdfinfo now checks the return value from mktime to avoid seg faults in
+ flakey strftime implementations.
+If duplex is not enabled in PostScript output, leave the duplex
+ setting alone, allowing the spooler to insert its own setting.
+Added three missing fclose calls.
+Change the default encoding for TrueType fonts (used when the PDF file
+ doesn't specify an encoding) from MacRomanEncoding to
+ WinAnsiEncoding.
+Move X_CFLAGS to the end of the list in CXXFLAGS (in Makefile.in) to
+ avoid some of the FreeType2 include path problems.
+Fixed an obscure bug in the LZW decoder. [Thanks to Martin
+ Schroeder.]
+Fixed a bug in decryption when using the newer (PDF 1.4) algorithm
+ with shorter-than-128-bit keys.
+Minor optimization for image data streams: the ImageStream class can
+ return an entire buffered line.
+
+2.01 (2002-dec-05)
+------------------
+Redesigned the text extraction process:
+ - process the text into "reading order"
+ - added a "-layout" flag to pdftotext to switch back to the old
+ style, where physical layout is maintained
+ - use of the "-raw" flag is no longer recommended
+Added the -reload option for xpdf (in remote mode).
+Added support for external CID fonts; added the displayCIDFontT1 and
+ displayNamedCIDFontT1 commands to the xpdfrc file.
+Handle the case of moveto/newpath/clip, which defines an empty
+ clipping region (just like moveto/closepath/clip).
+Accept XYZ link destinations with missing array elements.
+Fix some problems with state save/restore triggered by Type 3 fonts
+ that reference other fonts.
+Accept bogus font names based on "Symbol": Symbol,{Bold,Italic,
+ BoldItalic}.
+Fixed color and font resource names in the xpdf man page.
+Was using delete instead of gfree in OutlineItem::~OutlineItem.
+Set the busy cursor in the find dialog while searching.
+Map variants of the copyright, trademark, and registered trademark
+ symbols to the proper Unicode codes, not to Adobe's corporate use
+ area codes.
+Fixed a floating point exception bug in TextOutputDev (check for a
+ too-small denominator).
+Fixed a typo in TextOutputDev, in the code that generating blank lines
+ to add vertical whitespace.
+Config files whose last line didn't end with a LF (or CR+LF) weren't
+ being handled correctly.
+The code that handled CIDToGIDMaps in Type 2 CIDFonts was broken.
+Check the per-glyph bounding box in Type 3 fonts, and don't try to
+ cache glyphs with bogus bboxes.
+Allow ToUnicode CMaps to use fewer than four hex digits in the Unicode
+ char indexes.
+Added multithreading protection to the GlobalParams class.
+Fixed a bug in end-of-stream detection with the TIFF predictor.
+Added some characters to MacRomanEncoding to match up with Apple's
+ definition.
+
+2.02 (2003-mar-24)
+------------------
+Rewrote the text extractor code that assembles words into lines to
+ better handle vertically overlapping lines.
+Add the "match" option for paper size (in PostScript output).
+Added support for external 16-bit TrueType fonts; added the
+ displayCIDFontTT and displayNamedCIDFontTT commands to the xpdfrc
+ file.
+Added an Arabic language support package.
+Added the Windows-1255 encoding to the Hebrew language package.
+A missing NULL check was causing a crash when closing the file in a
+ single window (which clears out the window, but leaves it open).
+Deal with TrueType fonts whose glyph data is out of order - this
+ affected both FreeType rasterization and PostScript generation.
+Munge font names in PSOutputDev to avoid names that are problematic
+ for ghostscript because they start with an out-of-limits number
+ (e.g., 1e999foo).
+Modify the TrueType font encoding deciphering algorithm in yet another
+ attempt to match up with Acrobat's behavior.
+Bounds check the indexHigh value in indexed color spaces.
+The text extractor no longer bothers trying to get an average
+ character width for Type 3 fonts, since it generally doesn't work
+ very well (because Type 3 metrics are unreliable).
+Don't crash if the user hits ctrl-G ("find again") before doing a
+ find.
+Set the button pixmap foreground color correctly.
+Handle text drawn backward on 180 degree rotated pages.
+Added a magic call to XtUngrabButton after calling XmCreatePopupMenu
+ which appears to prevent some very odd problems (idea taken from the
+ DDD source code).
+Fix the MacOS X fix (needed to include <AvailabilityMacros.h>).
+Fixed a bunch of Motif 1.x / X11R5 incompatibilities. [Thanks to
+ William Bader and Albert Chin-A-Young.]
+Fixed various bugs in previously untested code in the JBIG2 decoder.
+Modify the XPDFCore destructor to avoid a bogus warning message from
+ OpenMotif 2.2.
+Modified the Type 1C font parser to do proper bounds checking.
+Fixed the bounds checking in the TrueType font parser.
+Text extractor shouldn't do block merging in physical layout mode.
+Fixed a problem in PSOutputDev in level2sep mode with images in a
+ Separation color space and with a non-default Decode array.
+Text extraction with "-raw" was concatenating lines from the bottom of
+ one column and the top of the next.
+Handle Type 1C subroutines in the font converters.
+Correctly handle progressive JPEG images whose scans are slightly
+ different sizes (e.g., the Y scan rounds up to a multiple of 8
+ pixels and the Cb/Cr scans round up to 16 pixels).
+Avoid a potential divide-by-zero problem in TextOutputDev.
+Modified the T1Font and FTFont modules to correctly handle glyphs that
+ are larger than the font's claimed bounding box.
+Tweak dupMaxDeltaX parameter in TextOutputDev to avoid triggering on
+ double characters.
+Improved detection in pdfinfo for ISO paper sizes. [Thanks to Hartmut
+ Henkel.]
+Xpdf wasn't responding to the TARGETS atom, which prevented pasting
+ the selection into various applications. [Thanks to Phillip Ezolt.]
+Handle XObjects with recursive references in their Resources
+ dictionaries (in PSOutputDev).
+Change PSOutputDev to deal with invalid PDF files that use
+ non-embedded TrueType fonts with no encoding.
+Check for undersized Widths arrays in fonts.
+Add bounds checking code to Array class.
+Updated VMS build scripts. [Thanks to Martin Zinser.]
+Tweak the TrueType font handling code (again):
+ - char codes in symbolic fonts may or may not be offset by 0xf000
+ - discard empty tables because they sometimes confuse FreeType
+Fixed bounds checking in the Flate decoder.
+Removed a bogus error message for exponential functions without
+ explicit C0/C1 values. [Thanks to Hartmut Henkel.]
+Handle the other Unicode cmap type (platform=0) in TrueType fonts.
+Added support for the SGI Motif horizontal paned window widget.
+ [Thanks to Felix Ritter.]
+Ignore extra elements in link destination arrays.
+Accept external Type 1 font files with a suffix of ".ps" or no suffix
+ at all.
+Add a bounds check in the DCT decoder.
+Added instructions for building xpdf.exe under cygwin/XFree86.
+Tweaked the word separation parameter for raw-mode text extraction.
+
+2.03 (2003-oct-10)
+------------------
+Rewrote the text extractor to:
+ - do a better job with rotated text;
+ - handle right-to-left scripts;
+ - be faster.
+Changed the zoom setting to use a percentage (relative to 72 dpi)
+ instead of a zoom "factor".
+If the PDF file has an outline, open the outline pane initially.
+Added -f and -l options to pdfinfo; print multiple page sizes.
+The HAVE_XTAPPSETEXITFLAG test in XPDFApp.cc was backwards.
+The BitsPerComponent entry is optional in image mask objects.
+Render any annotation with an appearance stream, instead of just
+ Widget and Stamp annotations.
+Fix a bug in the TrueType font checker: the test for an unsorted
+ 'loca' table was wrong.
+Modify the TrueType cmap selection algorithm yet again to try to match
+ Adobe's behavior.
+Changed sqrt(2) to sqrt(2.0) in pdfinfo.cc to make various compilers
+ happy.
+Fixed a deadlock problem (when MULTITHREADING is set); cleaned up some
+ other problems with the locking code.
+Fixed a bug in the interpolation code for type 0 (sampled) functions.
+Implemented type 1 (function-based) shaded fills.
+Fixed some stupid bugs in the JBIG2 decoder (introduced with the
+ previous optimization work).
+Fixed a typo in the code that parses vertical font metrics for CID
+ fonts that was causing a seg fault.
+Fixed a couple of bugs that were causing seg faults with badly damaged
+ PDF files.
+Limit the number of nested Forms to avoid infinite recursion (in buggy
+ PDF files).
+Add a special case for rectangular clip regions - make sure these
+ don't drop pixels on the right and bottom edges.
+Tell FreeType not to use glyph bitmaps when in anti-aliased mode.
+Read all of the border style info for links.
+All of the shaded fill types now do at least one bisection to avoid
+ problems when the colors at the endpoints of the domain are the
+ same.
+If the Length2 parameter for an embedded Type 1 font was incorrect
+ (too small), pdftops was losing font data.
+Deal with (broken) DCT streams that use the same component ID number
+ for different components.
+The MediaBox page attribute was not being inherited correctly.
+Fixed a bug in the Type 1C font converter related to local
+ subroutines.
+The Type 1C -> Type 1 font converter was allocating the font dictionary
+ one slot too small.
+Added a missing private dictionary entry to Type 1 fonts generated by
+ the Type 1C converter. [Thanks to Michael Shell.]
+Fixed bugs in the tiling pattern fill code.
+Try the TrueType 0xf000 char code offset hack for the MacRoman
+ encoding too (in addition to MS Symbol).
+Update the font metrics info for the Base 14 fonts to include the Euro
+ character.
+SECURITY HOLE: Escape various characters in URLs before running a web
+ browser (or movie viewer). [Fixed in 2.02p11]
+SECURITY HOLE: In the dialog used to verify "launch" links, provide a
+ scrolling view if the command to be run is excessively long. [Fixed
+ in 2.02p11]
+Added an option to disable insertion of page breaks (form feed
+ characters) in extracted text (pdftotext -nopgbrk; xpdfrc
+ "textPageBreaks" option).
+Check for 8-bit fonts that specify an out-of-range FirstChar or
+ LastChar.
+Correctly handle an obsolete Type 2 charstring op (in the Type
+ 1C-to-Type 1 font converter). [Thanks to Helge Blischke.]
+Use the font encoding info to fill in holes in the ToUnicode map.
+Added character names for Bulgarian (in the Cyrillic support pacakage)
+ and Greek.
+Handle clipping to text in xpdf and pdftops.
+Fix color space detection in DCT decoder. [Thanks to Dwight Kelly.]
+Added the "unicodeToUnicode" xpdfrc option, intended (initially) for
+ Arabic support.
+Handle the case in PSOutputDev where two font objects refer to the
+ same embedded TrueType font, but with different encodings. [Thanks
+ to Frank Siegert.]
+Kill any pre-existing path before drawing a form (or annotation).
+Save state before rendering page content; restore state afterward.
+Fix Stream::reset/close to work correctly with encoder streams; fix
+ PSOutputDev to use Stream::close consistently.
+Fix a seg fault when hitting the 'back' button after closing a file.
+GfxState::getStrokeGray was returning the fill gray value (this only
+ affected Level 1 PS output).
+Change PSOutputDev to reuse dictionaries in Level 1 mode (since Level
+ 1 PS interpreters don't do garbage collection). [Thanks to Frank
+ Siegert.]
+PSOutputDev was generating incorrect translations for landscape-mode
+ pages.
+Implemented shading pattern color spaces.
+PSOutputDev wasn't correctly handling Type 3 fonts which used image
+ resources (as opposed to inline images). [Thanks to Frank Siegert.]
+The fix from 1.00 which clipped out-of-bounds points was a bit too
+ aggressive.
+Do proper Floyd-Steinberg dithering in XOutputDev.
+Don't automatically check for a null owner password (to match Adobe's
+ behavior).
+Allow the FlateDecode filter in Level 3 PostScript output.
+Fixed small bugs in the Type 1C -> Type 1 converter and Type 1C ->
+ Type 0 converter. [Thanks to Tom Kacvinsky.]
+Work around another weird Motif problem with the right button menu
+ (which was sometimes causing the menu to not be displayed).
+Make the code that handles fonts defined directly in the resource dict
+ more robust.
+Add a brief description of the outline pane to the xpdf man page.
+Ignore extra operands to content stream operators.
+Fixed a bug in the CCITTFax decoder.
+Allow the Count entry in a Pages dictionary to be a real number
+ (because some PDF generators actually do this).
+Shading pattern fills weren't being clipped correctly.
+Incorrect shallow copies in GfxRadialShading and StitchingFunction.
+The StitchingFunction destructor wasn't checking for funcs being
+ NULL.
+Change the TrueType code-to-GID mapping code so it looks at the
+ TrueType 'post' table.
+Set the print command in the print dialog once at startup, don't
+ change it each time a file is (re)loaded.
+Generate the %%BoundingBox comment in regular PostScript files (not
+ just EPS files).
+Fixed a bug in the Unicode CMap parser.
+
+3.00 (2004-jan-22)
+------------------
+New PDF rasterizer ("Splash").
+Added support for PDF 1.5:
+ - JPX (JPEG 2000) decoder
+ - XRef streams
+ - object streams
+ - DeviceN color spaces with up to 32 components
+ - Added new CMaps to the CJK language support packages
+Replaced pdftopbm with pdftoppm (which can generate PBM, PGM, and PPM
+ files).
+Reorganized the font file parser code into a new library ("Fofi").
+Removed support for FreeType 1.x.
+Removed support for X server fonts - Xpdf (and pdftoppm) will now
+ search for the URW fonts (from ghostscript).
+Changed the "-t1lib" and "-freetype" switches; replaced the
+ "t1libControl" and "freetypeControl" config file options with
+ "enableT1lib", "enableFreeType", and "antialias".
+Added the "-box" option to pdfinfo.
+Added imageable area support to PSOutputDev (for CUPS); added the
+ "psImageableArea" config file option.
+Added the "-nocrop", "-expand", "-noshrink", and "-nocenter" switches
+ to pdftops; added the "psCrop", "psExpandSmaller", "psShrinkLarger",
+ and "psCenter" config file options.
+Dictionary size was in PostScript code generated for Type 3 fonts.
+The PS code generated for images in Type 3 characters was broken.
+Tweaked the text extractor.
+Accept xref entries that are one byte too short (matching Adobe's
+ behavior).
+Change things so "xpdf -h" and "xpdf -v" work if DISPLAY isn't set.
+Fix a problem in the damaged file repair code that handles the trailer
+ dictionary.
+Use the "Last" entries in "Outlines" objects - this avoids a problem
+ with PDF files generated by buggy software that, e.g., sets the last
+ item's Next pointer to point to itself.
+PSOutputDev was not handling DeviceN color spaces correctly in Level 2
+ images.
+Fixed a stupid little bug that broke PS output for JBIG2 images.
+Work around a Lesstif bug: set up an extra callback so hitting <Enter>
+ in the find dialog performs a search. [Thanks to Elliott Hughes.]
+Pdftops was crashing on zero page PDF files.
+Add an AC_PREREQ call to configure.in.
+Change the 'find' dialog so the text entry box resizes with the
+ dialog.
+Skip extraneous zero bits at the start of a CCITTFax stream.
+The PostScript text clipping operator was missing a 'newpath'.
+ [Thanks to Frank Siegert.]
+Fix a bug in tiling patterns with bboxes that don't start at (0,0).
+Fix a bug in Type 3 font handling with rotated text.
+The tiled pattern fill code was destroying the current path, which
+ broke the fill+stroke operators when the fill color space was a
+ tiled pattern.
+ICCBased color spaces don't always set their Ranges values correctly,
+ so just use the values from the alternate color space.
+Modified GHash to accept int or void* - this avoids some conversion
+ warnings.
+Check for missing Type 3 CharProcs - avoid a segfault.
+Pdffonts now marks all Type 3 fonts as embedded.
+Outline entries with no Title string weren't being handled correctly,
+ resulting in segfaults.
+PSOutputDev now forces the text horizontal scale factor to be non-zero
+ to avoid singular font matrices in the PS code.
+Tweaked the error recovery in the CCITTFax decoder.
+The LZW/Flate predictor should treat any Predictor value (in the
+ stream dictionary) >= 10 identically.
+PSOutputDev and pdffonts check for NULL font objects (which can
+ happen, e.g., because of missing CMap files).
+Swap the left and right mouse wheel button numbers.
+EPS output ("pdftops -eps") now uses the CropBox instead of the
+ MediaBox as the EPS bounding box.
+
+3.01 (2005-aug-17)
+------------------
+Added the continuous view mode, including the '-cont' switch and the
+ 'continuousView' config file option.
+At high zoom levels, don't rasterize the entire page - this avoids
+ problems running out of memory.
+Added "search backward" and "match case" options to the find dialog.
+Support explicitly masked images and soft masked images.
+Add support to DCTStream for 16-bit quant tables.
+Don't segfault if the user clicks on an outline entry with a broken
+ destination.
+Changed the makefiles and configure script to skip building pdftoppm
+ (in addition to xpdf) if X, Motif, or FreeType is not found; changed
+ the error message in the configure script to match.
+Move an inline function in JArithmeticDecoder.cc to avoid compiler
+ errors.
+Fixed a bug in the rasterizer that was sometimes causing infinite
+ loops with round line caps on vertical lines.
+Various rasterizer optimizations.
+Look for intermediate resize events - try to avoid lagging when the
+ user is doing an opaque resize.
+The FormType key in Form XObjects is optional.
+Handle external 16-bit TrueType fonts correctly, using the Unicode
+ cmap.
+Add class declarations to TextOutputDev.h to work with stricter C++
+ compilers.
+Support FreeType's weird include file stuff (ft2build.h, etc.).
+Fixed a bug handling empty paths.
+Fixed a text positioning problem in PostScript output.
+Handle TrueType collections in FoFiTrueType.cc.
+FoFiTrueType constructor was reporting a failure if the post table was
+ bad - this should be non-fatal.
+Type 1 font parser was missing a NULL test.
+Mask chars passed to isdigit in goo/parseargs.c to avoid problems with
+ signed chars.
+Added more error checking to the CCITTFax decoder.
+Fixed a bug (computing the MCU size) in the DCT decoder.
+Change a test in the Splash stroke code to avoid x86 floating point
+ weirdness.
+Reorganized the decryption code to allow security handler plugins;
+ removed the NO_DECRYPTION #ifdefs.
+Added a plugin interface, initially just for security handlers.
+Support color key masked images and explicitly masked images in PS
+ output (Level 2 only).
+When checking for aliases of the Base 14 fonts, ignore spaces in the
+ specified font name.
+Handle encrypted PDF files that are missing the file ID string.
+Handle tiling patterns more efficiently in the PostScript output.
+Rewrote the code that handles color spaces in PostScript output.
+Fixed a bug in the Type 1C font parser - zero-length indexes (and
+ zero-length names) weren't handled correctly.
+Handle shaded fills more efficiently in the PostScript output.
+Implement the remaining shading types (4-7).
+Rearranged the Splash color modes.
+Add the EarlyChange parameter to LZWStream when generating PostScript.
+Check for zero values in line dash arrays in PSOutputDev.
+Fixed an uninitialized variable in JArithmeticDecoder which was
+ causing crashes.
+Treat unknown CMap names as identity mappings (to match Adobe's
+ behavior).
+Fixed bugs in the XRef parser related to XRef streams in updated
+ files.
+Added a missing call to FT_Done_Glyph which was causing a memory leak.
+ [Thanks to Dave Formanek.]
+Fixed a bug in text copying that was causing the last word to be
+ dropped on some pages.
+Tweaked the image width/height computation in Splash::drawImage and
+ Splash::fillImageMask to make striped images work better.
+Ignore minus signs in the middle of numbers (to match Adobe's
+ behavior).
+Missing '%s' in format strings for dates in pdftotext '-htmlmeta'
+ mode.
+Change the TrueType code-to-GID mapping code so it looks at the
+ standard name-to-Unicode mapping before the ToUnicode mapping
+ defined in the font object.
+Added a matteColor setting (command line option and X resource).
+Tweaked the CMYK->RGB transform.
+Fix some problems in tracking the character position (to match up with
+ Adobe's highlight file format).
+Handle moveto/closepath/stroke correctly.
+Check for singular text matrices and font size of zero in PSOutputDev.
+Clip PS output to the size of the page (avoiding any gibberish that
+ lies outside the MediaBox, in the case where the MediaBox is smaller
+ than the paper).
+If the line dash element in an annotation's Border array is of an
+ invalid type (i.e., not an array), don't draw the link at all (this
+ matches Adobe's behavior).
+Don't remap small caps and oldstyle glyphs in the name-to-Unicode
+ table - it messes up TrueType font encodings.
+Pdftoppm wasn't setting the paper color correctly in mono and gray
+ modes (this only showed up on big-endian machines).
+Missing NULL check was causing crashes when attempting to read non-PDF
+ files that happened to contain the string '%PDF'.
+Fixed a problem in the text extractor that was breaking up words.
+Handle vertical text (CJK fonts) in PS output with TrueType fonts that
+ are missing the vertical metrics tables.
+Handle the case where a font object and the corresponding embedded
+ font are different types.
+Handle basic crypt filter functionality.
+Added more value checking in the XRef parser, to avoid potential
+ security problems.
+Updated the CJK language support packages: replaced the
+ displayCIDFontX references with displayCIDFontTT; added pointers to
+ free TrueType fonts.
+Added a missing error message when SplashOutputDev can't parse an
+ embedded TrueType font file.
+PDFCore and TextOutputDev now correctly handle searching for Unicode
+ strings, including real Unicode case-folding.
+Throw away tiling pattern fills that are completely outside the clip
+ region.
+The JPEG 2000 inverse reversible multiple component transform code was
+ wrong.
+Fixed some bugs in shading pattern fills: clipping was wrong, and
+ background color was not implemented.
+Added tool tips for the toolbar buttons.
+Decrease the max depth of recursive patch mesh filling if the pattern
+ has a large number of patches.
+Highlight the find text whenever the find dialog is mapped.
+Handle page boundary boxes with reversed coordinates.
+Fixed a bug in the text extractor code that handles duplicated text.
+Optimization work on SampledFunction::transform().
+Use the CropBox instead of the MediaBox as the display region.
+Dither for PseudoColor (8-bit) displays.
+Fix a bug in DCTStream that was causing an infinite loop with
+ corrupted DCT image data.
+Fix a bug in the ToUnicode CMap parser.
+Fix a bug in the text extractor - negative font sizes weren't being
+ handled correctly.
+Fix a bug in the text extractor - in certain cases, out-of-bounds text
+ could cause crashes (generally only in damaged PDF files).
+Fix a read-past-end-of-array bug in the JBIG2 decoder.
+Fix a case where pdftops was generating lines longer than 255 chars.
+Optimize redraws - don't regenerate the XImage every time redrawRect
+ is called.
+The ASCII85 decoder wasn't skipping whitespace properly.
+Optimize text extraction: skip (non-inline) image setup entirely.
+Added initial transparency support (stroke/fill alpha and blend mode).
+Added support for the overprint setting in PostScript output.
+Fixed various buffer overflow bugs.
+Handle negative font sizes and horizontal scaling correctly - this
+ affected PSOutputDev for all text operators, as well as the TJ
+ operator for all OutputDevs.
+Fixed a buffer overflow in the CCITTFax decoder.
+Fixed an out-of-order entry in the list of font name aliases.
+Fixed a backward loop in the PostScriptFunction code.
+Treat a zero-length base URI the same way as a nonexistent base URI.
+Add a divide-by-zero check in TextOutputDev (the problem was happening
+ in cases of mixed horizontal and vertical text).
+PSOutputDev wasn't rounding the page bounding box coordinates
+ correctly.
+Support the SOF1 marker in DCT (JPEG) image streams.
+Minor changes to GlobalParams.h and JPXStream.h because some compilers
+ don't like anonymous structs inside anonymous unions.
+Xpdf now complains about a negative page number.
+Changed GString::cmp and GString::cmpN to correctly handle '\0' chars
+ in the middle of strings.
+Fixed the radial shading code; corrected the handling of the 'extend'
+ parameters.
+Added the gmallocn and greallocn functions.
+Fixed a bug in the TIFF image component predictor which shows up with
+ components that are not 1 or 8 bits wide.
+Optimized FlateStream::loadFixedCodes().
+For non-embedded Base-14 fonts, don't use the ascent/descent/bbox
+ values from the FontDescriptor - various PDF generators get them
+ wrong.
+Fixed a bug in the text extractor - words on the same line (especially
+ in tables) were being split vertically onto multiple lines.
+Automatically select the correct radio button ("print with command"
+ vs. "print to file") in the print dialog.
+Don't create the "open" and "save as" dialogs until needed - this
+ avoids stat-ing every file in the directory at startup.
+Changed the Big5 and Big5ascii encodings (in the traditional Chinese
+ language support package) to include characters from the Unicode
+ database (which aren't mentioned in the Adobe character collection
+ documentation).
+Added the '-pagecrop' switch to pdftops.
+Tweaked the RGB->gray and CMYK->gray conversion functions to match the
+ PDF spec.
+The JPEG 2000 decoder wasn't correctly handling codeblocks split
+ across multiple packets/layers.
+Fixed a typecast that caused compile errors on 64-bit systems.
+The CMap parser wasn't handling the 'cidchar' construct.
+Handle the case in PSOutputDev where two font objects refer to the
+ same embedded 16-bit TrueType font, but with different CIDToGIDMaps.
+Changed the configure script to report more accurate warnings when
+ it can't find X / Motif / FreeType.
+Encryption with revision=2 always uses a 40-bit key, regardless of the
+ specified Length value.
+Yet another minor change to the TrueType font encoding deciphering
+ algorithm.
+Don't completely invalidate the Catalog if one (or more) of the page
+ objects are bogus -- just skip over those pages.
+Removed the workaround in pdftops for too-small Length2 values in Type
+ 1 fonts -- it was causing problems on various PostScript printers.
+Started adding error checking to the JBIG2 decoder (this is nowhere
+ near complete yet).
+Extended the "unicodeToUnicode" config option to also apply to CID
+ fonts.
+Added the narrow Latin characters to the Adobe-Korea1.cidToUnicode
+ file in the Korean language support package.
+Fixed the code that handles page rotation in PSOutputDev.
+When converting a Type 1C glyph to a Type 1 glyph, insert closepath
+ operators as appropriate.
+Check for a sane 'loca' table in TrueType fonts (FoFiTrueType::parse).
+Fix PSOutputDev to correctly handle the case of an empty name in a
+ font encoding.
+
+3.02 (2007-feb-27)
+------------------
+Added anti-aliasing for vector graphics; added the vectorAntialias
+ xpdfrc option; added the "-aaVector" switch to xpdf and pdftoppm.
+Implemented stroke adjustment (always enabled by default, ignoring the
+ SA parameter, to match Adobe's behavior), and added the strokeAdjust
+ xpdfrc command.
+Support PDF 1.6 and PDF 1.7.
+Added support for AES decryption.
+Added support for OpenType fonts (only tested with 8-bit CFF data so
+ far).
+Added user-configurable key/mouse bindings - the bind/unbind xpdfrc
+ commands.
+Cleaned up the full-screen mode code and added the ability to toggle
+ it on the fly (the default key binding is alt-f).
+Pdfimages with the -j option now writes JPEG files for 1-component
+ (grayscale) DCT images, in addition to 3-component (RGB) images.
+Fixed bugs in handling sampled (type 0) functions with 32-bit
+ samples.
+Fixed some things to support DeviceN color spaces with up to 32
+ colorants.
+Pdftops now constructs the %%Creator and %%Title DSC comments from the
+ relevant information in the PDF Info dictionary.
+Tweak the TrueType font encoding deciphering algorithm.
+Added the "mapUnkownCharNames" xpdfrc option.
+Fix a bug (that only showed up with certain window managers) in the
+ intermediate resize event optimization. [Thanks to Michael Rogers.]
+Check for a broken/missing embedded font (this was causing xpdf to
+ crash).
+Added support for transfer functions in PostScript output.
+Be a bit more tolerant of Link destinations that contain null values
+ for positioning parameters.
+Use ordered dot dithering instead of clustered dot dithering at
+ resolutions below 300 dpi (for monochrome output).
+Fixed security holes (bounds checking issues) in several places.
+Don't bother creating a SplashFont (allocating memory) for fonts that
+ are only used for hidden text - this avoids problems with fonts of
+ unreasonably large sizes.
+Clipping in TextOutputDev was off for characters on the left edge of
+ the page.
+The scn and SCN operators weren't correctly handling colors with more
+ than four components.
+FoFiType1::writeEncoded wasn't always correctly finding the end of the
+ encoding.
+Use the ColorTransform parameter in the DCTDecode stream dictionary.
+Type 3 fonts are allowed to have a bbox of [0 0 0 0], which means
+ "unspecified" -- don't issue error messages in that case.
+Perform the transform (to device space) in Splash instead of in
+ SplashOutputDev -- this is needed to correctly handle round joins
+ and caps on stroked paths.
+PSOutputDev now rasterizes any pages that use transparency.
+Limit the crop, bleed, trim, and art boxes to the edges of the media
+ box (per the PDF spec).
+Change GString to increase the allocation increment by powers of two.
+Handle whitespace in hex strings in CMap files/streams.
+Use strings instead of names for separation colorant names in
+ PSOutputDev.
+For explicitly masked images where the mask is higher resolution than
+ the image, use the soft mask code.
+Avoid problems with very large x-steps in the PostScript output for
+ tiling pattern fills.
+Avoid a divide-by-zero in stitching functions which have a subfunction
+ with empty bounds.
+Honor the "Hidden", "NoView", and "Print" flags on annotations.
+Rewrote the pixel rendering code in Splash to use a single set of
+ pixel pipeline functions.
+Added support for transparency groups and soft masks.
+Fixed the transparency blend functions to match the addendum published
+ by Adobe.
+Changed Splash/SplashBitmap to store alpha in a separate plane.
+Setting the color space now selects the correct default color for that
+ color space.
+Remove the mutex lock from GlobalParams::getErrQuiet() to avoid a
+ deadlock when parseCIDToUnicode() or parseUnicodeToUnicode() calls
+ it from inside a locked section.
+Added error checking (on the argument count) in the sc/SC/scn/SCN
+ operators.
+Skip over notdef glyphs in TrueType fonts (which sometimes get drawn
+ as little boxes), to match Adobe's behavior.
+Painting operations in a Separation color space with the "None"
+ colorant or a DeviceN color space with all colorants set to "None"
+ never mark the page.
+Fixed an obscure bug in the JPX decoder - it wasn't reading the extra
+ stuffing byte in the case where the last byte of a packet header was
+ 0xff.
+Change the TrueType font parser (FoFiTrueType) to change the glyph
+ count rather than report an error if the 'loca' table is too small.
+Fixed a couple of bugs in the JBIG2 decoder.
+Added stochastic clustered dot dithering.
+Added the screenType, screenSize, screenDotRadius, screenGamma,
+ screenBlackThreshold, and screenWhiteThreshold xpdfrc settings.
+PSOutputDev now correctly handles invalid Type 3 charprocs which don't
+ start with a d0 or d1 operator
+FreeType 2.2.x support - get rid of the FT_INTERNAL_OBJECTS_H include,
+ and add some 'const' declarations.
+Handle PDFDocEncoding in Info dictionary strings.
+Tweak the xref repair code - ignore whitespace at the start of lines
+ when looking for objects.
+Added the "-exec" switch to xpdf.
+Removed the xpdf.viKeys X resource.
+Changed the color key / explicit masked image code in PSOutputDev to
+ generate better PS code, including a Level 3 option.
+Tweaked the DEBUG_MEM code for performance.
+Move the JBIG2 global stream reading code into reset() instead of the
+ constructor - this way, pdftotext doesn't end up reading the global
+ stream.
+Added the "-preload" option to pdftops and the psPreload xpdfrc
+ command.
+Added the "zoom to selection" command (on the popup menu).
+Fix a bug (in xpdf/pdftoppm/pdftops) with tiling patterns whose bbox
+ size is different from their xStep/yStep.
+Implemented stroke with pattern color spaces.
+Following a link to a page whose CropBox was different from the
+ MediaBox was resulting in an incorrect scroll position.
+Parse truncated date strings from the Info dictionary correctly.
+Change FoFiType1 to handle Type 1 fonts with two /Encoding keys.
+Extend the PSOutputDev shaded fill code to handle DeviceCMYK shaded
+ fills in level2sep and level3sep modes.
+Detect infinite loops in the Page tree.
+Optimized the ASCII85Encoder code.
+Tweaked the text extractor to do a better job of lining up rows of
+ text.
+Leave images compressed (or re-compress them with RLE) in PostScript
+ output when setting up images for forms and Type 3 fonts (or with
+ -preload).
+Extend FoFiType1 to handle Type 1 fonts with octal character codes in
+ their encodings.
+Use a custom string formatter to avoid problems with locale-based decimal
+ formatting (commas instead of periods) in PS output.
+Allow comments in PostScript-type functions.
+Change the TrueType font parser (FoFiTrueType) to delete glyf table
+ entries that are too short.
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..a43ea21
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..6c6db87
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,248 @@
+Xpdf
+====
+
+version 3.02
+2007-feb-27
+
+The Xpdf software and documentation are
+copyright 1996-2007 Glyph & Cog, LLC.
+
+Email: derekn@foolabs.com
+WWW: http://www.foolabs.com/xpdf/
+
+
+Compiling xpdf
+--------------
+
+Xpdf is written in C++ (with a little bit of C). It should work with
+any ANSI-compliant C++ and C compilers. The systems and compilers
+it's been tested with are listed on the xpdf web page.
+
+Xpdf requires the Motif (or Lesstif) toolkit.
+
+The following notes give specific instructions for compiling on
+different systems.
+
+
+**************
+*** UNIX ***
+**************
+
+* Install t1lib (if desired). The resulting library (libt1) and
+ include files (t1lib.h and t1libx.h) should be placed somewhere on
+ the standard search paths. Alternatively, when you configure xpdf,
+ you can use the following options to tell configure where to find
+ the t1lib files:
+
+ --with-t1-library=PATH
+ --with-t1-includes=PATH
+
+* Install FreeType 2 (this is required). WARNING: You must have
+ version 2.0.5 or newer. Some older versions of XFree86 ship with an
+ older version of FreeType, which means you'll need to explicitly set
+ include and library paths to get the correct version:
+
+ --with-freetype2-library=PATH
+ --with-freetype2-includes=PATH
+
+ (The include path is the directory which contains the freetype2
+ directory, i.e., do NOT include "freetype2" in the
+ --with-freetype2-includes path. For example, with the default
+ installation, the library path is /usr/local/lib and the include
+ path is /usr/local/include/freetype2.)
+
+* If you have Motif (or Lesstif) installed in a non-standard place,
+ you can use the following options to tell configure where to find
+ it:
+
+ --with-Xm-library=PATH
+ --with-Xm-includes=PATH
+
+* Run the configure script:
+
+ ./configure
+
+ This should produce a set of makefiles customized for your system.
+ The configure script accepts the following options (in addition to
+ the usual things accepted by autoconf configure scripts):
+
+ --prefix=PREFIX
+ Changes the directory where xpdf is installed. The default is
+ /usr/local.
+
+ --enable-a4-paper
+ Switches the default paper size for PostScript output (xpdf
+ and pdftops) to A4. The default is Letter size.
+
+ --enable-no-text-select
+ With this option, xpdf will not copy text. (This is only
+ useful on closed systems where the user can't get at the PDF
+ file directly.)
+
+ --enable-opi
+ Enables support for generation of OPI (Open Prepress
+ Interface) comments with pdftops.
+
+ --sysconfdir=DIR
+ Look for the system-wide xpdfrc config file in this directory.
+ The default is PREFIX/etc.
+
+ --with-appdef-dir=DIR
+ Use the specified app-defaults directory. The default is
+ /usr/lib/X11/app-defaults.
+
+ If you need to pass specific options to the C and/or C++ compiler,
+ you can set the CFLAGS and/or CXXFLAGS environment variables before
+ running the configure script. Any options given that way will be
+ added to the CFLAGS/CXXFLAGS used by all of the Xpdf makefiles.
+
+* Type 'make'. This should build the executables:
+
+ xpdf/xpdf
+ xpdf/pdftops
+ xpdf/pdftotext
+ xpdf/pdfinfo
+ xpdf/pdffonts
+ xpdf/pdftoppm
+ xpdf/pdfimages
+
+* If desired, type 'make install' to install the binaries and man
+ pages.
+
+The old Makefile.config and Makefiles are no longer provided or
+supported. If you really want to manually configure Xpdf (which is
+not recommended), the files that need to be created are aconf.h,
+Makefile, goo/Makefile, fofi/Makefile, splash/Makefile, and
+xpdf/Makefile, all of which are generated from the corresponding '.in'
+files.
+
+If you want to run a quick test, there is a tiny PDF file included
+with xpdf, as misc/hello.pdf .
+
+
+*************
+*** VMS ***
+*************
+
+* The 'stddef.h' include file which comes with older versions of gcc
+ may be missing a definition for wchar_t. In this case, add the
+ following lines:
+ -----
+ File GNU_CC:[INCLUDE]STDDEF.H;2
+ 44 /* Wide characters, not yet supported by VAXCRTL [match VAXC's <stddef.
+ 45 #if !defined(_WCHAR_T) && !defined(_WCHAR_T_)
+ 46 typedef unsigned int wchar_t;
+ 47 #endif
+ 48 #define _WCHAR_T
+ 49 #define _WCHAR_T_
+ 50
+ -----
+
+* Read the instructions at the top of vms_make.com.
+
+* Type '@vms_make' in the top-level directory. You'll probably want
+ to use some of the options described in the file.
+
+* The executables will be left in '[.xpdf]'.
+
+If you want to run a quick test, there is a tiny PDF file included
+with xpdf, as misc/hello.pdf .
+
+
+**************
+*** OS/2 ***
+**************
+
+Xpdf is known to run under OS/2 with the EMX runtime environment and
+XFree86. Using a proper autoconf port you can generate a valid
+configure script version.
+
+
+***************
+*** Win32 ***
+***************
+
+The non-X programs (pdftops, pdftotext, pdfinfo, pdffonts, pdfimages,
+and pdftoppm) will compile with both gcc (from cygwin), djgpp (the
+Delorie port of gcc) and Microsoft Visual C++.
+
+With cygwin, the build procedure is the same as for Unix:
+
+ * Open a shell.
+
+ * ./configure
+
+ * make
+
+ * make install
+
+It is also possible to build the Xpdf viewer with the cygwin XFree86
+port (thanks to Michael A. Richmond for these instructions):
+
+ * make sure you have the lesstif, XFree86-base, and XFree86-prog
+ cygwin packages installed (all from the "XFree86" category)
+
+ * to build t1lib:
+ - cd T1Lib-x.y
+ - ./configure --without-x
+ - make without_doc
+ - make install
+ (this should install /usr/local/include/t1lib.h and
+ /usr/local/lib/libt1.a)
+
+ * to build xpdf:
+ - cd xpdf-x.yy
+ - ./configure --with-t1-library=/usr/local/lib
+ --with-t1-includes=/usr/local/include
+ --with-freetype2-library=/usr/X11R6/lib
+ --with-freetype2-includes=/usr/X11R6/include/freetype2
+ --with-Xm-library=/usr/X11R6/lib
+ --with-Xm-includes=/usr/X11R6/include
+ (all on one line)
+ - make
+ (this should build xpdf.exe, in addition to the command line
+ utilities)
+
+With djgpp:
+
+ * Open a DOS window.
+
+ * ./dj_make
+
+ * djgpp is a DOS/Windows port of gcc, available from
+ http://www.delorie.com/
+
+With djgpp, for DOS 6 (instead of Win32):
+
+ * ./dj_make
+
+ * cd xpdf
+
+ * strip pdftops.exe
+
+ * exe2coff pdftops.exe
+
+ * copy /B c:\djgpp\bin\cwsdstub.exe+pdftops pdftops.exe
+
+ * upx pdftops.exe
+ - if you want compressed executables
+
+ * and similarly for the other executables
+
+ * cwsdstub.exe comes from djgpp/v2misc/csdpmi5b.zip on any of the
+ djgpp ftp mirrors; exe2coff is part of the standard djgpp install
+
+ * upx comes from http://upx.sourceforge.net/
+
+With the Microsoft tools:
+
+ * Open a DOS window.
+
+ * Type "cl". If you get the message "Bad command or file name", you
+ must run VCVARS32.BAT. (The location of this BAT file can be
+ determined with Explorer.)
+
+ * Type "ms_make"
+
+The dj_make and ms_make scripts don't build pdftoppm -- you'll need to
+install t1lib and FreeType 2 before you can compile pdftoppm.
diff --git a/Makefile.in b/Makefile.in
new file mode 100644
index 0000000..c08e721
--- /dev/null
+++ b/Makefile.in
@@ -0,0 +1,126 @@
+#========================================================================
+#
+# Main xpdf Makefile.
+#
+# Copyright 1996-2003 Glyph & Cog, LLC
+#
+#========================================================================
+
+SHELL = /bin/sh
+
+DESTDIR =
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+srcdir = @srcdir@
+
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_DATA = @INSTALL_DATA@
+
+EXE = @EXE@
+
+all:
+ cd goo; $(MAKE)
+ cd @UP_DIR@fofi; $(MAKE)
+ cd @UP_DIR@splash; $(MAKE)
+ cd @UP_DIR@xpdf; $(MAKE) @XPDF_TARGET@
+
+all-no-x:
+ cd goo; $(MAKE)
+ cd @UP_DIR@fofi; $(MAKE)
+ cd @UP_DIR@xpdf; $(MAKE) all-no-x
+
+xpdf: dummy
+ cd goo; $(MAKE)
+ cd @UP_DIR@fofi; $(MAKE)
+ cd @UP_DIR@splash; $(MAKE)
+ cd @UP_DIR@xpdf; $(MAKE) xpdf$(EXE)
+
+pdftops: dummy
+ cd goo; $(MAKE)
+ cd @UP_DIR@fofi; $(MAKE)
+ cd @UP_DIR@splash; $(MAKE)
+ cd @UP_DIR@xpdf; $(MAKE) pdftops$(EXE)
+
+pdftotext: dummy
+ cd goo; $(MAKE)
+ cd @UP_DIR@fofi; $(MAKE)
+ cd @UP_DIR@splash; $(MAKE)
+ cd @UP_DIR@xpdf; $(MAKE) pdftotext$(EXE)
+
+pdfinfo:
+ cd goo; $(MAKE)
+ cd @UP_DIR@fofi; $(MAKE)
+ cd @UP_DIR@splash; $(MAKE)
+ cd @UP_DIR@xpdf; $(MAKE) pdfinfo$(EXE)
+
+pdffonts:
+ cd goo; $(MAKE)
+ cd @UP_DIR@fofi; $(MAKE)
+ cd @UP_DIR@splash; $(MAKE)
+ cd @UP_DIR@xpdf; $(MAKE) pdffonts$(EXE)
+
+pdftoppm:
+ cd goo; $(MAKE)
+ cd @UP_DIR@fofi; $(MAKE)
+ cd @UP_DIR@splash; $(MAKE)
+ cd @UP_DIR@xpdf; $(MAKE) pdftoppm$(EXE)
+
+pdfimages:
+ cd goo; $(MAKE)
+ cd @UP_DIR@fofi; $(MAKE)
+ cd @UP_DIR@splash; $(MAKE)
+ cd @UP_DIR@xpdf; $(MAKE) pdfimages$(EXE)
+
+dummy:
+
+install: dummy
+ -mkdir -p $(DESTDIR)@bindir@
+@X@ $(INSTALL_PROGRAM) xpdf/xpdf$(EXE) $(DESTDIR)@bindir@/xpdf$(EXE)
+ $(INSTALL_PROGRAM) xpdf/pdftops$(EXE) $(DESTDIR)@bindir@/pdftops$(EXE)
+ $(INSTALL_PROGRAM) xpdf/pdftotext$(EXE) $(DESTDIR)@bindir@/pdftotext$(EXE)
+ $(INSTALL_PROGRAM) xpdf/pdfinfo$(EXE) $(DESTDIR)@bindir@/pdfinfo$(EXE)
+ $(INSTALL_PROGRAM) xpdf/pdffonts$(EXE) $(DESTDIR)@bindir@/pdffonts$(EXE)
+@X@ $(INSTALL_PROGRAM) xpdf/pdftoppm$(EXE) $(DESTDIR)@bindir@/pdftoppm$(EXE)
+ $(INSTALL_PROGRAM) xpdf/pdfimages$(EXE) $(DESTDIR)@bindir@/pdfimages$(EXE)
+ -mkdir -p $(DESTDIR)@mandir@/man1
+@X@ $(INSTALL_DATA) $(srcdir)/doc/xpdf.1 $(DESTDIR)@mandir@/man1/xpdf.1
+ $(INSTALL_DATA) $(srcdir)/doc/pdftops.1 $(DESTDIR)@mandir@/man1/pdftops.1
+ $(INSTALL_DATA) $(srcdir)/doc/pdftotext.1 $(DESTDIR)@mandir@/man1/pdftotext.1
+ $(INSTALL_DATA) $(srcdir)/doc/pdfinfo.1 $(DESTDIR)@mandir@/man1/pdfinfo.1
+ $(INSTALL_DATA) $(srcdir)/doc/pdffonts.1 $(DESTDIR)@mandir@/man1/pdffonts.1
+@X@ $(INSTALL_DATA) $(srcdir)/doc/pdftoppm.1 $(DESTDIR)@mandir@/man1/pdftoppm.1
+ $(INSTALL_DATA) $(srcdir)/doc/pdfimages.1 $(DESTDIR)@mandir@/man1/pdfimages.1
+ -mkdir -p $(DESTDIR)@mandir@/man5
+ $(INSTALL_DATA) $(srcdir)/doc/xpdfrc.5 $(DESTDIR)@mandir@/man5/xpdfrc.5
+ -mkdir -p $(DESTDIR)@sysconfdir@
+ @if test ! -f $(DESTDIR)@sysconfdir@/xpdfrc; then \
+ echo "$(INSTALL_DATA) $(srcdir)/doc/sample-xpdfrc $(DESTDIR)@sysconfdir@/xpdfrc"; \
+ $(INSTALL_DATA) $(srcdir)/doc/sample-xpdfrc $(DESTDIR)@sysconfdir@/xpdfrc; \
+ else \
+ echo "# not overwriting the existing $(DESTDIR)@sysconfdir@/xpdfrc"; \
+ fi
+
+clean:
+ -cd goo; $(MAKE) clean
+ -cd @UP_DIR@fofi; $(MAKE) clean
+ -cd @UP_DIR@splash; $(MAKE) clean
+ -cd @UP_DIR@xpdf; $(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 @UP_DIR@fofi; $(MAKE) depend
+ cd @UP_DIR@splash; $(MAKE) depend
+ cd @UP_DIR@xpdf; $(MAKE) depend
diff --git a/README b/README
new file mode 100644
index 0000000..71fd62e
--- /dev/null
+++ b/README
@@ -0,0 +1,405 @@
+Xpdf
+====
+
+version 3.02
+2007-feb-27
+
+The Xpdf software and documentation are
+copyright 1996-2007 Glyph & Cog, LLC.
+
+Email: derekn@foolabs.com
+WWW: http://www.foolabs.com/xpdf/
+
+The PDF data structures, operators, and specification are
+copyright 1985-2006 Adobe Systems Inc.
+
+
+What is Xpdf?
+-------------
+
+Xpdf is an open source viewer for Portable Document Format (PDF)
+files. (These are also sometimes also called 'Acrobat' files, from
+the name of Adobe's PDF software.) The Xpdf project also includes a
+PDF text extractor, PDF-to-PostScript converter, and various other
+utilities.
+
+Xpdf runs under the X Window System on UNIX, VMS, and OS/2. The non-X
+components (pdftops, pdftotext, etc.) also run on Win32 systems and
+should run on pretty much any system with a decent C++ compiler.
+
+Xpdf is designed to be small and efficient. It can use Type 1 or
+TrueType fonts.
+
+
+Distribution
+------------
+
+Xpdf is licensed under the GNU General Public License (GPL), version
+2. In my opinion, the GPL is a convoluted, confusing, ambiguous mess.
+But it's also pervasive, and I'm sick of arguing. And even if it is
+confusing, the basic idea is good.
+
+In order to cut down on the confusion a little bit, here are some
+informal clarifications:
+
+- I don't mind if you redistribute Xpdf in source and/or binary form,
+ as long as you include all of the documentation: README, man pages
+ (or help files), and COPYING. (Note that the README file contains a
+ pointer to a web page with the source code.)
+
+- Selling a CD-ROM that contains Xpdf is fine with me, as long as it
+ includes the documentation. I wouldn't mind receiving a sample
+ copy, but it's not necessary.
+
+- If you make useful changes to Xpdf, please make the source code
+ available -- post it on a web site, email it to me, whatever.
+
+If you're interested in commercial licensing, please see the Glyph &
+Cog web site:
+
+ http://www.glyphandcog.com/
+
+
+Compatibility
+-------------
+
+Xpdf is developed and tested on a Linux 2.4 x86 system.
+
+In addition, it has been compiled by others on Solaris, AIX, HP-UX,
+Digital Unix, Irix, and numerous other Unix implementations, as well
+as VMS and OS/2. It should work on pretty much any system which runs
+X11 and has Unix-like libraries. You'll need ANSI C++ and C compilers
+to compile it.
+
+The non-X components of Xpdf (pdftops, pdftotext, pdfinfo, pdffonts,
+pdftoppm, and pdfimages) can also be compiled on Win32 systems. See
+the Xpdf web page for details.
+
+If you compile Xpdf for a system not listed on the web page, please
+let me know. If you're willing to make your binary available by ftp
+or on the web, I'll be happy to add a link from the Xpdf web page. I
+have decided not to host any binaries I didn't compile myself (for
+disk space and support reasons).
+
+If you can't get Xpdf to compile on your system, send me email and
+I'll try to help.
+
+Xpdf has been ported to the Acorn, Amiga, BeOS, and EPOC. See the
+Xpdf web page for links.
+
+
+Getting Xpdf
+------------
+
+The latest version is available from:
+
+ http://www.foolabs.com/xpdf/
+
+or:
+
+ ftp://ftp.foolabs.com/pub/xpdf/
+
+Source code and several precompiled executables are available.
+
+Announcements of new versions are posted to several newsgroups
+(comp.text.pdf, comp.os.linux.announce, and others) and emailed to a
+list of people. If you'd like to receive email notification of new
+versions, just let me know.
+
+
+Running Xpdf
+------------
+
+To run xpdf, simply type:
+
+ xpdf file.pdf
+
+To generate a PostScript file, hit the "print" button in xpdf, or run
+pdftops:
+
+ pdftops file.pdf
+
+To generate a plain text file, run pdftotext:
+
+ pdftotext file.pdf
+
+There are four additional utilities (which are fully described in
+their man pages):
+
+ pdfinfo -- dumps a PDF file's Info dictionary (plus some other
+ useful information)
+ pdffonts -- lists the fonts used in a PDF file along with various
+ information for each font
+ pdftoppm -- converts a PDF file to a series of PPM/PGM/PBM-format
+ bitmaps
+ pdfimages -- extracts the images from a PDF file
+
+Command line options and many other details are described in the man
+pages (xpdf.1, etc.) and the VMS help files (xpdf.hlp, etc.).
+
+
+Upgrading from Xpdf 2.xx
+------------------------
+
+WARNING: Xpdf 3.00 switched to a new PDF rasterizer, which no longer
+uses X fonts. You'll need a set of Base-14 fonts -- the URW fonts
+distributed with ghostscript can be used for this. Xpdf will search
+for the URW fonts, but if you have them installed in a non-standard
+directory, you'll need to set up an xpdfrc config file to point to
+them. For full details, please see the xpdfrc(5) man page.
+
+
+Compiling Xpdf
+--------------
+
+See the separate file, INSTALL.
+
+
+Bugs
+----
+
+If you find a bug in Xpdf, i.e., if it prints an error message,
+crashes, or incorrectly displays a document, and you don't see that
+bug listed here, please send me email, with a pointer (URL, ftp site,
+etc.) to the PDF file.
+
+
+Acknowledgments
+---------------
+
+Thanks to:
+
+* Patrick Voigt for help with the remote server code.
+* Patrick Moreau, Martin P.J. Zinser, and David Mathog for the VMS
+ port.
+* David Boldt and Rick Rodgers for sample man pages.
+* Brendan Miller for the icon idea.
+* Olly Betts for help testing pdftotext.
+* Peter Ganten for the OS/2 port.
+* Michael Richmond for the Win32 port of pdftops and pdftotext and the
+ xpdf/cygwin/XFree86 build instructions.
+* Frank M. Siegert for improvements in the PostScript code.
+* Leo Smiers for the decryption patches.
+* Rainer Menzner for creating t1lib, and for helping me adapt it to
+ xpdf.
+* Pine Tree Systems A/S for funding the OPI and EPS support in
+ pdftops.
+* Easy Software Products for funding several improvements to the
+ PostScript output code.
+* Tom Kacvinsky for help with FreeType and for being my interface to
+ the FreeType team.
+* Theppitak Karoonboonyanan for help with Thai support.
+* Leonard Rosenthol for help and contributions on a bunch of things.
+* Alexandros Diamantidis and Maria Adaloglou for help with Greek
+ support.
+* Lawrence Lai for help with the CJK Unicode maps.
+
+Various people have contributed modifications made for use by the
+pdftex project:
+
+* Han The Thanh
+* Martin Schröder of ArtCom GmbH
+
+
+References
+----------
+
+Adobe Systems Inc., _PDF Reference, sixth edition: Adobe Portable
+Document Format version 1.7_.
+http://www.adobe.com/devnet/pdf/pdf_reference.html
+[The manual for PDF version 1.7.]
+
+Adobe Systems Inc., "Errata for the PDF Reference, sixth edition,
+version 1.7", October 16, 2006.
+http://www.adobe.com/devnet/pdf/pdf_reference.html
+[The errata for the PDF 1.7 spec.]
+
+Adobe Systems Inc., _PostScript Language Reference_, 3rd ed.
+Addison-Wesley, 1999, ISBN 0-201-37922-8.
+[The official PostScript manual.]
+
+Adobe Systems, Inc., _The Type 42 Font Format Specification_,
+Adobe Developer Support Technical Specification #5012. 1998.
+http://partners.adobe.com/asn/developer/pdfs/tn/5012.Type42_Spec.pdf
+[Type 42 is the format used to embed TrueType fonts in PostScript
+files.]
+
+Adobe Systems, Inc., _Adobe CMap and CIDFont Files Specification_,
+Adobe Developer Support Technical Specification #5014. 1995.
+http://www.adobe.com/supportservice/devrelations/PDFS/TN/5014.CIDFont_Spec.pdf
+[CMap file format needed for Japanese and Chinese font support.]
+
+Adobe Systems, Inc., _Adobe-Japan1-4 Character Collection for
+CID-Keyed Fonts_, Adobe Developer Support Technical Note #5078.
+2000.
+http://partners.adobe.com/asn/developer/PDFS/TN/5078.CID_Glyph.pdf
+[The Adobe Japanese character set.]
+
+Adobe Systems, Inc., _Adobe-GB1-4 Character Collection for
+CID-Keyed Fonts_, Adobe Developer Support Technical Note #5079.
+2000.
+http://partners.adobe.com/asn/developer/pdfs/tn/5079.Adobe-GB1-4.pdf
+[The Adobe Chinese GB (simplified) character set.]
+
+Adobe Systems, Inc., _Adobe-CNS1-3 Character Collection for
+CID-Keyed Fonts_, Adobe Developer Support Technical Note #5080.
+2000.
+http://partners.adobe.com/asn/developer/PDFS/TN/5080.CNS_CharColl.pdf
+[The Adobe Chinese CNS (traditional) character set.]
+
+Adobe Systems Inc., _Supporting the DCT Filters in PostScript Level
+2_, Adobe Developer Support Technical Note #5116. 1992.
+http://www.adobe.com/supportservice/devrelations/PDFS/TN/5116.PS2_DCT.PDF
+[Description of the DCTDecode filter parameters.]
+
+Adobe Systems Inc., _Open Prepress Interface (OPI) Specification -
+Version 2.0_, Adobe Developer Support Technical Note #5660. 2000.
+http://partners.adobe.com/asn/developer/PDFS/TN/5660.OPI_2.0.pdf
+
+Adobe Systems Inc., CMap files.
+ftp://ftp.oreilly.com/pub/examples/nutshell/cjkv/adobe/
+[The actual CMap files for the 16-bit CJK encodings.]
+
+Adobe Systems Inc., Unicode glyph lists.
+http://partners.adobe.com/asn/developer/type/unicodegn.html
+http://partners.adobe.com/asn/developer/type/glyphlist.txt
+http://partners.adobe.com/asn/developer/type/corporateuse.txt
+http://partners.adobe.com/asn/developer/type/zapfdingbats.txt
+[Mappings between character names to Unicode.]
+
+Adobe Systems Inc., OpenType Specification v. 1.4.
+http://partners.adobe.com/public/developer/opentype/index_spec.html
+[The OpenType font format spec.]
+
+Aldus Corp., _OPI: Open Prepress Interface Specification 1.3_. 1993.
+http://partners.adobe.com/asn/developer/PDFS/TN/OPI_13.pdf
+
+Anonymous, RC4 source code.
+ftp://ftp.ox.ac.uk/pub/crypto/misc/rc4.tar.gz
+ftp://idea.sec.dsi.unimi.it/pub/crypt/code/rc4.tar.gz
+[This is the algorithm used to encrypt PDF files.]
+
+T. Boutell, et al., "PNG (Portable Network Graphics) Specification,
+Version 1.0". RFC 2083.
+[PDF uses the PNG filter algorithms.]
+
+CCITT, "Information Technology - Digital Compression and Coding of
+Continuous-tone Still Images - Requirements and Guidelines", CCITT
+Recommendation T.81.
+http://www.w3.org/Graphics/JPEG/
+[The official JPEG spec.]
+
+A. Chernov, "Registration of a Cyrillic Character Set". RFC 1489.
+[Documentation for the KOI8-R Cyrillic encoding.]
+
+Roman Czyborra, "The ISO 8859 Alphabet Soup".
+http://czyborra.com/charsets/iso8859.html
+[Documentation on the various ISO 859 encodings.]
+
+L. Peter Deutsch, "ZLIB Compressed Data Format Specification version
+3.3". RFC 1950.
+[Information on the general format used in FlateDecode streams.]
+
+L. Peter Deutsch, "DEFLATE Compressed Data Format Specification
+version 1.3". RFC 1951.
+[The definition of the compression algorithm used in FlateDecode
+streams.]
+
+Morris Dworkin, "Recommendation for Block Cipher Modes of Operation",
+National Institute of Standards, NIST Special Publication 800-38A,
+2001.
+[The cipher block chaining (CBC) mode used with AES in PDF files.]
+
+Federal Information Processing Standards Publication 197 (FIPS PUBS
+197), "Advanced Encryption Standard (AES)", November 26, 2001.
+[AES encryption, used in PDF 1.6.]
+
+Jim Flowers, "X Logical Font Description Conventions", Version 1.5, X
+Consortium Standard, X Version 11, Release 6.1.
+ftp://ftp.x.org/pub/R6.1/xc/doc/hardcopy/XLFD/xlfd.PS.Z
+[The official specification of X font descriptors, including font
+transformation matrices.]
+
+Foley, van Dam, Feiner, and Hughes, _Computer Graphics: Principles and
+Practice_, 2nd ed. Addison-Wesley, 1990, ISBN 0-201-12110-7.
+[Colorspace conversion functions, Bezier spline math.]
+
+Robert L. Hummel, _Programmer's Technical Reference: Data and Fax
+Communications_. Ziff-Davis Press, 1993, ISBN 1-56276-077-7.
+[CCITT Group 3 and 4 fax decoding.]
+
+ISO/IEC, _Information technology -- Lossy/lossless coding of bi-level
+images_. ISO/IEC 14492, First edition (2001-12-15).
+http://webstore.ansi.org/
+[The official JBIG2 standard. The final draft of this spec is
+available from http://www.jpeg.org/jbighomepage.html.]
+
+ISO/IEC, _Information technology -- JPEG 2000 image coding system --
+Part 1: Core coding system_. ISO/IEC 15444-1, First edition
+(2000-12-15).
+http://webstore.ansi.org/
+[The official JPEG 2000 standard. The final committee draft of this
+spec is available from http://www.jpeg.org/JPEG2000.html, but there
+were changes made to the bitstream format between that draft and the
+published spec.]
+
+ITU, "Standardization of Group 3 facsimile terminals for document
+transmission", ITU-T Recommendation T.4, 1999.
+ITU, "Facsimile coding schemes and coding control functions for Group 4
+facsimile apparatus", ITU-T Recommendation T.6, 1993.
+http://www.itu.int/
+[The official Group 3 and 4 fax standards - used by the CCITTFaxDecode
+stream, as well as the JBIG2Decode stream.]
+
+B. Kaliski, "PKCS #5: Password-Based Cryptography Specification,
+Version 2.0". RFC 2898.
+[Defines the padding scheme used with AES encryption in PDF files.]
+
+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 fast IDCT algorithm used in the DCTDecode filter.]
+
+Microsoft, _TrueType 1.0 Font Files_, rev. 1.66. 1995.
+http://www.microsoft.com/typography/tt/tt.htm
+[The TrueType font spec (in MS Word format, naturally).]
+
+V. Ostromoukhov, R.D. Hersch, "Stochastic Clustered-Dot Dithering",
+Conf. Color Imaging: Device-Independent Color, Color Hardcopy, and
+Graphic Arts IV, 1999, SPIE Vol. 3648, 496-505.
+http://diwww.epfl.ch/w3lsp/publications/colour/scd.html
+[The stochastic dithering algorithm used in Xpdf.]
+
+P. Peterlin, "ISO 8859-2 (Latin 2) Resources".
+http://sizif.mf.uni-lj.si/linux/cee/iso8859-2.html
+[This is a web page with all sorts of useful Latin-2 character set and
+font information.]
+
+Charles Poynton, "Color FAQ".
+http://www.inforamp.net/~poynton/ColorFAQ.html
+[The mapping from the CIE 1931 (XYZ) color space to RGB.]
+
+R. Rivest, "The MD5 Message-Digest Algorithm". RFC 1321.
+[MD5 is used in PDF document encryption.]
+
+Thai Industrial Standard, "Standard for Thai Character Codes for
+Computers", TIS-620-2533 (1990).
+http://www.nectec.or.th/it-standards/std620/std620.htm
+[The TIS-620 Thai encoding.]
+
+Unicode Consortium, "Unicode Home Page".
+http://www.unicode.org/
+[Online copy of the Unicode spec.]
+
+W3C Recommendation, "PNG (Portable Network Graphics) Specification
+Version 1.0".
+http://www.w3.org/Graphics/PNG/
+[Defines the PNG image predictor.]
+
+Gregory K. Wallace, "The JPEG Still Picture Compression Standard".
+ftp://ftp.uu.net/graphics/jpeg/wallace.ps.gz
+[Good description of the JPEG standard. Also published in CACM, April
+1991, and submitted to IEEE Transactions on Consumer Electronics.]
+
+F. Yergeau, "UTF-8, a transformation format of ISO 10646". RFC 2279.
+[A commonly used Unicode encoding.]
diff --git a/aclocal.m4 b/aclocal.m4
new file mode 100644
index 0000000..b3aeea2
--- /dev/null
+++ b/aclocal.m4
@@ -0,0 +1,274 @@
+# <<< 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])
+])
diff --git a/aconf-dj.h b/aconf-dj.h
new file mode 100644
index 0000000..fa92df3
--- /dev/null
+++ b/aconf-dj.h
@@ -0,0 +1,77 @@
+/*
+ * aconf-dj.h
+ *
+ * Copyright 2002-2003 Glyph & Cog, LLC
+ */
+
+#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
+
+/*
+ * 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.
+ */
+#define HAVE_DIRENT_H 1
+#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
+#define HAVE_POPEN 1
+#undef HAVE_MKSTEMP
+#undef SELECT_TAKES_INT
+#undef HAVE_FSEEK64
+
+/*
+ * 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
+
+/*
+ * This is defined if using FreeType version 2.
+ */
+#undef FREETYPE2
+
+/*
+ * This is defined if using libpaper.
+ */
+#undef HAVE_PAPER_H
+
+#endif
diff --git a/aconf-win32.h b/aconf-win32.h
new file mode 100644
index 0000000..0bdc763
--- /dev/null
+++ b/aconf-win32.h
@@ -0,0 +1,120 @@
+/*
+ * aconf-win32.h
+ *
+ * Copyright 2002-2003 Glyph & Cog, LLC
+ */
+
+#ifndef ACONF_H
+#define ACONF_H
+
+#include <aconf2.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
+
+/*
+ * Enable multithreading support.
+ */
+#define MULTITHREADED 1
+
+/*
+ * Enable C++ exceptions.
+ */
+#define USE_EXCEPTIONS 1
+
+/*
+ * Enable word list support.
+ */
+#undef TEXTOUT_WORD_LIST
+
+/*
+ * Use fixed point (instead of floating point) arithmetic.
+ */
+#undef USE_FIXEDPOINT
+
+/*
+ * 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
+#undef HAVE_FSEEKO
+#undef HAVE_FSEEK64
+#undef _FILE_OFFSET_BITS
+#undef _LARGE_FILES
+#undef _LARGEFILE_SOURCE
+#undef HAVE_XTAPPSETEXITFLAG
+
+/*
+ * 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
+#define HAVE_FREETYPE_FREETYPE_H 1
+
+/*
+ * This is defined if using FreeType version 2.
+ */
+#define FREETYPE2
+
+/*
+ * This is defined if using libpaper.
+ */
+#undef HAVE_PAPER_H
+
+/*
+ * Enable support for loading plugins.
+ */
+#undef ENABLE_PLUGINS
+
+/*
+ * Defined if the Splash library is avaiable.
+ */
+#undef HAVE_SPLASH
+
+/*
+ * Enable support for CMYK output.
+ */
+#undef SPLASH_CMYK
+
+#endif
diff --git a/aconf.h.in b/aconf.h.in
new file mode 100644
index 0000000..b6c6351
--- /dev/null
+++ b/aconf.h.in
@@ -0,0 +1,115 @@
+/*
+ * aconf.h
+ *
+ * Copyright 2002-2003 Glyph & Cog, LLC
+ */
+
+#ifndef ACONF_H
+#define ACONF_H
+
+#include <aconf2.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
+
+/*
+ * Enable multithreading support.
+ */
+#undef MULTITHREADED
+
+/*
+ * Enable C++ exceptions.
+ */
+#undef USE_EXCEPTIONS
+
+/*
+ * Enable word list support.
+ */
+#undef TEXTOUT_WORD_LIST
+
+/*
+ * Use fixed point (instead of floating point) arithmetic.
+ */
+#undef USE_FIXEDPOINT
+
+/*
+ * 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
+#undef HAVE_FSEEKO
+#undef HAVE_FSEEK64
+#undef _FILE_OFFSET_BITS
+#undef _LARGE_FILES
+#undef _LARGEFILE_SOURCE
+#undef HAVE_XTAPPSETEXITFLAG
+
+/*
+ * 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 2.
+ */
+#undef HAVE_FREETYPE_H
+#undef HAVE_FREETYPE_FREETYPE_H
+
+/*
+ * This is defined if using libpaper.
+ */
+#undef HAVE_PAPER_H
+
+/*
+ * Enable support for loading plugins.
+ */
+#undef ENABLE_PLUGINS
+
+/*
+ * Defined if the Splash library is avaiable.
+ */
+#undef HAVE_SPLASH
+
+/*
+ * Enable support for CMYK output.
+ */
+#undef SPLASH_CMYK
+
+#endif
diff --git a/aconf2.h b/aconf2.h
new file mode 100644
index 0000000..b51de1e
--- /dev/null
+++ b/aconf2.h
@@ -0,0 +1,38 @@
+/*
+ * aconf2.h
+ *
+ * This gets included by aconf.h, and contains miscellaneous global
+ * settings not directly controlled by autoconf. This is a separate
+ * file because otherwise the configure script will munge any
+ * #define/#undef constructs.
+ *
+ * Copyright 2002-2003 Glyph & Cog, LLC
+ */
+
+#ifndef ACONF2_H
+#define ACONF2_H
+
+/*
+ * This controls the use of the interface/implementation pragmas.
+ */
+#ifdef __GNUC__
+#define USE_GCC_PRAGMAS
+#endif
+/* There is a bug in the version of gcc which ships with MacOS X 10.2 */
+#if defined(__APPLE__) && defined(__MACH__)
+# include <AvailabilityMacros.h>
+#endif
+#ifdef MAC_OS_X_VERSION_MAX_ALLOWED
+# if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_2
+# undef USE_GCC_PRAGMAS
+# endif
+#endif
+
+/*
+ * Make sure WIN32 is defined if appropriate.
+ */
+#if defined(_WIN32) && !defined(WIN32)
+# define WIN32
+#endif
+
+#endif
diff --git a/configure b/configure
new file mode 100755
index 0000000..121e7b2
--- /dev/null
+++ b/configure
@@ -0,0 +1,11805 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.59.
+#
+# Copyright (C) 2003 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 Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+ set -o posix
+fi
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ as_unset=unset
+else
+ as_unset=false
+fi
+
+
+# Work around bugs in pre-3.0 UWIN ksh.
+$as_unset ENV MAIL MAILPATH
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+ LC_TELEPHONE LC_TIME
+do
+ if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+ eval $as_var=C; export $as_var
+ else
+ $as_unset $as_var
+ fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)$' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+ /^X\/\(\/\/\)$/{ s//\1/; q; }
+ /^X\/\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+
+
+# PATH needs CR, and LINENO needs CR and PATH.
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+
+
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x$as_lineno_3" = "x$as_lineno_2" || {
+ # Find who we are. Look in the path if we contain no path at all
+ # relative or not.
+ 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
+
+ ;;
+ esac
+ # We did not find ourselves, most probably we were run as `sh COMMAND'
+ # in which case we are not to be found in the path.
+ if test "x$as_myself" = x; then
+ as_myself=$0
+ fi
+ if test ! -f "$as_myself"; then
+ { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2
+ { (exit 1); exit 1; }; }
+ fi
+ case $CONFIG_SHELL in
+ '')
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for as_base in sh bash ksh sh5; do
+ case $as_dir in
+ /*)
+ if ("$as_dir/$as_base" -c '
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then
+ $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
+ $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
+ CONFIG_SHELL=$as_dir/$as_base
+ export CONFIG_SHELL
+ exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+ fi;;
+ esac
+ done
+done
+;;
+ esac
+
+ # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+ # uniformly replaced by the line number. The first 'sed' inserts a
+ # line-number line before each line; the second 'sed' does the real
+ # work. The second script uses 'N' to pair each line-number line
+ # with the numbered line, and appends trailing '-' during
+ # substitution so that $LINENO is not a special case at line end.
+ # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+ # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-)
+ sed '=' <$as_myself |
+ sed '
+ N
+ s,$,-,
+ : loop
+ s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+ t loop
+ s,-$,,
+ s,^['$as_cr_digits']*\n,,
+ ' >$as_me.lineno &&
+ chmod +x $as_me.lineno ||
+ { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
+ { (exit 1); exit 1; }; }
+
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensible to this).
+ . ./$as_me.lineno
+ # Exit status is that of the last command.
+ exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+ *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T=' ' ;;
+ *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+ *) ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+ # We could just check for DJGPP; but this test a) works b) is more generic
+ # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+ if test -f conf$$.exe; then
+ # Don't use ln at all; we don't have any links
+ as_ln_s='cp -p'
+ else
+ as_ln_s='ln -s'
+ fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+else
+ as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.file
+
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p=:
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+as_executable_p="test -f"
+
+# 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'"
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS=" $as_nl"
+
+# CDPATH.
+$as_unset CDPATH
+
+
+# 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`
+
+exec 6>&1
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_config_libobj_dir=.
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+# Maximum number of lines to put in a shell here document.
+# This variable seems obsolete. It should probably be removed, and
+# only ac_max_sed_lines should be used.
+: ${ac_max_here_lines=38}
+
+# Identity of this package.
+PACKAGE_NAME=
+PACKAGE_TARNAME=
+PACKAGE_VERSION=
+PACKAGE_STRING=
+PACKAGE_BUGREPORT=
+
+ac_unique_file="xpdf/xpdf.cc"
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stdio.h>
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#if HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#if STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# if HAVE_STDLIB_H
+# include <stdlib.h>
+# endif
+#endif
+#if HAVE_STRING_H
+# if !STDC_HEADERS && HAVE_MEMORY_H
+# include <memory.h>
+# endif
+# include <string.h>
+#endif
+#if HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#else
+# if HAVE_STDINT_H
+# include <stdint.h>
+# endif
+#endif
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CXX CXXFLAGS ac_ct_CXX INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA RANLIB ac_ct_RANLIB EXE LIBPREFIX AR UP_DIR CPP X_CFLAGS X_PRE_LIBS X_LIBS X_EXTRA_LIBS EGREP Xpm_LIBS Xpm_CFLAGS Xext_LIBS Xext_CFLAGS Xp_LIBS Xp_CFLAGS Xt_LIBS Xt_CFLAGS Xm_LIBS Xm_CFLAGS Sgm_LIBS Sgm_CFLAGS t1_LIBS t1_CFLAGS freetype2_LIBS freetype2_CFLAGS libpaper_LIBS libpaper_CFLAGS X XPDF_TARGET LIBOBJS LTLIBOBJS'
+ac_subst_files=''
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+ac_prev=
+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
+
+ ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'`
+
+ # Accept the important Cygnus configure options, so we can diagnose typos.
+
+ case $ac_option in
+
+ -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 | --data | --dat | --da)
+ ac_prev=datadir ;;
+ -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+ | --da=*)
+ datadir=$ac_optarg ;;
+
+ -disable-* | --disable-*)
+ ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+ { (exit 1); exit 1; }; }
+ ac_feature=`echo $ac_feature | sed 's/-/_/g'`
+ eval "enable_$ac_feature=no" ;;
+
+ -enable-* | --enable-*)
+ ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+ { (exit 1); exit 1; }; }
+ ac_feature=`echo $ac_feature | sed 's/-/_/g'`
+ case $ac_option in
+ *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "enable_$ac_feature='$ac_optarg'" ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix=$ac_optarg ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he | -h)
+ ac_init_help=long ;;
+ -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+ ac_init_help=recursive ;;
+ -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+ ac_init_help=short ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host_alias ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host_alias=$ac_optarg ;;
+
+ -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 ;;
+
+ -localstatedir | --localstatedir | --localstatedi | --localstated \
+ | --localstate | --localstat | --localsta | --localst \
+ | --locals | --local | --loca | --loc | --lo)
+ ac_prev=localstatedir ;;
+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+ | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+ | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+ 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 ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+ ac_prev=sbindir ;;
+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+ | --sbi=* | --sb=*)
+ sbindir=$ac_optarg ;;
+
+ -sharedstatedir | --sharedstatedir | --sharedstatedi \
+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+ | --sharedst | --shareds | --shared | --share | --shar \
+ | --sha | --sh)
+ ac_prev=sharedstatedir ;;
+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+ | --sha=* | --sh=*)
+ sharedstatedir=$ac_optarg ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site=$ac_optarg ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir=$ac_optarg ;;
+
+ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+ | --syscon | --sysco | --sysc | --sys | --sy)
+ ac_prev=sysconfdir ;;
+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+ sysconfdir=$ac_optarg ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target_alias ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target_alias=$ac_optarg ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers | -V)
+ ac_init_version=: ;;
+
+ -with-* | --with-*)
+ ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid package name: $ac_package" >&2
+ { (exit 1); exit 1; }; }
+ ac_package=`echo $ac_package| sed 's/-/_/g'`
+ case $ac_option in
+ *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "with_$ac_package='$ac_optarg'" ;;
+
+ -without-* | --without-*)
+ ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid package name: $ac_package" >&2
+ { (exit 1); exit 1; }; }
+ ac_package=`echo $ac_package | sed 's/-/_/g'`
+ eval "with_$ac_package=no" ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes=$ac_optarg ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries=$ac_optarg ;;
+
+ -*) { echo "$as_me: error: unrecognized option: $ac_option
+Try \`$0 --help' for more information." >&2
+ { (exit 1); exit 1; }; }
+ ;;
+
+ *=*)
+ ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid variable name: $ac_envvar" >&2
+ { (exit 1); exit 1; }; }
+ ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`
+ eval "$ac_envvar='$ac_optarg'"
+ export $ac_envvar ;;
+
+ *)
+ # FIXME: should be removed in autoconf 3.0.
+ echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+ expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+ echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+ : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+ { echo "$as_me: error: missing argument to $ac_option" >&2
+ { (exit 1); exit 1; }; }
+fi
+
+# Be sure to have absolute paths.
+for ac_var in exec_prefix prefix
+do
+ eval ac_val=$`echo $ac_var`
+ case $ac_val in
+ [\\/$]* | ?:[\\/]* | NONE | '' ) ;;
+ *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+ { (exit 1); exit 1; }; };;
+ esac
+done
+
+# Be sure to have absolute paths.
+for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \
+ localstatedir libdir includedir oldincludedir infodir mandir
+do
+ eval ac_val=$`echo $ac_var`
+ case $ac_val in
+ [\\/$]* | ?:[\\/]* ) ;;
+ *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+ { (exit 1); exit 1; }; };;
+ esac
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+ if test "x$build_alias" = x; then
+ cross_compiling=maybe
+ echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host.
+ If a cross compiler is detected then cross compile mode will be used." >&2
+ elif test "x$build_alias" != "x$host_alias"; then
+ cross_compiling=yes
+ fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+# 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 its parent.
+ ac_confdir=`(dirname "$0") 2>/dev/null ||
+$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$0" : 'X\(//\)[^/]' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X"$0" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+ /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+ /^X\(\/\/\)$/{ s//\1/; q; }
+ /^X\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+ srcdir=$ac_confdir
+ if test ! -r $srcdir/$ac_unique_file; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+ if test "$ac_srcdir_defaulted" = yes; then
+ { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2
+ { (exit 1); exit 1; }; }
+ else
+ { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2
+ { (exit 1); exit 1; }; }
+ fi
+fi
+(cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null ||
+ { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2
+ { (exit 1); exit 1; }; }
+srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'`
+ac_env_build_alias_set=${build_alias+set}
+ac_env_build_alias_value=$build_alias
+ac_cv_env_build_alias_set=${build_alias+set}
+ac_cv_env_build_alias_value=$build_alias
+ac_env_host_alias_set=${host_alias+set}
+ac_env_host_alias_value=$host_alias
+ac_cv_env_host_alias_set=${host_alias+set}
+ac_cv_env_host_alias_value=$host_alias
+ac_env_target_alias_set=${target_alias+set}
+ac_env_target_alias_value=$target_alias
+ac_cv_env_target_alias_set=${target_alias+set}
+ac_cv_env_target_alias_value=$target_alias
+ac_env_CC_set=${CC+set}
+ac_env_CC_value=$CC
+ac_cv_env_CC_set=${CC+set}
+ac_cv_env_CC_value=$CC
+ac_env_CFLAGS_set=${CFLAGS+set}
+ac_env_CFLAGS_value=$CFLAGS
+ac_cv_env_CFLAGS_set=${CFLAGS+set}
+ac_cv_env_CFLAGS_value=$CFLAGS
+ac_env_LDFLAGS_set=${LDFLAGS+set}
+ac_env_LDFLAGS_value=$LDFLAGS
+ac_cv_env_LDFLAGS_set=${LDFLAGS+set}
+ac_cv_env_LDFLAGS_value=$LDFLAGS
+ac_env_CPPFLAGS_set=${CPPFLAGS+set}
+ac_env_CPPFLAGS_value=$CPPFLAGS
+ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set}
+ac_cv_env_CPPFLAGS_value=$CPPFLAGS
+ac_env_CXX_set=${CXX+set}
+ac_env_CXX_value=$CXX
+ac_cv_env_CXX_set=${CXX+set}
+ac_cv_env_CXX_value=$CXX
+ac_env_CXXFLAGS_set=${CXXFLAGS+set}
+ac_env_CXXFLAGS_value=$CXXFLAGS
+ac_cv_env_CXXFLAGS_set=${CXXFLAGS+set}
+ac_cv_env_CXXFLAGS_value=$CXXFLAGS
+ac_env_CPP_set=${CPP+set}
+ac_env_CPP_value=$CPP
+ac_cv_env_CPP_set=${CPP+set}
+ac_cv_env_CPP_value=$CPP
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat <<_ACEOF
+\`configure' configures this package to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE. See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+ -h, --help display this help and exit
+ --help=short display options specific to this package
+ --help=recursive display the short help of all the included packages
+ -V, --version display version information and exit
+ -q, --quiet, --silent do not print \`checking...' messages
+ --cache-file=FILE cache test results in FILE [disabled]
+ -C, --config-cache alias for \`--cache-file=config.cache'
+ -n, --no-create do not create output files
+ --srcdir=DIR find the sources in DIR [configure dir or \`..']
+
+_ACEOF
+
+ cat <<_ACEOF
+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]
+ --datadir=DIR read-only architecture-independent data [PREFIX/share]
+ --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]
+ --infodir=DIR info documentation [PREFIX/info]
+ --mandir=DIR man documentation [PREFIX/man]
+_ACEOF
+
+ cat <<\_ACEOF
+
+X features:
+ --x-includes=DIR X include files are in DIR
+ --x-libraries=DIR X library files are in DIR
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+
+ cat <<\_ACEOF
+
+Optional Features:
+ --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
+ --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
+ --enable-a4-paper use A4 paper size instead of Letter for
+ PostScript output
+ --enable-no-text-select do not allow text selection
+ --enable-opi include support for OPI comments
+ --enable-multithreaded include support for multithreading
+ --enable-exceptions use C++ exceptions
+ --enable-wordlist include support for building word lists
+ --enable-fixedpoint use fixed point (instead of floating point) arithmetic
+ --enable-cmyk include support for CMYK rasterization
+ --disable-largefile omit support for large files
+
+Optional Packages:
+ --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
+ --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
+ --with-appdef-dir set app-defaults directory
+ --with-x use the X Window System
+ --with-Xpm-library=PATH use Xpm library (pixmap library - used only for icon)
+ --with-Xpm-includes=DIR set directory for Xpm headers
+ --with-Xext-library=PATH
+ use Xext library (Motif library)
+ --with-Xext-includes=DIR
+ set directory for Xext headers
+ --with-Xp-library=PATH use Xp library (Motif library)
+ --with-Xp-includes=DIR set directory for Xp headers
+ --with-Xt-library=PATH use Xt library (Motif library)
+ --with-Xt-includes=DIR set directory for Xt headers
+ --with-Xm-library=PATH use Xm library (Motif library)
+ --with-Xm-includes=DIR set directory for Xm headers
+ --with-Sgm-library=PATH use Sgm library (SGI Motif library)
+ --with-Sgm-includes=DIR set directory for Sgm headers
+ --with-t1-library=PATH use t1 library (Type 1 font rasterizer)
+ --with-t1-includes=DIR set directory for t1 headers
+ --with-freetype2-library=PATH
+ use freetype2 library (FreeType2 font rasterizer - version 2.0.5+)
+ --with-freetype2-includes=DIR
+ set directory for freetype2 headers
+ --with-libpaper-library=PATH
+ use libpaper library (Debian libpaper)
+ --with-libpaper-includes=DIR
+ set directory for libpaper headers
+
+Some influential environment variables:
+ CC C compiler command
+ CFLAGS C compiler flags
+ LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a
+ nonstandard directory <lib dir>
+ CPPFLAGS C/C++ preprocessor flags, e.g. -I<include dir> if you have
+ headers in a nonstandard directory <include dir>
+ CXX C++ compiler command
+ CXXFLAGS C++ compiler flags
+ 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.
+
+_ACEOF
+fi
+
+if test "$ac_init_help" = "recursive"; then
+ # If there are subdirs, report their specific --help.
+ ac_popdir=`pwd`
+ for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+ test -d $ac_dir || continue
+ ac_builddir=.
+
+if test "$ac_dir" != .; then
+ ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+ # A "../" for each directory in $ac_dir_suffix.
+ ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+ ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+ .) # No --srcdir option. We are building in place.
+ ac_srcdir=.
+ if test -z "$ac_top_builddir"; then
+ ac_top_srcdir=.
+ else
+ ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+ fi ;;
+ [\\/]* | ?:[\\/]* ) # Absolute path.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir ;;
+ *) # Relative path.
+ ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+ case "$ac_dir" in
+ .) ac_abs_builddir=`pwd`;;
+ [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+ *) ac_abs_builddir=`pwd`/"$ac_dir";;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+ case ${ac_top_builddir}. in
+ .) ac_abs_top_builddir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+ *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+ case $ac_srcdir in
+ .) ac_abs_srcdir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+ *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+ case $ac_top_srcdir in
+ .) ac_abs_top_srcdir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+ *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+ esac;;
+esac
+
+ cd $ac_dir
+ # Check for guested configure; otherwise get Cygnus style 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
+ elif test -f $ac_srcdir/configure.ac ||
+ test -f $ac_srcdir/configure.in; then
+ echo
+ $ac_configure --help
+ else
+ echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+ fi
+ cd $ac_popdir
+ done
+fi
+
+test -n "$ac_init_help" && exit 0
+if $ac_init_version; then
+ cat <<\_ACEOF
+
+Copyright (C) 2003 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 0
+fi
+exec 5>config.log
+cat >&5 <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by $as_me, which was
+generated by GNU Autoconf 2.59. Invocation command line was
+
+ $ $0 $@
+
+_ACEOF
+{
+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`
+hostinfo = `(hostinfo) 2>/dev/null || echo unknown`
+/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown`
+/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ echo "PATH: $as_dir"
+done
+
+} >&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_sep=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+ for ac_arg
+ do
+ case $ac_arg in
+ -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ continue ;;
+ *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
+ ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ case $ac_pass in
+ 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;;
+ 2)
+ ac_configure_args1="$ac_configure_args1 '$ac_arg'"
+ if test $ac_must_keep_next = true; then
+ ac_must_keep_next=false # Got value, back to normal.
+ else
+ case $ac_arg in
+ *=* | --config-cache | -C | -disable-* | --disable-* \
+ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+ | -with-* | --with-* | -without-* | --without-* | --x)
+ case "$ac_configure_args0 " in
+ "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+ esac
+ ;;
+ -* ) ac_must_keep_next=true ;;
+ esac
+ fi
+ ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'"
+ # Get rid of the leading space.
+ ac_sep=" "
+ ;;
+ esac
+ done
+done
+$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; }
+$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; }
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log. We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Be sure not to use single quotes in there, as some shells,
+# such as our DU 5.0 friend, will then `close' the trap.
+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,
+{
+ (set) 2>&1 |
+ case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in
+ *ac_space=\ *)
+ sed -n \
+ "s/'"'"'/'"'"'\\\\'"'"''"'"'/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p"
+ ;;
+ *)
+ sed -n \
+ "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
+ ;;
+ esac;
+}
+ echo
+
+ cat <<\_ASBOX
+## ----------------- ##
+## Output variables. ##
+## ----------------- ##
+_ASBOX
+ echo
+ for ac_var in $ac_subst_vars
+ do
+ eval ac_val=$`echo $ac_var`
+ echo "$ac_var='"'"'$ac_val'"'"'"
+ done | sort
+ echo
+
+ if test -n "$ac_subst_files"; then
+ cat <<\_ASBOX
+## ------------- ##
+## Output files. ##
+## ------------- ##
+_ASBOX
+ echo
+ for ac_var in $ac_subst_files
+ do
+ eval ac_val=$`echo $ac_var`
+ echo "$ac_var='"'"'$ac_val'"'"'"
+ done | sort
+ echo
+ fi
+
+ if test -s confdefs.h; then
+ cat <<\_ASBOX
+## ----------- ##
+## confdefs.h. ##
+## ----------- ##
+_ASBOX
+ echo
+ sed "/^$/d" confdefs.h | sort
+ echo
+ fi
+ test "$ac_signal" != 0 &&
+ echo "$as_me: caught signal $ac_signal"
+ echo "$as_me: exit $exit_status"
+ } >&5
+ rm -f core *.core &&
+ rm -rf conftest* confdefs* conf$$* $ac_clean_files &&
+ exit $exit_status
+ ' 0
+for ac_signal in 1 2 13 15; do
+ trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo >confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+ if test "x$prefix" != xNONE; then
+ CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+ else
+ CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+ fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+ if test -r "$ac_site_file"; then
+ { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5
+echo "$as_me: loading site script $ac_site_file" >&6;}
+ sed 's/^/| /' "$ac_site_file" >&5
+ . "$ac_site_file"
+ fi
+done
+
+if test -r "$cache_file"; then
+ # Some versions of bash will fail to source /dev/null (special
+ # files actually), so we avoid doing that.
+ if test -f "$cache_file"; then
+ { echo "$as_me:$LINENO: loading cache $cache_file" >&5
+echo "$as_me: loading cache $cache_file" >&6;}
+ case $cache_file in
+ [\\/]* | ?:[\\/]* ) . $cache_file;;
+ *) . ./$cache_file;;
+ esac
+ fi
+else
+ { echo "$as_me:$LINENO: creating cache $cache_file" >&5
+echo "$as_me: creating cache $cache_file" >&6;}
+ >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in `(set) 2>&1 |
+ sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do
+ eval ac_old_set=\$ac_cv_env_${ac_var}_set
+ eval ac_new_set=\$ac_env_${ac_var}_set
+ eval ac_old_val="\$ac_cv_env_${ac_var}_value"
+ eval ac_new_val="\$ac_env_${ac_var}_value"
+ case $ac_old_set,$ac_new_set in
+ set,)
+ { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,set)
+ { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,);;
+ *)
+ if test "x$ac_old_val" != "x$ac_new_val"; then
+ { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5
+echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+ { echo "$as_me:$LINENO: former value: $ac_old_val" >&5
+echo "$as_me: former value: $ac_old_val" >&2;}
+ { echo "$as_me:$LINENO: current value: $ac_new_val" >&5
+echo "$as_me: current value: $ac_new_val" >&2;}
+ ac_cache_corrupted=:
+ fi;;
+ esac
+ # Pass precious variables to config.status.
+ if test "$ac_new_set" = set; then
+ case $ac_new_val in
+ *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
+ ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+ *) ac_arg=$ac_var=$ac_new_val ;;
+ esac
+ case " $ac_configure_args " in
+ *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy.
+ *) ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+ esac
+ fi
+done
+if $ac_cache_corrupted; then
+ { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5
+echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+ { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5
+echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ac_config_headers="$ac_config_headers aconf.h"
+
+
+# Check whether --enable-a4-paper or --disable-a4-paper was given.
+if test "${enable_a4_paper+set}" = set; then
+ enableval="$enable_a4_paper"
+ cat >>confdefs.h <<\_ACEOF
+#define A4_PAPER 1
+_ACEOF
+
+fi;
+# Check whether --enable-no-text-select or --disable-no-text-select was given.
+if test "${enable_no_text_select+set}" = set; then
+ enableval="$enable_no_text_select"
+ cat >>confdefs.h <<\_ACEOF
+#define NO_TEXT_SELECT 1
+_ACEOF
+
+fi;
+# Check whether --enable-opi or --disable-opi was given.
+if test "${enable_opi+set}" = set; then
+ enableval="$enable_opi"
+ cat >>confdefs.h <<\_ACEOF
+#define OPI_SUPPORT 1
+_ACEOF
+
+fi;
+# Check whether --enable-multithreaded or --disable-multithreaded was given.
+if test "${enable_multithreaded+set}" = set; then
+ enableval="$enable_multithreaded"
+ cat >>confdefs.h <<\_ACEOF
+#define MULTITHREADED 1
+_ACEOF
+
+fi;
+# Check whether --enable-exceptions or --disable-exceptions was given.
+if test "${enable_exceptions+set}" = set; then
+ enableval="$enable_exceptions"
+ cat >>confdefs.h <<\_ACEOF
+#define USE_EXCEPTIONS 1
+_ACEOF
+
+fi;
+# Check whether --enable-wordlist or --disable-wordlist was given.
+if test "${enable_wordlist+set}" = set; then
+ enableval="$enable_wordlist"
+ cat >>confdefs.h <<\_ACEOF
+#define TEXTOUT_WORD_LIST 1
+_ACEOF
+
+fi;
+# Check whether --enable-fixedpoint or --disable-fixedpoint was given.
+if test "${enable_fixedpoint+set}" = set; then
+ enableval="$enable_fixedpoint"
+ cat >>confdefs.h <<\_ACEOF
+#define USE_FIXEDPOINT 1
+_ACEOF
+
+fi;
+# Check whether --enable-cmyk or --disable-cmyk was given.
+if test "${enable_cmyk+set}" = set; then
+ enableval="$enable_cmyk"
+ cat >>confdefs.h <<\_ACEOF
+#define SPLASH_CMYK 1
+_ACEOF
+
+fi;
+
+# Check whether --with-appdef-dir or --without-appdef-dir was given.
+if test "${with_appdef_dir+set}" = set; then
+ withval="$with_appdef_dir"
+ cat >>confdefs.h <<_ACEOF
+#define APPDEFDIR "$with_appdef_dir"
+_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
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="${ac_tool_prefix}gcc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+ ac_ct_CC=$CC
+ # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CC="gcc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ CC=$ac_ct_CC
+else
+ CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="${ac_tool_prefix}cc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+ ac_ct_CC=$CC
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CC="cc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ CC=$ac_ct_CC
+else
+ CC="$ac_cv_prog_CC"
+fi
+
+fi
+if test -z "$CC"; then
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+ ac_prog_rejected=yes
+ continue
+ fi
+ ac_cv_prog_CC="cc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+if test $ac_prog_rejected = yes; then
+ # We found a bogon in the path, so make sure we never use it.
+ set dummy $ac_cv_prog_CC
+ shift
+ if test $# != 0; then
+ # We chose a different compiler from the bogus one.
+ # However, it has the same basename, so the bogon will be chosen
+ # first if we set CC to just the basename; use the full file name.
+ shift
+ ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+ fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ for ac_prog in cl
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ test -n "$CC" && break
+ done
+fi
+if test -z "$CC"; then
+ ac_ct_CC=$CC
+ for ac_prog in cl
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CC="$ac_prog"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ test -n "$ac_ct_CC" && break
+done
+
+ CC=$ac_ct_CC
+fi
+
+fi
+
+
+test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&5
+echo "$as_me: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+
+# Provide some information about the compiler.
+echo "$as_me:$LINENO:" \
+ "checking for C compiler version" >&5
+ac_compiler=`set X $ac_compile; echo $2`
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5
+ (eval $ac_compiler --version </dev/null >&5) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v </dev/null >&5\"") >&5
+ (eval $ac_compiler -v </dev/null >&5) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V </dev/null >&5\"") >&5
+ (eval $ac_compiler -V </dev/null >&5) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+echo "$as_me:$LINENO: checking for C compiler default output file name" >&5
+echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6
+ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5
+ (eval $ac_link_default) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ # Find the output, starting from the most likely. This scheme is
+# not robust to junk in `.', hence go to wildcards (a.*) only as a last
+# resort.
+
+# Be careful to initialize this variable, since it used to be cached.
+# Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile.
+ac_cv_exeext=
+# b.out is created by i960 compilers.
+for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out
+do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj )
+ ;;
+ conftest.$ac_ext )
+ # This is the source file.
+ ;;
+ [ab].out )
+ # We found the default executable, but exeext='' is most
+ # certainly right.
+ break;;
+ *.* )
+ ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ # FIXME: I believe we export ac_cv_exeext for Libtool,
+ # but it would be cool to find out if it's true. Does anybody
+ # maintain Libtool? --akim.
+ export ac_cv_exeext
+ break;;
+ * )
+ break;;
+ esac
+done
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: C compiler cannot create executables
+See \`config.log' for more details." >&5
+echo "$as_me: error: C compiler cannot create executables
+See \`config.log' for more details." >&2;}
+ { (exit 77); exit 77; }; }
+fi
+
+ac_exeext=$ac_cv_exeext
+echo "$as_me:$LINENO: result: $ac_file" >&5
+echo "${ECHO_T}$ac_file" >&6
+
+# Check the compiler produces executables we can run. If not, either
+# the compiler is broken, or we cross compile.
+echo "$as_me:$LINENO: checking whether the C compiler works" >&5
+echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6
+# FIXME: These cross compiler hacks should be removed for Autoconf 3.0
+# If not cross compiling, check that we can run a simple program.
+if test "$cross_compiling" != yes; then
+ if { ac_try='./$ac_file'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ cross_compiling=no
+ else
+ if test "$cross_compiling" = maybe; then
+ cross_compiling=yes
+ else
+ { { echo "$as_me:$LINENO: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ fi
+fi
+echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+
+rm -f a.out a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+# Check the compiler produces executables we can run. If not, either
+# the compiler is broken, or we cross compile.
+echo "$as_me:$LINENO: checking whether we are cross compiling" >&5
+echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6
+echo "$as_me:$LINENO: result: $cross_compiling" >&5
+echo "${ECHO_T}$cross_compiling" >&6
+
+echo "$as_me:$LINENO: checking for suffix of executables" >&5
+echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;;
+ *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ export ac_cv_exeext
+ break;;
+ * ) break;;
+ esac
+done
+else
+ { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest$ac_cv_exeext
+echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5
+echo "${ECHO_T}$ac_cv_exeext" >&6
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+echo "$as_me:$LINENO: checking for suffix of object files" >&5
+echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6
+if test "${ac_cv_objext+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;;
+ *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+ break;;
+ esac
+done
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_objext" >&5
+echo "${ECHO_T}$ac_cv_objext" >&6
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5
+echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6
+if test "${ac_cv_c_compiler_gnu+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+#ifndef __GNUC__
+ choke me
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_compiler_gnu=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_compiler_gnu=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5
+echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6
+GCC=`test $ac_compiler_gnu = yes && echo yes`
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+CFLAGS="-g"
+echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5
+echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6
+if test "${ac_cv_prog_cc_g+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_prog_cc_g=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_prog_cc_g=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_g" >&6
+if test "$ac_test_CFLAGS" = set; then
+ CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+ if test "$GCC" = yes; then
+ CFLAGS="-g -O2"
+ else
+ CFLAGS="-g"
+ fi
+else
+ if test "$GCC" = yes; then
+ CFLAGS="-O2"
+ else
+ CFLAGS=
+ fi
+fi
+echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5
+echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6
+if test "${ac_cv_prog_cc_stdc+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_prog_cc_stdc=no
+ac_save_CC=$CC
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+ char **p;
+ int i;
+{
+ return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+ char *s;
+ va_list v;
+ va_start (v,p);
+ s = g (p, va_arg (v,int));
+ va_end (v);
+ return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
+ function prototypes and stuff, but not '\xHH' hex character constants.
+ These don't provoke an error unfortunately, instead are silently treated
+ as 'x'. The following induces an error, until -std1 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 -std1. */
+int osf4_cc_array ['\x00' == 0 ? 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
+# Don't try gcc -ansi; that turns off useful extensions and
+# breaks some systems' header files.
+# AIX -qlanglvl=ansi
+# Ultrix and OSF/1 -std1
+# HP-UX 10.20 and later -Ae
+# HP-UX older versions -Aa -D_HPUX_SOURCE
+# SVR4 -Xc -D__EXTENSIONS__
+for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+ CC="$ac_save_CC $ac_arg"
+ rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_prog_cc_stdc=$ac_arg
+break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext
+done
+rm -f conftest.$ac_ext conftest.$ac_objext
+CC=$ac_save_CC
+
+fi
+
+case "x$ac_cv_prog_cc_stdc" in
+ x|xno)
+ echo "$as_me:$LINENO: result: none needed" >&5
+echo "${ECHO_T}none needed" >&6 ;;
+ *)
+ echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6
+ CC="$CC $ac_cv_prog_cc_stdc" ;;
+esac
+
+# Some people use a C++ compiler to compile C. Since we use `exit',
+# in C++ we need to declare it. In case someone uses the same compiler
+# for both compiling C and C++ we need to have the C++ compiler decide
+# the declaration of exit, since it's the most demanding environment.
+cat >conftest.$ac_ext <<_ACEOF
+#ifndef __cplusplus
+ choke me
+#endif
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ for ac_declaration in \
+ '' \
+ 'extern "C" void std::exit (int) throw (); using std::exit;' \
+ 'extern "C" void std::exit (int); using std::exit;' \
+ 'extern "C" void exit (int) throw ();' \
+ 'extern "C" void exit (int);' \
+ 'void exit (int);'
+do
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_declaration
+#include <stdlib.h>
+int
+main ()
+{
+exit (42);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ :
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+continue
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_declaration
+int
+main ()
+{
+exit (42);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+done
+rm -f conftest*
+if test -n "$ac_declaration"; then
+ echo '#ifdef __cplusplus' >>confdefs.h
+ echo $ac_declaration >>confdefs.h
+ echo '#endif' >>confdefs.h
+fi
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+echo "$as_me:$LINENO: checking for library containing strerror" >&5
+echo $ECHO_N "checking for library containing strerror... $ECHO_C" >&6
+if test "${ac_cv_search_strerror+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+ac_cv_search_strerror=no
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char strerror ();
+int
+main ()
+{
+strerror ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_search_strerror="none required"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+if test "$ac_cv_search_strerror" = no; then
+ for ac_lib in cposix; do
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char strerror ();
+int
+main ()
+{
+strerror ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_search_strerror="-l$ac_lib"
+break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ done
+fi
+LIBS=$ac_func_search_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_search_strerror" >&5
+echo "${ECHO_T}$ac_cv_search_strerror" >&6
+if test "$ac_cv_search_strerror" != no; then
+ test "$ac_cv_search_strerror" = "none required" || LIBS="$ac_cv_search_strerror $LIBS"
+
+fi
+
+
+#if test -z "$CXX" -a "$CC" = "gcc"; then
+# CXX="gcc"
+#fi
+ac_ext=cc
+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 -n "$ac_tool_prefix"; then
+ for ac_prog in $CCC g++ c++ gpp aCC CC cxx cc++ cl 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
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CXX+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&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 $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CXX="$ac_tool_prefix$ac_prog"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+CXX=$ac_cv_prog_CXX
+if test -n "$CXX"; then
+ echo "$as_me:$LINENO: result: $CXX" >&5
+echo "${ECHO_T}$CXX" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ test -n "$CXX" && break
+ done
+fi
+if test -z "$CXX"; then
+ ac_ct_CXX=$CXX
+ for ac_prog in $CCC g++ c++ gpp aCC CC cxx cc++ cl 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
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CXX+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&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 $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CXX="$ac_prog"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+ac_ct_CXX=$ac_cv_prog_ac_ct_CXX
+if test -n "$ac_ct_CXX"; then
+ echo "$as_me:$LINENO: result: $ac_ct_CXX" >&5
+echo "${ECHO_T}$ac_ct_CXX" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ test -n "$ac_ct_CXX" && break
+done
+test -n "$ac_ct_CXX" || ac_ct_CXX="g++"
+
+ CXX=$ac_ct_CXX
+fi
+
+
+# Provide some information about the compiler.
+echo "$as_me:$LINENO:" \
+ "checking for C++ compiler version" >&5
+ac_compiler=`set X $ac_compile; echo $2`
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5
+ (eval $ac_compiler --version </dev/null >&5) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v </dev/null >&5\"") >&5
+ (eval $ac_compiler -v </dev/null >&5) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V </dev/null >&5\"") >&5
+ (eval $ac_compiler -V </dev/null >&5) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+
+echo "$as_me:$LINENO: checking whether we are using the GNU C++ compiler" >&5
+echo $ECHO_N "checking whether we are using the GNU C++ compiler... $ECHO_C" >&6
+if test "${ac_cv_cxx_compiler_gnu+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+#ifndef __GNUC__
+ choke me
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_compiler_gnu=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_compiler_gnu=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_cxx_compiler_gnu=$ac_compiler_gnu
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_cxx_compiler_gnu" >&5
+echo "${ECHO_T}$ac_cv_cxx_compiler_gnu" >&6
+GXX=`test $ac_compiler_gnu = yes && echo yes`
+ac_test_CXXFLAGS=${CXXFLAGS+set}
+ac_save_CXXFLAGS=$CXXFLAGS
+CXXFLAGS="-g"
+echo "$as_me:$LINENO: checking whether $CXX accepts -g" >&5
+echo $ECHO_N "checking whether $CXX accepts -g... $ECHO_C" >&6
+if test "${ac_cv_prog_cxx_g+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_prog_cxx_g=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_prog_cxx_g=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_prog_cxx_g" >&5
+echo "${ECHO_T}$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
+for ac_declaration in \
+ '' \
+ 'extern "C" void std::exit (int) throw (); using std::exit;' \
+ 'extern "C" void std::exit (int); using std::exit;' \
+ 'extern "C" void exit (int) throw ();' \
+ 'extern "C" void exit (int);' \
+ 'void exit (int);'
+do
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_declaration
+#include <stdlib.h>
+int
+main ()
+{
+exit (42);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ :
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+continue
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_declaration
+int
+main ()
+{
+exit (42);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+done
+rm -f conftest*
+if test -n "$ac_declaration"; then
+ echo '#ifdef __cplusplus' >>confdefs.h
+ echo $ac_declaration >>confdefs.h
+ echo '#endif' >>confdefs.h
+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_aux_dir=
+for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do
+ if test -f $ac_dir/install-sh; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install-sh -c"
+ break
+ elif test -f $ac_dir/install.sh; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install.sh -c"
+ break
+ elif test -f $ac_dir/shtool; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/shtool install -c"
+ break
+ fi
+done
+if test -z "$ac_aux_dir"; then
+ { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&5
+echo "$as_me: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+ac_config_guess="$SHELL $ac_aux_dir/config.guess"
+ac_config_sub="$SHELL $ac_aux_dir/config.sub"
+ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure.
+
+# Find a good install program. We prefer a C program (faster),
+# so one script is as good as another. But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# OS/2's system install, which has a completely different semantic
+# ./install, which can be erroneously created by make from ./install.sh.
+echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5
+echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6
+if test -z "$INSTALL"; then
+if test "${ac_cv_path_install+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ # Account for people who put trailing slashes in PATH elements.
+case $as_dir/ in
+ ./ | .// | /cC/* | \
+ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+ ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \
+ /usr/ucb/* ) ;;
+ *)
+ # OSF1 and SCO ODT 3.0 have their own names for install.
+ # Don't use installbsd from OSF since it installs stuff as root
+ # by default.
+ for ac_prog in ginstall scoinst install; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then
+ if test $ac_prog = install &&
+ grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # AIX install. It has an incompatible calling convention.
+ :
+ elif test $ac_prog = install &&
+ grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # program-specific install script used by HP pwplus--don't use.
+ :
+ else
+ ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+ break 3
+ fi
+ fi
+ done
+ done
+ ;;
+esac
+done
+
+
+fi
+ if test "${ac_cv_path_install+set}" = set; then
+ INSTALL=$ac_cv_path_install
+ else
+ # As a last resort, use the slow shell script. We don't cache a
+ # path for INSTALL within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the path is relative.
+ INSTALL=$ac_install_sh
+ fi
+fi
+echo "$as_me:$LINENO: result: $INSTALL" >&5
+echo "${ECHO_T}$INSTALL" >&6
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+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
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_RANLIB+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&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 $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+RANLIB=$ac_cv_prog_RANLIB
+if test -n "$RANLIB"; then
+ echo "$as_me:$LINENO: result: $RANLIB" >&5
+echo "${ECHO_T}$RANLIB" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}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
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&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 $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_RANLIB="ranlib"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+ test -z "$ac_cv_prog_ac_ct_RANLIB" && ac_cv_prog_ac_ct_RANLIB=":"
+fi
+fi
+ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
+if test -n "$ac_ct_RANLIB"; then
+ echo "$as_me:$LINENO: result: $ac_ct_RANLIB" >&5
+echo "${ECHO_T}$ac_ct_RANLIB" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ RANLIB=$ac_ct_RANLIB
+else
+ RANLIB="$ac_cv_prog_RANLIB"
+fi
+
+
+EXE=""
+LIBPREFIX="lib"
+AR="ar rc"
+UP_DIR=""
+
+echo "$as_me:$LINENO: checking for OS/2 (with EMX)" >&5
+echo $ECHO_N "checking for OS/2 (with EMX)... $ECHO_C" >&6
+if test "${xpdf_cv_sys_os2+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+__EMX__
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ xpdf_cv_sys_os2=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+xpdf_cv_sys_os2=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $xpdf_cv_sys_os2" >&5
+echo "${ECHO_T}$xpdf_cv_sys_os2" >&6
+if test "$xpdf_cv_sys_os2" = yes; then
+ EXE=".exe"
+ LIBPREFIX=""
+ AR="ar -rc"
+fi
+
+echo "$as_me:$LINENO: checking for DOS (with DJGPP)" >&5
+echo $ECHO_N "checking for DOS (with DJGPP)... $ECHO_C" >&6
+if test "${xpdf_cv_sys_dos+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+__DJGPP__
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ xpdf_cv_sys_dos=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+xpdf_cv_sys_dos=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $xpdf_cv_sys_dos" >&5
+echo "${ECHO_T}$xpdf_cv_sys_dos" >&6
+if test "$xpdf_cv_sys_dos" = yes; then
+ EXE=".exe"
+ LIBPREFIX="lib"
+ AR="ar -rc"
+ UP_DIR="../"
+fi
+
+
+
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5
+echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+ CPP=
+fi
+if test -z "$CPP"; then
+ if test "${ac_cv_prog_CPP+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ # Double quotes because CPP needs to be expanded
+ for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+ do
+ ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+ (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null; then
+ if test -s conftest.err; then
+ ac_cpp_err=$ac_c_preproc_warn_flag
+ ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+ else
+ ac_cpp_err=
+ fi
+else
+ ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+ :
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether non-existent headers
+ # can be detected and how.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+ (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null; then
+ if test -s conftest.err; then
+ ac_cpp_err=$ac_c_preproc_warn_flag
+ ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+ else
+ ac_cpp_err=
+ fi
+else
+ ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+ # Broken: success on invalid input.
+continue
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+ break
+fi
+
+ done
+ ac_cv_prog_CPP=$CPP
+
+fi
+ CPP=$ac_cv_prog_CPP
+else
+ ac_cv_prog_CPP=$CPP
+fi
+echo "$as_me:$LINENO: result: $CPP" >&5
+echo "${ECHO_T}$CPP" >&6
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+ (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null; then
+ if test -s conftest.err; then
+ ac_cpp_err=$ac_c_preproc_warn_flag
+ ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+ else
+ ac_cpp_err=
+ fi
+else
+ ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+ :
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether non-existent headers
+ # can be detected and how.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+ (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null; then
+ if test -s conftest.err; then
+ ac_cpp_err=$ac_c_preproc_warn_flag
+ ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+ else
+ ac_cpp_err=
+ fi
+else
+ ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+ # Broken: success on invalid input.
+continue
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+ :
+else
+ { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&5
+echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+echo "$as_me:$LINENO: checking for X" >&5
+echo $ECHO_N "checking for X... $ECHO_C" >&6
+
+
+# Check whether --with-x or --without-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
+ if test "x$x_includes" != xNONE && test "x$x_libraries" != xNONE; then
+ # Both variables are already set.
+ have_x=yes
+ else
+ if test "${ac_cv_have_x+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ # One or both of the vars are not set, and there is no cached value.
+ac_x_includes=no ac_x_libraries=no
+rm -fr conftest.dir
+if mkdir conftest.dir; then
+ cd conftest.dir
+ # Make sure to not put "make" in the Imakefile rules, since we grep it out.
+ cat >Imakefile <<'_ACEOF'
+acfindx:
+ @echo 'ac_im_incroot="${INCROOT}"; ac_im_usrlibdir="${USRLIBDIR}"; ac_im_libdir="${LIBDIR}"'
+_ACEOF
+ if (xmkmf) >/dev/null 2>/dev/null && test -f Makefile; then
+ # GNU make sometimes prints "make[1]: Entering...", which would confuse us.
+ eval `${MAKE-make} acfindx 2>/dev/null | grep -v make`
+ # Open Windows xmkmf reportedly sets LIBDIR instead of USRLIBDIR.
+ for ac_extension in a so sl; do
+ if test ! -f $ac_im_usrlibdir/libX11.$ac_extension &&
+ test -f $ac_im_libdir/libX11.$ac_extension; then
+ ac_im_usrlibdir=$ac_im_libdir; break
+ fi
+ done
+ # Screen out bogus values from the imake configuration. They are
+ # bogus both because they are the default anyway, and because
+ # using them would break gcc on systems where it needs fixed includes.
+ case $ac_im_incroot in
+ /usr/include) ;;
+ *) test -f "$ac_im_incroot/X11/Xos.h" && ac_x_includes=$ac_im_incroot;;
+ esac
+ case $ac_im_usrlibdir in
+ /usr/lib | /lib) ;;
+ *) test -d "$ac_im_usrlibdir" && ac_x_libraries=$ac_im_usrlibdir ;;
+ esac
+ fi
+ cd ..
+ rm -fr conftest.dir
+fi
+
+# Standard set of common directories for X headers.
+# Check X11 before X11Rn because it is often a symlink to the current release.
+ac_x_header_dirs='
+/usr/X11/include
+/usr/X11R6/include
+/usr/X11R5/include
+/usr/X11R4/include
+
+/usr/include/X11
+/usr/include/X11R6
+/usr/include/X11R5
+/usr/include/X11R4
+
+/usr/local/X11/include
+/usr/local/X11R6/include
+/usr/local/X11R5/include
+/usr/local/X11R4/include
+
+/usr/local/include/X11
+/usr/local/include/X11R6
+/usr/local/include/X11R5
+/usr/local/include/X11R4
+
+/usr/X386/include
+/usr/x386/include
+/usr/XFree86/include/X11
+
+/usr/include
+/usr/local/include
+/usr/unsupported/include
+/usr/athena/include
+/usr/local/x11r5/include
+/usr/lpp/Xamples/include
+
+/usr/openwin/include
+/usr/openwin/share/include'
+
+if test "$ac_x_includes" = no; then
+ # Guess where to find include files, by looking for Intrinsic.h.
+ # First, try using that file with no special directory specified.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <X11/Intrinsic.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+ (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null; then
+ if test -s conftest.err; then
+ ac_cpp_err=$ac_c_preproc_warn_flag
+ ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+ else
+ ac_cpp_err=
+ fi
+else
+ ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+ # We can compile using X headers with no special include directory.
+ac_x_includes=
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ for ac_dir in $ac_x_header_dirs; do
+ if test -r "$ac_dir/X11/Intrinsic.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="-lXt $LIBS"
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <X11/Intrinsic.h>
+int
+main ()
+{
+XtMalloc (0)
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ LIBS=$ac_save_LIBS
+# We can link X programs with no special library path.
+ac_x_libraries=
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+LIBS=$ac_save_LIBS
+for ac_dir in `echo "$ac_x_includes $ac_x_header_dirs" | sed s/include/lib/g`
+do
+ # Don't even attempt the hair of trying to link an X program!
+ for ac_extension in a so sl; do
+ if test -r $ac_dir/libXt.$ac_extension; then
+ ac_x_libraries=$ac_dir
+ break 2
+ fi
+ done
+done
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+fi # $ac_x_libraries = no
+
+if test "$ac_x_includes" = no || test "$ac_x_libraries" = no; then
+ # Didn't find X anywhere. Cache the known absence of X.
+ ac_cv_have_x="have_x=no"
+else
+ # 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"
+fi
+fi
+
+ fi
+ eval "$ac_cv_have_x"
+fi # $with_x != no
+
+if test "$have_x" != yes; then
+ echo "$as_me:$LINENO: result: $have_x" >&5
+echo "${ECHO_T}$have_x" >&6
+ no_x=yes
+else
+ # If each of the values was on the command line, it overrides each guess.
+ test "x$x_includes" = xNONE && x_includes=$ac_x_includes
+ test "x$x_libraries" = xNONE && x_libraries=$ac_x_libraries
+ # Update the cache value to reflect the command line values.
+ ac_cv_have_x="have_x=yes \
+ ac_x_includes=$x_includes ac_x_libraries=$x_libraries"
+ echo "$as_me:$LINENO: result: libraries $x_libraries, headers $x_includes" >&5
+echo "${ECHO_T}libraries $x_libraries, headers $x_includes" >&6
+fi
+
+if test "$no_x" = yes; then
+ # Not all programs may use this symbol, but it does not hurt to define it.
+
+cat >>confdefs.h <<\_ACEOF
+#define X_DISPLAY_MISSING 1
+_ACEOF
+
+ X_CFLAGS= X_PRE_LIBS= X_LIBS= X_EXTRA_LIBS=
+else
+ if test -n "$x_includes"; then
+ X_CFLAGS="$X_CFLAGS -I$x_includes"
+ fi
+
+ # It would also be nice to do this for all -L options, not just this one.
+ if test -n "$x_libraries"; then
+ X_LIBS="$X_LIBS -L$x_libraries"
+ # For Solaris; some versions of Sun CC require a space after -R and
+ # others require no space. Words are not sufficient . . . .
+ case `(uname -sr) 2>/dev/null` in
+ "SunOS 5"*)
+ echo "$as_me:$LINENO: checking whether -R must be followed by a space" >&5
+echo $ECHO_N "checking whether -R must be followed by a space... $ECHO_C" >&6
+ ac_xsave_LIBS=$LIBS; LIBS="$LIBS -R$x_libraries"
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_R_nospace=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_R_nospace=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ if test $ac_R_nospace = yes; then
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+ X_LIBS="$X_LIBS -R$x_libraries"
+ else
+ LIBS="$ac_xsave_LIBS -R $x_libraries"
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_R_space=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_R_space=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ if test $ac_R_space = yes; then
+ echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+ X_LIBS="$X_LIBS -R $x_libraries"
+ else
+ echo "$as_me:$LINENO: result: neither works" >&5
+echo "${ECHO_T}neither works" >&6
+ fi
+ fi
+ LIBS=$ac_xsave_LIBS
+ esac
+ fi
+
+ # Check for system-dependent libraries X programs must link with.
+ # Do this before checking for the system-independent R6 libraries
+ # (-lICE), since we may need -lsocket or whatever for X linking.
+
+ if test "$ISC" = yes; then
+ X_EXTRA_LIBS="$X_EXTRA_LIBS -lnsl_s -linet"
+ else
+ # Martyn Johnson says this is needed for Ultrix, if the X
+ # libraries were built with DECnet support. And Karl Berry says
+ # the Alpha needs dnet_stub (dnet does not exist).
+ ac_xsave_LIBS="$LIBS"; LIBS="$LIBS $X_LIBS -lX11"
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char XOpenDisplay ();
+int
+main ()
+{
+XOpenDisplay ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ :
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+echo "$as_me:$LINENO: checking for dnet_ntoa in -ldnet" >&5
+echo $ECHO_N "checking for dnet_ntoa in -ldnet... $ECHO_C" >&6
+if test "${ac_cv_lib_dnet_dnet_ntoa+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldnet $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char dnet_ntoa ();
+int
+main ()
+{
+dnet_ntoa ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_lib_dnet_dnet_ntoa=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_dnet_dnet_ntoa=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_dnet_dnet_ntoa" >&5
+echo "${ECHO_T}$ac_cv_lib_dnet_dnet_ntoa" >&6
+if test $ac_cv_lib_dnet_dnet_ntoa = yes; then
+ X_EXTRA_LIBS="$X_EXTRA_LIBS -ldnet"
+fi
+
+ if test $ac_cv_lib_dnet_dnet_ntoa = no; then
+ echo "$as_me:$LINENO: checking for dnet_ntoa in -ldnet_stub" >&5
+echo $ECHO_N "checking for dnet_ntoa in -ldnet_stub... $ECHO_C" >&6
+if test "${ac_cv_lib_dnet_stub_dnet_ntoa+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldnet_stub $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char dnet_ntoa ();
+int
+main ()
+{
+dnet_ntoa ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_lib_dnet_stub_dnet_ntoa=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_dnet_stub_dnet_ntoa=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_dnet_stub_dnet_ntoa" >&5
+echo "${ECHO_T}$ac_cv_lib_dnet_stub_dnet_ntoa" >&6
+if test $ac_cv_lib_dnet_stub_dnet_ntoa = yes; then
+ X_EXTRA_LIBS="$X_EXTRA_LIBS -ldnet_stub"
+fi
+
+ fi
+fi
+rm -f 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.
+ echo "$as_me:$LINENO: checking for gethostbyname" >&5
+echo $ECHO_N "checking for gethostbyname... $ECHO_C" >&6
+if test "${ac_cv_func_gethostbyname+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define gethostbyname to an innocuous variant, in case <limits.h> declares gethostbyname.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define gethostbyname innocuous_gethostbyname
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char gethostbyname (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef gethostbyname
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char gethostbyname ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_gethostbyname) || defined (__stub___gethostbyname)
+choke me
+#else
+char (*f) () = gethostbyname;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != gethostbyname;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_func_gethostbyname=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_func_gethostbyname=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_func_gethostbyname" >&5
+echo "${ECHO_T}$ac_cv_func_gethostbyname" >&6
+
+ if test $ac_cv_func_gethostbyname = no; then
+ echo "$as_me:$LINENO: checking for gethostbyname in -lnsl" >&5
+echo $ECHO_N "checking for gethostbyname in -lnsl... $ECHO_C" >&6
+if test "${ac_cv_lib_nsl_gethostbyname+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lnsl $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char gethostbyname ();
+int
+main ()
+{
+gethostbyname ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_lib_nsl_gethostbyname=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_nsl_gethostbyname=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_nsl_gethostbyname" >&5
+echo "${ECHO_T}$ac_cv_lib_nsl_gethostbyname" >&6
+if test $ac_cv_lib_nsl_gethostbyname = yes; then
+ X_EXTRA_LIBS="$X_EXTRA_LIBS -lnsl"
+fi
+
+ if test $ac_cv_lib_nsl_gethostbyname = no; then
+ echo "$as_me:$LINENO: checking for gethostbyname in -lbsd" >&5
+echo $ECHO_N "checking for gethostbyname in -lbsd... $ECHO_C" >&6
+if test "${ac_cv_lib_bsd_gethostbyname+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lbsd $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char gethostbyname ();
+int
+main ()
+{
+gethostbyname ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_lib_bsd_gethostbyname=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_bsd_gethostbyname=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_bsd_gethostbyname" >&5
+echo "${ECHO_T}$ac_cv_lib_bsd_gethostbyname" >&6
+if test $ac_cv_lib_bsd_gethostbyname = yes; then
+ X_EXTRA_LIBS="$X_EXTRA_LIBS -lbsd"
+fi
+
+ fi
+ fi
+
+ # lieder@skyler.mavd.honeywell.com says without -lsocket,
+ # socket/setsockopt and other routines are undefined under SCO ODT
+ # 2.0. But -lsocket is broken on IRIX 5.2 (and is not necessary
+ # on later versions), says Simon Leinen: it contains gethostby*
+ # variants that don't use the name server (or something). -lsocket
+ # must be given before -lnsl if both are needed. We assume that
+ # if connect needs -lnsl, so does gethostbyname.
+ echo "$as_me:$LINENO: checking for connect" >&5
+echo $ECHO_N "checking for connect... $ECHO_C" >&6
+if test "${ac_cv_func_connect+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define connect to an innocuous variant, in case <limits.h> declares connect.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define connect innocuous_connect
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char connect (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef connect
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char connect ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_connect) || defined (__stub___connect)
+choke me
+#else
+char (*f) () = connect;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != connect;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_func_connect=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_func_connect=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_func_connect" >&5
+echo "${ECHO_T}$ac_cv_func_connect" >&6
+
+ if test $ac_cv_func_connect = no; then
+ echo "$as_me:$LINENO: checking for connect in -lsocket" >&5
+echo $ECHO_N "checking for connect in -lsocket... $ECHO_C" >&6
+if test "${ac_cv_lib_socket_connect+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lsocket $X_EXTRA_LIBS $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char connect ();
+int
+main ()
+{
+connect ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_lib_socket_connect=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_socket_connect=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_socket_connect" >&5
+echo "${ECHO_T}$ac_cv_lib_socket_connect" >&6
+if test $ac_cv_lib_socket_connect = yes; then
+ X_EXTRA_LIBS="-lsocket $X_EXTRA_LIBS"
+fi
+
+ fi
+
+ # Guillermo Gomez says -lposix is necessary on A/UX.
+ echo "$as_me:$LINENO: checking for remove" >&5
+echo $ECHO_N "checking for remove... $ECHO_C" >&6
+if test "${ac_cv_func_remove+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define remove to an innocuous variant, in case <limits.h> declares remove.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define remove innocuous_remove
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char remove (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef remove
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char remove ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_remove) || defined (__stub___remove)
+choke me
+#else
+char (*f) () = remove;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != remove;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_func_remove=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_func_remove=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_func_remove" >&5
+echo "${ECHO_T}$ac_cv_func_remove" >&6
+
+ if test $ac_cv_func_remove = no; then
+ echo "$as_me:$LINENO: checking for remove in -lposix" >&5
+echo $ECHO_N "checking for remove in -lposix... $ECHO_C" >&6
+if test "${ac_cv_lib_posix_remove+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lposix $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char remove ();
+int
+main ()
+{
+remove ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_lib_posix_remove=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_posix_remove=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_posix_remove" >&5
+echo "${ECHO_T}$ac_cv_lib_posix_remove" >&6
+if test $ac_cv_lib_posix_remove = yes; then
+ X_EXTRA_LIBS="$X_EXTRA_LIBS -lposix"
+fi
+
+ fi
+
+ # BSDI BSD/OS 2.1 needs -lipc for XOpenDisplay.
+ echo "$as_me:$LINENO: checking for shmat" >&5
+echo $ECHO_N "checking for shmat... $ECHO_C" >&6
+if test "${ac_cv_func_shmat+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define shmat to an innocuous variant, in case <limits.h> declares shmat.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define shmat innocuous_shmat
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char shmat (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef shmat
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char shmat ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_shmat) || defined (__stub___shmat)
+choke me
+#else
+char (*f) () = shmat;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != shmat;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_func_shmat=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_func_shmat=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_func_shmat" >&5
+echo "${ECHO_T}$ac_cv_func_shmat" >&6
+
+ if test $ac_cv_func_shmat = no; then
+ echo "$as_me:$LINENO: checking for shmat in -lipc" >&5
+echo $ECHO_N "checking for shmat in -lipc... $ECHO_C" >&6
+if test "${ac_cv_lib_ipc_shmat+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lipc $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char shmat ();
+int
+main ()
+{
+shmat ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_lib_ipc_shmat=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_ipc_shmat=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_ipc_shmat" >&5
+echo "${ECHO_T}$ac_cv_lib_ipc_shmat" >&6
+if test $ac_cv_lib_ipc_shmat = yes; then
+ X_EXTRA_LIBS="$X_EXTRA_LIBS -lipc"
+fi
+
+ fi
+ fi
+
+ # Check for libraries that X11R6 Xt/Xaw programs need.
+ ac_save_LDFLAGS=$LDFLAGS
+ test -n "$x_libraries" && LDFLAGS="$LDFLAGS -L$x_libraries"
+ # SM needs ICE to (dynamically) link under SunOS 4.x (so we have to
+ # check for ICE first), but we must link in the order -lSM -lICE or
+ # we get undefined symbols. So assume we have SM if we have ICE.
+ # These have to be linked with before -lX11, unlike the other
+ # libraries we check for below, so use a different variable.
+ # John Interrante, Karl Berry
+ echo "$as_me:$LINENO: checking for IceConnectionNumber in -lICE" >&5
+echo $ECHO_N "checking for IceConnectionNumber in -lICE... $ECHO_C" >&6
+if test "${ac_cv_lib_ICE_IceConnectionNumber+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lICE $X_EXTRA_LIBS $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char IceConnectionNumber ();
+int
+main ()
+{
+IceConnectionNumber ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_lib_ICE_IceConnectionNumber=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_ICE_IceConnectionNumber=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_ICE_IceConnectionNumber" >&5
+echo "${ECHO_T}$ac_cv_lib_ICE_IceConnectionNumber" >&6
+if test $ac_cv_lib_ICE_IceConnectionNumber = yes; then
+ X_PRE_LIBS="$X_PRE_LIBS -lSM -lICE"
+fi
+
+ LDFLAGS=$ac_save_LDFLAGS
+
+fi
+
+
+
+
+
+
+ac_header_dirent=no
+for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h; do
+ as_ac_Header=`echo "ac_cv_header_dirent_$ac_hdr" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_hdr that defines DIR" >&5
+echo $ECHO_N "checking for $ac_hdr that defines DIR... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <$ac_hdr>
+
+int
+main ()
+{
+if ((DIR *) 0)
+return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ eval "$as_ac_Header=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_Header=no"
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `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
+ echo "$as_me:$LINENO: checking for library containing opendir" >&5
+echo $ECHO_N "checking for library containing opendir... $ECHO_C" >&6
+if test "${ac_cv_search_opendir+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+ac_cv_search_opendir=no
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char opendir ();
+int
+main ()
+{
+opendir ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_search_opendir="none required"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+if test "$ac_cv_search_opendir" = no; then
+ for ac_lib in dir; do
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char opendir ();
+int
+main ()
+{
+opendir ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_search_opendir="-l$ac_lib"
+break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ done
+fi
+LIBS=$ac_func_search_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_search_opendir" >&5
+echo "${ECHO_T}$ac_cv_search_opendir" >&6
+if test "$ac_cv_search_opendir" != no; then
+ test "$ac_cv_search_opendir" = "none required" || LIBS="$ac_cv_search_opendir $LIBS"
+
+fi
+
+else
+ echo "$as_me:$LINENO: checking for library containing opendir" >&5
+echo $ECHO_N "checking for library containing opendir... $ECHO_C" >&6
+if test "${ac_cv_search_opendir+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+ac_cv_search_opendir=no
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char opendir ();
+int
+main ()
+{
+opendir ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_search_opendir="none required"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+if test "$ac_cv_search_opendir" = no; then
+ for ac_lib in x; do
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char opendir ();
+int
+main ()
+{
+opendir ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_search_opendir="-l$ac_lib"
+break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ done
+fi
+LIBS=$ac_func_search_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_search_opendir" >&5
+echo "${ECHO_T}$ac_cv_search_opendir" >&6
+if test "$ac_cv_search_opendir" != no; then
+ test "$ac_cv_search_opendir" = "none required" || LIBS="$ac_cv_search_opendir $LIBS"
+
+fi
+
+fi
+
+
+ac_ext=cc
+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
+
+
+
+echo "$as_me:$LINENO: checking for gethostbyname" >&5
+echo $ECHO_N "checking for gethostbyname... $ECHO_C" >&6
+if test "${ac_cv_func_gethostbyname+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define gethostbyname to an innocuous variant, in case <limits.h> declares gethostbyname.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define gethostbyname innocuous_gethostbyname
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char gethostbyname (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef gethostbyname
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char gethostbyname ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_gethostbyname) || defined (__stub___gethostbyname)
+choke me
+#else
+char (*f) () = gethostbyname;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != gethostbyname;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_func_gethostbyname=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_func_gethostbyname=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_func_gethostbyname" >&5
+echo "${ECHO_T}$ac_cv_func_gethostbyname" >&6
+
+if test $ac_cv_func_gethostbyname = no; then
+ echo "$as_me:$LINENO: checking for gethostbyname in -lbsd" >&5
+echo $ECHO_N "checking for gethostbyname in -lbsd... $ECHO_C" >&6
+if test "${ac_cv_lib_bsd_gethostbyname+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lbsd $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char gethostbyname ();
+int
+main ()
+{
+gethostbyname ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_lib_bsd_gethostbyname=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_bsd_gethostbyname=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_bsd_gethostbyname" >&5
+echo "${ECHO_T}$ac_cv_lib_bsd_gethostbyname" >&6
+if test $ac_cv_lib_bsd_gethostbyname = yes; then
+ X_EXTRA_LIBS="$X_EXTRA_LIBS -lbsd"
+fi
+
+fi
+
+echo "$as_me:$LINENO: checking select() and fd_set in sys/select.h and sys/bsdtypes.h" >&5
+echo $ECHO_N "checking select() and fd_set in sys/select.h and sys/bsdtypes.h... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <sys/types.h>
+int
+main ()
+{
+fd_set fds;
+select(0, NULL, NULL, NULL, NULL);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ xpdf_ok=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+xpdf_ok=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+if test $xpdf_ok = yes; then
+ echo "$as_me:$LINENO: result: not needed" >&5
+echo "${ECHO_T}not needed" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/select.h>
+int
+main ()
+{
+fd_set fds;
+select(0, NULL, NULL, NULL, NULL);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ xpdf_ok=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+xpdf_ok=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ if test $xpdf_ok = yes; then
+ cat >>confdefs.h <<\_ACEOF
+#define HAVE_SYS_SELECT_H 1
+_ACEOF
+
+ echo "$as_me:$LINENO: result: need sys/select.h" >&5
+echo "${ECHO_T}need sys/select.h" >&6
+ else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/bsdtypes.h>
+int
+main ()
+{
+fd_set fds;
+select(0, NULL, NULL, NULL, NULL);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ xpdf_ok=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+xpdf_ok=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ if test $xpdf_ok = yes; then
+ cat >>confdefs.h <<\_ACEOF
+#define HAVE_SYS_BSDTYPES_H 1
+_ACEOF
+
+ echo "$as_me:$LINENO: result: need sys/bsdtypes.h" >&5
+echo "${ECHO_T}need sys/bsdtypes.h" >&6
+ else
+ echo "$as_me:$LINENO: result: problem" >&5
+echo "${ECHO_T}problem" >&6
+ fi
+ fi
+fi
+
+echo "$as_me:$LINENO: checking FD_ZERO and strings.h or bstring.h" >&5
+echo $ECHO_N "checking FD_ZERO and strings.h or bstring.h... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+int
+main ()
+{
+fd_set fds; FD_ZERO(&fds);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ xpdf_ok=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+xpdf_ok=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+if test $xpdf_ok = yes; then
+ echo "$as_me:$LINENO: result: not needed" >&5
+echo "${ECHO_T}not needed" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <sys/types.h>
+#include <strings.h>
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+int
+main ()
+{
+fd_set fds; FD_ZERO(&fds);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ xpdf_ok=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+xpdf_ok=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ if test $xpdf_ok = yes; then
+ cat >>confdefs.h <<\_ACEOF
+#define HAVE_STRINGS_H 1
+_ACEOF
+
+ echo "$as_me:$LINENO: result: need strings.h" >&5
+echo "${ECHO_T}need strings.h" >&6
+ else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <sys/types.h>
+#include <bstring.h>
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+int
+main ()
+{
+fd_set fds; FD_ZERO(&fds);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ xpdf_ok=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+xpdf_ok=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ if test $xpdf_ok = yes; then
+ cat >>confdefs.h <<\_ACEOF
+#define HAVE_BSTRING_H 1
+_ACEOF
+
+ echo "$as_me:$LINENO: result: need bstring.h" >&5
+echo "${ECHO_T}need bstring.h" >&6
+ else
+ echo "$as_me:$LINENO: result: problem" >&5
+echo "${ECHO_T}problem" >&6
+ fi
+ fi
+fi
+
+
+for ac_func in rewinddir
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
+if eval "test \"\${$as_ac_var+set}\" = set"; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+char (*f) () = $ac_func;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != $ac_func;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ eval "$as_ac_var=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_var=no"
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+if test $ac_cv_func_rewinddir = no; then
+
+echo "$as_me:$LINENO: checking for rewinddir in -lcposix" >&5
+echo $ECHO_N "checking for rewinddir in -lcposix... $ECHO_C" >&6
+if test "${ac_cv_lib_cposix_rewinddir+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lcposix $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char rewinddir ();
+int
+main ()
+{
+rewinddir ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_lib_cposix_rewinddir=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_cposix_rewinddir=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_cposix_rewinddir" >&5
+echo "${ECHO_T}$ac_cv_lib_cposix_rewinddir" >&6
+if test $ac_cv_lib_cposix_rewinddir = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBCPOSIX 1
+_ACEOF
+
+ LIBS="-lcposix $LIBS"
+
+fi
+
+fi
+
+
+for ac_func in popen
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
+if eval "test \"\${$as_ac_var+set}\" = set"; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+char (*f) () = $ac_func;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != $ac_func;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ eval "$as_ac_var=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_var=no"
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+echo "$as_me:$LINENO: checking for mkstemp" >&5
+echo $ECHO_N "checking for mkstemp... $ECHO_C" >&6
+if test "${xpdf_cv_func_mkstemp+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <unistd.h>
+int
+main ()
+{
+mkstemp("foo");
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ xpdf_cv_func_mkstemp=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+xpdf_cv_func_mkstemp=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $xpdf_cv_func_mkstemp" >&5
+echo "${ECHO_T}$xpdf_cv_func_mkstemp" >&6
+if test "$xpdf_cv_func_mkstemp" = yes; then
+ cat >>confdefs.h <<\_ACEOF
+#define HAVE_MKSTEMP 1
+_ACEOF
+
+fi
+echo "$as_me:$LINENO: checking for mkstemps" >&5
+echo $ECHO_N "checking for mkstemps... $ECHO_C" >&6
+if test "${xpdf_cv_func_mkstemps+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <unistd.h>
+int
+main ()
+{
+mkstemps("foo", 0);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ xpdf_cv_func_mkstemps=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+xpdf_cv_func_mkstemps=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $xpdf_cv_func_mkstemps" >&5
+echo "${ECHO_T}$xpdf_cv_func_mkstemps" >&6
+if test "$xpdf_cv_func_mkstemps" = yes; then
+ cat >>confdefs.h <<\_ACEOF
+#define HAVE_MKSTEMPS 1
+_ACEOF
+
+fi
+
+echo "$as_me:$LINENO: checking whether select takes fd_set arguments" >&5
+echo $ECHO_N "checking whether select takes fd_set arguments... $ECHO_C" >&6
+if test "${xpdf_cv_func_select_arg+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+int
+main ()
+{
+fd_set fds;
+select(1, &fds, &fds, &fds, 0);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_cxx_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ xpdf_cv_func_select_arg=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+xpdf_cv_func_select_arg=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $xpdf_cv_func_select_arg" >&5
+echo "${ECHO_T}$xpdf_cv_func_select_arg" >&6
+if test "$xpdf_cv_func_select_arg" != yes; then
+ cat >>confdefs.h <<\_ACEOF
+#define SELECT_TAKES_INT 1
+_ACEOF
+
+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
+
+
+# Check whether --enable-largefile or --disable-largefile was given.
+if test "${enable_largefile+set}" = set; then
+ enableval="$enable_largefile"
+
+fi;
+if test "$enable_largefile" != no; then
+
+ echo "$as_me:$LINENO: checking for special C compiler options needed for large files" >&5
+echo $ECHO_N "checking for special C compiler options needed for large files... $ECHO_C" >&6
+if test "${ac_cv_sys_largefile_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_sys_largefile_CC=no
+ if test "$GCC" != yes; then
+ ac_save_CC=$CC
+ while :; do
+ # IRIX 6.2 and later do not support large files by default,
+ # so use the C compiler's -n32 option if that helps.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/types.h>
+ /* Check that off_t can represent 2**63 - 1 correctly.
+ We can't simply define LARGE_OFF_T to be 9223372036854775807,
+ since some C++ compilers masquerading as C compilers
+ incorrectly reject 9223372036854775807. */
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+ int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
+ && LARGE_OFF_T % 2147483647 == 1)
+ ? 1 : -1];
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+ rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext
+ CC="$CC -n32"
+ rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_sys_largefile_CC=' -n32'; break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext
+ break
+ done
+ CC=$ac_save_CC
+ rm -f conftest.$ac_ext
+ fi
+fi
+echo "$as_me:$LINENO: result: $ac_cv_sys_largefile_CC" >&5
+echo "${ECHO_T}$ac_cv_sys_largefile_CC" >&6
+ if test "$ac_cv_sys_largefile_CC" != no; then
+ CC=$CC$ac_cv_sys_largefile_CC
+ fi
+
+ echo "$as_me:$LINENO: checking for _FILE_OFFSET_BITS value needed for large files" >&5
+echo $ECHO_N "checking for _FILE_OFFSET_BITS value needed for large files... $ECHO_C" >&6
+if test "${ac_cv_sys_file_offset_bits+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ while :; do
+ ac_cv_sys_file_offset_bits=no
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/types.h>
+ /* Check that off_t can represent 2**63 - 1 correctly.
+ We can't simply define LARGE_OFF_T to be 9223372036854775807,
+ since some C++ compilers masquerading as C compilers
+ incorrectly reject 9223372036854775807. */
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+ int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
+ && LARGE_OFF_T % 2147483647 == 1)
+ ? 1 : -1];
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#define _FILE_OFFSET_BITS 64
+#include <sys/types.h>
+ /* Check that off_t can represent 2**63 - 1 correctly.
+ We can't simply define LARGE_OFF_T to be 9223372036854775807,
+ since some C++ compilers masquerading as C compilers
+ incorrectly reject 9223372036854775807. */
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+ int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
+ && LARGE_OFF_T % 2147483647 == 1)
+ ? 1 : -1];
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_sys_file_offset_bits=64; break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ break
+done
+fi
+echo "$as_me:$LINENO: result: $ac_cv_sys_file_offset_bits" >&5
+echo "${ECHO_T}$ac_cv_sys_file_offset_bits" >&6
+if test "$ac_cv_sys_file_offset_bits" != no; then
+
+cat >>confdefs.h <<_ACEOF
+#define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits
+_ACEOF
+
+fi
+rm -f conftest*
+ echo "$as_me:$LINENO: checking for _LARGE_FILES value needed for large files" >&5
+echo $ECHO_N "checking for _LARGE_FILES value needed for large files... $ECHO_C" >&6
+if test "${ac_cv_sys_large_files+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ while :; do
+ ac_cv_sys_large_files=no
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/types.h>
+ /* Check that off_t can represent 2**63 - 1 correctly.
+ We can't simply define LARGE_OFF_T to be 9223372036854775807,
+ since some C++ compilers masquerading as C compilers
+ incorrectly reject 9223372036854775807. */
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+ int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
+ && LARGE_OFF_T % 2147483647 == 1)
+ ? 1 : -1];
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#define _LARGE_FILES 1
+#include <sys/types.h>
+ /* Check that off_t can represent 2**63 - 1 correctly.
+ We can't simply define LARGE_OFF_T to be 9223372036854775807,
+ since some C++ compilers masquerading as C compilers
+ incorrectly reject 9223372036854775807. */
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+ int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
+ && LARGE_OFF_T % 2147483647 == 1)
+ ? 1 : -1];
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_sys_large_files=1; break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ break
+done
+fi
+echo "$as_me:$LINENO: result: $ac_cv_sys_large_files" >&5
+echo "${ECHO_T}$ac_cv_sys_large_files" >&6
+if test "$ac_cv_sys_large_files" != no; then
+
+cat >>confdefs.h <<_ACEOF
+#define _LARGE_FILES $ac_cv_sys_large_files
+_ACEOF
+
+fi
+rm -f conftest*
+fi
+
+echo "$as_me:$LINENO: checking for _LARGEFILE_SOURCE value needed for large files" >&5
+echo $ECHO_N "checking for _LARGEFILE_SOURCE value needed for large files... $ECHO_C" >&6
+if test "${ac_cv_sys_largefile_source+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ while :; do
+ ac_cv_sys_largefile_source=no
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdio.h>
+int
+main ()
+{
+return !fseeko;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#define _LARGEFILE_SOURCE 1
+#include <stdio.h>
+int
+main ()
+{
+return !fseeko;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_sys_largefile_source=1; break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ break
+done
+fi
+echo "$as_me:$LINENO: result: $ac_cv_sys_largefile_source" >&5
+echo "${ECHO_T}$ac_cv_sys_largefile_source" >&6
+if test "$ac_cv_sys_largefile_source" != no; then
+
+cat >>confdefs.h <<_ACEOF
+#define _LARGEFILE_SOURCE $ac_cv_sys_largefile_source
+_ACEOF
+
+fi
+rm -f 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.
+echo "$as_me:$LINENO: checking for fseeko" >&5
+echo $ECHO_N "checking for fseeko... $ECHO_C" >&6
+if test "${ac_cv_func_fseeko+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdio.h>
+int
+main ()
+{
+return fseeko && fseeko (stdin, 0, 0);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_func_fseeko=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_func_fseeko=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_func_fseeko" >&5
+echo "${ECHO_T}$ac_cv_func_fseeko" >&6
+if test $ac_cv_func_fseeko = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_FSEEKO 1
+_ACEOF
+
+fi
+
+
+for ac_func in fseek64
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
+if eval "test \"\${$as_ac_var+set}\" = set"; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+char (*f) () = $ac_func;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != $ac_func;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ eval "$as_ac_var=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_var=no"
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+ xpdf_cv_func_fseek64=yes
+else
+ xpdf_cv_func_fseek64=no
+fi
+done
+
+
+for ac_func in ftell64
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
+if eval "test \"\${$as_ac_var+set}\" = set"; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+char (*f) () = $ac_func;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != $ac_func;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ eval "$as_ac_var=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_var=no"
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+ xpdf_cv_func_ftell64=yes
+else
+ xpdf_cv_func_ftell64=no
+fi
+done
+
+if test "$xpdf_cv_func_fseek64" = yes -a "$xpdf_cv_func_ftell64" = yes; then
+ cat >>confdefs.h <<\_ACEOF
+#define HAVE_FSEEK64 1
+_ACEOF
+
+fi
+
+if test -z "$no_x"; then
+ echo "$as_me:$LINENO: checking for egrep" >&5
+echo $ECHO_N "checking for egrep... $ECHO_C" >&6
+if test "${ac_cv_prog_egrep+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if echo a | (grep -E '(a|b)') >/dev/null 2>&1
+ then ac_cv_prog_egrep='grep -E'
+ else ac_cv_prog_egrep='egrep'
+ fi
+fi
+echo "$as_me:$LINENO: result: $ac_cv_prog_egrep" >&5
+echo "${ECHO_T}$ac_cv_prog_egrep" >&6
+ EGREP=$ac_cv_prog_egrep
+
+
+echo "$as_me:$LINENO: checking for ANSI C header files" >&5
+echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6
+if test "${ac_cv_header_stdc+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_header_stdc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_header_stdc=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "memchr" >/dev/null 2>&1; then
+ :
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "free" >/dev/null 2>&1; then
+ :
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+ if test "$cross_compiling" = yes; then
+ :
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <ctype.h>
+#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))
+ exit(2);
+ exit (0);
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ :
+else
+ echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_header_stdc=no
+fi
+rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+fi
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5
+echo "${ECHO_T}$ac_cv_header_stdc" >&6
+if test $ac_cv_header_stdc = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define STDC_HEADERS 1
+_ACEOF
+
+fi
+
+# On IRIX 5.3, sys/types and inttypes.h are conflicting.
+
+
+
+
+
+
+
+
+
+for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
+ inttypes.h stdint.h unistd.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ eval "$as_ac_Header=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_Header=no"
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# Check whether --with-Xpm-library or --without-Xpm-library was given.
+if test "${with_Xpm_library+set}" = set; then
+ withval="$with_Xpm_library"
+ smr_cv_with_Xpm_library=$withval
+fi;
+
+ echo "$as_me:$LINENO: checking whether to use Xpm library" >&5
+echo $ECHO_N "checking whether to use Xpm library... $ECHO_C" >&6
+if test "${smr_cv_with_Xpm_library+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ smr_cv_with_Xpm_library=maybe
+fi
+echo "$as_me:$LINENO: result: $smr_cv_with_Xpm_library" >&5
+echo "${ECHO_T}$smr_cv_with_Xpm_library" >&6
+
+
+ case x"$smr_cv_with_Xpm_library" in
+ xyes | xmaybe)
+ Xpm_LIBS="-lXpm"
+ with_Xpm=$smr_cv_with_Xpm_library
+ ;;
+ xno)
+ Xpm_LIBS=
+ with_Xpm=no
+ ;;
+ *)
+ if test -f "$smr_cv_with_Xpm_library"; then
+ Xpm_LIBS=$smr_cv_with_Xpm_library
+ elif test -d "$smr_cv_with_Xpm_library"; then
+ Xpm_LIBS="-L$smr_cv_with_Xpm_library -lXpm"
+ else
+ { { echo "$as_me:$LINENO: error: argument must be boolean, file, or directory" >&5
+echo "$as_me: error: argument must be boolean, file, or directory" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ with_Xpm=yes
+ ;;
+ esac
+
+
+
+
+
+
+
+ if test ! x"$with_Xpm" = xno; then
+
+ # If we got this far, then the user didn't explicitly ask not to use
+ # the library.
+
+
+
+
+
+
+
+
+# Check whether --with-Xpm-includes or --without-Xpm-includes was given.
+if test "${with_Xpm_includes+set}" = set; then
+ withval="$with_Xpm_includes"
+ smr_cv_with_Xpm_includes=$withval
+fi;
+
+ echo "$as_me:$LINENO: checking where to find the Xpm header files" >&5
+echo $ECHO_N "checking where to find the Xpm header files... $ECHO_C" >&6
+if test "${smr_cv_with_Xpm_includes+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ smr_cv_with_Xpm_includes=
+fi
+echo "$as_me:$LINENO: result: $smr_cv_with_Xpm_includes" >&5
+echo "${ECHO_T}$smr_cv_with_Xpm_includes" >&6
+
+ if test ! x"$smr_cv_with_Xpm_includes" = x; then
+ if test -d "$smr_cv_with_Xpm_includes"; then
+ Xpm_CFLAGS="-I$smr_cv_with_Xpm_includes"
+ else
+ { { echo "$as_me:$LINENO: error: argument must be a directory" >&5
+echo "$as_me: error: argument must be a directory" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ else
+ Xpm_CFLAGS=
+ fi
+
+ smr_test_CPPFLAGS="${CPPFLAGS+set}"
+ smr_save_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $Xpm_CFLAGS $X_CFLAGS"
+
+
+for ac_header in X11/xpm.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+else
+ # Is the header compilable?
+echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+ (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null; then
+ if test -s conftest.err; then
+ ac_cpp_err=$ac_c_preproc_warn_flag
+ ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+ else
+ ac_cpp_err=
+ fi
+else
+ ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+ (
+ cat <<\_ASBOX
+## ------------------------------------------ ##
+## Report this to the AC_PACKAGE_NAME lists. ##
+## ------------------------------------------ ##
+_ASBOX
+ ) |
+ sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+ smr_have_Xpm_header=yes
+else
+ smr_have_Xpm_header=no
+fi
+
+done
+
+
+ if test x"$smr_test_CPPFLAGS" = xset; then
+ CPPFLAGS=$smr_save_CPPFLAGS
+ else
+ unset CPPFLAGS
+ fi
+
+
+
+
+
+
+
+ # We need only look for the library if the header has been found
+ # (or no header is needed).
+ if test $smr_have_Xpm_header != no; then
+
+ echo "$as_me:$LINENO: checking for XpmCreatePixmapFromData in -lXpm" >&5
+echo $ECHO_N "checking for XpmCreatePixmapFromData in -lXpm... $ECHO_C" >&6
+if test "${ac_cv_lib_Xpm_XpmCreatePixmapFromData+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lXpm $Xpm_CFLAGS $X_CFLAGS $Xpm_LIBS $X_LIBS $X_PRE_LIBS $X_EXTRA_LIBS -lX11 $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char XpmCreatePixmapFromData ();
+int
+main ()
+{
+XpmCreatePixmapFromData ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_lib_Xpm_XpmCreatePixmapFromData=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_Xpm_XpmCreatePixmapFromData=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_Xpm_XpmCreatePixmapFromData" >&5
+echo "${ECHO_T}$ac_cv_lib_Xpm_XpmCreatePixmapFromData" >&6
+if test $ac_cv_lib_Xpm_XpmCreatePixmapFromData = yes; then
+ smr_have_Xpm_library=yes
+else
+ smr_have_Xpm_library=no
+fi
+
+ fi
+
+ if test x"$smr_have_Xpm_library" = xyes; then
+ echo "$as_me:$LINENO: result: using Xpm library" >&5
+echo "${ECHO_T}using Xpm library" >&6
+ else
+ Xpm_LIBS=
+ Xpm_CFLAGS=
+
+ if test x"$with_Xpm" = xmaybe; then
+ echo "$as_me:$LINENO: result: not using Xpm library" >&5
+echo "${ECHO_T}not using Xpm library" >&6
+ else
+ { echo "$as_me:$LINENO: WARNING: requested Xpm library not found!" >&5
+echo "$as_me: WARNING: requested Xpm library not found!" >&2;}
+ fi
+ fi
+ fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+fi
+
+if test -z "$no_x"; then
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# Check whether --with-Xext-library or --without-Xext-library was given.
+if test "${with_Xext_library+set}" = set; then
+ withval="$with_Xext_library"
+ smr_cv_with_Xext_library=$withval
+fi;
+
+ echo "$as_me:$LINENO: checking whether to use Xext library" >&5
+echo $ECHO_N "checking whether to use Xext library... $ECHO_C" >&6
+if test "${smr_cv_with_Xext_library+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ smr_cv_with_Xext_library=maybe
+fi
+echo "$as_me:$LINENO: result: $smr_cv_with_Xext_library" >&5
+echo "${ECHO_T}$smr_cv_with_Xext_library" >&6
+
+
+ case x"$smr_cv_with_Xext_library" in
+ xyes | xmaybe)
+ Xext_LIBS="-lXext"
+ with_Xext=$smr_cv_with_Xext_library
+ ;;
+ xno)
+ Xext_LIBS=
+ with_Xext=no
+ ;;
+ *)
+ if test -f "$smr_cv_with_Xext_library"; then
+ Xext_LIBS=$smr_cv_with_Xext_library
+ elif test -d "$smr_cv_with_Xext_library"; then
+ Xext_LIBS="-L$smr_cv_with_Xext_library -lXext"
+ else
+ { { echo "$as_me:$LINENO: error: argument must be boolean, file, or directory" >&5
+echo "$as_me: error: argument must be boolean, file, or directory" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ with_Xext=yes
+ ;;
+ esac
+
+
+
+
+
+
+
+ if test ! x"$with_Xext" = xno; then
+
+ # If we got this far, then the user didn't explicitly ask not to use
+ # the library.
+
+
+
+
+
+
+
+
+# Check whether --with-Xext-includes or --without-Xext-includes was given.
+if test "${with_Xext_includes+set}" = set; then
+ withval="$with_Xext_includes"
+ smr_cv_with_Xext_includes=$withval
+fi;
+
+ echo "$as_me:$LINENO: checking where to find the Xext header files" >&5
+echo $ECHO_N "checking where to find the Xext header files... $ECHO_C" >&6
+if test "${smr_cv_with_Xext_includes+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ smr_cv_with_Xext_includes=
+fi
+echo "$as_me:$LINENO: result: $smr_cv_with_Xext_includes" >&5
+echo "${ECHO_T}$smr_cv_with_Xext_includes" >&6
+
+ if test ! x"$smr_cv_with_Xext_includes" = x; then
+ if test -d "$smr_cv_with_Xext_includes"; then
+ Xext_CFLAGS="-I$smr_cv_with_Xext_includes"
+ else
+ { { echo "$as_me:$LINENO: error: argument must be a directory" >&5
+echo "$as_me: error: argument must be a directory" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ else
+ Xext_CFLAGS=
+ fi
+
+ smr_test_CPPFLAGS="${CPPFLAGS+set}"
+ smr_save_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $Xext_CFLAGS $X_CFLAGS"
+
+
+for ac_header in X11/Xlib.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+else
+ # Is the header compilable?
+echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+ (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null; then
+ if test -s conftest.err; then
+ ac_cpp_err=$ac_c_preproc_warn_flag
+ ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+ else
+ ac_cpp_err=
+ fi
+else
+ ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+ (
+ cat <<\_ASBOX
+## ------------------------------------------ ##
+## Report this to the AC_PACKAGE_NAME lists. ##
+## ------------------------------------------ ##
+_ASBOX
+ ) |
+ sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+ smr_have_Xext_header=yes
+else
+ smr_have_Xext_header=no
+fi
+
+done
+
+
+ if test x"$smr_test_CPPFLAGS" = xset; then
+ CPPFLAGS=$smr_save_CPPFLAGS
+ else
+ unset CPPFLAGS
+ fi
+
+
+
+
+
+
+
+ # We need only look for the library if the header has been found
+ # (or no header is needed).
+ if test $smr_have_Xext_header != no; then
+
+ echo "$as_me:$LINENO: checking for XextAddDisplay in -lXext" >&5
+echo $ECHO_N "checking for XextAddDisplay in -lXext... $ECHO_C" >&6
+if test "${ac_cv_lib_Xext_XextAddDisplay+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lXext $Xext_CFLAGS $X_CFLAGS $Xext_LIBS $X_LIBS $X_PRE_LIBS $X_EXTRA_LIBS -lX11 $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char XextAddDisplay ();
+int
+main ()
+{
+XextAddDisplay ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_lib_Xext_XextAddDisplay=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_Xext_XextAddDisplay=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_Xext_XextAddDisplay" >&5
+echo "${ECHO_T}$ac_cv_lib_Xext_XextAddDisplay" >&6
+if test $ac_cv_lib_Xext_XextAddDisplay = yes; then
+ smr_have_Xext_library=yes
+else
+ smr_have_Xext_library=no
+fi
+
+ fi
+
+ if test x"$smr_have_Xext_library" = xyes; then
+ echo "$as_me:$LINENO: result: using Xext library" >&5
+echo "${ECHO_T}using Xext library" >&6
+ else
+ Xext_LIBS=
+ Xext_CFLAGS=
+
+ if test x"$with_Xext" = xmaybe; then
+ echo "$as_me:$LINENO: result: not using Xext library" >&5
+echo "${ECHO_T}not using Xext library" >&6
+ else
+ { echo "$as_me:$LINENO: WARNING: requested Xext library not found!" >&5
+echo "$as_me: WARNING: requested Xext library not found!" >&2;}
+ fi
+ fi
+ fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# Check whether --with-Xp-library or --without-Xp-library was given.
+if test "${with_Xp_library+set}" = set; then
+ withval="$with_Xp_library"
+ smr_cv_with_Xp_library=$withval
+fi;
+
+ echo "$as_me:$LINENO: checking whether to use Xp library" >&5
+echo $ECHO_N "checking whether to use Xp library... $ECHO_C" >&6
+if test "${smr_cv_with_Xp_library+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ smr_cv_with_Xp_library=maybe
+fi
+echo "$as_me:$LINENO: result: $smr_cv_with_Xp_library" >&5
+echo "${ECHO_T}$smr_cv_with_Xp_library" >&6
+
+
+ case x"$smr_cv_with_Xp_library" in
+ xyes | xmaybe)
+ Xp_LIBS="-lXp"
+ with_Xp=$smr_cv_with_Xp_library
+ ;;
+ xno)
+ Xp_LIBS=
+ with_Xp=no
+ ;;
+ *)
+ if test -f "$smr_cv_with_Xp_library"; then
+ Xp_LIBS=$smr_cv_with_Xp_library
+ elif test -d "$smr_cv_with_Xp_library"; then
+ Xp_LIBS="-L$smr_cv_with_Xp_library -lXp"
+ else
+ { { echo "$as_me:$LINENO: error: argument must be boolean, file, or directory" >&5
+echo "$as_me: error: argument must be boolean, file, or directory" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ with_Xp=yes
+ ;;
+ esac
+
+
+
+
+
+
+
+ if test ! x"$with_Xp" = xno; then
+
+ # If we got this far, then the user didn't explicitly ask not to use
+ # the library.
+
+
+
+
+
+
+
+
+# Check whether --with-Xp-includes or --without-Xp-includes was given.
+if test "${with_Xp_includes+set}" = set; then
+ withval="$with_Xp_includes"
+ smr_cv_with_Xp_includes=$withval
+fi;
+
+ echo "$as_me:$LINENO: checking where to find the Xp header files" >&5
+echo $ECHO_N "checking where to find the Xp header files... $ECHO_C" >&6
+if test "${smr_cv_with_Xp_includes+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ smr_cv_with_Xp_includes=
+fi
+echo "$as_me:$LINENO: result: $smr_cv_with_Xp_includes" >&5
+echo "${ECHO_T}$smr_cv_with_Xp_includes" >&6
+
+ if test ! x"$smr_cv_with_Xp_includes" = x; then
+ if test -d "$smr_cv_with_Xp_includes"; then
+ Xp_CFLAGS="-I$smr_cv_with_Xp_includes"
+ else
+ { { echo "$as_me:$LINENO: error: argument must be a directory" >&5
+echo "$as_me: error: argument must be a directory" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ else
+ Xp_CFLAGS=
+ fi
+
+ smr_test_CPPFLAGS="${CPPFLAGS+set}"
+ smr_save_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $Xp_CFLAGS $X_CFLAGS"
+
+
+for ac_header in X11/extensions/Print.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+else
+ # Is the header compilable?
+echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+ (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null; then
+ if test -s conftest.err; then
+ ac_cpp_err=$ac_c_preproc_warn_flag
+ ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+ else
+ ac_cpp_err=
+ fi
+else
+ ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+ (
+ cat <<\_ASBOX
+## ------------------------------------------ ##
+## Report this to the AC_PACKAGE_NAME lists. ##
+## ------------------------------------------ ##
+_ASBOX
+ ) |
+ sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+ smr_have_Xp_header=yes
+else
+ smr_have_Xp_header=no
+fi
+
+done
+
+
+ if test x"$smr_test_CPPFLAGS" = xset; then
+ CPPFLAGS=$smr_save_CPPFLAGS
+ else
+ unset CPPFLAGS
+ fi
+
+
+
+
+
+
+
+ # We need only look for the library if the header has been found
+ # (or no header is needed).
+ if test $smr_have_Xp_header != no; then
+
+ echo "$as_me:$LINENO: checking for XpStartPage in -lXp" >&5
+echo $ECHO_N "checking for XpStartPage in -lXp... $ECHO_C" >&6
+if test "${ac_cv_lib_Xp_XpStartPage+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lXp $Xp_CFLAGS $X_CFLAGS $Xp_LIBS $X_LIBS $X_PRE_LIBS $Xext_LIBS $X_EXTRA_LIBS -lX11 $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char XpStartPage ();
+int
+main ()
+{
+XpStartPage ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_lib_Xp_XpStartPage=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_Xp_XpStartPage=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_Xp_XpStartPage" >&5
+echo "${ECHO_T}$ac_cv_lib_Xp_XpStartPage" >&6
+if test $ac_cv_lib_Xp_XpStartPage = yes; then
+ smr_have_Xp_library=yes
+else
+ smr_have_Xp_library=no
+fi
+
+ fi
+
+ if test x"$smr_have_Xp_library" = xyes; then
+ echo "$as_me:$LINENO: result: using Xp library" >&5
+echo "${ECHO_T}using Xp library" >&6
+ else
+ Xp_LIBS=
+ Xp_CFLAGS=
+
+ if test x"$with_Xp" = xmaybe; then
+ echo "$as_me:$LINENO: result: not using Xp library" >&5
+echo "${ECHO_T}not using Xp library" >&6
+ else
+ { echo "$as_me:$LINENO: WARNING: requested Xp library not found!" >&5
+echo "$as_me: WARNING: requested Xp library not found!" >&2;}
+ fi
+ fi
+ fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# Check whether --with-Xt-library or --without-Xt-library was given.
+if test "${with_Xt_library+set}" = set; then
+ withval="$with_Xt_library"
+ smr_cv_with_Xt_library=$withval
+fi;
+
+ echo "$as_me:$LINENO: checking whether to use Xt library" >&5
+echo $ECHO_N "checking whether to use Xt library... $ECHO_C" >&6
+if test "${smr_cv_with_Xt_library+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ smr_cv_with_Xt_library=maybe
+fi
+echo "$as_me:$LINENO: result: $smr_cv_with_Xt_library" >&5
+echo "${ECHO_T}$smr_cv_with_Xt_library" >&6
+
+
+ case x"$smr_cv_with_Xt_library" in
+ xyes | xmaybe)
+ Xt_LIBS="-lXt"
+ with_Xt=$smr_cv_with_Xt_library
+ ;;
+ xno)
+ Xt_LIBS=
+ with_Xt=no
+ ;;
+ *)
+ if test -f "$smr_cv_with_Xt_library"; then
+ Xt_LIBS=$smr_cv_with_Xt_library
+ elif test -d "$smr_cv_with_Xt_library"; then
+ Xt_LIBS="-L$smr_cv_with_Xt_library -lXt"
+ else
+ { { echo "$as_me:$LINENO: error: argument must be boolean, file, or directory" >&5
+echo "$as_me: error: argument must be boolean, file, or directory" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ with_Xt=yes
+ ;;
+ esac
+
+
+
+
+
+
+
+ if test ! x"$with_Xt" = xno; then
+
+ # If we got this far, then the user didn't explicitly ask not to use
+ # the library.
+
+
+
+
+
+
+
+
+# Check whether --with-Xt-includes or --without-Xt-includes was given.
+if test "${with_Xt_includes+set}" = set; then
+ withval="$with_Xt_includes"
+ smr_cv_with_Xt_includes=$withval
+fi;
+
+ echo "$as_me:$LINENO: checking where to find the Xt header files" >&5
+echo $ECHO_N "checking where to find the Xt header files... $ECHO_C" >&6
+if test "${smr_cv_with_Xt_includes+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ smr_cv_with_Xt_includes=
+fi
+echo "$as_me:$LINENO: result: $smr_cv_with_Xt_includes" >&5
+echo "${ECHO_T}$smr_cv_with_Xt_includes" >&6
+
+ if test ! x"$smr_cv_with_Xt_includes" = x; then
+ if test -d "$smr_cv_with_Xt_includes"; then
+ Xt_CFLAGS="-I$smr_cv_with_Xt_includes"
+ else
+ { { echo "$as_me:$LINENO: error: argument must be a directory" >&5
+echo "$as_me: error: argument must be a directory" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ else
+ Xt_CFLAGS=
+ fi
+
+ smr_test_CPPFLAGS="${CPPFLAGS+set}"
+ smr_save_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $Xt_CFLAGS $X_CFLAGS"
+
+
+for ac_header in X11/Intrinsic.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+else
+ # Is the header compilable?
+echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+ (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null; then
+ if test -s conftest.err; then
+ ac_cpp_err=$ac_c_preproc_warn_flag
+ ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+ else
+ ac_cpp_err=
+ fi
+else
+ ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+ (
+ cat <<\_ASBOX
+## ------------------------------------------ ##
+## Report this to the AC_PACKAGE_NAME lists. ##
+## ------------------------------------------ ##
+_ASBOX
+ ) |
+ sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+ smr_have_Xt_header=yes
+else
+ smr_have_Xt_header=no
+fi
+
+done
+
+
+ if test x"$smr_test_CPPFLAGS" = xset; then
+ CPPFLAGS=$smr_save_CPPFLAGS
+ else
+ unset CPPFLAGS
+ fi
+
+
+
+
+
+
+
+ # We need only look for the library if the header has been found
+ # (or no header is needed).
+ if test $smr_have_Xt_header != no; then
+
+ echo "$as_me:$LINENO: checking for XtAppInitialize in -lXt" >&5
+echo $ECHO_N "checking for XtAppInitialize in -lXt... $ECHO_C" >&6
+if test "${ac_cv_lib_Xt_XtAppInitialize+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lXt $Xt_CFLAGS $X_CFLAGS $Xt_LIBS $X_LIBS $X_PRE_LIBS $X_EXTRA_LIBS -lX11 $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char XtAppInitialize ();
+int
+main ()
+{
+XtAppInitialize ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_lib_Xt_XtAppInitialize=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_Xt_XtAppInitialize=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_Xt_XtAppInitialize" >&5
+echo "${ECHO_T}$ac_cv_lib_Xt_XtAppInitialize" >&6
+if test $ac_cv_lib_Xt_XtAppInitialize = yes; then
+ smr_have_Xt_library=yes
+else
+ smr_have_Xt_library=no
+fi
+
+ fi
+
+ if test x"$smr_have_Xt_library" = xyes; then
+ echo "$as_me:$LINENO: result: using Xt library" >&5
+echo "${ECHO_T}using Xt library" >&6
+ else
+ Xt_LIBS=
+ Xt_CFLAGS=
+
+ if test x"$with_Xt" = xmaybe; then
+ echo "$as_me:$LINENO: result: not using Xt library" >&5
+echo "${ECHO_T}not using Xt library" >&6
+ else
+ { echo "$as_me:$LINENO: WARNING: requested Xt library not found!" >&5
+echo "$as_me: WARNING: requested Xt library not found!" >&2;}
+ fi
+ fi
+ fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# Check whether --with-Xm-library or --without-Xm-library was given.
+if test "${with_Xm_library+set}" = set; then
+ withval="$with_Xm_library"
+ smr_cv_with_Xm_library=$withval
+fi;
+
+ echo "$as_me:$LINENO: checking whether to use Xm library" >&5
+echo $ECHO_N "checking whether to use Xm library... $ECHO_C" >&6
+if test "${smr_cv_with_Xm_library+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ smr_cv_with_Xm_library=maybe
+fi
+echo "$as_me:$LINENO: result: $smr_cv_with_Xm_library" >&5
+echo "${ECHO_T}$smr_cv_with_Xm_library" >&6
+
+
+ case x"$smr_cv_with_Xm_library" in
+ xyes | xmaybe)
+ Xm_LIBS="-lXm"
+ with_Xm=$smr_cv_with_Xm_library
+ ;;
+ xno)
+ Xm_LIBS=
+ with_Xm=no
+ ;;
+ *)
+ if test -f "$smr_cv_with_Xm_library"; then
+ Xm_LIBS=$smr_cv_with_Xm_library
+ elif test -d "$smr_cv_with_Xm_library"; then
+ Xm_LIBS="-L$smr_cv_with_Xm_library -lXm"
+ else
+ { { echo "$as_me:$LINENO: error: argument must be boolean, file, or directory" >&5
+echo "$as_me: error: argument must be boolean, file, or directory" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ with_Xm=yes
+ ;;
+ esac
+
+
+
+
+
+
+
+ if test ! x"$with_Xm" = xno; then
+
+ # If we got this far, then the user didn't explicitly ask not to use
+ # the library.
+
+
+
+
+
+
+
+
+# Check whether --with-Xm-includes or --without-Xm-includes was given.
+if test "${with_Xm_includes+set}" = set; then
+ withval="$with_Xm_includes"
+ smr_cv_with_Xm_includes=$withval
+fi;
+
+ echo "$as_me:$LINENO: checking where to find the Xm header files" >&5
+echo $ECHO_N "checking where to find the Xm header files... $ECHO_C" >&6
+if test "${smr_cv_with_Xm_includes+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ smr_cv_with_Xm_includes=
+fi
+echo "$as_me:$LINENO: result: $smr_cv_with_Xm_includes" >&5
+echo "${ECHO_T}$smr_cv_with_Xm_includes" >&6
+
+ if test ! x"$smr_cv_with_Xm_includes" = x; then
+ if test -d "$smr_cv_with_Xm_includes"; then
+ Xm_CFLAGS="-I$smr_cv_with_Xm_includes"
+ else
+ { { echo "$as_me:$LINENO: error: argument must be a directory" >&5
+echo "$as_me: error: argument must be a directory" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ else
+ Xm_CFLAGS=
+ fi
+
+ smr_test_CPPFLAGS="${CPPFLAGS+set}"
+ smr_save_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $Xm_CFLAGS $X_CFLAGS"
+
+
+for ac_header in Xm/XmAll.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+else
+ # Is the header compilable?
+echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+ (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null; then
+ if test -s conftest.err; then
+ ac_cpp_err=$ac_c_preproc_warn_flag
+ ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+ else
+ ac_cpp_err=
+ fi
+else
+ ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+ (
+ cat <<\_ASBOX
+## ------------------------------------------ ##
+## Report this to the AC_PACKAGE_NAME lists. ##
+## ------------------------------------------ ##
+_ASBOX
+ ) |
+ sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+ smr_have_Xm_header=yes
+else
+ smr_have_Xm_header=no
+fi
+
+done
+
+
+ if test x"$smr_test_CPPFLAGS" = xset; then
+ CPPFLAGS=$smr_save_CPPFLAGS
+ else
+ unset CPPFLAGS
+ fi
+
+
+
+
+
+
+
+ # We need only look for the library if the header has been found
+ # (or no header is needed).
+ if test $smr_have_Xm_header != no; then
+
+ echo "$as_me:$LINENO: checking for XmCreateForm in -lXm" >&5
+echo $ECHO_N "checking for XmCreateForm in -lXm... $ECHO_C" >&6
+if test "${ac_cv_lib_Xm_XmCreateForm+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lXm $Xm_CFLAGS $X_CFLAGS $Xm_LIBS $Xt_LIBS $X_LIBS $X_PRE_LIBS $Xp_LIBS $Xext_LIBS $X_EXTRA_LIBS -lX11 $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char XmCreateForm ();
+int
+main ()
+{
+XmCreateForm ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_lib_Xm_XmCreateForm=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_Xm_XmCreateForm=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_Xm_XmCreateForm" >&5
+echo "${ECHO_T}$ac_cv_lib_Xm_XmCreateForm" >&6
+if test $ac_cv_lib_Xm_XmCreateForm = yes; then
+ smr_have_Xm_library=yes
+else
+ smr_have_Xm_library=no
+fi
+
+ fi
+
+ if test x"$smr_have_Xm_library" = xyes; then
+ echo "$as_me:$LINENO: result: using Xm library" >&5
+echo "${ECHO_T}using Xm library" >&6
+ else
+ Xm_LIBS=
+ Xm_CFLAGS=
+
+ if test x"$with_Xm" = xmaybe; then
+ echo "$as_me:$LINENO: result: not using Xm library" >&5
+echo "${ECHO_T}not using Xm library" >&6
+ else
+ { echo "$as_me:$LINENO: WARNING: requested Xm library not found!" >&5
+echo "$as_me: WARNING: requested Xm library not found!" >&2;}
+ fi
+ fi
+ fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# Check whether --with-Sgm-library or --without-Sgm-library was given.
+if test "${with_Sgm_library+set}" = set; then
+ withval="$with_Sgm_library"
+ smr_cv_with_Sgm_library=$withval
+fi;
+
+ echo "$as_me:$LINENO: checking whether to use Sgm library" >&5
+echo $ECHO_N "checking whether to use Sgm library... $ECHO_C" >&6
+if test "${smr_cv_with_Sgm_library+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ smr_cv_with_Sgm_library=maybe
+fi
+echo "$as_me:$LINENO: result: $smr_cv_with_Sgm_library" >&5
+echo "${ECHO_T}$smr_cv_with_Sgm_library" >&6
+
+
+ case x"$smr_cv_with_Sgm_library" in
+ xyes | xmaybe)
+ Sgm_LIBS="-lSgm"
+ with_Sgm=$smr_cv_with_Sgm_library
+ ;;
+ xno)
+ Sgm_LIBS=
+ with_Sgm=no
+ ;;
+ *)
+ if test -f "$smr_cv_with_Sgm_library"; then
+ Sgm_LIBS=$smr_cv_with_Sgm_library
+ elif test -d "$smr_cv_with_Sgm_library"; then
+ Sgm_LIBS="-L$smr_cv_with_Sgm_library -lSgm"
+ else
+ { { echo "$as_me:$LINENO: error: argument must be boolean, file, or directory" >&5
+echo "$as_me: error: argument must be boolean, file, or directory" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ with_Sgm=yes
+ ;;
+ esac
+
+
+
+
+
+
+
+ if test ! x"$with_Sgm" = xno; then
+
+ # If we got this far, then the user didn't explicitly ask not to use
+ # the library.
+
+
+
+
+
+
+
+
+# Check whether --with-Sgm-includes or --without-Sgm-includes was given.
+if test "${with_Sgm_includes+set}" = set; then
+ withval="$with_Sgm_includes"
+ smr_cv_with_Sgm_includes=$withval
+fi;
+
+ echo "$as_me:$LINENO: checking where to find the Sgm header files" >&5
+echo $ECHO_N "checking where to find the Sgm header files... $ECHO_C" >&6
+if test "${smr_cv_with_Sgm_includes+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ smr_cv_with_Sgm_includes=
+fi
+echo "$as_me:$LINENO: result: $smr_cv_with_Sgm_includes" >&5
+echo "${ECHO_T}$smr_cv_with_Sgm_includes" >&6
+
+ if test ! x"$smr_cv_with_Sgm_includes" = x; then
+ if test -d "$smr_cv_with_Sgm_includes"; then
+ Sgm_CFLAGS="-I$smr_cv_with_Sgm_includes"
+ else
+ { { echo "$as_me:$LINENO: error: argument must be a directory" >&5
+echo "$as_me: error: argument must be a directory" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ else
+ Sgm_CFLAGS=
+ fi
+
+ smr_test_CPPFLAGS="${CPPFLAGS+set}"
+ smr_save_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $Sgm_CFLAGS $X_CFLAGS"
+
+
+for ac_header in Sgm/HPanedW.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+else
+ # Is the header compilable?
+echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+ (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null; then
+ if test -s conftest.err; then
+ ac_cpp_err=$ac_c_preproc_warn_flag
+ ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+ else
+ ac_cpp_err=
+ fi
+else
+ ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+ (
+ cat <<\_ASBOX
+## ------------------------------------------ ##
+## Report this to the AC_PACKAGE_NAME lists. ##
+## ------------------------------------------ ##
+_ASBOX
+ ) |
+ sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+ smr_have_Sgm_header=yes
+else
+ smr_have_Sgm_header=no
+fi
+
+done
+
+
+ if test x"$smr_test_CPPFLAGS" = xset; then
+ CPPFLAGS=$smr_save_CPPFLAGS
+ else
+ unset CPPFLAGS
+ fi
+
+
+
+
+
+
+
+ # We need only look for the library if the header has been found
+ # (or no header is needed).
+ if test $smr_have_Sgm_header != no; then
+
+ echo "$as_me:$LINENO: checking for SgCreateHorzPanedWindow in -lSgm" >&5
+echo $ECHO_N "checking for SgCreateHorzPanedWindow in -lSgm... $ECHO_C" >&6
+if test "${ac_cv_lib_Sgm_SgCreateHorzPanedWindow+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lSgm $Sgm_CFLAGS $X_CFLAGS $Sgm_LIBS $Xm_LIBS $Xt_LIBS $X_LIBS $X_PRE_LIBS $Xp_LIBS $Xext_LIBS $X_EXTRA_LIBS -lX11 $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char SgCreateHorzPanedWindow ();
+int
+main ()
+{
+SgCreateHorzPanedWindow ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_lib_Sgm_SgCreateHorzPanedWindow=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_Sgm_SgCreateHorzPanedWindow=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_Sgm_SgCreateHorzPanedWindow" >&5
+echo "${ECHO_T}$ac_cv_lib_Sgm_SgCreateHorzPanedWindow" >&6
+if test $ac_cv_lib_Sgm_SgCreateHorzPanedWindow = yes; then
+ smr_have_Sgm_library=yes
+else
+ smr_have_Sgm_library=no
+fi
+
+ fi
+
+ if test x"$smr_have_Sgm_library" = xyes; then
+ echo "$as_me:$LINENO: result: using Sgm library" >&5
+echo "${ECHO_T}using Sgm library" >&6
+ else
+ Sgm_LIBS=
+ Sgm_CFLAGS=
+
+ if test x"$with_Sgm" = xmaybe; then
+ echo "$as_me:$LINENO: result: not using Sgm library" >&5
+echo "${ECHO_T}not using Sgm library" >&6
+ else
+ { echo "$as_me:$LINENO: WARNING: requested Sgm library not found!" >&5
+echo "$as_me: WARNING: requested Sgm library not found!" >&2;}
+ fi
+ fi
+ fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ if test "x$smr_have_Xt_library" = xyes; then
+ echo "$as_me:$LINENO: checking for XtAppSetExitFlag in -lXt" >&5
+echo $ECHO_N "checking for XtAppSetExitFlag in -lXt... $ECHO_C" >&6
+if test "${ac_cv_lib_Xt_XtAppSetExitFlag+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lXt $Xt_LIBS $X_LIBS $X_PRE_LIBS $Xp_LIBS $Xext_LIBS $X_EXTRA_LIBS -lX11 $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char XtAppSetExitFlag ();
+int
+main ()
+{
+XtAppSetExitFlag ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_lib_Xt_XtAppSetExitFlag=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_Xt_XtAppSetExitFlag=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_Xt_XtAppSetExitFlag" >&5
+echo "${ECHO_T}$ac_cv_lib_Xt_XtAppSetExitFlag" >&6
+if test $ac_cv_lib_Xt_XtAppSetExitFlag = yes; then
+ cat >>confdefs.h <<\_ACEOF
+#define HAVE_XTAPPSETEXITFLAG 1
+_ACEOF
+
+fi
+
+ fi
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# Check whether --with-t1-library or --without-t1-library was given.
+if test "${with_t1_library+set}" = set; then
+ withval="$with_t1_library"
+ smr_cv_with_t1_library=$withval
+fi;
+
+ echo "$as_me:$LINENO: checking whether to use t1 library" >&5
+echo $ECHO_N "checking whether to use t1 library... $ECHO_C" >&6
+if test "${smr_cv_with_t1_library+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ smr_cv_with_t1_library=maybe
+fi
+echo "$as_me:$LINENO: result: $smr_cv_with_t1_library" >&5
+echo "${ECHO_T}$smr_cv_with_t1_library" >&6
+
+
+ case x"$smr_cv_with_t1_library" in
+ xyes | xmaybe)
+ t1_LIBS="-lt1"
+ with_t1=$smr_cv_with_t1_library
+ ;;
+ xno)
+ t1_LIBS=
+ with_t1=no
+ ;;
+ *)
+ if test -f "$smr_cv_with_t1_library"; then
+ t1_LIBS=$smr_cv_with_t1_library
+ elif test -d "$smr_cv_with_t1_library"; then
+ t1_LIBS="-L$smr_cv_with_t1_library -lt1"
+ else
+ { { echo "$as_me:$LINENO: error: argument must be boolean, file, or directory" >&5
+echo "$as_me: error: argument must be boolean, file, or directory" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ with_t1=yes
+ ;;
+ esac
+
+
+
+
+
+
+
+ if test ! x"$with_t1" = xno; then
+
+ # If we got this far, then the user didn't explicitly ask not to use
+ # the library.
+
+
+
+
+
+
+
+
+# Check whether --with-t1-includes or --without-t1-includes was given.
+if test "${with_t1_includes+set}" = set; then
+ withval="$with_t1_includes"
+ smr_cv_with_t1_includes=$withval
+fi;
+
+ echo "$as_me:$LINENO: checking where to find the t1 header files" >&5
+echo $ECHO_N "checking where to find the t1 header files... $ECHO_C" >&6
+if test "${smr_cv_with_t1_includes+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ smr_cv_with_t1_includes=
+fi
+echo "$as_me:$LINENO: result: $smr_cv_with_t1_includes" >&5
+echo "${ECHO_T}$smr_cv_with_t1_includes" >&6
+
+ if test ! x"$smr_cv_with_t1_includes" = x; then
+ if test -d "$smr_cv_with_t1_includes"; then
+ t1_CFLAGS="-I$smr_cv_with_t1_includes"
+ else
+ { { echo "$as_me:$LINENO: error: argument must be a directory" >&5
+echo "$as_me: error: argument must be a directory" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ else
+ t1_CFLAGS=
+ fi
+
+ smr_test_CPPFLAGS="${CPPFLAGS+set}"
+ smr_save_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $t1_CFLAGS $X_CFLAGS"
+
+
+for ac_header in t1lib.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+else
+ # Is the header compilable?
+echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+ (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null; then
+ if test -s conftest.err; then
+ ac_cpp_err=$ac_c_preproc_warn_flag
+ ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+ else
+ ac_cpp_err=
+ fi
+else
+ ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+ (
+ cat <<\_ASBOX
+## ------------------------------------------ ##
+## Report this to the AC_PACKAGE_NAME lists. ##
+## ------------------------------------------ ##
+_ASBOX
+ ) |
+ sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+ smr_have_t1_header=yes
+else
+ smr_have_t1_header=no
+fi
+
+done
+
+
+ if test x"$smr_test_CPPFLAGS" = xset; then
+ CPPFLAGS=$smr_save_CPPFLAGS
+ else
+ unset CPPFLAGS
+ fi
+
+
+
+
+
+
+
+ # We need only look for the library if the header has been found
+ # (or no header is needed).
+ if test $smr_have_t1_header != no; then
+
+ echo "$as_me:$LINENO: checking for T1_InitLib in -lt1" >&5
+echo $ECHO_N "checking for T1_InitLib in -lt1... $ECHO_C" >&6
+if test "${ac_cv_lib_t1_T1_InitLib+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lt1 $t1_CFLAGS $X_CFLAGS $t1_LIBS -lm $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char T1_InitLib ();
+int
+main ()
+{
+T1_InitLib ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_lib_t1_T1_InitLib=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_t1_T1_InitLib=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_t1_T1_InitLib" >&5
+echo "${ECHO_T}$ac_cv_lib_t1_T1_InitLib" >&6
+if test $ac_cv_lib_t1_T1_InitLib = yes; then
+ smr_have_t1_library=yes
+else
+ smr_have_t1_library=no
+fi
+
+ fi
+
+ if test x"$smr_have_t1_library" = xyes; then
+ echo "$as_me:$LINENO: result: using t1 library" >&5
+echo "${ECHO_T}using t1 library" >&6
+ else
+ t1_LIBS=
+ t1_CFLAGS=
+
+ if test x"$with_t1" = xmaybe; then
+ echo "$as_me:$LINENO: result: not using t1 library" >&5
+echo "${ECHO_T}not using t1 library" >&6
+ else
+ { echo "$as_me:$LINENO: WARNING: requested t1 library not found!" >&5
+echo "$as_me: WARNING: requested t1 library not found!" >&2;}
+ fi
+ fi
+ fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# Check whether --with-freetype2-library or --without-freetype2-library was given.
+if test "${with_freetype2_library+set}" = set; then
+ withval="$with_freetype2_library"
+ smr_cv_with_freetype2_library=$withval
+fi;
+
+ echo "$as_me:$LINENO: checking whether to use freetype2 library" >&5
+echo $ECHO_N "checking whether to use freetype2 library... $ECHO_C" >&6
+if test "${smr_cv_with_freetype2_library+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ smr_cv_with_freetype2_library=maybe
+fi
+echo "$as_me:$LINENO: result: $smr_cv_with_freetype2_library" >&5
+echo "${ECHO_T}$smr_cv_with_freetype2_library" >&6
+
+
+ case x"$smr_cv_with_freetype2_library" in
+ xyes | xmaybe)
+ freetype2_LIBS="-lfreetype"
+ with_freetype2=$smr_cv_with_freetype2_library
+ ;;
+ xno)
+ freetype2_LIBS=
+ with_freetype2=no
+ ;;
+ *)
+ if test -f "$smr_cv_with_freetype2_library"; then
+ freetype2_LIBS=$smr_cv_with_freetype2_library
+ elif test -d "$smr_cv_with_freetype2_library"; then
+ freetype2_LIBS="-L$smr_cv_with_freetype2_library -lfreetype"
+ else
+ { { echo "$as_me:$LINENO: error: argument must be boolean, file, or directory" >&5
+echo "$as_me: error: argument must be boolean, file, or directory" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ with_freetype2=yes
+ ;;
+ esac
+
+
+
+
+
+
+
+ if test ! x"$with_freetype2" = xno; then
+
+ # If we got this far, then the user didn't explicitly ask not to use
+ # the library.
+
+
+
+
+
+
+
+
+# Check whether --with-freetype2-includes or --without-freetype2-includes was given.
+if test "${with_freetype2_includes+set}" = set; then
+ withval="$with_freetype2_includes"
+ smr_cv_with_freetype2_includes=$withval
+fi;
+
+ echo "$as_me:$LINENO: checking where to find the freetype2 header files" >&5
+echo $ECHO_N "checking where to find the freetype2 header files... $ECHO_C" >&6
+if test "${smr_cv_with_freetype2_includes+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ smr_cv_with_freetype2_includes=
+fi
+echo "$as_me:$LINENO: result: $smr_cv_with_freetype2_includes" >&5
+echo "${ECHO_T}$smr_cv_with_freetype2_includes" >&6
+
+ if test ! x"$smr_cv_with_freetype2_includes" = x; then
+ if test -d "$smr_cv_with_freetype2_includes"; then
+ freetype2_CFLAGS="-I$smr_cv_with_freetype2_includes"
+ else
+ { { echo "$as_me:$LINENO: error: argument must be a directory" >&5
+echo "$as_me: error: argument must be a directory" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ else
+ freetype2_CFLAGS=
+ fi
+
+ smr_test_CPPFLAGS="${CPPFLAGS+set}"
+ smr_save_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $freetype2_CFLAGS "
+
+
+for ac_header in ft2build.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+else
+ # Is the header compilable?
+echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+ (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null; then
+ if test -s conftest.err; then
+ ac_cpp_err=$ac_c_preproc_warn_flag
+ ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+ else
+ ac_cpp_err=
+ fi
+else
+ ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+ (
+ cat <<\_ASBOX
+## ------------------------------------------ ##
+## Report this to the AC_PACKAGE_NAME lists. ##
+## ------------------------------------------ ##
+_ASBOX
+ ) |
+ sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+ smr_have_freetype2_header=yes
+else
+ smr_have_freetype2_header=no
+fi
+
+done
+
+
+ if test x"$smr_test_CPPFLAGS" = xset; then
+ CPPFLAGS=$smr_save_CPPFLAGS
+ else
+ unset CPPFLAGS
+ fi
+
+
+
+
+
+
+
+ # We need only look for the library if the header has been found
+ # (or no header is needed).
+ if test $smr_have_freetype2_header != no; then
+
+ echo "$as_me:$LINENO: checking for FT_Get_Name_Index in -lfreetype" >&5
+echo $ECHO_N "checking for FT_Get_Name_Index in -lfreetype... $ECHO_C" >&6
+if test "${ac_cv_lib_freetype_FT_Get_Name_Index+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lfreetype $freetype2_CFLAGS $freetype2_LIBS -lm $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char FT_Get_Name_Index ();
+int
+main ()
+{
+FT_Get_Name_Index ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_lib_freetype_FT_Get_Name_Index=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_freetype_FT_Get_Name_Index=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_freetype_FT_Get_Name_Index" >&5
+echo "${ECHO_T}$ac_cv_lib_freetype_FT_Get_Name_Index" >&6
+if test $ac_cv_lib_freetype_FT_Get_Name_Index = yes; then
+ smr_have_freetype2_library=yes
+else
+ smr_have_freetype2_library=no
+fi
+
+ fi
+
+ if test x"$smr_have_freetype2_library" = xyes; then
+ echo "$as_me:$LINENO: result: using freetype2 library" >&5
+echo "${ECHO_T}using freetype2 library" >&6
+ else
+ freetype2_LIBS=
+ freetype2_CFLAGS=
+
+ if test x"$with_freetype2" = xmaybe; then
+ echo "$as_me:$LINENO: result: not using freetype2 library" >&5
+echo "${ECHO_T}not using freetype2 library" >&6
+ else
+ { echo "$as_me:$LINENO: WARNING: requested freetype2 library not found!" >&5
+echo "$as_me: WARNING: requested freetype2 library not found!" >&2;}
+ fi
+ fi
+ fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+if test "x$smr_have_freetype2_library" = xyes; then
+ cat >>confdefs.h <<\_ACEOF
+#define HAVE_FREETYPE_FREETYPE_H 1
+_ACEOF
+
+ cat >>confdefs.h <<\_ACEOF
+#define HAVE_SPLASH 1
+_ACEOF
+
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# Check whether --with-libpaper-library or --without-libpaper-library was given.
+if test "${with_libpaper_library+set}" = set; then
+ withval="$with_libpaper_library"
+ smr_cv_with_libpaper_library=$withval
+fi;
+
+ echo "$as_me:$LINENO: checking whether to use libpaper library" >&5
+echo $ECHO_N "checking whether to use libpaper library... $ECHO_C" >&6
+if test "${smr_cv_with_libpaper_library+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ smr_cv_with_libpaper_library=maybe
+fi
+echo "$as_me:$LINENO: result: $smr_cv_with_libpaper_library" >&5
+echo "${ECHO_T}$smr_cv_with_libpaper_library" >&6
+
+
+ case x"$smr_cv_with_libpaper_library" in
+ xyes | xmaybe)
+ libpaper_LIBS="-lpaper"
+ with_libpaper=$smr_cv_with_libpaper_library
+ ;;
+ xno)
+ libpaper_LIBS=
+ with_libpaper=no
+ ;;
+ *)
+ if test -f "$smr_cv_with_libpaper_library"; then
+ libpaper_LIBS=$smr_cv_with_libpaper_library
+ elif test -d "$smr_cv_with_libpaper_library"; then
+ libpaper_LIBS="-L$smr_cv_with_libpaper_library -lpaper"
+ else
+ { { echo "$as_me:$LINENO: error: argument must be boolean, file, or directory" >&5
+echo "$as_me: error: argument must be boolean, file, or directory" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ with_libpaper=yes
+ ;;
+ esac
+
+
+
+
+
+
+
+ if test ! x"$with_libpaper" = xno; then
+
+ # If we got this far, then the user didn't explicitly ask not to use
+ # the library.
+
+
+
+
+
+
+
+
+# Check whether --with-libpaper-includes or --without-libpaper-includes was given.
+if test "${with_libpaper_includes+set}" = set; then
+ withval="$with_libpaper_includes"
+ smr_cv_with_libpaper_includes=$withval
+fi;
+
+ echo "$as_me:$LINENO: checking where to find the libpaper header files" >&5
+echo $ECHO_N "checking where to find the libpaper header files... $ECHO_C" >&6
+if test "${smr_cv_with_libpaper_includes+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ smr_cv_with_libpaper_includes=
+fi
+echo "$as_me:$LINENO: result: $smr_cv_with_libpaper_includes" >&5
+echo "${ECHO_T}$smr_cv_with_libpaper_includes" >&6
+
+ if test ! x"$smr_cv_with_libpaper_includes" = x; then
+ if test -d "$smr_cv_with_libpaper_includes"; then
+ libpaper_CFLAGS="-I$smr_cv_with_libpaper_includes"
+ else
+ { { echo "$as_me:$LINENO: error: argument must be a directory" >&5
+echo "$as_me: error: argument must be a directory" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ else
+ libpaper_CFLAGS=
+ fi
+
+ smr_test_CPPFLAGS="${CPPFLAGS+set}"
+ smr_save_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $libpaper_CFLAGS "
+
+
+for ac_header in paper.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+else
+ # Is the header compilable?
+echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+ (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null; then
+ if test -s conftest.err; then
+ ac_cpp_err=$ac_c_preproc_warn_flag
+ ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+ else
+ ac_cpp_err=
+ fi
+else
+ ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+ (
+ cat <<\_ASBOX
+## ------------------------------------------ ##
+## Report this to the AC_PACKAGE_NAME lists. ##
+## ------------------------------------------ ##
+_ASBOX
+ ) |
+ sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+ smr_have_libpaper_header=yes
+else
+ smr_have_libpaper_header=no
+fi
+
+done
+
+
+ if test x"$smr_test_CPPFLAGS" = xset; then
+ CPPFLAGS=$smr_save_CPPFLAGS
+ else
+ unset CPPFLAGS
+ fi
+
+
+
+
+
+
+
+ # We need only look for the library if the header has been found
+ # (or no header is needed).
+ if test $smr_have_libpaper_header != no; then
+
+ echo "$as_me:$LINENO: checking for paperinit in -lpaper" >&5
+echo $ECHO_N "checking for paperinit in -lpaper... $ECHO_C" >&6
+if test "${ac_cv_lib_paper_paperinit+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpaper $libpaper_CFLAGS $libpaper_LIBS $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char paperinit ();
+int
+main ()
+{
+paperinit ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_lib_paper_paperinit=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_paper_paperinit=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_paper_paperinit" >&5
+echo "${ECHO_T}$ac_cv_lib_paper_paperinit" >&6
+if test $ac_cv_lib_paper_paperinit = yes; then
+ smr_have_libpaper_library=yes
+else
+ smr_have_libpaper_library=no
+fi
+
+ fi
+
+ if test x"$smr_have_libpaper_library" = xyes; then
+ echo "$as_me:$LINENO: result: using libpaper library" >&5
+echo "${ECHO_T}using libpaper library" >&6
+ else
+ libpaper_LIBS=
+ libpaper_CFLAGS=
+
+ if test x"$with_libpaper" = xmaybe; then
+ echo "$as_me:$LINENO: result: not using libpaper library" >&5
+echo "${ECHO_T}not using libpaper library" >&6
+ else
+ { echo "$as_me:$LINENO: WARNING: requested libpaper library not found!" >&5
+echo "$as_me: WARNING: requested libpaper library not found!" >&2;}
+ fi
+ fi
+ fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+if test -n "$no_x" -o "x$smr_have_Xm_library" != xyes -o "x$smr_have_freetype2_library" != xyes; then
+ X="#"
+ XPDF_TARGET="all-no-x"
+else
+ X=""
+ XPDF_TARGET="all"
+fi
+
+
+
+ ac_config_files="$ac_config_files Makefile goo/Makefile fofi/Makefile splash/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, don't put newlines in cache variables' values.
+# 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.
+{
+ (set) 2>&1 |
+ case `(ac_space=' '; set | grep ac_space) 2>&1` in
+ *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 \
+ "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
+ ;;
+ esac;
+} |
+ sed '
+ t clear
+ : clear
+ s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+ t end
+ /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+ : end' >>confcache
+if diff $cache_file confcache >/dev/null 2>&1; then :; else
+ if test -w $cache_file; then
+ test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file"
+ cat confcache >$cache_file
+ else
+ echo "not updating unwritable cache $cache_file"
+ 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}'
+
+# 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
+
+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_i=`echo "$ac_i" |
+ sed 's/\$U\././;s/\.o$//;s/\.obj$//'`
+ # 2. Add them.
+ ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext"
+ ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+
+: ${CONFIG_STATUS=./config.status}
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5
+echo "$as_me: creating $CONFIG_STATUS" >&6;}
+cat >$CONFIG_STATUS <<_ACEOF
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+SHELL=\${CONFIG_SHELL-$SHELL}
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+## --------------------- ##
+## M4sh Initialization. ##
+## --------------------- ##
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+ set -o posix
+fi
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ as_unset=unset
+else
+ as_unset=false
+fi
+
+
+# Work around bugs in pre-3.0 UWIN ksh.
+$as_unset ENV MAIL MAILPATH
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+ LC_TELEPHONE LC_TIME
+do
+ if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+ eval $as_var=C; export $as_var
+ else
+ $as_unset $as_var
+ fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)$' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+ /^X\/\(\/\/\)$/{ s//\1/; q; }
+ /^X\/\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+
+
+# PATH needs CR, and LINENO needs CR and PATH.
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+
+
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x$as_lineno_3" = "x$as_lineno_2" || {
+ # Find who we are. Look in the path if we contain no path at all
+ # relative or not.
+ 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
+
+ ;;
+ esac
+ # We did not find ourselves, most probably we were run as `sh COMMAND'
+ # in which case we are not to be found in the path.
+ if test "x$as_myself" = x; then
+ as_myself=$0
+ fi
+ if test ! -f "$as_myself"; then
+ { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5
+echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ case $CONFIG_SHELL in
+ '')
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for as_base in sh bash ksh sh5; do
+ case $as_dir in
+ /*)
+ if ("$as_dir/$as_base" -c '
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then
+ $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
+ $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
+ CONFIG_SHELL=$as_dir/$as_base
+ export CONFIG_SHELL
+ exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+ fi;;
+ esac
+ done
+done
+;;
+ esac
+
+ # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+ # uniformly replaced by the line number. The first 'sed' inserts a
+ # line-number line before each line; the second 'sed' does the real
+ # work. The second script uses 'N' to pair each line-number line
+ # with the numbered line, and appends trailing '-' during
+ # substitution so that $LINENO is not a special case at line end.
+ # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+ # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-)
+ sed '=' <$as_myself |
+ sed '
+ N
+ s,$,-,
+ : loop
+ s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+ t loop
+ s,-$,,
+ s,^['$as_cr_digits']*\n,,
+ ' >$as_me.lineno &&
+ chmod +x $as_me.lineno ||
+ { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5
+echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;}
+ { (exit 1); exit 1; }; }
+
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensible to this).
+ . ./$as_me.lineno
+ # Exit status is that of the last command.
+ exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+ *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T=' ' ;;
+ *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+ *) ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+ # We could just check for DJGPP; but this test a) works b) is more generic
+ # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+ if test -f conf$$.exe; then
+ # Don't use ln at all; we don't have any links
+ as_ln_s='cp -p'
+ else
+ as_ln_s='ln -s'
+ fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+else
+ as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.file
+
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p=:
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+as_executable_p="test -f"
+
+# 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'"
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS=" $as_nl"
+
+# CDPATH.
+$as_unset CDPATH
+
+exec 6>&1
+
+# Open the log real soon, to keep \$[0] and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling. Logging --version etc. is OK.
+exec 5>>config.log
+{
+ echo
+ sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+} >&5
+cat >&5 <<_CSEOF
+
+This file was extended by $as_me, which was
+generated by GNU Autoconf 2.59. Invocation command line was
+
+ CONFIG_FILES = $CONFIG_FILES
+ CONFIG_HEADERS = $CONFIG_HEADERS
+ CONFIG_LINKS = $CONFIG_LINKS
+ CONFIG_COMMANDS = $CONFIG_COMMANDS
+ $ $0 $@
+
+_CSEOF
+echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5
+echo >&5
+_ACEOF
+
+# Files that config.status was made for.
+if test -n "$ac_config_files"; then
+ echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_headers"; then
+ echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_links"; then
+ echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_commands"; then
+ echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+ac_cs_usage="\
+\`$as_me' instantiates files from templates according to the
+current configuration.
+
+Usage: $0 [OPTIONS] [FILE]...
+
+ -h, --help print this help, then exit
+ -V, --version print version number, then exit
+ -q, --quiet do not print progress messages
+ -d, --debug don't remove temporary files
+ --recheck update $as_me by reconfiguring in the same conditions
+ --file=FILE[:TEMPLATE]
+ instantiate the configuration file FILE
+ --header=FILE[:TEMPLATE]
+ instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Report bugs to <bug-autoconf@gnu.org>."
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+ac_cs_version="\\
+config.status
+configured by $0, generated by GNU Autoconf 2.59,
+ with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\"
+
+Copyright (C) 2003 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+srcdir=$srcdir
+INSTALL="$INSTALL"
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+# If no file are specified by the user, then we need to provide default
+# value. By we need to know if files were specified by the user.
+ac_need_defaults=:
+while test $# != 0
+do
+ case $1 in
+ --*=*)
+ ac_option=`expr "x$1" : 'x\([^=]*\)='`
+ ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'`
+ ac_shift=:
+ ;;
+ -*)
+ ac_option=$1
+ ac_optarg=$2
+ ac_shift=shift
+ ;;
+ *) # This is not an option, so the user has probably given explicit
+ # arguments.
+ ac_option=$1
+ ac_need_defaults=false;;
+ esac
+
+ case $ac_option in
+ # Handling of the options.
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ ac_cs_recheck=: ;;
+ --version | --vers* | -V )
+ echo "$ac_cs_version"; exit 0 ;;
+ --he | --h)
+ # Conflict between --help and --header
+ { { echo "$as_me:$LINENO: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&2;}
+ { (exit 1); exit 1; }; };;
+ --help | --hel | -h )
+ echo "$ac_cs_usage"; exit 0 ;;
+ --debug | --d* | -d )
+ debug=: ;;
+ --file | --fil | --fi | --f )
+ $ac_shift
+ CONFIG_FILES="$CONFIG_FILES $ac_optarg"
+ ac_need_defaults=false;;
+ --header | --heade | --head | --hea )
+ $ac_shift
+ CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg"
+ ac_need_defaults=false;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil | --si | --s)
+ ac_cs_silent=: ;;
+
+ # This is an error.
+ -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&2;}
+ { (exit 1); exit 1; }; } ;;
+
+ *) ac_config_targets="$ac_config_targets $1" ;;
+
+ esac
+ shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+ exec 6>/dev/null
+ ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+if \$ac_cs_recheck; then
+ echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6
+ exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+fi
+
+_ACEOF
+
+
+
+
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+for ac_config_target in $ac_config_targets
+do
+ case "$ac_config_target" in
+ # Handling of arguments.
+ "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+ "goo/Makefile" ) CONFIG_FILES="$CONFIG_FILES goo/Makefile" ;;
+ "fofi/Makefile" ) CONFIG_FILES="$CONFIG_FILES fofi/Makefile" ;;
+ "splash/Makefile" ) CONFIG_FILES="$CONFIG_FILES splash/Makefile" ;;
+ "xpdf/Makefile" ) CONFIG_FILES="$CONFIG_FILES xpdf/Makefile" ;;
+ "aconf.h" ) CONFIG_HEADERS="$CONFIG_HEADERS aconf.h" ;;
+ *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
+echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
+ { (exit 1); exit 1; }; };;
+ esac
+done
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used. Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+ test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+ 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 to put it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Create a temporary directory, and hook for its removal unless debugging.
+$debug ||
+{
+ trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0
+ trap '{ (exit 1); exit 1; }' 1 2 13 15
+}
+
+# Create a (secure) tmp directory for tmp files.
+
+{
+ tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` &&
+ test -n "$tmp" && test -d "$tmp"
+} ||
+{
+ tmp=./confstat$$-$RANDOM
+ (umask 077 && mkdir $tmp)
+} ||
+{
+ echo "$me: cannot create a temporary directory in ." >&2
+ { (exit 1); exit 1; }
+}
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+
+#
+# CONFIG_FILES section.
+#
+
+# No need to generate the scripts if there are no CONFIG_FILES.
+# This happens for instance when ./config.status config.h
+if test -n "\$CONFIG_FILES"; then
+ # Protect against being on the right side of a sed subst in config.status.
+ sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g;
+ s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF
+s,@SHELL@,$SHELL,;t t
+s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t
+s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t
+s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t
+s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t
+s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t
+s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t
+s,@exec_prefix@,$exec_prefix,;t t
+s,@prefix@,$prefix,;t t
+s,@program_transform_name@,$program_transform_name,;t t
+s,@bindir@,$bindir,;t t
+s,@sbindir@,$sbindir,;t t
+s,@libexecdir@,$libexecdir,;t t
+s,@datadir@,$datadir,;t t
+s,@sysconfdir@,$sysconfdir,;t t
+s,@sharedstatedir@,$sharedstatedir,;t t
+s,@localstatedir@,$localstatedir,;t t
+s,@libdir@,$libdir,;t t
+s,@includedir@,$includedir,;t t
+s,@oldincludedir@,$oldincludedir,;t t
+s,@infodir@,$infodir,;t t
+s,@mandir@,$mandir,;t t
+s,@build_alias@,$build_alias,;t t
+s,@host_alias@,$host_alias,;t t
+s,@target_alias@,$target_alias,;t t
+s,@DEFS@,$DEFS,;t t
+s,@ECHO_C@,$ECHO_C,;t t
+s,@ECHO_N@,$ECHO_N,;t t
+s,@ECHO_T@,$ECHO_T,;t t
+s,@LIBS@,$LIBS,;t t
+s,@CC@,$CC,;t t
+s,@CFLAGS@,$CFLAGS,;t t
+s,@LDFLAGS@,$LDFLAGS,;t t
+s,@CPPFLAGS@,$CPPFLAGS,;t t
+s,@ac_ct_CC@,$ac_ct_CC,;t t
+s,@EXEEXT@,$EXEEXT,;t t
+s,@OBJEXT@,$OBJEXT,;t t
+s,@CXX@,$CXX,;t t
+s,@CXXFLAGS@,$CXXFLAGS,;t t
+s,@ac_ct_CXX@,$ac_ct_CXX,;t t
+s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t
+s,@INSTALL_SCRIPT@,$INSTALL_SCRIPT,;t t
+s,@INSTALL_DATA@,$INSTALL_DATA,;t t
+s,@RANLIB@,$RANLIB,;t t
+s,@ac_ct_RANLIB@,$ac_ct_RANLIB,;t t
+s,@EXE@,$EXE,;t t
+s,@LIBPREFIX@,$LIBPREFIX,;t t
+s,@AR@,$AR,;t t
+s,@UP_DIR@,$UP_DIR,;t t
+s,@CPP@,$CPP,;t t
+s,@X_CFLAGS@,$X_CFLAGS,;t t
+s,@X_PRE_LIBS@,$X_PRE_LIBS,;t t
+s,@X_LIBS@,$X_LIBS,;t t
+s,@X_EXTRA_LIBS@,$X_EXTRA_LIBS,;t t
+s,@EGREP@,$EGREP,;t t
+s,@Xpm_LIBS@,$Xpm_LIBS,;t t
+s,@Xpm_CFLAGS@,$Xpm_CFLAGS,;t t
+s,@Xext_LIBS@,$Xext_LIBS,;t t
+s,@Xext_CFLAGS@,$Xext_CFLAGS,;t t
+s,@Xp_LIBS@,$Xp_LIBS,;t t
+s,@Xp_CFLAGS@,$Xp_CFLAGS,;t t
+s,@Xt_LIBS@,$Xt_LIBS,;t t
+s,@Xt_CFLAGS@,$Xt_CFLAGS,;t t
+s,@Xm_LIBS@,$Xm_LIBS,;t t
+s,@Xm_CFLAGS@,$Xm_CFLAGS,;t t
+s,@Sgm_LIBS@,$Sgm_LIBS,;t t
+s,@Sgm_CFLAGS@,$Sgm_CFLAGS,;t t
+s,@t1_LIBS@,$t1_LIBS,;t t
+s,@t1_CFLAGS@,$t1_CFLAGS,;t t
+s,@freetype2_LIBS@,$freetype2_LIBS,;t t
+s,@freetype2_CFLAGS@,$freetype2_CFLAGS,;t t
+s,@libpaper_LIBS@,$libpaper_LIBS,;t t
+s,@libpaper_CFLAGS@,$libpaper_CFLAGS,;t t
+s,@X@,$X,;t t
+s,@XPDF_TARGET@,$XPDF_TARGET,;t t
+s,@LIBOBJS@,$LIBOBJS,;t t
+s,@LTLIBOBJS@,$LTLIBOBJS,;t t
+CEOF
+
+_ACEOF
+
+ cat >>$CONFIG_STATUS <<\_ACEOF
+ # Split the substitutions into bite-sized pieces for seds with
+ # small command number limits, like on Digital OSF/1 and HP-UX.
+ ac_max_sed_lines=48
+ ac_sed_frag=1 # Number of current file.
+ ac_beg=1 # First line for current file.
+ ac_end=$ac_max_sed_lines # Line after last line for current file.
+ ac_more_lines=:
+ ac_sed_cmds=
+ while $ac_more_lines; do
+ if test $ac_beg -gt 1; then
+ sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+ else
+ sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+ fi
+ if test ! -s $tmp/subs.frag; then
+ ac_more_lines=false
+ else
+ # The purpose of the label and of the branching condition is to
+ # speed up the sed processing (if there are no `@' at all, there
+ # is no need to browse any of the substitutions).
+ # These are the two extra sed commands mentioned above.
+ (echo ':t
+ /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed
+ if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed"
+ else
+ ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed"
+ fi
+ ac_sed_frag=`expr $ac_sed_frag + 1`
+ ac_beg=$ac_end
+ ac_end=`expr $ac_end + $ac_max_sed_lines`
+ fi
+ done
+ if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds=cat
+ fi
+fi # test -n "$CONFIG_FILES"
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue
+ # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+ case $ac_file in
+ - | *:- | *:-:* ) # input from stdin
+ cat >$tmp/stdin
+ ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+ ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+ *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+ ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+ * ) ac_file_in=$ac_file.in ;;
+ esac
+
+ # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories.
+ ac_dir=`(dirname "$ac_file") 2>/dev/null ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$ac_file" : 'X\(//\)[^/]' \| \
+ X"$ac_file" : 'X\(//\)$' \| \
+ X"$ac_file" : 'X\(/\)' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X"$ac_file" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+ /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+ /^X\(\/\/\)$/{ s//\1/; q; }
+ /^X\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+ { if $as_mkdir_p; then
+ mkdir -p "$ac_dir"
+ else
+ as_dir="$ac_dir"
+ as_dirs=
+ while test ! -d "$as_dir"; do
+ as_dirs="$as_dir $as_dirs"
+ as_dir=`(dirname "$as_dir") 2>/dev/null ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+ /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+ /^X\(\/\/\)$/{ s//\1/; q; }
+ /^X\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+ done
+ test ! -n "$as_dirs" || mkdir $as_dirs
+ fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
+echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
+ { (exit 1); exit 1; }; }; }
+
+ ac_builddir=.
+
+if test "$ac_dir" != .; then
+ ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+ # A "../" for each directory in $ac_dir_suffix.
+ ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+ ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+ .) # No --srcdir option. We are building in place.
+ ac_srcdir=.
+ if test -z "$ac_top_builddir"; then
+ ac_top_srcdir=.
+ else
+ ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+ fi ;;
+ [\\/]* | ?:[\\/]* ) # Absolute path.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir ;;
+ *) # Relative path.
+ ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+ case "$ac_dir" in
+ .) ac_abs_builddir=`pwd`;;
+ [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+ *) ac_abs_builddir=`pwd`/"$ac_dir";;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+ case ${ac_top_builddir}. in
+ .) ac_abs_top_builddir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+ *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+ case $ac_srcdir in
+ .) ac_abs_srcdir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+ *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+ case $ac_top_srcdir in
+ .) ac_abs_top_srcdir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+ *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+ esac;;
+esac
+
+
+ case $INSTALL in
+ [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
+ *) ac_INSTALL=$ac_top_builddir$INSTALL ;;
+ esac
+
+ if test x"$ac_file" != x-; then
+ { echo "$as_me:$LINENO: creating $ac_file" >&5
+echo "$as_me: creating $ac_file" >&6;}
+ rm -f "$ac_file"
+ fi
+ # 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. */
+ if test x"$ac_file" = x-; then
+ configure_input=
+ else
+ configure_input="$ac_file. "
+ fi
+ configure_input=$configure_input"Generated from `echo $ac_file_in |
+ sed 's,.*/,,'` by configure."
+
+ # First look for the input files in the build tree, otherwise in the
+ # src tree.
+ ac_file_inputs=`IFS=:
+ for f in $ac_file_in; do
+ case $f in
+ -) echo $tmp/stdin ;;
+ [\\/$]*)
+ # Absolute (can't be DOS-style, as IFS=:)
+ test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+ { (exit 1); exit 1; }; }
+ echo "$f";;
+ *) # Relative
+ if test -f "$f"; then
+ # Build tree
+ echo "$f"
+ elif test -f "$srcdir/$f"; then
+ # Source tree
+ echo "$srcdir/$f"
+ else
+ # /dev/null tree
+ { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+ { (exit 1); exit 1; }; }
+ fi;;
+ esac
+ done` || { (exit 1); exit 1; }
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+ sed "$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s,@configure_input@,$configure_input,;t t
+s,@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,@top_builddir@,$ac_top_builddir,;t t
+s,@abs_top_builddir@,$ac_abs_top_builddir,;t t
+s,@INSTALL@,$ac_INSTALL,;t t
+" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out
+ rm -f $tmp/stdin
+ if test x"$ac_file" != x-; then
+ mv $tmp/out $ac_file
+ else
+ cat $tmp/out
+ rm -f $tmp/out
+ fi
+
+done
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+#
+# CONFIG_HEADER section.
+#
+
+# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where
+# NAME is the cpp macro being defined and VALUE is the value it is being given.
+#
+# ac_d sets the value in "#define NAME VALUE" lines.
+ac_dA='s,^\([ ]*\)#\([ ]*define[ ][ ]*\)'
+ac_dB='[ ].*$,\1#\2'
+ac_dC=' '
+ac_dD=',;t'
+# ac_u turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
+ac_uA='s,^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)'
+ac_uB='$,\1#\2define\3'
+ac_uC=' '
+ac_uD=',;t'
+
+for ac_file in : $CONFIG_HEADERS; do test "x$ac_file" = x: && continue
+ # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+ case $ac_file in
+ - | *:- | *:-:* ) # input from stdin
+ cat >$tmp/stdin
+ ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+ ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+ *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+ ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+ * ) ac_file_in=$ac_file.in ;;
+ esac
+
+ test x"$ac_file" != x- && { echo "$as_me:$LINENO: creating $ac_file" >&5
+echo "$as_me: creating $ac_file" >&6;}
+
+ # First look for the input files in the build tree, otherwise in the
+ # src tree.
+ ac_file_inputs=`IFS=:
+ for f in $ac_file_in; do
+ case $f in
+ -) echo $tmp/stdin ;;
+ [\\/$]*)
+ # Absolute (can't be DOS-style, as IFS=:)
+ test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+ { (exit 1); exit 1; }; }
+ # Do quote $f, to prevent DOS paths from being IFS'd.
+ echo "$f";;
+ *) # Relative
+ if test -f "$f"; then
+ # Build tree
+ echo "$f"
+ elif test -f "$srcdir/$f"; then
+ # Source tree
+ echo "$srcdir/$f"
+ else
+ # /dev/null tree
+ { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+ { (exit 1); exit 1; }; }
+ fi;;
+ esac
+ done` || { (exit 1); exit 1; }
+ # Remove the trailing spaces.
+ sed 's/[ ]*$//' $ac_file_inputs >$tmp/in
+
+_ACEOF
+
+# Transform confdefs.h into two sed scripts, `conftest.defines' and
+# `conftest.undefs', that substitutes the proper values into
+# config.h.in to produce config.h. The first handles `#define'
+# templates, and the second `#undef' templates.
+# And first: Protect against being on the right side of a sed subst in
+# config.status. Protect against being in an unquoted here document
+# in config.status.
+rm -f conftest.defines conftest.undefs
+# Using a here document instead of a string reduces the quoting nightmare.
+# Putting comments in sed scripts is not portable.
+#
+# `end' is used to avoid that the second main sed command (meant for
+# 0-ary CPP macros) applies to n-ary macro definitions.
+# See the Autoconf documentation for `clear'.
+cat >confdef2sed.sed <<\_ACEOF
+s/[\\&,]/\\&/g
+s,[\\$`],\\&,g
+t clear
+: clear
+s,^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*\)\(([^)]*)\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1\2${ac_dC}\3${ac_dD},gp
+t end
+s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD},gp
+: end
+_ACEOF
+# If some macros were called several times there might be several times
+# the same #defines, which is useless. Nevertheless, we may not want to
+# sort them, since we want the *last* AC-DEFINE to be honored.
+uniq confdefs.h | sed -n -f confdef2sed.sed >conftest.defines
+sed 's/ac_d/ac_u/g' conftest.defines >conftest.undefs
+rm -f confdef2sed.sed
+
+# This sed command replaces #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.
+cat >>conftest.undefs <<\_ACEOF
+s,^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*,/* & */,
+_ACEOF
+
+# Break up conftest.defines because some shells have a limit on the size
+# of here documents, and old seds have small limits too (100 cmds).
+echo ' # Handle all the #define templates only if necessary.' >>$CONFIG_STATUS
+echo ' if grep "^[ ]*#[ ]*define" $tmp/in >/dev/null; then' >>$CONFIG_STATUS
+echo ' # If there are no defines, we may have an empty if/fi' >>$CONFIG_STATUS
+echo ' :' >>$CONFIG_STATUS
+rm -f conftest.tail
+while grep . conftest.defines >/dev/null
+do
+ # Write a limited-size here document to $tmp/defines.sed.
+ echo ' cat >$tmp/defines.sed <<CEOF' >>$CONFIG_STATUS
+ # Speed up: don't consider the non `#define' lines.
+ echo '/^[ ]*#[ ]*define/!b' >>$CONFIG_STATUS
+ # Work around the forget-to-reset-the-flag bug.
+ echo 't clr' >>$CONFIG_STATUS
+ echo ': clr' >>$CONFIG_STATUS
+ sed ${ac_max_here_lines}q conftest.defines >>$CONFIG_STATUS
+ echo 'CEOF
+ sed -f $tmp/defines.sed $tmp/in >$tmp/out
+ rm -f $tmp/in
+ mv $tmp/out $tmp/in
+' >>$CONFIG_STATUS
+ sed 1,${ac_max_here_lines}d conftest.defines >conftest.tail
+ rm -f conftest.defines
+ mv conftest.tail conftest.defines
+done
+rm -f conftest.defines
+echo ' fi # grep' >>$CONFIG_STATUS
+echo >>$CONFIG_STATUS
+
+# Break up conftest.undefs because some shells have a limit on the size
+# of here documents, and old seds have small limits too (100 cmds).
+echo ' # Handle all the #undef templates' >>$CONFIG_STATUS
+rm -f conftest.tail
+while grep . conftest.undefs >/dev/null
+do
+ # Write a limited-size here document to $tmp/undefs.sed.
+ echo ' cat >$tmp/undefs.sed <<CEOF' >>$CONFIG_STATUS
+ # Speed up: don't consider the non `#undef'
+ echo '/^[ ]*#[ ]*undef/!b' >>$CONFIG_STATUS
+ # Work around the forget-to-reset-the-flag bug.
+ echo 't clr' >>$CONFIG_STATUS
+ echo ': clr' >>$CONFIG_STATUS
+ sed ${ac_max_here_lines}q conftest.undefs >>$CONFIG_STATUS
+ echo 'CEOF
+ sed -f $tmp/undefs.sed $tmp/in >$tmp/out
+ rm -f $tmp/in
+ mv $tmp/out $tmp/in
+' >>$CONFIG_STATUS
+ sed 1,${ac_max_here_lines}d conftest.undefs >conftest.tail
+ rm -f conftest.undefs
+ mv conftest.tail conftest.undefs
+done
+rm -f conftest.undefs
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+ # 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. */
+ if test x"$ac_file" = x-; then
+ echo "/* Generated by configure. */" >$tmp/config.h
+ else
+ echo "/* $ac_file. Generated by configure. */" >$tmp/config.h
+ fi
+ cat $tmp/in >>$tmp/config.h
+ rm -f $tmp/in
+ if test x"$ac_file" != x-; then
+ if diff $ac_file $tmp/config.h >/dev/null 2>&1; then
+ { echo "$as_me:$LINENO: $ac_file is unchanged" >&5
+echo "$as_me: $ac_file is unchanged" >&6;}
+ else
+ ac_dir=`(dirname "$ac_file") 2>/dev/null ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$ac_file" : 'X\(//\)[^/]' \| \
+ X"$ac_file" : 'X\(//\)$' \| \
+ X"$ac_file" : 'X\(/\)' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X"$ac_file" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+ /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+ /^X\(\/\/\)$/{ s//\1/; q; }
+ /^X\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+ { if $as_mkdir_p; then
+ mkdir -p "$ac_dir"
+ else
+ as_dir="$ac_dir"
+ as_dirs=
+ while test ! -d "$as_dir"; do
+ as_dirs="$as_dir $as_dirs"
+ as_dir=`(dirname "$as_dir") 2>/dev/null ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+ /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+ /^X\(\/\/\)$/{ s//\1/; q; }
+ /^X\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+ done
+ test ! -n "$as_dirs" || mkdir $as_dirs
+ fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
+echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
+ { (exit 1); exit 1; }; }; }
+
+ rm -f $ac_file
+ mv $tmp/config.h $ac_file
+ fi
+ else
+ cat $tmp/config.h
+ rm -f $tmp/config.h
+ fi
+done
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+{ (exit 0); exit 0; }
+_ACEOF
+chmod +x $CONFIG_STATUS
+ac_clean_files=$ac_clean_files_save
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded. So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status. When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+ ac_cs_success=:
+ ac_config_status_args=
+ test "$silent" = yes &&
+ ac_config_status_args="$ac_config_status_args --quiet"
+ exec 5>/dev/null
+ $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+ exec 5>>config.log
+ # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+ # would make configure fail if this is the last instruction.
+ $ac_cs_success || { (exit 1); exit 1; }
+fi
+
+
+if test -n "$no_x" -o "x$smr_have_Xm_library" != xyes -o "x$smr_have_freetype2_library" != xyes; then
+ if test -n "$no_x"; then
+ { echo "$as_me:$LINENO: WARNING: Couldn't find X" >&5
+echo "$as_me: WARNING: Couldn't find X" >&2;};
+ fi
+ if test "x$smr_have_Xm_library" != xyes; then
+ { echo "$as_me:$LINENO: WARNING: Couldn't find Motif" >&5
+echo "$as_me: WARNING: Couldn't find Motif" >&2;};
+ fi
+ if test "x$smr_have_freetype2_library" != xyes; then
+ { echo "$as_me:$LINENO: WARNING: Couldn't find FreeType" >&5
+echo "$as_me: WARNING: Couldn't find FreeType" >&2;};
+ fi
+ { echo "$as_me:$LINENO: WARNING: -- You will be able to compile pdftops, pdftotext,
+ pdfinfo, pdffonts, and pdfimages, but not xpdf or pdftoppm" >&5
+echo "$as_me: WARNING: -- You will be able to compile pdftops, pdftotext,
+ pdfinfo, pdffonts, and pdfimages, but not xpdf or pdftoppm" >&2;}
+fi
diff --git a/configure.in b/configure.in
new file mode 100644
index 0000000..81e7015
--- /dev/null
+++ b/configure.in
@@ -0,0 +1,353 @@
+dnl Process this file with autoconf to produce a configure script.
+dnl Copyright 1998-2005 Glyph & Cog, LLC
+
+AC_PREREQ(2.57)
+
+AC_INIT(xpdf/xpdf.cc)
+AC_CONFIG_HEADER(aconf.h)
+
+dnl ##### Optional features.
+AC_ARG_ENABLE(a4-paper,
+[ --enable-a4-paper use A4 paper size instead of Letter for
+ PostScript output],
+AC_DEFINE(A4_PAPER))
+AC_ARG_ENABLE(no-text-select,
+[ --enable-no-text-select do not allow text selection],
+AC_DEFINE(NO_TEXT_SELECT))
+AC_ARG_ENABLE(opi,
+[ --enable-opi include support for OPI comments],
+AC_DEFINE(OPI_SUPPORT))
+AC_ARG_ENABLE(multithreaded,
+[ --enable-multithreaded include support for multithreading],
+AC_DEFINE(MULTITHREADED))
+AC_ARG_ENABLE(exceptions,
+[ --enable-exceptions use C++ exceptions],
+AC_DEFINE(USE_EXCEPTIONS))
+AC_ARG_ENABLE(wordlist,
+[ --enable-wordlist include support for building word lists],
+AC_DEFINE(TEXTOUT_WORD_LIST))
+AC_ARG_ENABLE(fixedpoint,
+[ --enable-fixedpoint use fixed point (instead of floating point) arithmetic],
+AC_DEFINE(USE_FIXEDPOINT))
+AC_ARG_ENABLE(cmyk,
+[ --enable-cmyk include support for CMYK rasterization],
+AC_DEFINE(SPLASH_CMYK))
+AC_ARG_WITH(appdef-dir,
+[ --with-appdef-dir set app-defaults directory],
+AC_DEFINE_UNQUOTED(APPDEFDIR, "$with_appdef_dir"))
+
+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 programs.
+AC_PROG_CC
+AC_ISC_POSIX
+AC_PROG_CC_STDC
+#if test -z "$CXX" -a "$CC" = "gcc"; then
+# CXX="gcc"
+#fi
+AC_PROG_CXX
+AC_PROG_INSTALL
+AC_PROG_RANLIB
+
+dnl ##### Default values for Unix.
+EXE=""
+LIBPREFIX="lib"
+AR="ar rc"
+UP_DIR=""
+
+dnl ##### Check for OS/2.
+AC_CACHE_CHECK([for OS/2 (with EMX)],
+xpdf_cv_sys_os2,
+[AC_TRY_COMPILE([],
+[__EMX__],
+xpdf_cv_sys_os2=yes, xpdf_cv_sys_os2=no)])
+if test "$xpdf_cv_sys_os2" = yes; then
+ EXE=".exe"
+ LIBPREFIX=""
+ AR="ar -rc"
+fi
+
+dnl ##### Check for DOS (with DJGPP).
+AC_CACHE_CHECK([for DOS (with DJGPP)],
+xpdf_cv_sys_dos,
+[AC_TRY_COMPILE([],
+[__DJGPP__],
+xpdf_cv_sys_dos=yes, xpdf_cv_sys_dos=no)])
+if test "$xpdf_cv_sys_dos" = yes; then
+ EXE=".exe"
+ LIBPREFIX="lib"
+ AR="ar -rc"
+ UP_DIR="../"
+fi
+
+dnl ##### Do substitutions.
+AC_SUBST(EXE)
+AC_SUBST(LIBPREFIX)
+AC_SUBST(AR)
+AC_SUBST(UP_DIR)
+
+dnl ##### Checks for header files.
+AC_PATH_XTRA
+AC_HEADER_DIRENT
+
+dnl ##### Switch over to C++. This will make the checks below a little
+dnl ##### bit stricter (requiring function prototypes in include files).
+dnl ##### (99% of xpdf is written in C++.)
+AC_LANG_CPLUSPLUS
+
+dnl ##### Check for extra libraries needed by X. (LynxOS needs this.)
+AC_CHECK_FUNC(gethostbyname)
+if test $ac_cv_func_gethostbyname = no; then
+ AC_CHECK_LIB(bsd, gethostbyname, X_EXTRA_LIBS="$X_EXTRA_LIBS -lbsd")
+fi
+
+dnl ##### Look for header that defines select() and fd_set.
+AC_MSG_CHECKING([select() and fd_set in sys/select.h and sys/bsdtypes.h])
+AC_TRY_COMPILE([#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <sys/types.h>],
+ [fd_set fds;
+select(0, NULL, NULL, NULL, NULL);], xpdf_ok=yes, xpdf_ok=no)
+if test $xpdf_ok = yes; then
+ AC_MSG_RESULT([not needed])
+else
+ AC_TRY_COMPILE([#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/select.h>],
+ [fd_set fds;
+select(0, NULL, NULL, NULL, NULL);], xpdf_ok=yes, xpdf_ok=no)
+ if test $xpdf_ok = yes; then
+ AC_DEFINE(HAVE_SYS_SELECT_H)
+ AC_MSG_RESULT([need sys/select.h])
+ else
+ AC_TRY_COMPILE([#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/bsdtypes.h>],
+ [fd_set fds;
+select(0, NULL, NULL, NULL, NULL);], xpdf_ok=yes, xpdf_ok=no)
+ if test $xpdf_ok = yes; then
+ AC_DEFINE(HAVE_SYS_BSDTYPES_H)
+ AC_MSG_RESULT([need sys/bsdtypes.h])
+ else
+ AC_MSG_RESULT([problem])
+ fi
+ fi
+fi
+
+dnl ##### Look for header that defines FD_ZERO.
+AC_MSG_CHECKING([FD_ZERO and strings.h or bstring.h])
+AC_TRY_COMPILE([#include <stdlib.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif],
+[fd_set fds; FD_ZERO(&fds);], xpdf_ok=yes, xpdf_ok=no)
+if test $xpdf_ok = yes; then
+ AC_MSG_RESULT([not needed])
+else
+ AC_TRY_COMPILE([#include <stdlib.h>
+#include <sys/types.h>
+#include <strings.h>
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif],
+ [fd_set fds; FD_ZERO(&fds);], xpdf_ok=yes, xpdf_ok=no)
+ if test $xpdf_ok = yes; then
+ AC_DEFINE(HAVE_STRINGS_H)
+ AC_MSG_RESULT([need strings.h])
+ else
+ AC_TRY_COMPILE([#include <stdlib.h>
+#include <sys/types.h>
+#include <bstring.h>
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif],
+ [fd_set fds; FD_ZERO(&fds);], xpdf_ok=yes, xpdf_ok=no)
+ if test $xpdf_ok = yes; then
+ AC_DEFINE(HAVE_BSTRING_H)
+ AC_MSG_RESULT([need bstring.h])
+ else
+ AC_MSG_RESULT([problem])
+ fi
+ fi
+fi
+
+dnl ##### Look for rewinddir.
+AC_CHECK_FUNCS(rewinddir)
+if test $ac_cv_func_rewinddir = no; then
+ AC_CHECK_LIB(cposix, rewinddir)
+fi
+
+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
+
+dnl ##### Check select argument type: on HP-UX before version 10, select
+dnl ##### takes (int *) instead of (fd_set *).
+AC_CACHE_CHECK([whether select takes fd_set arguments],
+xpdf_cv_func_select_arg,
+[AC_TRY_COMPILE([#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif],
+[fd_set fds;
+select(1, &fds, &fds, &fds, 0);],
+xpdf_cv_func_select_arg=yes, xpdf_cv_func_select_arg=no)])
+if test "$xpdf_cv_func_select_arg" != yes; then
+ AC_DEFINE(SELECT_TAKES_INT)
+fi
+
+dnl ##### Back to C for the library tests.
+AC_LANG_C
+
+dnl ##### Check for fseeko/ftello or fseek64/ftell64
+dnl The LARGEFILE and FSEEKO macros have to be called in C, not C++, mode.
+AC_SYS_LARGEFILE
+AC_FUNC_FSEEKO
+AC_CHECK_FUNCS(fseek64, xpdf_cv_func_fseek64=yes, xpdf_cv_func_fseek64=no)
+AC_CHECK_FUNCS(ftell64, xpdf_cv_func_ftell64=yes, xpdf_cv_func_ftell64=no)
+if test "$xpdf_cv_func_fseek64" = yes -a "$xpdf_cv_func_ftell64" = yes; then
+ AC_DEFINE(HAVE_FSEEK64)
+fi
+
+dnl ##### Check for libXpm.
+if test -z "$no_x"; then
+ smr_CHECK_LIB(Xpm, Xpm, [pixmap library - used only for icon],
+ XpmCreatePixmapFromData, X11/xpm.h,
+ $X_LIBS $X_PRE_LIBS $X_EXTRA_LIBS -lX11, $X_CFLAGS)
+ AC_SUBST(Xpm_LIBS)
+ AC_SUBST(Xpm_CFLAGS)
+fi
+
+dnl ##### Check for Motif (libXm).
+if test -z "$no_x"; then
+ dnl # XextAddDisplay isn't defined in any header file, so we provide a
+ dnl # bogus prototype (so the compiler doesn't complain) and a bogus
+ dnl # header file (so the smr macro doesn't break).
+ smr_CHECK_LIB(Xext, Xext, [Motif library],
+ XextAddDisplay, X11/Xlib.h,
+ $X_LIBS $X_PRE_LIBS $X_EXTRA_LIBS -lX11, $X_CFLAGS,
+ [int XextAddDisplay();])
+ AC_SUBST(Xext_LIBS)
+ AC_SUBST(Xext_CFLAGS)
+ smr_CHECK_LIB(Xp, Xp, [Motif library],
+ XpStartPage, X11/extensions/Print.h,
+ $X_LIBS $X_PRE_LIBS $Xext_LIBS $X_EXTRA_LIBS -lX11, $X_CFLAGS)
+ AC_SUBST(Xp_LIBS)
+ AC_SUBST(Xp_CFLAGS)
+ smr_CHECK_LIB(Xt, Xt, [Motif library],
+ XtAppInitialize, X11/Intrinsic.h,
+ $X_LIBS $X_PRE_LIBS $X_EXTRA_LIBS -lX11, $X_CFLAGS)
+ AC_SUBST(Xt_LIBS)
+ AC_SUBST(Xt_CFLAGS)
+ smr_CHECK_LIB(Xm, Xm, [Motif library],
+ XmCreateForm, Xm/XmAll.h,
+ $Xt_LIBS $X_LIBS $X_PRE_LIBS $Xp_LIBS $Xext_LIBS $X_EXTRA_LIBS -lX11, $X_CFLAGS)
+ AC_SUBST(Xm_LIBS)
+ AC_SUBST(Xm_CFLAGS)
+ smr_CHECK_LIB(Sgm, Sgm, [SGI Motif library],
+ SgCreateHorzPanedWindow, Sgm/HPanedW.h,
+ $Xm_LIBS $Xt_LIBS $X_LIBS $X_PRE_LIBS $Xp_LIBS $Xext_LIBS $X_EXTRA_LIBS -lX11, $X_CFLAGS)
+ AC_SUBST(Sgm_LIBS)
+ AC_SUBST(Sgm_CFLAGS)
+
+ dnl # check for XtAppSetExitFlag, which didn't exist prior to X11R6 (?)
+ if test "x$smr_have_Xt_library" = xyes; then
+ AC_CHECK_LIB(Xt, XtAppSetExitFlag,
+ AC_DEFINE(HAVE_XTAPPSETEXITFLAG), ,
+ [$Xt_LIBS $X_LIBS $X_PRE_LIBS $Xp_LIBS $Xext_LIBS $X_EXTRA_LIBS -lX11])
+ fi
+fi
+
+dnl ##### Check for t1lib.
+smr_CHECK_LIB(t1, t1, [Type 1 font rasterizer],
+ T1_InitLib, t1lib.h,
+ -lm, $X_CFLAGS)
+AC_SUBST(t1_LIBS)
+AC_SUBST(t1_CFLAGS)
+
+dnl ##### Check for FreeType 2.x.
+dnl ##### (Note: FT_Get_Name_Index was added in FT 2.0.5, and is
+dnl ##### the reason that Xpdf requires 2.0.5+.)
+smr_CHECK_LIB(freetype2, freetype, [FreeType2 font rasterizer - version 2.0.5+],
+ FT_Get_Name_Index, ft2build.h, -lm)
+AC_SUBST(freetype2_LIBS)
+AC_SUBST(freetype2_CFLAGS)
+if test "x$smr_have_freetype2_library" = xyes; then
+ AC_DEFINE(HAVE_FREETYPE_FREETYPE_H)
+ AC_DEFINE(HAVE_SPLASH)
+fi
+
+dnl ##### Check for libpaper (Debian).
+smr_CHECK_LIB(libpaper, paper, [Debian libpaper], paperinit, paper.h)
+AC_SUBST(libpaper_LIBS)
+AC_SUBST(libpaper_CFLAGS)
+
+dnl ##### Disable X-specific stuff in top-level Makefile.
+if test -n "$no_x" -o "x$smr_have_Xm_library" != xyes -o "x$smr_have_freetype2_library" != xyes; then
+ X="#"
+ XPDF_TARGET="all-no-x"
+else
+ X=""
+ XPDF_TARGET="all"
+fi
+AC_SUBST(X)
+AC_SUBST(XPDF_TARGET)
+
+dnl ##### Write the makefiles.
+AC_OUTPUT(Makefile goo/Makefile fofi/Makefile splash/Makefile xpdf/Makefile)
+
+dnl ##### Warn user if X is missing.
+if test -n "$no_x" -o "x$smr_have_Xm_library" != xyes -o "x$smr_have_freetype2_library" != xyes; then
+ if test -n "$no_x"; then
+ AC_MSG_WARN([Couldn't find X]);
+ fi
+ if test "x$smr_have_Xm_library" != xyes; then
+ AC_MSG_WARN([Couldn't find Motif]);
+ fi
+ if test "x$smr_have_freetype2_library" != xyes; then
+ AC_MSG_WARN([Couldn't find FreeType]);
+ fi
+ AC_MSG_WARN([-- You will be able to compile pdftops, pdftotext,
+ pdfinfo, pdffonts, and pdfimages, but not xpdf or pdftoppm])
+fi
diff --git a/dj_make.bat b/dj_make.bat
new file mode 100644
index 0000000..d01b792
--- /dev/null
+++ b/dj_make.bat
@@ -0,0 +1,81 @@
+set CC=gcc
+set CFLAGS=-g -O2 -I.. -I..\fofi -I..\goo
+set CXX=gpp
+set CXXFLAGS=%CFLAGS%
+set LIBPROG=ar
+
+copy aconf-dj.h aconf.h
+
+cd goo
+%CXX% %CXXFLAGS% -c GHash.cc
+%CXX% %CXXFLAGS% -c GList.cc
+%CXX% %CXXFLAGS% -c GString.cc
+%CXX% %CXXFLAGS% -c gmem.cc
+%CXX% %CXXFLAGS% -c gmempp.cc
+%CXX% %CXXFLAGS% -c gfile.cc
+%CC% %CFLAGS% -c parseargs.c
+del libGoo.a
+%LIBPROG% -rc libGoo.a GHash.o GList.o GString.o gmempp.o gfile.o gmem.o parseargs.o
+
+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% -rc libfofi.a FoFiBase.o FoFiEncodings.o FoFiTrueType.o FoFiType1.o FoFiType1C.o
+
+cd ..\xpdf
+del *.o
+%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 ImageOutputDev.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 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 SecurityHandler.cc
+%CXX% %CXXFLAGS% -c Stream.cc
+%CXX% %CXXFLAGS% -c TextOutputDev.cc
+%CXX% %CXXFLAGS% -c UnicodeMap.cc
+%CXX% %CXXFLAGS% -c UnicodeTypeTable.cc
+%CXX% %CXXFLAGS% -c XRef.cc
+del libxpdf.a
+%LIBPROG% -rc libxpdf.a *.o
+
+%CXX% %CXXFLAGS% -o pdftops.exe pdftops.cc libxpdf.a ..\fofi\libfofi.a ..\goo\libGoo.a
+
+%CXX% %CXXFLAGS% -o pdftotext.exe pdftotext.cc libxpdf.a ..\fofi\libfofi.a ..\goo\libGoo.a
+
+%CXX% %CXXFLAGS% -o pdfinfo.exe pdfinfo.cc libxpdf.a ..\fofi\libfofi.a ..\goo\libGoo.a
+
+%CXX% %CXXFLAGS% -o pdffonts.exe pdffonts.cc libxpdf.a ..\fofi\libfofi.a ..\goo\libGoo.a
+
+%CXX% %CXXFLAGS% -o pdfimages.exe pdfimages.cc libxpdf.a ..\fofi\libfofi.a ..\goo\libGoo.a
+
+cd ..
diff --git a/doc/pdffonts.1 b/doc/pdffonts.1
new file mode 100644
index 0000000..4cb3059
--- /dev/null
+++ b/doc/pdffonts.1
@@ -0,0 +1,142 @@
+.\" Copyright 1999-2007 Glyph & Cog, LLC
+.TH pdffonts 1 "27 February 2007"
+.SH NAME
+pdffonts \- Portable Document Format (PDF) font analyzer (version
+3.02)
+.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 1C (OT) -- OpenType with 8-bit CFF data
+.RE
+.RS
+Type 3
+.RE
+.RS
+TrueType
+.RE
+.RS
+TrueType (OT) -- OpenType with 8-bit TrueType data
+.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 Type 0C (OT) -- OpenType with CID CFF data
+.RE
+.RS
+CID TrueType -- 16-bit TrueType font
+.RE
+.RS
+CID TrueType (OT) -- OpenType with CID TrueType data
+.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-2007 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/
diff --git a/doc/pdffonts.cat b/doc/pdffonts.cat
new file mode 100644
index 0000000..fa50dcf
--- /dev/null
+++ b/doc/pdffonts.cat
@@ -0,0 +1,112 @@
+pdffonts(1) pdffonts(1)
+
+
+
+NAME
+ pdffonts - Portable Document Format (PDF) font analyzer
+ (version 3.02)
+
+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 1C (OT) -- OpenType with 8-bit CFF data
+ Type 3
+ TrueType
+ TrueType (OT) -- OpenType with 8-bit TrueType data
+ CID Type 0 -- 16-bit font with no specified type
+ CID Type 0C -- 16-bit PostScript CFF font
+ CID Type 0C (OT) -- OpenType with CID CFF data
+ CID TrueType -- 16-bit TrueType font
+ CID TrueType (OT) -- OpenType with CID TrueType
+ data
+
+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-2007 Glyph & Cog, LLC.
+
+SEE ALSO
+ xpdf(1), pdftops(1), pdftotext(1), pdfinfo(1),
+ pdftoppm(1), pdfimages(1), xpdfrc(5)
+ http://www.foolabs.com/xpdf/
+
+
+
+ 27 February 2007 pdffonts(1)
diff --git a/doc/pdffonts.hlp b/doc/pdffonts.hlp
new file mode 100644
index 0000000..f964dc1
--- /dev/null
+++ b/doc/pdffonts.hlp
@@ -0,0 +1,122 @@
+! 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 1C (OT) -- OpenType with 8-bit CFF data
+ Type 3
+ TrueType
+ TrueType (OT) -- OpenType with 8-bit TrueType data
+ CID Type 0 -- 16-bit font with no specified type
+ CID Type 0C -- 16-bit PostScript CFF font
+ CID Type 0C (OT) -- OpenType with CID CFF data
+ CID TrueType -- 16-bit TrueType font
+ CID TrueType (OT) -- OpenType with CID TrueType
+ data
+
+ ()
+
+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-2007 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/
+
+ ()
+
diff --git a/doc/pdfimages.1 b/doc/pdfimages.1
new file mode 100644
index 0000000..8440147
--- /dev/null
+++ b/doc/pdfimages.1
@@ -0,0 +1,102 @@
+.\" Copyright 1998-2007 Glyph & Cog, LLC
+.TH pdfimages 1 "27 February 2007"
+.SH NAME
+pdfimages \- Portable Document Format (PDF) image extractor
+(version 3.02)
+.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-2007 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/
diff --git a/doc/pdfimages.cat b/doc/pdfimages.cat
new file mode 100644
index 0000000..b5ae119
--- /dev/null
+++ b/doc/pdfimages.cat
@@ -0,0 +1,92 @@
+pdfimages(1) pdfimages(1)
+
+
+
+NAME
+ pdfimages - Portable Document Format (PDF) image extractor
+ (version 3.02)
+
+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-2007 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/
+
+
+
+ 27 February 2007 pdfimages(1)
diff --git a/doc/pdfimages.hlp b/doc/pdfimages.hlp
new file mode 100644
index 0000000..9706099
--- /dev/null
+++ b/doc/pdfimages.hlp
@@ -0,0 +1,101 @@
+! 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-2007 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/
+
+ ()
+
diff --git a/doc/pdfinfo.1 b/doc/pdfinfo.1
new file mode 100644
index 0000000..ca2a813
--- /dev/null
+++ b/doc/pdfinfo.1
@@ -0,0 +1,158 @@
+.\" Copyright 1999-2007 Glyph & Cog, LLC
+.TH pdfinfo 1 "27 February 2007"
+.SH NAME
+pdfinfo \- Portable Document Format (PDF) document information
+extractor (version 3.02)
+.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-2007 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/
diff --git a/doc/pdfinfo.cat b/doc/pdfinfo.cat
new file mode 100644
index 0000000..4b429ab
--- /dev/null
+++ b/doc/pdfinfo.cat
@@ -0,0 +1,119 @@
+pdfinfo(1) pdfinfo(1)
+
+
+
+NAME
+ pdfinfo - Portable Document Format (PDF) document informa-
+ tion extractor (version 3.02)
+
+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-2007 Glyph & Cog, LLC.
+
+SEE ALSO
+ xpdf(1), pdftops(1), pdftotext(1), pdffonts(1),
+ pdftoppm(1), pdfimages(1), xpdfrc(5)
+ http://www.foolabs.com/xpdf/
+
+
+
+ 27 February 2007 pdfinfo(1)
diff --git a/doc/pdfinfo.hlp b/doc/pdfinfo.hlp
new file mode 100644
index 0000000..559a123
--- /dev/null
+++ b/doc/pdfinfo.hlp
@@ -0,0 +1,129 @@
+! 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-2007 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/
+
+ ()
+
diff --git a/doc/pdftoppm.1 b/doc/pdftoppm.1
new file mode 100644
index 0000000..f159490
--- /dev/null
+++ b/doc/pdftoppm.1
@@ -0,0 +1,119 @@
+.\" Copyright 2005-2007 Glyph & Cog, LLC
+.TH pdftoppm 1 "27 February 2007"
+.SH NAME
+pdftoppm \- Portable Document Format (PDF) to Portable Pixmap (PPM)
+converter (version 3.02)
+.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 \-aaVector " yes | no"
+Enable or disable vector anti-aliasing. This defaults to "yes".
+.RB "[config file: " vectorAntialias ]
+.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-2007 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/
diff --git a/doc/pdftoppm.cat b/doc/pdftoppm.cat
new file mode 100644
index 0000000..af7d3c9
--- /dev/null
+++ b/doc/pdftoppm.cat
@@ -0,0 +1,108 @@
+pdftoppm(1) pdftoppm(1)
+
+
+
+NAME
+ pdftoppm - Portable Document Format (PDF) to Portable
+ Pixmap (PPM) converter (version 3.02)
+
+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]
+
+ -aaVector yes | no
+ Enable or disable vector anti-aliasing. This
+ defaults to "yes". [config file: vectorAntialias]
+
+ -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 pdftoppm software and documentation are copyright
+ 1996-2007 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/
+
+
+
+ 27 February 2007 pdftoppm(1)
diff --git a/doc/pdftoppm.hlp b/doc/pdftoppm.hlp
new file mode 100644
index 0000000..bc83e7c
--- /dev/null
+++ b/doc/pdftoppm.hlp
@@ -0,0 +1,118 @@
+! 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]
+
+ -aaVector yes | no
+ Enable or disable vector anti-aliasing. This
+ defaults to "yes". [config file: vectorAntialias]
+
+ -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-2007 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/
+
+ ()
+
diff --git a/doc/pdftops.1 b/doc/pdftops.1
new file mode 100644
index 0000000..c9ec258
--- /dev/null
+++ b/doc/pdftops.1
@@ -0,0 +1,235 @@
+.\" Copyright 1996-2007 Glyph & Cog, LLC
+.TH pdftops 1 "27 February 2007"
+.SH NAME
+pdftops \- Portable Document Format (PDF) to PostScript converter
+(version 3.02)
+.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 and masked image generation.
+.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
+.B \-preload
+Convert PDF forms to PS procedures, and preload image data. This uses
+more memory in the PostScript interpreter, but generates significantly
+smaller PS files in situations where, e.g., the same image is drawn on
+every page of a long document.
+.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-2007 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/
diff --git a/doc/pdftops.cat b/doc/pdftops.cat
new file mode 100644
index 0000000..b370774
--- /dev/null
+++ b/doc/pdftops.cat
@@ -0,0 +1,229 @@
+pdftops(1) pdftops(1)
+
+
+
+NAME
+ pdftops - Portable Document Format (PDF) to PostScript
+ converter (version 3.02)
+
+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 and masked
+ image generation. [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]
+
+ -preload
+ Convert PDF forms to PS procedures, and preload
+ image data. This uses more memory in the
+ PostScript interpreter, but generates significantly
+ smaller PS files in situations where, e.g., the
+ same image is drawn on every page of a long docu-
+ ment.
+
+ -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-2007 Glyph & Cog, LLC.
+
+SEE ALSO
+ xpdf(1), pdftotext(1), pdfinfo(1), pdffonts(1),
+ pdftoppm(1), pdfimages(1), xpdfrc(5)
+ http://www.foolabs.com/xpdf/
+
+
+
+ 27 February 2007 pdftops(1)
diff --git a/doc/pdftops.hlp b/doc/pdftops.hlp
new file mode 100644
index 0000000..2cdf8da
--- /dev/null
+++ b/doc/pdftops.hlp
@@ -0,0 +1,239 @@
+! 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 and masked
+ image generation. [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]
+
+ -preload
+ Convert PDF forms to PS procedures, and preload
+ image data. This uses more memory in the
+ PostScript interpreter, but generates significantly
+ smaller PS files in situations where, e.g., the
+ same image is drawn on every page of a long docu-
+ ment.
+
+ -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
+ cropping. [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-2007 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/
+
+ ()
+
diff --git a/doc/pdftotext.1 b/doc/pdftotext.1
new file mode 100644
index 0000000..c16a1ea
--- /dev/null
+++ b/doc/pdftotext.1
@@ -0,0 +1,137 @@
+.\" Copyright 1997-2007 Glyph & Cog, LLC
+.TH pdftotext 1 "27 Febuary 2007"
+.SH NAME
+pdftotext \- Portable Document Format (PDF) to text converter
+(version 3.02)
+.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-2007 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/
diff --git a/doc/pdftotext.cat b/doc/pdftotext.cat
new file mode 100644
index 0000000..9990226
--- /dev/null
+++ b/doc/pdftotext.cat
@@ -0,0 +1,120 @@
+pdftotext(1) pdftotext(1)
+
+
+
+NAME
+ pdftotext - Portable Document Format (PDF) to text con-
+ verter (version 3.02)
+
+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-2007 Glyph & Cog, LLC.
+
+SEE ALSO
+ xpdf(1), pdftops(1), pdfinfo(1), pdffonts(1), pdftoppm(1),
+ pdfimages(1), xpdfrc(5)
+ http://www.foolabs.com/xpdf/
+
+
+
+ 27 Febuary 2007 pdftotext(1)
diff --git a/doc/pdftotext.hlp b/doc/pdftotext.hlp
new file mode 100644
index 0000000..f527172
--- /dev/null
+++ b/doc/pdftotext.hlp
@@ -0,0 +1,133 @@
+! 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-2007 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/
+
+ ()
+
diff --git a/doc/sample-xpdfrc b/doc/sample-xpdfrc
new file mode 100644
index 0000000..481e417
--- /dev/null
+++ b/doc/sample-xpdfrc
@@ -0,0 +1,91 @@
+#========================================================================
+#
+# 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)'"
diff --git a/doc/xpdf.1 b/doc/xpdf.1
new file mode 100644
index 0000000..6548557
--- /dev/null
+++ b/doc/xpdf.1
@@ -0,0 +1,863 @@
+.\" Copyright 1996-2007 Glyph & Cog, LLC
+.TH xpdf 1 "27 February 2007"
+.SH NAME
+xpdf \- Portable Document Format (PDF) file viewer for X (version 3.02)
+.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. (There is a separate setting,
+xpdf.fullScreenMatteColor, for full-screen mode.)
+.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 \-aaVector " yes | no"
+Enable or disable vector anti-aliasing. This defaults to "yes".
+.RB "[config file: " vectorAntialias ]
+.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 full-screen mode, useful for presentations.
+.TP
+.BI \-remote " name"
+Start/contact xpdf remote server with specified name (see the
+.B "REMOTE SERVER MODE"
+section below).
+.TP
+.BI \-exec " command"
+Execute a command (see the
+.B COMMANDS
+section below) in an xpdf remote server window (with -remote only).
+.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.toolTipEnable
+Enables (if set to true) or disables (if set to false) the tool-tips
+on the toolbar buttons.
+.TP
+.B xpdf.fullScreenMatteColor
+Sets the matte color to be used in full-screen mode. The default
+setting is "black".
+.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 "Zoom to selection"
+Zoom in to the currently selected rectangle.
+.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 alt-F
+Toggle full-screen mode.
+.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 COMMANDS
+Xpdf's key and mouse bindings are user-configurable, using the bind and
+unbind options in the config file (see
+.BR xpdfrc (5)).
+The bind command allows you to bind a key or mouse button to a
+sequence of one or more commands.
+.SS Available Commands
+The following commands are supported:
+.TP
+.BI gotoPage( page )
+Go to the specified page.
+.TP
+.BI gotoPageNoScroll( page )
+Go to the specified page, with the current relative scroll position.
+.TP
+.BI gotoDest( dest )
+Go to a named destination.
+.TP
+.B gotoLastPage
+Go to the last page in the PDF file.
+.TP
+.B gotoLastPageNoScroll
+Go to the last page in the PDF file, with the current relative scroll
+position.
+.TP
+.B nextPage
+Go to the next page.
+.TP
+.B nextPageNoScroll
+Go to the next page, with the current relative scroll position.
+.TP
+.B prevPage
+Go to the previous page.
+.TP
+.B prevPageNoScroll
+Go to the previous page, with the current relative scroll position.
+.TP
+.B pageUp
+Scroll up by one screenful.
+.TP
+.B pageDown
+Scroll down by one screenful.
+.TP
+.BI scrollLeft( n )
+Scroll left by
+.I n
+pixels.
+.TP
+.BI scrollRight( n )
+Scroll right by
+.I n
+pixels.
+.TP
+.BI scrollUp( n )
+Scroll up by
+.I n
+pixels.
+.TP
+.BI scrollDown( n )
+Scroll down by
+.I n
+pixels.
+.TP
+.BI scrollUpPrevPage( n )
+Scroll up by
+.I n
+pixels, moving to the previous page if appropriate.
+.TP
+.BI scrollDownPrevPage( n )
+Scroll down by
+.I n
+pixels, moving to the next page if appropriate.
+.TP
+.B scrollToTopEdge
+Scroll to the top edge of the current page, with no horizontal
+movement.
+.TP
+.B scrollToBottomEdge
+Scroll to the bottom edge of the current page, with no horizontal
+movement.
+.TP
+.B scrollToLeftEdge
+Scroll to the left edge of the current page, with no vertical
+movement.
+.TP
+.B scrollToRightEdge
+Scroll to the right edge of the current page, with no vertical
+movement.
+.TP
+.B scrollToTopLeft
+Scroll to the top-left corner of the current page.
+.TP
+.B scrollToBottomRight
+Scroll to the bottom-right corner of the current page.
+.TP
+.B goForward
+Move forward along the history path.
+.TP
+.B goBackward
+Move backward along the history path.
+.TP
+.BI zoomPercent( z )
+Set the zoom factor to
+.IR z %.
+.TP
+.B zoomFitPage
+Set the zoom factor to fit-page.
+.TP
+.B zoomFitWidth
+Set the zoom factor to fit-width.
+.TP
+.B zoomIn
+Zoom in - go to the next higher zoom factor.
+.TP
+.B zoomOut
+Zoom out - go the next lower zoom factor.
+.TP
+.B continuousMode
+Go to continuous view mode.
+.TP
+.B singlePageMode
+Go to single-page view mode.
+.TP
+.B toggleContinuousMode
+Toggle between continuous and single page view modes.
+.TP
+.B fullScreenMode
+Go to full-screen mode.
+.TP
+.B windowMode
+Go to window (non-full-screen) mode.
+.TP
+.B toggleFullScreenMode
+Toggle between full-screen and window modes.
+.TP
+.B open
+Open a PDF file in this window, using the open dialog.
+.TP
+.B openInNewWin
+Open a PDF file in a new window, using the open dialog.
+.TP
+.BI openFile( file )
+Open a specified PDF file in this window.
+.TP
+.BI openFileInNewWin( file )
+Open a specified PDF file in a new window.
+.TP
+.BI openFileAtDest( file , dest )
+Open a specified PDF file in this window and go to a named
+destination.
+.TP
+.BI openFileAtDestInNewWin( file , dest )
+Open a specified PDF file in a new window and go to a named
+destination.
+.TP
+.B reload
+Reload the current PDF file.
+.TP
+.B redraw
+Redraw the window.
+.TP
+.B raise
+Raise the window to the front.
+.TP
+.B closeWindow
+Close the window.
+.TP
+.BI run( external-command-string )
+Run an external command. The following escapes are allowed in the
+command string:
+.nf
+
+ %f => PDF file name (or an empty string if no
+ file is open)
+ %b => PDF file base name, i.e., file name minus
+ the extension (or an empty string if no
+ file is open)
+ %u => link URL (or an empty string if not over
+ a URL link)
+ %x => selection upper-left x coordinate
+ (or 0 if there is no selection)
+ %y => selection upper-left y coordinate
+ (or 0 if there is no selection)
+ %X => selection lower-right x coordinate
+ (or 0 if there is no selection)
+ %Y => selection lower-right y coordinate
+ (or 0 if there is no selection)
+ %% => %
+
+.fi
+.TP
+.B openOutline
+Open the outline pane.
+.TP
+.B closeOutline
+Close the outline pane.
+.TP
+.B toggleOutline
+Toggle the outline pane between open and closed.
+.TP
+.BI scrollOutlineDown( n )
+Scroll the outline down by
+.I n
+increments.
+.TP
+.BI scrollOutlineUp( n )
+Scroll the outline up by
+.I n
+increments.
+.TP
+.B focusToDocWin
+Set the keyboard focus to the main document window.
+.TP
+.B focusToPageNum
+Set the keyboard focus to the page number text box.
+.TP
+.B find
+Open the 'find' dialog.
+.TP
+.B findNext
+Finds the next occurrence of the search string (no dialog).
+.TP
+.B print
+Open the 'print' dialog.
+.TP
+.B about
+Open the 'about' dialog.
+.TP
+.B quit
+Quit from xpdf.
+.PP
+The following commands depend on the current mouse position:
+.TP
+.B startSelection
+Start a selection, which will be extended as the mouse moves.
+.TP
+.B endSelection
+End a selection.
+.TP
+.B startPan
+Start a pan, which will scroll the document as the mouse moves
+.TP
+.B endPan
+End a pan.
+.TP
+.B postPopupMenu
+Display the popup menu.
+.TP
+.B followLink
+Follow a hyperlink (does nothing if the mouse is not over a link).
+.TP
+.B followLinkInNewWin
+Follow a hyperlink, opening PDF files in a new window (does nothing if
+the mouse is not over a link). For links to non-PDF files, this
+command is identical to followLink.
+.TP
+.B followLinkNoSel
+Same as followLink, but does nothing if there is a non-empty selection.
+(This is useful as a mouse button binding.)
+.TP
+.B followLinkInNewWinNoSel
+Same as followLinkInNewWin, but does nothing if there is a non-empty
+selection. (This is useful as a mouse button binding.)
+.SS Default Bindings
+The default mouse bindings are as follows:
+.nf
+
+ bind mousePress1 any startSelection
+ bind mouseRelease1 any endSelection followLinkNoSel
+ bind mousePress2 any startPan
+ bind mouseRelease2 any endPan
+ bind mousePress3 any postPopupMenu
+ bind mousePress4 any scrollUpPrevPage(16)
+ bind mousePress5 any scrollDownNextPage(16)
+ bind mousePress6 any scrollLeft(16)
+ bind mousePress7 any scrollRight(16)
+
+.fi
+The default key bindings are as follows:
+.nf
+
+ bind ctrl-home any gotoPage(1)
+ bind home any scrollToTopLeft
+ bind ctrl-end any gotoLastPage
+ bind end any scrollToBottomRight
+ bind pgup any pageUp
+ bind backspace any pageUp
+ bind delete any pageUp
+ bind pgdn any pageDown
+ bind space any pageDown
+ bind left any scrollLeft(16)
+ bind right any scrollRight(16)
+ bind up any scrollUp(16)
+ bind down any scrollDown(16)
+ bind o any open
+ bind O any open
+ bind r any reload
+ bind R any reload
+ bind f any find
+ bind F any find
+ bind ctrl-f any find
+ bind ctrl-g any findNext
+ bind ctrl-p any print
+ bind n scrLockOff nextPage
+ bind N scrLockOff nextPage
+ bind n scrLockOn nextPageNoScroll
+ bind N scrLockOn nextPageNoScroll
+ bind p scrLockOff prevPage
+ bind P scrLockOff prevPage
+ bind p scrLockOn prevPageNoScroll
+ bind P scrLockOn prevPageNoScroll
+ bind v any goForward
+ bind b any goBackward
+ bind g any focusToPageNum
+ bind 0 any zoomPercent(125)
+ bind + any zoomIn
+ bind - any zoomOut
+ bind z any zoomFitPage
+ bind w any zoomFitWidth
+ bind alt-f any toggleFullScreenMode
+ bind ctrl-l any redraw
+ bind ctrl-w any closeWindow
+ bind ? any about
+ bind q any quit
+ bind Q any quit
+
+.fi
+Previous versions of xpdf included a "viKeys" X resource. It is no
+longer available, but the following bindings are equivalent:
+.nf
+
+ bind h any scrollLeft(16)
+ bind l any scrollRight(16)
+ bind k any scrollUp(16)
+ bind j any scrollDown(16)
+
+.fi
+.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-2007 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/
diff --git a/doc/xpdf.cat b/doc/xpdf.cat
new file mode 100644
index 0000000..8ab7291
--- /dev/null
+++ b/doc/xpdf.cat
@@ -0,0 +1,809 @@
+xpdf(1) xpdf(1)
+
+
+
+NAME
+ xpdf - Portable Document Format (PDF) file viewer for X
+ (version 3.02)
+
+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. (There is a
+ separate setting, xpdf.fullScreenMatteColor, for
+ full-screen mode.) [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]
+
+ -aaVector yes | no
+ Enable or disable vector anti-aliasing. This
+ defaults to "yes". [config file: vectorAntialias]
+
+ -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 full-screen mode, useful for presenta-
+ tions.
+
+ -remote name
+ Start/contact xpdf remote server with specified
+ name (see the REMOTE SERVER MODE section below).
+
+ -exec command
+ Execute a command (see the COMMANDS section below)
+ in an xpdf remote server window (with -remote
+ only).
+
+ -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.toolTipEnable
+ Enables (if set to true) or disables (if set to
+ false) the tool-tips on the toolbar buttons.
+
+ xpdf.fullScreenMatteColor
+ Sets the matte color to be used in full-screen
+ mode. The default setting is "black".
+
+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.
+
+ Zoom to selection
+ Zoom in to the currently selected rectangle.
+
+ 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
+ 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', exe-
+ cute 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).
+
+ alt-F Toggle full-screen mode.
+
+ 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)'"
+
+COMMANDS
+ Xpdf's key and mouse bindings are user-configurable, using
+ the bind and unbind options in the config file (see
+ xpdfrc(5)). The bind command allows you to bind a key or
+ mouse button to a sequence of one or more commands.
+
+ Available Commands
+ The following commands are supported:
+
+ gotoPage(page)
+ Go to the specified page.
+
+ gotoPageNoScroll(page)
+ Go to the specified page, with the current relative
+ scroll position.
+
+ gotoDest(dest)
+ Go to a named destination.
+
+ gotoLastPage
+ Go to the last page in the PDF file.
+
+ gotoLastPageNoScroll
+ Go to the last page in the PDF file, with the cur-
+ rent relative scroll position.
+
+ nextPage
+ Go to the next page.
+
+ nextPageNoScroll
+ Go to the next page, with the current relative
+ scroll position.
+
+ prevPage
+ Go to the previous page.
+
+ prevPageNoScroll
+ Go to the previous page, with the current relative
+ scroll position.
+
+ pageUp Scroll up by one screenful.
+
+ pageDown
+ Scroll down by one screenful.
+
+ scrollLeft(n)
+ Scroll left by n pixels.
+
+ scrollRight(n)
+ Scroll right by n pixels.
+
+ scrollUp(n)
+ Scroll up by n pixels.
+
+ scrollDown(n)
+ Scroll down by n pixels.
+
+ scrollUpPrevPage(n)
+ Scroll up by n pixels, moving to the previous page
+ if appropriate.
+
+ scrollDownPrevPage(n)
+ Scroll down by n pixels, moving to the next page if
+ appropriate.
+
+ scrollToTopEdge
+ Scroll to the top edge of the current page, with no
+ horizontal movement.
+
+ scrollToBottomEdge
+ Scroll to the bottom edge of the current page, with
+ no horizontal movement.
+
+ scrollToLeftEdge
+ Scroll to the left edge of the current page, with
+ no vertical movement.
+
+ scrollToRightEdge
+ Scroll to the right edge of the current page, with
+ no vertical movement.
+
+ scrollToTopLeft
+ Scroll to the top-left corner of the current page.
+
+ scrollToBottomRight
+ Scroll to the bottom-right corner of the current
+ page.
+
+ goForward
+ Move forward along the history path.
+
+ goBackward
+ Move backward along the history path.
+
+ zoomPercent(z)
+ Set the zoom factor to z%.
+
+ zoomFitPage
+ Set the zoom factor to fit-page.
+
+ zoomFitWidth
+ Set the zoom factor to fit-width.
+
+ zoomIn Zoom in - go to the next higher zoom factor.
+
+ zoomOut
+ Zoom out - go the next lower zoom factor.
+
+ continuousMode
+ Go to continuous view mode.
+
+ singlePageMode
+ Go to single-page view mode.
+
+ toggleContinuousMode
+ Toggle between continuous and single page view
+ modes.
+
+ fullScreenMode
+ Go to full-screen mode.
+
+ windowMode
+ Go to window (non-full-screen) mode.
+
+ toggleFullScreenMode
+ Toggle between full-screen and window modes.
+
+ open Open a PDF file in this window, using the open dia-
+ log.
+
+ openInNewWin
+ Open a PDF file in a new window, using the open
+ dialog.
+
+ openFile(file)
+ Open a specified PDF file in this window.
+
+ openFileInNewWin(file)
+ Open a specified PDF file in a new window.
+
+ openFileAtDest(file,dest)
+ Open a specified PDF file in this window and go to
+ a named destination.
+
+ openFileAtDestInNewWin(file,dest)
+ Open a specified PDF file in a new window and go to
+ a named destination.
+
+ reload Reload the current PDF file.
+
+ redraw Redraw the window.
+
+ raise Raise the window to the front.
+
+ closeWindow
+ Close the window.
+
+ run(external-command-string)
+ Run an external command. The following escapes are
+ allowed in the command string:
+
+ %f => PDF file name (or an empty string if no
+ file is open)
+ %b => PDF file base name, i.e., file name minus
+ the extension (or an empty string if no
+ file is open)
+ %u => link URL (or an empty string if not over
+ a URL link)
+ %x => selection upper-left x coordinate
+ (or 0 if there is no selection)
+ %y => selection upper-left y coordinate
+ (or 0 if there is no selection)
+ %X => selection lower-right x coordinate
+ (or 0 if there is no selection)
+ %Y => selection lower-right y coordinate
+ (or 0 if there is no selection)
+ %% => %
+
+
+ openOutline
+ Open the outline pane.
+
+ closeOutline
+ Close the outline pane.
+
+ toggleOutline
+ Toggle the outline pane between open and closed.
+
+ scrollOutlineDown(n)
+ Scroll the outline down by n increments.
+
+ scrollOutlineUp(n)
+ Scroll the outline up by n increments.
+
+ focusToDocWin
+ Set the keyboard focus to the main document window.
+
+ focusToPageNum
+ Set the keyboard focus to the page number text box.
+
+ find Open the 'find' dialog.
+
+ findNext
+ Finds the next occurrence of the search string (no
+ dialog).
+
+ print Open the 'print' dialog.
+
+ about Open the 'about' dialog.
+
+ quit Quit from xpdf.
+
+ The following commands depend on the current mouse posi-
+ tion:
+
+ startSelection
+ Start a selection, which will be extended as the
+ mouse moves.
+
+ endSelection
+ End a selection.
+
+ startPan
+ Start a pan, which will scroll the document as the
+ mouse moves
+
+ endPan End a pan.
+
+ postPopupMenu
+ Display the popup menu.
+
+ followLink
+ Follow a hyperlink (does nothing if the mouse is
+ not over a link).
+
+ followLinkInNewWin
+ Follow a hyperlink, opening PDF files in a new win-
+ dow (does nothing if the mouse is not over a link).
+ For links to non-PDF files, this command is identi-
+ cal to followLink.
+
+ followLinkNoSel
+ Same as followLink, but does nothing if there is a
+ non-empty selection. (This is useful as a mouse
+ button binding.)
+
+ followLinkInNewWinNoSel
+ Same as followLinkInNewWin, but does nothing if
+ there is a non-empty selection. (This is useful as
+ a mouse button binding.)
+
+ Default Bindings
+ The default mouse bindings are as follows:
+
+ bind mousePress1 any startSelection
+ bind mouseRelease1 any endSelection followLinkNoSel
+ bind mousePress2 any startPan
+ bind mouseRelease2 any endPan
+ bind mousePress3 any postPopupMenu
+ bind mousePress4 any scrollUpPrevPage(16)
+ bind mousePress5 any scrollDownNextPage(16)
+ bind mousePress6 any scrollLeft(16)
+ bind mousePress7 any scrollRight(16)
+
+ The default key bindings are as follows:
+
+ bind ctrl-home any gotoPage(1)
+ bind home any scrollToTopLeft
+ bind ctrl-end any gotoLastPage
+ bind end any scrollToBottomRight
+ bind pgup any pageUp
+ bind backspace any pageUp
+ bind delete any pageUp
+ bind pgdn any pageDown
+ bind space any pageDown
+ bind left any scrollLeft(16)
+ bind right any scrollRight(16)
+ bind up any scrollUp(16)
+ bind down any scrollDown(16)
+ bind o any open
+ bind O any open
+ bind r any reload
+ bind R any reload
+ bind f any find
+ bind F any find
+ bind ctrl-f any find
+ bind ctrl-g any findNext
+ bind ctrl-p any print
+ bind n scrLockOff nextPage
+ bind N scrLockOff nextPage
+ bind n scrLockOn nextPageNoScroll
+ bind N scrLockOn nextPageNoScroll
+ bind p scrLockOff prevPage
+ bind P scrLockOff prevPage
+ bind p scrLockOn prevPageNoScroll
+ bind P scrLockOn prevPageNoScroll
+ bind v any goForward
+ bind b any goBackward
+ bind g any focusToPageNum
+ bind 0 any zoomPercent(125)
+ bind + any zoomIn
+ bind - any zoomOut
+ bind z any zoomFitPage
+ bind w any zoomFitWidth
+ bind alt-f any toggleFullScreenMode
+ bind ctrl-l any redraw
+ bind ctrl-w any closeWindow
+ bind ? any about
+ bind q any quit
+ bind Q any quit
+
+ Previous versions of xpdf included a "viKeys" X resource.
+ It is no longer available, but the following bindings are
+ equivalent:
+
+ bind h any scrollLeft(16)
+ bind l any scrollRight(16)
+ bind k any scrollUp(16)
+ bind j any scrollDown(16)
+
+
+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-2007 Glyph & Cog, LLC.
+
+SEE ALSO
+ pdftops(1), pdftotext(1), pdfinfo(1), pdffonts(1),
+ pdftoppm(1), pdfimages(1), xpdfrc(5)
+ http://www.foolabs.com/xpdf/
+
+
+
+ 27 February 2007 xpdf(1)
diff --git a/doc/xpdf.hlp b/doc/xpdf.hlp
new file mode 100644
index 0000000..1f90bab
--- /dev/null
+++ b/doc/xpdf.hlp
@@ -0,0 +1,823 @@
+! Generated automatically by mantohlp
+1 xpdf
+
+ xpdf - Portable Document Format (PDF) file viewer for X
+ (version 3.02)
+
+ 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. (There is a
+ separate setting, xpdf.fullScreenMatteColor, for
+ full-screen mode.) [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]
+
+ -aaVector yes | no
+ Enable or disable vector anti-aliasing. This
+ defaults to "yes". [config file: vectorAntialias]
+
+ -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 full-screen mode, useful for presenta-
+ tions.
+
+ -remote name
+ Start/contact xpdf remote server with specified
+ name (see the REMOTE SERVER MODE section below).
+
+ -exec command
+ Execute a command (see the COMMANDS section below)
+ in an xpdf remote server window (with -remote
+ only).
+
+ -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.toolTipEnable
+ Enables (if set to true) or disables (if set to
+ false) the tool-tips on the toolbar buttons.
+
+ xpdf.fullScreenMatteColor
+ Sets the matte color to be used in full-screen
+ mode. The default setting is "black".
+
+ ()
+
+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.
+
+ Zoom to selection
+ Zoom in to the currently selected rectangle.
+
+ 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).
+
+ alt-F Toggle full-screen mode.
+
+ 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 COMMANDS
+
+ Xpdf's key and mouse bindings are user-configurable, using
+ the bind and unbind options in the config file (see
+ xpdfrc(5)). The bind command allows you to bind a key or
+ mouse button to a sequence of one or more commands.
+
+ Available Commands
+ The following commands are supported:
+
+ gotoPage(page)
+ Go to the specified page.
+
+ gotoPageNoScroll(page)
+ Go to the specified page, with the current relative
+ scroll position.
+
+ gotoDest(dest)
+ Go to a named destination.
+
+ gotoLastPage
+ Go to the last page in the PDF file.
+
+ gotoLastPageNoScroll
+ Go to the last page in the PDF file, with the cur-
+ rent relative scroll position.
+
+ nextPage
+ Go to the next page.
+
+ nextPageNoScroll
+ Go to the next page, with the current relative
+ scroll position.
+
+ prevPage
+ Go to the previous page.
+
+ prevPageNoScroll
+ Go to the previous page, with the current relative
+ scroll position.
+
+ pageUp Scroll up by one screenful.
+
+ pageDown
+ Scroll down by one screenful.
+
+ scrollLeft(n)
+ Scroll left by n pixels.
+
+ scrollRight(n)
+ Scroll right by n pixels.
+
+ scrollUp(n)
+ Scroll up by n pixels.
+
+ scrollDown(n)
+ Scroll down by n pixels.
+
+ scrollUpPrevPage(n)
+ Scroll up by n pixels, moving to the previous page
+ if appropriate.
+
+ scrollDownPrevPage(n)
+ Scroll down by n pixels, moving to the next page if
+ appropriate.
+
+ scrollToTopEdge
+ Scroll to the top edge of the current page, with no
+ horizontal movement.
+
+ scrollToBottomEdge
+ Scroll to the bottom edge of the current page, with
+ no horizontal movement.
+
+ scrollToLeftEdge
+ Scroll to the left edge of the current page, with
+ no vertical movement.
+
+ scrollToRightEdge
+ Scroll to the right edge of the current page, with
+ no vertical movement.
+
+ scrollToTopLeft
+ Scroll to the top-left corner of the current page.
+
+ scrollToBottomRight
+ Scroll to the bottom-right corner of the current
+ page.
+
+ goForward
+ Move forward along the history path.
+
+ goBackward
+ Move backward along the history path.
+
+ zoomPercent(z)
+ Set the zoom factor to z%.
+
+ zoomFitPage
+ Set the zoom factor to fit-page.
+
+ zoomFitWidth
+ Set the zoom factor to fit-width.
+
+ zoomIn Zoom in - go to the next higher zoom factor.
+
+ zoomOut
+ Zoom out - go the next lower zoom factor.
+
+ continuousMode
+ Go to continuous view mode.
+
+ singlePageMode
+ Go to single-page view mode.
+
+ toggleContinuousMode
+ Toggle between continuous and single page view
+ modes.
+
+ fullScreenMode
+ Go to full-screen mode.
+
+ windowMode
+ Go to window (non-full-screen) mode.
+
+ toggleFullScreenMode
+ Toggle between full-screen and window modes.
+
+ open Open a PDF file in this window, using the open
+ dialog.
+
+ openInNewWin
+ Open a PDF file in a new window, using the open
+ dialog.
+
+ openFile(file)
+ Open a specified PDF file in this window.
+
+ openFileInNewWin(file)
+ Open a specified PDF file in a new window.
+
+ openFileAtDest(file,dest)
+ Open a specified PDF file in this window and go to
+ a named destination.
+
+ openFileAtDestInNewWin(file,dest)
+ Open a specified PDF file in a new window and go to
+ a named destination.
+
+ reload Reload the current PDF file.
+
+ redraw Redraw the window.
+
+ raise Raise the window to the front.
+
+ closeWindow
+ Close the window.
+
+ run(external-command-string)
+ Run an external command. The following escapes are
+ allowed in the command string:
+
+ %f => PDF file name (or an empty string if no
+ file is open)
+ %b => PDF file base name, i.e., file name minus
+ the extension (or an empty string if no
+ file is open)
+ %u => link URL (or an empty string if not over
+ a URL link)
+ %x => selection upper-left x coordinate
+ (or 0 if there is no selection)
+ %y => selection upper-left y coordinate
+ (or 0 if there is no selection)
+ %X => selection lower-right x coordinate
+ (or 0 if there is no selection)
+ %Y => selection lower-right y coordinate
+ (or 0 if there is no selection)
+ %% => %
+
+ openOutline
+ Open the outline pane.
+
+ closeOutline
+ Close the outline pane.
+
+ toggleOutline
+ Toggle the outline pane between open and closed.
+
+ scrollOutlineDown(n)
+ Scroll the outline down by n increments.
+
+ scrollOutlineUp(n)
+ Scroll the outline up by n increments.
+
+ focusToDocWin
+ Set the keyboard focus to the main document window.
+
+ focusToPageNum
+ Set the keyboard focus to the page number text box.
+
+ find Open the 'find' dialog.
+
+ findNext
+ Finds the next occurrence of the search string (no
+ dialog).
+
+ print Open the 'print' dialog.
+
+ about Open the 'about' dialog.
+
+ quit Quit from xpdf.
+
+ The following commands depend on the current mouse posi-
+ tion:
+
+ startSelection
+ Start a selection, which will be extended as the
+ mouse moves.
+
+ endSelection
+ End a selection.
+
+ startPan
+ Start a pan, which will scroll the document as the
+ mouse moves
+
+ endPan End a pan.
+
+ postPopupMenu
+ Display the popup menu.
+
+ followLink
+ Follow a hyperlink (does nothing if the mouse is
+ not over a link).
+
+ followLinkInNewWin
+ Follow a hyperlink, opening PDF files in a new win-
+ dow (does nothing if the mouse is not over a link).
+ For links to non-PDF files, this command is identi-
+ cal to followLink.
+
+ followLinkNoSel
+ Same as followLink, but does nothing if there is a
+ non-empty selection. (This is useful as a mouse
+ button binding.)
+
+ followLinkInNewWinNoSel
+ Same as followLinkInNewWin, but does nothing if
+ there is a non-empty selection. (This is useful as
+ a mouse button binding.)
+
+ Default Bindings
+ The default mouse bindings are as follows:
+
+ bind mousePress1 any startSelection
+ bind mouseRelease1 any endSelection followLinkNoSel
+ bind mousePress2 any startPan
+ bind mouseRelease2 any endPan
+ bind mousePress3 any postPopupMenu
+ bind mousePress4 any scrollUpPrevPage(16)
+ bind mousePress5 any scrollDownNextPage(16)
+ bind mousePress6 any scrollLeft(16)
+ bind mousePress7 any scrollRight(16)
+
+ The default key bindings are as follows:
+
+ bind ctrl-home any gotoPage(1)
+ bind home any scrollToTopLeft
+ bind ctrl-end any gotoLastPage
+ bind end any scrollToBottomRight
+ bind pgup any pageUp
+ bind backspace any pageUp
+ bind delete any pageUp
+ bind pgdn any pageDown
+ bind space any pageDown
+ bind left any scrollLeft(16)
+ bind right any scrollRight(16)
+ bind up any scrollUp(16)
+ bind down any scrollDown(16)
+ bind o any open
+ bind O any open
+ bind r any reload
+ bind R any reload
+ bind f any find
+ bind F any find
+ bind ctrl-f any find
+ bind ctrl-g any findNext
+ bind ctrl-p any print
+ bind n scrLockOff nextPage
+ bind N scrLockOff nextPage
+ bind n scrLockOn nextPageNoScroll
+ bind N scrLockOn nextPageNoScroll
+ bind p scrLockOff prevPage
+ bind P scrLockOff prevPage
+ bind p scrLockOn prevPageNoScroll
+ bind P scrLockOn prevPageNoScroll
+ bind v any goForward
+ bind b any goBackward
+ bind g any focusToPageNum
+ bind 0 any zoomPercent(125)
+ bind + any zoomIn
+ bind - any zoomOut
+ bind z any zoomFitPage
+ bind w any zoomFitWidth
+ bind alt-f any toggleFullScreenMode
+ bind ctrl-l any redraw
+ bind ctrl-w any closeWindow
+ bind ? any about
+ bind q any quit
+ bind Q any quit
+
+ Previous versions of xpdf included a "viKeys" X resource.
+ It is no longer available, but the following bindings are
+ equivalent:
+
+ bind h any scrollLeft(16)
+ bind l any scrollRight(16)
+ bind k any scrollUp(16)
+ bind j any scrollDown(16)
+
+ ()
+
+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-2007 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/
+
+ ()
+
diff --git a/doc/xpdfrc.5 b/doc/xpdfrc.5
new file mode 100644
index 0000000..4e15455
--- /dev/null
+++ b/doc/xpdfrc.5
@@ -0,0 +1,617 @@
+.\" Copyright 2002-2007 Glyph & Cog, LLC
+.TH xpdfrc 5 "27 February 2007"
+.SH NAME
+xpdfrc \- configuration file for Xpdf tools (version 3.02)
+.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 psPreload " yes | no"
+If set to "yes", PDF forms are converted to PS procedures, and image
+data is preloaded. This uses more memory in the PostScript
+interpreter, but generates significantly smaller PS files in
+situations where, e.g., the same image is drawn on every page of a
+long document. This defaults to "no".
+.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 continuousView " yes | no"
+If set to "yes", xpdf will start in continuous view mode, i.e., with
+one vertical screoll bar for the whole document. This defaults to
+"no".
+.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
+.BR vectorAntialias " yes | no"
+Enables or disables anti-aliasing of vector graphics in the PDF
+rasterizer. This defaults to "yes".
+.TP
+.BR strokeAdjust " yes | no"
+Enables or disables stroke adjustment. This defaults to "yes".
+.TP
+.BR screenType " dispersed | clustered | stochasticClustered"
+Sets the halftone screen type, which will be used when generating a
+monochrome (1-bit) bitmap. The three options are dispersed-dot
+dithering, clustered-dot dithering (with a round dot and 45-degree
+screen angle), and stochastic clustered-dot dithering. By default,
+"stochasticClustered" is used for resolutions of 300 dpi and higher,
+and "dispersed" is used for resolutions lower then 300 dpi.
+.TP
+.BI screenSize " integer"
+Sets the size of the (square) halftone screen threshold matrix. By
+default, this is 4 for dispersed-dot dithering, 10 for clustered-dot
+dithering, and 100 for stochastic clustered-dot dithering.
+.TP
+.BI screenDotRadius " integer"
+Sets the halftone screen dot radius. This is only used when
+screenType is set to stochasticClustered, and it defaults to 2. In
+clustered-dot mode, the dot radius is half of the screen size.
+Dispersed-dot dithering doesn't have a dot radius.
+.TP
+.BI screenGamma " float"
+Sets the halftone screen gamma correction parameter. Gamma values
+greater than 1 make the output brighter; gamma values less than 1 make
+it darker. The default value is 1.
+.TP
+.BI screenBlackThreshold " float"
+When halftoning, all values below this threshold are forced to solid
+black. This parameter is a floating point value between 0 (black) and
+1 (white). The default value is 0.
+.TP
+.BI screenWhiteThreshold " float"
+When halftoning, all values above this threshold are forced to solid
+white. This parameter is a floating point value between 0 (black) and
+1 (white). The default value is 1.
+.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 mapUnknownCharNames " yes | no"
+If set to "yes", and mapNumericCharNames is set to "no", the Xpdf
+tools will apply a simple pass-through mapping (Unicode index =
+character code) for all unrecognized glyph names. 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 "no".
+.TP
+.BI bind " modifiers-key context command ..."
+Add a key or mouse button binding.
+.I Modifiers
+can be zero or more of:
+.nf
+
+ shift-
+ ctrl-
+ alt-
+
+.fi
+.I Key
+can be a regular ASCII character, or any one of:
+.nf
+
+ space
+ tab
+ return
+ enter
+ backspace
+ insert
+ delete
+ home
+ end
+ pgup
+ pgdn
+ left / right / up / down (arrow keys)
+ f1 .. f35 (function keys)
+ mousePress1 .. mousePress7 (mouse buttons)
+ mouseRelease1 .. mouseRelease7 (mouse buttons)
+
+.fi
+.I Context
+is either "any" or a comma-separated combination of:
+.nf
+
+ fullScreen / window (full screen mode on/off)
+ continuous / singlePage (continuous mode on/off)
+ overLink / offLink (mouse over link or not)
+ scrLockOn / scrLockOff (scroll lock on/off)
+
+.fi
+The context string can include only one of each pair in the above
+list.
+
+.I Command
+is an Xpdf command (see the COMMANDS section of the
+.BR xpdf (1)
+man page for details). Multiple commands are separated by whitespace.
+
+The bind command replaces any existing binding, but only if it was
+defined for the exact same modifiers, key, and context. All tokens
+(modifiers, key, context, commands) are case-sensitive.
+
+Example key bindings:
+.nf
+
+ # bind ctrl-a in any context to the nextPage
+ # command
+ bind ctrl-a any nextPage
+
+ # bind uppercase B, when in continuous mode
+ # with scroll lock on, to the reload command
+ # followed by the prevPage command
+ bind B continuous,scrLockOn reload prevPage
+
+.fi
+See the
+.BR xpdf (1)
+man page for more examples.
+.TP
+.BI unbind " modifiers-key context"
+Removes a key binding established with the bind command. This is most
+useful to remove default key bindings before establishing new ones
+(e.g., if the default key binding is given for "any" context, and you
+want to create new key bindings for multiple contexts).
+.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-2007 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/
diff --git a/doc/xpdfrc.cat b/doc/xpdfrc.cat
new file mode 100644
index 0000000..3201f09
--- /dev/null
+++ b/doc/xpdfrc.cat
@@ -0,0 +1,614 @@
+xpdfrc(5) xpdfrc(5)
+
+
+
+NAME
+ xpdfrc - configuration file for Xpdf tools (version 3.02)
+
+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.
+
+ psPreload yes | no
+ If set to "yes", PDF forms are converted to PS pro-
+ cedures, and image data is preloaded. This uses
+ more memory in the PostScript interpreter, but gen-
+ erates significantly smaller PS files in situations
+ where, e.g., the same image is drawn on every page
+ of a long document. This defaults to "no".
+
+ 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.
+
+ continuousView yes | no
+ If set to "yes", xpdf will start in continuous view
+ mode, i.e., with one vertical screoll bar for the
+ whole document. This defaults to "no".
+
+ 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".
+
+ vectorAntialias yes | no
+ Enables or disables anti-aliasing of vector graph-
+ ics in the PDF rasterizer. This defaults to "yes".
+
+ strokeAdjust yes | no
+ Enables or disables stroke adjustment. This
+ defaults to "yes".
+
+ screenType dispersed | clustered | stochasticClustered
+ Sets the halftone screen type, which will be used
+ when generating a monochrome (1-bit) bitmap. The
+ three options are dispersed-dot dithering, clus-
+ tered-dot dithering (with a round dot and 45-degree
+ screen angle), and stochastic clustered-dot dither-
+ ing. By default, "stochasticClustered" is used for
+ resolutions of 300 dpi and higher, and "dispersed"
+ is used for resolutions lower then 300 dpi.
+
+ screenSize integer
+ Sets the size of the (square) halftone screen
+ threshold matrix. By default, this is 4 for dis-
+ persed-dot dithering, 10 for clustered-dot dither-
+ ing, and 100 for stochastic clustered-dot dither-
+ ing.
+
+ screenDotRadius integer
+ Sets the halftone screen dot radius. This is only
+ used when screenType is set to stochasticClustered,
+ and it defaults to 2. In clustered-dot mode, the
+ dot radius is half of the screen size. Dispersed-
+ dot dithering doesn't have a dot radius.
+
+ screenGamma float
+ Sets the halftone screen gamma correction parame-
+ ter. Gamma values greater than 1 make the output
+ brighter; gamma values less than 1 make it darker.
+ The default value is 1.
+
+ screenBlackThreshold float
+ When halftoning, all values below this threshold
+ are forced to solid black. This parameter is a
+ floating point value between 0 (black) and 1
+ (white). The default value is 0.
+
+ screenWhiteThreshold float
+ When halftoning, all values above this threshold
+ are forced to solid white. This parameter is a
+ floating point value between 0 (black) and 1
+ (white). The default value is 1.
+
+ 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".
+
+ mapUnknownCharNames yes | no
+ If set to "yes", and mapNumericCharNames is set to
+ "no", the Xpdf tools will apply a simple pass-
+ through mapping (Unicode index = character code)
+ for all unrecognized glyph names. 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 "no".
+
+ bind modifiers-key context command ...
+ Add a key or mouse button binding. Modifiers can
+ be zero or more of:
+
+ shift-
+ ctrl-
+ alt-
+
+ Key can be a regular ASCII character, or any one
+ of:
+
+ space
+ tab
+ return
+ enter
+ backspace
+ insert
+ delete
+ home
+ end
+ pgup
+ pgdn
+ left / right / up / down (arrow keys)
+ f1 .. f35 (function keys)
+ mousePress1 .. mousePress7 (mouse buttons)
+ mouseRelease1 .. mouseRelease7 (mouse buttons)
+
+ Context is either "any" or a comma-separated combi-
+ nation of:
+
+ fullScreen / window (full screen mode on/off)
+ continuous / singlePage (continuous mode on/off)
+ overLink / offLink (mouse over link or not)
+ scrLockOn / scrLockOff (scroll lock on/off)
+
+ The context string can include only one of each
+ pair in the above list.
+
+ Command is an Xpdf command (see the COMMANDS sec-
+ tion of the xpdf(1) man page for details). Multi-
+ ple commands are separated by whitespace.
+
+ The bind command replaces any existing binding, but
+ only if it was defined for the exact same modi-
+ fiers, key, and context. All tokens (modifiers,
+ key, context, commands) are case-sensitive.
+
+ Example key bindings:
+
+ # bind ctrl-a in any context to the nextPage
+ # command
+ bind ctrl-a any nextPage
+
+ # bind uppercase B, when in continuous mode
+ # with scroll lock on, to the reload command
+ # followed by the prevPage command
+ bind B continuous,scrLockOn reload prevPage
+
+ See the xpdf(1) man page for more examples.
+
+ unbind modifiers-key context
+ Removes a key binding established with the bind
+ command. This is most useful to remove default key
+ bindings before establishing new ones (e.g., if the
+ default key binding is given for "any" context, and
+ you want to create new key bindings for multiple
+ contexts).
+
+ 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-2007 Glyph & Cog, LLC.
+
+SEE ALSO
+ xpdf(1), pdftops(1), pdftotext(1), pdfinfo(1),
+ pdftoppm(1), pdfimages(1)
+ http://www.foolabs.com/xpdf/
+
+
+
+ 27 February 2007 xpdfrc(5)
diff --git a/doc/xpdfrc.hlp b/doc/xpdfrc.hlp
new file mode 100644
index 0000000..deb9fb4
--- /dev/null
+++ b/doc/xpdfrc.hlp
@@ -0,0 +1,619 @@
+! Generated automatically by mantohlp
+1 xpdfrc
+2 NCLUDE_FILE
+
+ xpdfrc - configuration file for Xpdf tools (version 3.02)
+
+ 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.
+
+ psPreload yes | no
+ If set to "yes", PDF forms are converted to PS pro-
+ cedures, and image data is preloaded. This uses
+ more memory in the PostScript interpreter, but gen-
+ erates significantly smaller PS files in situations
+ where, e.g., the same image is drawn on every page
+ of a long document. This defaults to "no".
+
+ 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.
+
+ continuousView yes | no
+ If set to "yes", xpdf will start in continuous view
+ mode, i.e., with one vertical screoll bar for the
+ whole document. This defaults to "no".
+
+ 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".
+
+ vectorAntialias yes | no
+ Enables or disables anti-aliasing of vector graph-
+ ics in the PDF rasterizer. This defaults to "yes".
+
+ strokeAdjust yes | no
+ Enables or disables stroke adjustment. This
+ defaults to "yes".
+
+ screenType dispersed | clustered | stochasticClustered
+ Sets the halftone screen type, which will be used
+ when generating a monochrome (1-bit) bitmap. The
+ three options are dispersed-dot dithering, clus-
+ tered-dot dithering (with a round dot and 45-degree
+ screen angle), and stochastic clustered-dot dither-
+ ing. By default, "stochasticClustered" is used for
+ resolutions of 300 dpi and higher, and "dispersed"
+ is used for resolutions lower then 300 dpi.
+
+ screenSize integer
+ Sets the size of the (square) halftone screen
+ threshold matrix. By default, this is 4 for dis-
+ persed-dot dithering, 10 for clustered-dot dither-
+ ing, and 100 for stochastic clustered-dot dither-
+ ing.
+
+ screenDotRadius integer
+ Sets the halftone screen dot radius. This is only
+ used when screenType is set to stochasticClustered,
+ and it defaults to 2. In clustered-dot mode, the
+ dot radius is half of the screen size. Dispersed-
+ dot dithering doesn't have a dot radius.
+
+ screenGamma float
+ Sets the halftone screen gamma correction parame-
+ ter. Gamma values greater than 1 make the output
+ brighter; gamma values less than 1 make it darker.
+ The default value is 1.
+
+ screenBlackThreshold float
+ When halftoning, all values below this threshold
+ are forced to solid black. This parameter is a
+ floating point value between 0 (black) and 1
+ (white). The default value is 0.
+
+ screenWhiteThreshold float
+ When halftoning, all values above this threshold
+ are forced to solid white. This parameter is a
+ floating point value between 0 (black) and 1
+ (white). The default value is 1.
+
+ 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".
+
+ mapUnknownCharNames yes | no
+ If set to "yes", and mapNumericCharNames is set to
+ "no", the Xpdf tools will apply a simple pass-
+ through mapping (Unicode index = character code)
+ for all unrecognized glyph names. 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 "no".
+
+ bind modifiers-key context command ...
+ Add a key or mouse button binding. Modifiers can
+ be zero or more of:
+
+ shift-
+ ctrl-
+ alt-
+
+ Key can be a regular ASCII character, or any one
+ of:
+
+ space
+ tab
+ return
+ enter
+ backspace
+ insert
+ delete
+ home
+ end
+ pgup
+ pgdn
+ left / right / up / down (arrow keys)
+ f1 .. f35 (function keys)
+ mousePress1 .. mousePress7 (mouse buttons)
+ mouseRelease1 .. mouseRelease7 (mouse buttons)
+
+ Context is either "any" or a comma-separated combi-
+ nation of:
+
+ fullScreen / window (full screen mode on/off)
+ continuous / singlePage (continuous mode on/off)
+ overLink / offLink (mouse over link or not)
+ scrLockOn / scrLockOff (scroll lock on/off)
+
+ The context string can include only one of each
+ pair in the above list.
+
+ Command is an Xpdf command (see the COMMANDS sec-
+ tion of the xpdf(1) man page for details). Multi-
+ ple commands are separated by whitespace.
+
+ The bind command replaces any existing binding, but
+ only if it was defined for the exact same modi-
+ fiers, key, and context. All tokens (modifiers,
+ key, context, commands) are case-sensitive.
+
+ Example key bindings:
+
+ # bind ctrl-a in any context to the nextPage
+ # command
+ bind ctrl-a any nextPage
+
+ # bind uppercase B, when in continuous mode
+ # with scroll lock on, to the reload command
+ # followed by the prevPage command
+ bind B continuous,scrLockOn reload prevPage
+
+ See the xpdf(1) man page for more examples.
+
+ unbind modifiers-key context
+ Removes a key binding established with the bind
+ command. This is most useful to remove default key
+ bindings before establishing new ones (e.g., if the
+ default key binding is given for "any" context, and
+ you want to create new key bindings for multiple
+ contexts).
+
+ 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-2007 Glyph & Cog, LLC.
+
+ ()
+
+2 SEE_ALSO
+
+ xpdf(1), pdftops(1), pdftotext(1), pdfinfo(1),
+ pdftoppm(1), pdfimages(1)
+ http://www.foolabs.com/xpdf/
+
+ ()
+
diff --git a/fofi/FoFiBase.cc b/fofi/FoFiBase.cc
new file mode 100644
index 0000000..28d0b8c
--- /dev/null
+++ b/fofi/FoFiBase.cc
@@ -0,0 +1,156 @@
+//========================================================================
+//
+// 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;
+}
diff --git a/fofi/FoFiBase.h b/fofi/FoFiBase.h
new file mode 100644
index 0000000..b78840b
--- /dev/null
+++ b/fofi/FoFiBase.h
@@ -0,0 +1,57 @@
+//========================================================================
+//
+// 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
diff --git a/fofi/FoFiEncodings.cc b/fofi/FoFiEncodings.cc
new file mode 100644
index 0000000..37a17f5
--- /dev/null
+++ b/fofi/FoFiEncodings.cc
@@ -0,0 +1,994 @@
+//========================================================================
+//
+// 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
+};
diff --git a/fofi/FoFiEncodings.h b/fofi/FoFiEncodings.h
new file mode 100644
index 0000000..50e285d
--- /dev/null
+++ b/fofi/FoFiEncodings.h
@@ -0,0 +1,36 @@
+//========================================================================
+//
+// 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
diff --git a/fofi/FoFiTrueType.cc b/fofi/FoFiTrueType.cc
new file mode 100644
index 0000000..f8a1aa3
--- /dev/null
+++ b/fofi/FoFiTrueType.cc
@@ -0,0 +1,2027 @@
+//========================================================================
+//
+// 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;
+}
diff --git a/fofi/FoFiTrueType.h b/fofi/FoFiTrueType.h
new file mode 100644
index 0000000..cf06ae7
--- /dev/null
+++ b/fofi/FoFiTrueType.h
@@ -0,0 +1,174 @@
+//========================================================================
+//
+// 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
diff --git a/fofi/FoFiType1.cc b/fofi/FoFiType1.cc
new file mode 100644
index 0000000..efad5ee
--- /dev/null
+++ b/fofi/FoFiType1.cc
@@ -0,0 +1,252 @@
+//========================================================================
+//
+// 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;
+}
diff --git a/fofi/FoFiType1.h b/fofi/FoFiType1.h
new file mode 100644
index 0000000..843352b
--- /dev/null
+++ b/fofi/FoFiType1.h
@@ -0,0 +1,59 @@
+//========================================================================
+//
+// 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
diff --git a/fofi/FoFiType1C.cc b/fofi/FoFiType1C.cc
new file mode 100644
index 0000000..3b28f32
--- /dev/null
+++ b/fofi/FoFiType1C.cc
@@ -0,0 +1,2603 @@
+//========================================================================
+//
+// 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;
+}
diff --git a/fofi/FoFiType1C.h b/fofi/FoFiType1C.h
new file mode 100644
index 0000000..eec2755
--- /dev/null
+++ b/fofi/FoFiType1C.h
@@ -0,0 +1,233 @@
+//========================================================================
+//
+// 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
diff --git a/fofi/Makefile.dep b/fofi/Makefile.dep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/fofi/Makefile.dep
diff --git a/fofi/Makefile.in b/fofi/Makefile.in
new file mode 100644
index 0000000..d0ac7a7
--- /dev/null
+++ b/fofi/Makefile.in
@@ -0,0 +1,69 @@
+#========================================================================
+#
+# FoFi library Makefile
+#
+# Copyright 2003 Glyph & Cog, LLC
+#
+#========================================================================
+
+SHELL = /bin/sh
+
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+GOOSRCDIR = $(srcdir)/../goo
+GOOLIBDIR = ../goo
+
+CXXFLAGS = @CXXFLAGS@ @DEFS@ -I.. -I$(GOOSRCDIR) -I$(srcdir)
+
+CXX = @CXX@
+AR = @AR@
+RANLIB = @RANLIB@
+
+LIBPREFIX = @LIBPREFIX@
+
+#------------------------------------------------------------------------
+
+.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
+
+#------------------------------------------------------------------------
+
+clean:
+ rm -f $(FOFI_OBJS) $(LIBPREFIX)fofi.a
+
+#------------------------------------------------------------------------
+
+depend:
+ $(CXX) $(CXXFLAGS) -MM $(CXX_SRC) >Makefile.dep
+
+include Makefile.dep
diff --git a/fofi/vms_make.com b/fofi/vms_make.com
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/fofi/vms_make.com
diff --git a/goo/FixedPoint.cc b/goo/FixedPoint.cc
new file mode 100644
index 0000000..47c2834
--- /dev/null
+++ b/goo/FixedPoint.cc
@@ -0,0 +1,118 @@
+//========================================================================
+//
+// 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
diff --git a/goo/FixedPoint.h b/goo/FixedPoint.h
new file mode 100644
index 0000000..abcb212
--- /dev/null
+++ b/goo/FixedPoint.h
@@ -0,0 +1,155 @@
+//========================================================================
+//
+// 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
diff --git a/goo/GHash.cc b/goo/GHash.cc
new file mode 100644
index 0000000..b51a764
--- /dev/null
+++ b/goo/GHash.cc
@@ -0,0 +1,380 @@
+//========================================================================
+//
+// 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);
+}
diff --git a/goo/GHash.h b/goo/GHash.h
new file mode 100644
index 0000000..31aba93
--- /dev/null
+++ b/goo/GHash.h
@@ -0,0 +1,78 @@
+//========================================================================
+//
+// 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
diff --git a/goo/GList.cc b/goo/GList.cc
new file mode 100644
index 0000000..fb5fd62
--- /dev/null
+++ b/goo/GList.cc
@@ -0,0 +1,97 @@
+//========================================================================
+//
+// 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*));
+}
diff --git a/goo/GList.h b/goo/GList.h
new file mode 100644
index 0000000..e4d8ff8
--- /dev/null
+++ b/goo/GList.h
@@ -0,0 +1,96 @@
+//========================================================================
+//
+// 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
diff --git a/goo/GMutex.h b/goo/GMutex.h
new file mode 100644
index 0000000..7fa93d8
--- /dev/null
+++ b/goo/GMutex.h
@@ -0,0 +1,49 @@
+//========================================================================
+//
+// 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
diff --git a/goo/GString.cc b/goo/GString.cc
new file mode 100644
index 0000000..e21fd3e
--- /dev/null
+++ b/goo/GString.cc
@@ -0,0 +1,718 @@
+//========================================================================
+//
+// 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(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;
+}
diff --git a/goo/GString.h b/goo/GString.h
new file mode 100644
index 0000000..dd22e2d
--- /dev/null
+++ b/goo/GString.h
@@ -0,0 +1,136 @@
+//========================================================================
+//
+// 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
diff --git a/goo/Makefile.dep b/goo/Makefile.dep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/goo/Makefile.dep
diff --git a/goo/Makefile.in b/goo/Makefile.in
new file mode 100644
index 0000000..0baddbb
--- /dev/null
+++ b/goo/Makefile.in
@@ -0,0 +1,71 @@
+#========================================================================
+#
+# Goo library Makefile
+#
+# Copyright 1996-2003 Glyph & Cog, LLC
+#
+#========================================================================
+
+SHELL = /bin/sh
+
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+CFLAGS = @CFLAGS@ @DEFS@ -I.. -I$(srcdir)
+CXXFLAGS = @CXXFLAGS@ @DEFS@ -I.. -I$(srcdir)
+
+CC = @CC@
+CXX = @CXX@
+AR = @AR@
+RANLIB = @RANLIB@
+
+LIBPREFIX = @LIBPREFIX@
+
+#------------------------------------------------------------------------
+
+.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)
+
+$(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
+
+#------------------------------------------------------------------------
+
+depend:
+ $(CXX) $(CXXFLAGS) -MM $(CXX_SRC) >Makefile.dep
+ $(CC) $(CFLAGS) -MM $(C_SRC) >>Makefile.dep
+
+include Makefile.dep
diff --git a/goo/gfile.cc b/goo/gfile.cc
new file mode 100644
index 0000000..54a7be3
--- /dev/null
+++ b/goo/gfile.cc
@@ -0,0 +1,731 @@
+//========================================================================
+//
+// 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
+}
diff --git a/goo/gfile.h b/goo/gfile.h
new file mode 100644
index 0000000..82f1d7a
--- /dev/null
+++ b/goo/gfile.h
@@ -0,0 +1,138 @@
+//========================================================================
+//
+// 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
diff --git a/goo/gmem.cc b/goo/gmem.cc
new file mode 100644
index 0000000..6b13b38
--- /dev/null
+++ b/goo/gmem.cc
@@ -0,0 +1,264 @@
+/*
+ * 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;
+}
diff --git a/goo/gmem.h b/goo/gmem.h
new file mode 100644
index 0000000..da34736
--- /dev/null
+++ b/goo/gmem.h
@@ -0,0 +1,79 @@
+/*
+ * 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
diff --git a/goo/gmempp.cc b/goo/gmempp.cc
new file mode 100644
index 0000000..b1ee970
--- /dev/null
+++ b/goo/gmempp.cc
@@ -0,0 +1,32 @@
+//========================================================================
+//
+// 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
diff --git a/goo/gtypes.h b/goo/gtypes.h
new file mode 100644
index 0000000..9f64f57
--- /dev/null
+++ b/goo/gtypes.h
@@ -0,0 +1,29 @@
+/*
+ * 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
diff --git a/goo/parseargs.c b/goo/parseargs.c
new file mode 100644
index 0000000..f77cc85
--- /dev/null
+++ b/goo/parseargs.c
@@ -0,0 +1,190 @@
+/*
+ * 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;
+}
diff --git a/goo/parseargs.h b/goo/parseargs.h
new file mode 100644
index 0000000..0d163b9
--- /dev/null
+++ b/goo/parseargs.h
@@ -0,0 +1,71 @@
+/*
+ * 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
diff --git a/goo/vms_directory.c b/goo/vms_directory.c
new file mode 100644
index 0000000..92d9493
--- /dev/null
+++ b/goo/vms_directory.c
@@ -0,0 +1,214 @@
+/*
+ * 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);
+}
diff --git a/goo/vms_dirent.h b/goo/vms_dirent.h
new file mode 100644
index 0000000..13e21a0
--- /dev/null
+++ b/goo/vms_dirent.h
@@ -0,0 +1,67 @@
+/* @(#)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 */
diff --git a/goo/vms_make.com b/goo/vms_make.com
new file mode 100644
index 0000000..676643f
--- /dev/null
+++ b/goo/vms_make.com
@@ -0,0 +1,82 @@
+$!========================================================================
+$!
+$! 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
diff --git a/goo/vms_sys_dirent.h b/goo/vms_sys_dirent.h
new file mode 100644
index 0000000..2c20d71
--- /dev/null
+++ b/goo/vms_sys_dirent.h
@@ -0,0 +1,54 @@
+/* @(#)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 */
diff --git a/goo/vms_unix_time.h b/goo/vms_unix_time.h
new file mode 100644
index 0000000..f8e8382
--- /dev/null
+++ b/goo/vms_unix_time.h
@@ -0,0 +1,102 @@
+/* @(#)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_*/
+
diff --git a/goo/vms_unix_times.c b/goo/vms_unix_times.c
new file mode 100644
index 0000000..004c0d0
--- /dev/null
+++ b/goo/vms_unix_times.c
@@ -0,0 +1,42 @@
+/*
+ * 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
diff --git a/goo/vms_unlink.c b/goo/vms_unlink.c
new file mode 100644
index 0000000..e2cf687
--- /dev/null
+++ b/goo/vms_unlink.c
@@ -0,0 +1,22 @@
+/*
+ * 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));
+}
diff --git a/install-sh b/install-sh
new file mode 100755
index 0000000..ebc6691
--- /dev/null
+++ b/install-sh
@@ -0,0 +1,250 @@
+#! /bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5 (mit/util/scripts/install.sh).
+#
+# Copyright 1991 by the Massachusetts Institute of Technology
+#
+# Permission to use, copy, modify, distribute, and sell this software and its
+# documentation for any purpose is hereby granted without fee, provided that
+# the above copyright notice appear in all copies and that both that
+# copyright notice and this permission notice appear in supporting
+# documentation, and that the name of M.I.T. not be used in advertising or
+# publicity pertaining to distribution of the software without specific,
+# written prior permission. M.I.T. makes no representations about the
+# suitability of this software for any purpose. It is provided "as is"
+# without express or implied warranty.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch. It can only install one file at a time, a restriction
+# shared with many OS's install programs.
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+ case $1 in
+ -c) instcmd="$cpprog"
+ shift
+ continue;;
+
+ -d) dir_arg=true
+ shift
+ continue;;
+
+ -m) chmodcmd="$chmodprog $2"
+ shift
+ shift
+ continue;;
+
+ -o) chowncmd="$chownprog $2"
+ shift
+ shift
+ continue;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift
+ shift
+ continue;;
+
+ -s) stripcmd="$stripprog"
+ shift
+ continue;;
+
+ -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+ shift
+ continue;;
+
+ -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+ shift
+ continue;;
+
+ *) if [ x"$src" = x ]
+ then
+ src=$1
+ else
+ # this colon is to work around a 386BSD /bin/sh bug
+ :
+ dst=$1
+ fi
+ shift
+ continue;;
+ esac
+done
+
+if [ x"$src" = x ]
+then
+ echo "install: no input file specified"
+ exit 1
+else
+ true
+fi
+
+if [ x"$dir_arg" != x ]; then
+ dst=$src
+ src=""
+
+ if [ -d $dst ]; then
+ instcmd=:
+ else
+ instcmd=mkdir
+ fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad
+# if $src (and thus $dsttmp) contains '*'.
+
+ if [ -f $src -o -d $src ]
+ then
+ true
+ else
+ echo "install: $src does not exist"
+ exit 1
+ fi
+
+ if [ x"$dst" = x ]
+ then
+ echo "install: no destination specified"
+ exit 1
+ else
+ true
+ fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+ if [ -d $dst ]
+ then
+ dst="$dst"/`basename $src`
+ else
+ true
+ fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+# this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+ pathcomp="${pathcomp}${1}"
+ shift
+
+ if [ ! -d "${pathcomp}" ] ;
+ then
+ $mkdirprog "${pathcomp}"
+ else
+ true
+ fi
+
+ pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+ $doit $instcmd $dst &&
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+ if [ x"$transformarg" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ dstfile=`basename $dst $transformbasename |
+ sed $transformarg`$transformbasename
+ fi
+
+# don't allow the sed command to completely eliminate the filename
+
+ if [ x"$dstfile" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ true
+ fi
+
+# Make a temp file name in the proper directory.
+
+ dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+ $doit $instcmd $src $dsttmp &&
+
+ trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing. If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+ $doit $rmcmd -f $dstdir/$dstfile &&
+ $doit $mvcmd $dsttmp $dstdir/$dstfile
+
+fi &&
+
+
+exit 0
diff --git a/misc/hello.pdf b/misc/hello.pdf
new file mode 100644
index 0000000..d097335
--- /dev/null
+++ b/misc/hello.pdf
Binary files differ
diff --git a/misc/xpdf.dt b/misc/xpdf.dt
new file mode 100644
index 0000000..d3d41b7
--- /dev/null
+++ b/misc/xpdf.dt
@@ -0,0 +1,28 @@
+# simple CDE action for xpdf
+# written by Roland.Mainz@informatik.med.uni-giessen.de
+#
+# To use, copy this file into $HOME/.dt/types
+#
+# NOTE: this overrides the actions AcroRead and AcroReadPrint
+# defined in /usr/dt/appconfig/types/C/solaris.dt
+
+ACTION AcroRead
+{
+ TYPE COMMAND
+ WINDOW_TYPE NO_STDIO
+ EXEC_STRING xpdf "%(File)Arg_1%"
+}
+
+# NOTE: Add a '-level1' switch to pdftops if your printer does not
+# support Level 2 Postscript.
+ACTION AcroReadPrint
+{
+ TYPE COMMAND
+ WINDOW_TYPE NO_STDIO
+ EXEC_STRING ksh -c ' \
+ MYFILE="%(File)Arg_1%" ; \
+ pdftops "${MYFILE}" /dev/stdout | \
+ dtlp -w -b "${MYFILE}"'
+}
+
+# EOF.
diff --git a/ms_make.bat b/ms_make.bat
new file mode 100644
index 0000000..6bbaf35
--- /dev/null
+++ b/ms_make.bat
@@ -0,0 +1,118 @@
+set CC=cl
+set CFLAGS=/DWIN32 /I.. /I..\goo /I..\fofi /O2 /nologo
+set CXX=cl
+set CXXFLAGS=%CFLAGS% /TP
+set LIBPROG=lib
+set LINKFLAGS=/MT /nologo
+
+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 gmem.cc
+%CXX% %CXXFLAGS% /c gmempp.cc
+%CXX% %CXXFLAGS% /c gfile.cc
+%CC% %CFLAGS% /c parseargs.c
+%LIBPROG% /nologo /out:Goo.lib GHash.obj GList.obj GString.obj gmempp.obj gfile.obj gmem.obj parseargs.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:fofi.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 ImageOutputDev.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 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 SecurityHandler.cc
+%CXX% %CXXFLAGS% /c Stream.cc
+%CXX% %CXXFLAGS% /c TextOutputDev.cc
+%CXX% %CXXFLAGS% /c UnicodeMap.cc
+%CXX% %CXXFLAGS% /c UnicodeTypeTable.cc
+%CXX% %CXXFLAGS% /c XRef.cc
+%CXX% %CXXFLAGS% /c pdftops.cc
+%CXX% %CXXFLAGS% /c pdftotext.cc
+%CXX% %CXXFLAGS% /c pdfinfo.cc
+%CXX% %CXXFLAGS% /c pdffonts.cc
+%CXX% %CXXFLAGS% /c pdfimages.cc
+
+%CXX% %LINKFLAGS% /Fepdftops.exe 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 PSOutputDev.obj PSTokenizer.obj SecurityHandler.obj Stream.obj UnicodeMap.obj XRef.obj pdftops.obj ..\fofi\fofi.lib ..\goo\Goo.lib shell32.lib user32.lib gdi32.lib advapi32.lib
+
+%CXX% %LINKFLAGS% /Fepdftotext.exe 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 PSTokenizer.obj SecurityHandler.obj Stream.obj TextOutputDev.obj UnicodeMap.obj UnicodeTypeTable.obj XRef.obj pdftotext.obj ..\fofi\fofi.lib ..\goo\Goo.lib shell32.lib user32.lib gdi32.lib advapi32.lib
+
+%CXX% %LINKFLAGS% /Fepdfinfo.exe 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 PSTokenizer.obj SecurityHandler.obj Stream.obj UnicodeMap.obj XRef.obj pdfinfo.obj ..\fofi\fofi.lib ..\goo\Goo.lib shell32.lib user32.lib gdi32.lib advapi32.lib
+
+%CXX% %LINKFLAGS% /Fepdffonts.exe 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 PSTokenizer.obj SecurityHandler.obj Stream.obj UnicodeMap.obj XRef.obj pdffonts.obj ..\fofi\fofi.lib ..\goo\Goo.lib shell32.lib user32.lib gdi32.lib advapi32.lib
+
+%CXX% %LINKFLAGS% /Fepdfimages.exe 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 ImageOutputDev.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 PSTokenizer.obj SecurityHandler.obj Stream.obj UnicodeMap.obj XRef.obj pdfimages.obj ..\fofi\fofi.lib ..\goo\Goo.lib shell32.lib user32.lib gdi32.lib advapi32.lib
+
+cd ..
+
+rem --- This part will only work if you have FreeType installed ---
+
+set FT2DIR=..\freetype-2.3.1
+set CXXFLAGS=%CXXFLAGS% /I..\splash /I%FT2DIR%\include
+
+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:splash.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 ..\xpdf
+%CXX% %CXXFLAGS% /c SplashOutputDev.cc
+%CXX% %CXXFLAGS% /c pdftoppm.cc
+
+%CXX% %LINKFLAGS% /Fepdftoppm.exe 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 PSTokenizer.obj SecurityHandler.obj SplashOutputDev.obj Stream.obj UnicodeMap.obj UnicodeTypeTable.obj XRef.obj pdftoppm.obj ..\splash\splash.lib ..\fofi\fofi.lib ..\goo\Goo.lib %FT2DIR%\freetype2.lib shell32.lib user32.lib gdi32.lib advapi32.lib
+
+cd ..
diff --git a/splash/Makefile.dep b/splash/Makefile.dep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/splash/Makefile.dep
diff --git a/splash/Makefile.in b/splash/Makefile.in
new file mode 100644
index 0000000..66c449b
--- /dev/null
+++ b/splash/Makefile.in
@@ -0,0 +1,99 @@
+#========================================================================
+#
+# 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 = @CXXFLAGS@ @DEFS@ -I.. -I$(GOOSRCDIR) -I$(FOFISRCDIR) -I$(srcdir) @t1_CFLAGS@ @freetype2_CFLAGS@
+
+CXX = @CXX@
+AR = @AR@
+RANLIB = @RANLIB@
+
+LIBPREFIX = @LIBPREFIX@
+
+#------------------------------------------------------------------------
+
+.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
+
+#------------------------------------------------------------------------
+
+clean:
+ rm -f $(SPLASH_OBJS) $(LIBPREFIX)splash.a
+
+#------------------------------------------------------------------------
+
+depend:
+ $(CXX) $(CXXFLAGS) -MM $(CXX_SRC) >Makefile.dep
+
+include Makefile.dep
diff --git a/splash/Splash.cc b/splash/Splash.cc
new file mode 100644
index 0000000..537ee1a
--- /dev/null
+++ b/splash/Splash.cc
@@ -0,0 +1,3310 @@
+//========================================================================
+//
+// 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" : " ");
+ }
+}
diff --git a/splash/Splash.h b/splash/Splash.h
new file mode 100644
index 0000000..c624622
--- /dev/null
+++ b/splash/Splash.h
@@ -0,0 +1,293 @@
+//========================================================================
+//
+// 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
diff --git a/splash/SplashBitmap.cc b/splash/SplashBitmap.cc
new file mode 100644
index 0000000..0cb1a75
--- /dev/null
+++ b/splash/SplashBitmap.cc
@@ -0,0 +1,188 @@
+//========================================================================
+//
+// 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];
+}
diff --git a/splash/SplashBitmap.h b/splash/SplashBitmap.h
new file mode 100644
index 0000000..69ab058
--- /dev/null
+++ b/splash/SplashBitmap.h
@@ -0,0 +1,61 @@
+//========================================================================
+//
+// SplashBitmap.h
+//
+//========================================================================
+
+#ifndef SPLASHBITMAP_H
+#define SPLASHBITMAP_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "SplashTypes.h"
+
+//------------------------------------------------------------------------
+// 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
diff --git a/splash/SplashClip.cc b/splash/SplashClip.cc
new file mode 100644
index 0000000..ef8acba
--- /dev/null
+++ b/splash/SplashClip.cc
@@ -0,0 +1,382 @@
+//========================================================================
+//
+// 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);
+ }
+}
diff --git a/splash/SplashClip.h b/splash/SplashClip.h
new file mode 100644
index 0000000..8ae2154
--- /dev/null
+++ b/splash/SplashClip.h
@@ -0,0 +1,107 @@
+//========================================================================
+//
+// 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
diff --git a/splash/SplashErrorCodes.h b/splash/SplashErrorCodes.h
new file mode 100644
index 0000000..2a70d4b
--- /dev/null
+++ b/splash/SplashErrorCodes.h
@@ -0,0 +1,32 @@
+//========================================================================
+//
+// 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
diff --git a/splash/SplashFTFont.cc b/splash/SplashFTFont.cc
new file mode 100644
index 0000000..21b76c6
--- /dev/null
+++ b/splash/SplashFTFont.cc
@@ -0,0 +1,357 @@
+//========================================================================
+//
+// 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
diff --git a/splash/SplashFTFont.h b/splash/SplashFTFont.h
new file mode 100644
index 0000000..8e31d14
--- /dev/null
+++ b/splash/SplashFTFont.h
@@ -0,0 +1,58 @@
+//========================================================================
+//
+// 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
diff --git a/splash/SplashFTFontEngine.cc b/splash/SplashFTFontEngine.cc
new file mode 100644
index 0000000..2270f7b
--- /dev/null
+++ b/splash/SplashFTFontEngine.cc
@@ -0,0 +1,179 @@
+//========================================================================
+//
+// 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
diff --git a/splash/SplashFTFontEngine.h b/splash/SplashFTFontEngine.h
new file mode 100644
index 0000000..b8b9f7f
--- /dev/null
+++ b/splash/SplashFTFontEngine.h
@@ -0,0 +1,65 @@
+//========================================================================
+//
+// 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
diff --git a/splash/SplashFTFontFile.cc b/splash/SplashFTFontFile.cc
new file mode 100644
index 0000000..dd37676
--- /dev/null
+++ b/splash/SplashFTFontFile.cc
@@ -0,0 +1,114 @@
+//========================================================================
+//
+// 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
diff --git a/splash/SplashFTFontFile.h b/splash/SplashFTFontFile.h
new file mode 100644
index 0000000..af6a8a0
--- /dev/null
+++ b/splash/SplashFTFontFile.h
@@ -0,0 +1,73 @@
+//========================================================================
+//
+// 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
diff --git a/splash/SplashFont.cc b/splash/SplashFont.cc
new file mode 100644
index 0000000..3a30cc8
--- /dev/null
+++ b/splash/SplashFont.cc
@@ -0,0 +1,176 @@
+//========================================================================
+//
+// 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;
+}
diff --git a/splash/SplashFont.h b/splash/SplashFont.h
new file mode 100644
index 0000000..60a2db7
--- /dev/null
+++ b/splash/SplashFont.h
@@ -0,0 +1,104 @@
+//========================================================================
+//
+// 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
diff --git a/splash/SplashFontEngine.cc b/splash/SplashFontEngine.cc
new file mode 100644
index 0000000..1c090f6
--- /dev/null
+++ b/splash/SplashFontEngine.cc
@@ -0,0 +1,317 @@
+//========================================================================
+//
+// 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;
+}
diff --git a/splash/SplashFontEngine.h b/splash/SplashFontEngine.h
new file mode 100644
index 0000000..6da4a74
--- /dev/null
+++ b/splash/SplashFontEngine.h
@@ -0,0 +1,91 @@
+//========================================================================
+//
+// 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
diff --git a/splash/SplashFontFile.cc b/splash/SplashFontFile.cc
new file mode 100644
index 0000000..acbc12a
--- /dev/null
+++ b/splash/SplashFontFile.cc
@@ -0,0 +1,55 @@
+//========================================================================
+//
+// 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;
+ }
+}
diff --git a/splash/SplashFontFile.h b/splash/SplashFontFile.h
new file mode 100644
index 0000000..9f89312
--- /dev/null
+++ b/splash/SplashFontFile.h
@@ -0,0 +1,60 @@
+//========================================================================
+//
+// 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
diff --git a/splash/SplashFontFileID.cc b/splash/SplashFontFileID.cc
new file mode 100644
index 0000000..af37cb2
--- /dev/null
+++ b/splash/SplashFontFileID.cc
@@ -0,0 +1,23 @@
+//========================================================================
+//
+// SplashFontFileID.cc
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include "SplashFontFileID.h"
+
+//------------------------------------------------------------------------
+// SplashFontFileID
+//------------------------------------------------------------------------
+
+SplashFontFileID::SplashFontFileID() {
+}
+
+SplashFontFileID::~SplashFontFileID() {
+}
diff --git a/splash/SplashFontFileID.h b/splash/SplashFontFileID.h
new file mode 100644
index 0000000..bed11d3
--- /dev/null
+++ b/splash/SplashFontFileID.h
@@ -0,0 +1,30 @@
+//========================================================================
+//
+// 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
diff --git a/splash/SplashGlyphBitmap.h b/splash/SplashGlyphBitmap.h
new file mode 100644
index 0000000..044ba4a
--- /dev/null
+++ b/splash/SplashGlyphBitmap.h
@@ -0,0 +1,26 @@
+//========================================================================
+//
+// 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
diff --git a/splash/SplashMath.h b/splash/SplashMath.h
new file mode 100644
index 0000000..1dd60dd
--- /dev/null
+++ b/splash/SplashMath.h
@@ -0,0 +1,89 @@
+//========================================================================
+//
+// 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
diff --git a/splash/SplashPath.cc b/splash/SplashPath.cc
new file mode 100644
index 0000000..e3a8927
--- /dev/null
+++ b/splash/SplashPath.cc
@@ -0,0 +1,184 @@
+//========================================================================
+//
+// 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;
+}
diff --git a/splash/SplashPath.h b/splash/SplashPath.h
new file mode 100644
index 0000000..b63ee5d
--- /dev/null
+++ b/splash/SplashPath.h
@@ -0,0 +1,121 @@
+//========================================================================
+//
+// 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
diff --git a/splash/SplashPattern.cc b/splash/SplashPattern.cc
new file mode 100644
index 0000000..b77658e
--- /dev/null
+++ b/splash/SplashPattern.cc
@@ -0,0 +1,40 @@
+//========================================================================
+//
+// 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);
+}
diff --git a/splash/SplashPattern.h b/splash/SplashPattern.h
new file mode 100644
index 0000000..0a02e9c
--- /dev/null
+++ b/splash/SplashPattern.h
@@ -0,0 +1,65 @@
+//========================================================================
+//
+// 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
diff --git a/splash/SplashScreen.cc b/splash/SplashScreen.cc
new file mode 100644
index 0000000..318629d
--- /dev/null
+++ b/splash/SplashScreen.cc
@@ -0,0 +1,383 @@
+//========================================================================
+//
+// 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;
+}
diff --git a/splash/SplashScreen.h b/splash/SplashScreen.h
new file mode 100644
index 0000000..2baa9b5
--- /dev/null
+++ b/splash/SplashScreen.h
@@ -0,0 +1,56 @@
+//========================================================================
+//
+// 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
diff --git a/splash/SplashState.cc b/splash/SplashState.cc
new file mode 100644
index 0000000..e2c34c4
--- /dev/null
+++ b/splash/SplashState.cc
@@ -0,0 +1,165 @@
+//========================================================================
+//
+// 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;
+}
diff --git a/splash/SplashState.h b/splash/SplashState.h
new file mode 100644
index 0000000..1f5a88d
--- /dev/null
+++ b/splash/SplashState.h
@@ -0,0 +1,103 @@
+//========================================================================
+//
+// 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
diff --git a/splash/SplashT1Font.cc b/splash/SplashT1Font.cc
new file mode 100644
index 0000000..8219596
--- /dev/null
+++ b/splash/SplashT1Font.cc
@@ -0,0 +1,287 @@
+//========================================================================
+//
+// 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
diff --git a/splash/SplashT1Font.h b/splash/SplashT1Font.h
new file mode 100644
index 0000000..8ea74de
--- /dev/null
+++ b/splash/SplashT1Font.h
@@ -0,0 +1,57 @@
+//========================================================================
+//
+// 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
diff --git a/splash/SplashT1FontEngine.cc b/splash/SplashT1FontEngine.cc
new file mode 100644
index 0000000..ca1453a
--- /dev/null
+++ b/splash/SplashT1FontEngine.cc
@@ -0,0 +1,124 @@
+//========================================================================
+//
+// 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
diff --git a/splash/SplashT1FontEngine.h b/splash/SplashT1FontEngine.h
new file mode 100644
index 0000000..57a0448
--- /dev/null
+++ b/splash/SplashT1FontEngine.h
@@ -0,0 +1,53 @@
+//========================================================================
+//
+// 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
diff --git a/splash/SplashT1FontFile.cc b/splash/SplashT1FontFile.cc
new file mode 100644
index 0000000..0dbb8f0
--- /dev/null
+++ b/splash/SplashT1FontFile.cc
@@ -0,0 +1,97 @@
+//========================================================================
+//
+// 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
diff --git a/splash/SplashT1FontFile.h b/splash/SplashT1FontFile.h
new file mode 100644
index 0000000..69c9caf
--- /dev/null
+++ b/splash/SplashT1FontFile.h
@@ -0,0 +1,58 @@
+//========================================================================
+//
+// 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
diff --git a/splash/SplashTypes.h b/splash/SplashTypes.h
new file mode 100644
index 0000000..35551b9
--- /dev/null
+++ b/splash/SplashTypes.h
@@ -0,0 +1,132 @@
+//========================================================================
+//
+// 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
diff --git a/splash/SplashXPath.cc b/splash/SplashXPath.cc
new file mode 100644
index 0000000..da296b1
--- /dev/null
+++ b/splash/SplashXPath.cc
@@ -0,0 +1,438 @@
+//========================================================================
+//
+// 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);
+}
diff --git a/splash/SplashXPath.h b/splash/SplashXPath.h
new file mode 100644
index 0000000..43276b8
--- /dev/null
+++ b/splash/SplashXPath.h
@@ -0,0 +1,100 @@
+//========================================================================
+//
+// 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
diff --git a/splash/SplashXPathScanner.cc b/splash/SplashXPathScanner.cc
new file mode 100644
index 0000000..d13206b
--- /dev/null
+++ b/splash/SplashXPathScanner.cc
@@ -0,0 +1,428 @@
+//========================================================================
+//
+// 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);
+ }
+ }
+ }
+}
diff --git a/splash/SplashXPathScanner.h b/splash/SplashXPathScanner.h
new file mode 100644
index 0000000..ab02fcc
--- /dev/null
+++ b/splash/SplashXPathScanner.h
@@ -0,0 +1,87 @@
+//========================================================================
+//
+// 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
diff --git a/splash/vms_make.com b/splash/vms_make.com
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/splash/vms_make.com
diff --git a/vms_make.com b/vms_make.com
new file mode 100644
index 0000000..d70caad
--- /dev/null
+++ b/vms_make.com
@@ -0,0 +1,736 @@
+$!========================================================================
+$!
+$! Main Xpdf compile script for VMS.
+$!
+$! Written by Patrick Moreau, Martin P.J. Zinser.
+$!
+$! Copyright 1996-2003 Glyph & Cog, LLC
+$!
+$!========================================================================
+$!
+$! This procedure takes up to three (optional) parameters:
+$!
+$! 1.) Configuration settings:
+$!
+$! a4 - Use european A4 as the default paper size.
+$!
+$! no_text_select - Disable text selection in Xpdf
+$!
+$! opi_support - Compile Xpdf with support for the Open Prepress
+$! Interface (OPI)
+$!
+$! 2.) Compiler detection:
+$!
+$! In case you want to override the automatic compiler detection
+$! specify either DECC or GCC as the second paramter,
+$! e.g. @vms_make "" GCC
+$!
+$! 3.) System Xpdf resource file
+$!
+$! The default for this is decw$system_defaults:xpdfrc.dat, since this
+$! is the standard place for systemwide Xdefaults files on OpenVMS. You
+$! may provide a different file in p3.
+$!
+$! External libraries (like T1lib, Freetype, and XPM) are supported via the
+$! config file VMSLIB.DAT. Please check the sample file, which will be created
+$! by this procedure upon first invocation, for the information you need to
+$! provide
+$!
+$! Sample invocation of the script:
+$! @vms_make a4,opi_support ""
+$!
+$! In case of problems with the compile you may contact me at
+$! zinser@decus.de (preferred) or zinser@sysdev.deutsche-boerse.com (work).
+$!
+$!========================================================================
+$!
+$ on error then goto err_exit
+$!
+$!
+$! Just some general constants...
+$!
+$ true = 1
+$ false = 0
+$ xpdf_link :== link
+$ tmpnam = "temp_" + f$getjpi("","pid")
+$ tc = tmpnam + ".c"
+$!
+$! Setup variables holding "config" information
+$!
+$ aconf_in_file = "aconf_h.in#aconf.h_in#aconf.h.in"
+$ name = "Xpdf"
+$ version = "?.?"
+$ mydefs = "#"
+$ xlibs = "xt#xmu#motif"
+$ cxxdefs = ""
+$ libdefs = "\"
+$ libincs = ""
+$ float = ""
+$ compress_def = false
+$ ft2def = false
+$ x11_save = ""
+$ p2 = f$edit(p2,"upcase,trim")
+$ if f$edit(p3,"trim") .eqs. ""
+$ then
+$ resfil = "decw$system_defaults:xpdfrc.dat"
+$ else
+$ resfil = "'p3'"
+$ endif
+$!
+$ gosub proc_config
+$ gosub check_version
+$!
+$! Start building the option file
+$!
+$ open/write optf xpdf.opt
+$ open/write topt tmp.opt
+$ write optf "Identification=""''name' ''version'"""
+$ gosub check_create_vmslib
+$ gosub check_xlib
+$!
+$ if (f$getsyi("HW_MODEL").ge.1024) .and. -
+ (f$locate("T1LIB",f$edit(libdefs,"UPCASE")) .lt. f$length(libdefs)) -
+ then float = "/float=ieee_float"
+$ incs = "sys$library:,[-],[],[-.goo]''libincs'"
+$!
+$ gosub check_compiler
+$ close optf
+$ close topt
+$!
+$! aconf.h.in might be mapped in different ways, so go figure
+$!
+$ i = 0
+$FIND_ACONF:
+$ fname = f$element(i,"#",aconf_in_file)
+$ if fname .eqs. "#" then goto AMISS_ERR
+$ if f$search(fname) .eqs. ""
+$ then
+$ i = i + 1
+$ goto find_aconf
+$ endif
+$ open/read/err=aconf_err aconf_in 'fname'
+$ open/write aconf aconf.h
+$ACONF_LOOP:
+$ read/end_of_file=aconf_exit aconf_in line
+$ work = f$edit(line, "compress,trim")
+$ if f$extract(0,6,work) .nes. "#undef"
+$ then
+$ write aconf line
+$ else
+$ def = f$element(1," ",work)
+$ if ((f$locate("\''def'\",f$edit(libdefs,"UPCASE")) .lt. f$length(libdefs)) -
+ .or. (f$locate("#''def'#",f$edit(mydefs,"UPCASE")) .lt. f$length(mydefs)))
+$ then
+$ write aconf "#define ", def, " 1"
+$ else
+$ gosub check_config
+$ endif
+$ endif
+$!
+$! Make sure old-style VMS is defined along with __VMS
+$!
+$ if f$locate("define ACONF_H",line) .lt. f$length(line)
+$ then
+$ write aconf "#define VMS 1"
+$ endif
+$ goto aconf_loop
+$ACONF_EXIT:
+$ close aconf_in
+$ close aconf
+$ write sys$output "Compiling in [.GOO]"
+$ set default [.goo]
+$ @vms_make
+$ write sys$output "Compiling in [.XPDF]"
+$ set default [-.xpdf]
+$ @vms_make
+$ set default [-]
+$ gosub reset_env
+$ dele/noconf/nolog tmp.opt;*
+$ exit
+$ACONF_ERR:
+$ write sys$output "Input file ''fname' could not be opened"
+$ goto err_exit
+$AMISS_ERR:
+$ write sys$output "No source for aconf.h found."
+$ write sys$output "Tried any of ''aconf_in_file'"
+$CXX_ERR:
+$ write sys$output "C++ compiler required to build Xpdf"
+$ goto err_exit
+$FT2_ERR:
+$ write sys$output "Can not find [.internal] sub-dir in Freetype 2 tree"
+$ goto err_exit
+$ERR_EXIT:
+$ set message/facil/ident/sever/text
+$ gosub reset_env
+$ close/nolog aconf_in
+$ close/nolog aconf
+$ close/nolog optf
+$ close/nolog tmpc
+$ close/nolop topt
+$ write sys$output "Exiting..."
+$ exit 2
+$!------------------------------------------------------------------------------
+$!
+$! Take care of driver file with information about external libraries
+$!
+$CHECK_CREATE_VMSLIB:
+$!
+$ if f$search("VMSLIB.DAT") .eqs. ""
+$ then
+$ type/out=vmslib.dat sys$input
+!
+! This is a simple driver file with information used by vms_make.com to
+! check if external libraries (like t1lib and freetype) are available on
+! the system.
+!
+! Layout of the file:
+!
+! - Lines starting with ! are treated as comments
+! - Elements in a data line are separated by # signs
+! - The elements need to be listed in the following order
+! 1.) Name of the Library (only used for informative messages
+! from vms_make.com)
+! 2.) Location where the object library can be found
+! 3.) Location where the include files for the library can be found
+! 4.) Include file used to verify library location
+! 5.) CPP define to pass to the build to indicate availability of
+! the library
+!
+! Example: The following lines show how definitions
+! might look like. They are site specific and the locations of the
+! library and include files need almost certainly to be changed.
+!
+! Location: All of the libaries can be found at the following addresses
+!
+! T1LIB: http://www.decus.de:8080/www/vms/sw/t1lib.htmlx
+! FREETYPE: http://www.decus.de:8080/www/vms/sw/freetype2.htmlx
+! XPM: http://www.decus.de:8080/www/vms/sw/xpm.htmlx
+! LIBPAPER: http://www.decus.de:8080/www/vms/sw/libpaper.htmlx
+!
+!T1LIB # pubbin:t1shr.exe # public$root:[xtools.libs.t1lib.lib.t1lib] # t1lib.h # HAVE_T1LIB_H
+!FREETYPE # pubbin:freetype2shr.exe # public$root:[xtools.libs.ft2.include.freetype],public$root:[xtools.libs.ft2.include] # freetype.h # HAVE_FREETYPE_H\FREETYPE2
+!XPM # pubbin:libxpm.olb # X11: # xpm.h # HAVE_X11_XPM_H
+!LIBPAPER # pubbin:libpapershr.exe # public$root:[util.libs.paper.lib] # paper.h # HAVE_PAPER_H
+$ write sys$output "New driver file vmslib.dat created."
+$ write sys$output "Please customize libary locations for your site"
+$ write sys$output "and afterwards re-execute vms_make.com"
+$ write sys$output "Exiting..."
+$ close/nolog optf
+$ exit
+$ endif
+$!
+$! Open data file with location of libraries
+$!
+$ open/read/end=end_lib/err=err_lib libdata VMSLIB.DAT
+$LIB_LOOP:
+$ read/end=end_lib libdata libline
+$ libline = f$edit(libline, "UNCOMMENT,COLLAPSE")
+$ if libline .eqs. "" then goto LIB_LOOP ! Comment line
+$ libname = f$edit(f$element(0,"#",libline),"UPCASE")
+$ write sys$output "Processing ''libname' setup ..."
+$ libloc = f$element(1,"#",libline)
+$ libsrc = f$element(2,"#",libline)
+$ testinc = f$element(3,"#",libline)
+$ cppdef = f$element(4,"#",libline)
+$ old_cpp = f$locate("=1",cppdef)
+$ if old_cpp.lt.f$length(cppdef) then cppdef = f$extract(0,old_cpp,cppdef)
+$ if f$search("''libloc'").eqs. ""
+$ then
+$ write sys$output "Can not find library ''libloc' - Skipping ''libname'"
+$ goto LIB_LOOP
+$ endif
+$ libsrc_elem = 0
+$ libsrc_found = false
+$LIBSRC_LOOP:
+$ libsrcdir = f$element(libsrc_elem,",",libsrc)
+$ if (libsrcdir .eqs. ",") then goto END_LIBSRC
+$ if f$search("''libsrcdir'''testinc'") .nes. "" then libsrc_found = true
+$ libsrc_elem = libsrc_elem + 1
+$ goto LIBSRC_LOOP
+$END_LIBSRC:
+$ if .not. libsrc_found
+$ then
+$ write sys$output "Can not find includes at ''libsrc' - Skipping ''libname'"
+$ goto LIB_LOOP
+$ endif
+$ libdefs = libdefs + cppdef + "\"
+$ libincs = libincs + "," + libsrc
+$ lqual = "/lib"
+$ libtype = f$edit(f$parse(libloc,,,"TYPE"),"UPCASE")
+$ if f$locate("EXE",libtype) .lt. f$length(libtype) then lqual = "/share"
+$ write optf libloc , lqual
+$ write topt libloc , lqual
+$!
+$! Nasty hack to get the freetype includes to work
+$!
+$ if ((libname .eqs. "FREETYPE") .and. -
+ (f$locate("FREETYPE2",cppdef) .lt. f$length(cppdef)))
+$ then
+$ if ((f$search("freetype:freetype.h") .nes. "") .and. -
+ (f$search("freetype:[internal]ftobjs.h") .nes. ""))
+$ then
+$ write sys$output "Will use local definition of freetype logical"
+$ ft2def = false
+$ else
+$ ft2elem = 0
+$FT2_LOOP:
+$ ft2srcdir = f$element(ft2elem,",",libsrc)
+$ if f$search("''ft2srcdir'''testinc'") .nes. ""
+$ then
+$ if f$search("''ft2srcdir'internal.dir") .nes. ""
+$ then
+$ ft2dev = f$parse("''ft2srcdir'",,,"device","no_conceal")
+$ ft2dir = f$parse("''ft2srcdir'",,,"directory","no_conceal")
+$ ft2conc = f$locate("][",ft2dir)
+$ ft2len = f$length(ft2dir)
+$ if ft2conc .lt. ft2len
+$ then
+$ ft2dir = f$extract(0,ft2conc,ft2dir) + -
+ f$extract(ft2conc+2,ft2len-2,ft2dir)
+$ endif
+$ ft2dir = ft2dir - "]" + ".]"
+$ define freetype 'ft2dev''ft2dir','ft2srcdir'
+$ ft2def = true
+$ else
+$ goto ft2_err
+$ endif
+$ else
+$ ft2elem = ft2elem + 1
+$ goto ft2_loop
+$ endif
+$ endif
+$ endif
+$!
+$! Yet another special treatment for Xpm/X11
+$!
+$ if (libname .eqs. "XPM")
+$ then
+$ my_x11 = f$parse("''libsrc'xpm.h",,,"device") + -
+ f$parse("''libsrc'xpm.h",,,"directory")
+$ x11_save = f$trnlnm("X11")
+$ define x11 'my_x11',decw$include
+$ endif
+$ goto LIB_LOOP
+$END_LIB:
+$ close libdata
+$ return
+$!------------------------------------------------------------------------------
+$!
+$! Take care of driver file with information about external libraries
+$!
+$CHECK_CONFIG:
+$!
+$ if (def .eqs. "SYSTEM_XPDFRC")
+$ then
+$ write aconf "#define SYSTEM_XPDFRC """, resfil, """"
+$ else
+$ gosub check_cc_def
+$ endif
+$ return
+$!------------------------------------------------------------------------------
+$!
+$! Check if this is a define relating to the properties of the C/C++
+$! compiler
+$!
+$CHECK_CC_DEF:
+$ if (def .eqs. "HAVE_DIRENT_H")
+$ then
+$ copy sys$input: 'tc
+$ deck
+#include <dirent.h>
+int main(){
+}
+$ eod
+$ gosub cc_prop_check
+$ return
+$ endif
+$ if (def .eqs. "HAVE_SYS_NDIR_H")
+$ then
+$ copy sys$input: 'tc
+$ deck
+#include <sys/ndir.h>
+int main(){
+}
+$ eod
+$ gosub cc_prop_check
+$ return
+$ endif
+$ if (def .eqs. "HAVE_SYS_DIR_H")
+$ then
+$ copy sys$input: 'tc
+$ deck
+#include <sys/dir.h>
+int main(){
+}
+$ eod
+$ gosub cc_prop_check
+$ return
+$ endif
+$ if (def .eqs. "HAVE_NDIR_H")
+$ then
+$ copy sys$input: 'tc
+$ deck
+#include <ndir.h>
+int main(){
+}
+$ eod
+$ gosub cc_prop_check
+$ return
+$ endif
+$ if (def .eqs. "HAVE_SYS_SELECT_H")
+$ then
+$ copy sys$input: 'tc
+$ deck
+#include <sys/select.h>
+int main(){
+}
+$ eod
+$ gosub cc_prop_check
+$ return
+$ endif
+$ if (def .eqs. "HAVE_SYS_BSDTYPES_H")
+$ then
+$ copy sys$input: 'tc
+$ deck
+#include <sys/bsdtypes.h>
+int main(){
+}
+$ eod
+$ gosub cc_prop_check
+$ return
+$ endif
+$ if (def .eqs. "HAVE_STRINGS_H")
+$ then
+$ copy sys$input: 'tc
+$ deck
+#include <strings.h>
+int main(){
+}
+$ eod
+$ gosub cc_prop_check
+$ return
+$ endif
+$ if (def .eqs. "HAVE_POPEN")
+$ then
+$ copy sys$input: 'tc
+$ deck
+#include <stdio.h>
+
+int main(){
+FILE *pipe;
+ pipe = popen("DIR","r");
+ pclose(pipe);
+}
+$ eod
+$ gosub cc_prop_check
+$ return
+$ endif
+$ if (def .eqs. "HAVE_MKSTEMP")
+$ then
+$ copy sys$input: 'tc
+$ deck
+#include <stdlib.h>
+
+int main(){
+ mkstemp("tempXXXXXX");
+}
+$ eod
+$ gosub cc_prop_check
+$ return
+$ endif
+$ if (def .eqs. "HAVE_FSEEKO")
+$ then
+$ copy sys$input: 'tc
+$ deck
+#define _LARGEFILE
+#include <stdio.h>
+
+int main(){
+FILE *fp;
+ fp = fopen("temp.txt","r");
+ fseeko(fp,1,SEEK_SET);
+ fclose(fp);
+}
+$ eod
+$ gosub cc_prop_check
+$ return
+$ endif
+$ if (def .eqs. "_LARGE_FILES")
+$ then
+$ copy sys$input: 'tc
+$ deck
+#define _LARGEFILE
+#include <stdio.h>
+
+int main(){
+FILE *fp;
+ fp = fopen("temp.txt","r");
+ fseeko(fp,1,SEEK_SET);
+ fclose(fp);
+}
+$ eod
+$ gosub cc_prop_check
+$ return
+$ endif
+$ if (def .eqs. "HAVE_XTAPPSETEXITFLAG")
+$ then
+$ copy sys$input: 'tc
+$ deck
+#include <X11/Intrinsic.h>
+
+int main(){
+ XtAppContext app_context;
+ app_context = XtCreateApplicationContext();
+ XtAppSetExitFlag(app_context);
+ return 0;
+}
+$ eod
+$ gosub cc_prop_check
+$ return
+$ endif
+$ write aconf "/* ", line, " */"
+$ return
+$!------------------------------------------------------------------------------
+$!
+$! Process config settings passed from the command line
+$! (analog to Unix --enable-xxx)
+$!
+$PROC_CONFIG:
+$ if (p1.nes."")
+$ then
+$ i = 0
+$ qual_list = f$edit(p1,"upcase")
+$DEF_LOOP:
+$ qual = f$element(i,",",qual_list)
+$ if qual .eqs. "," then goto FINISH_DEF
+$ i = i + 1
+$ if (qual .eqs. "A4")
+$ then
+$ mydefs = mydefs + "A4_PAPER#"
+$ goto def_loop
+$ endif
+$ if (qual .eqs. "NO_TEXT_SELECT")
+$ then
+$ mydefs = mydefs + "NO_TEXT_SELECT#"
+$ goto def_loop
+$ endif
+$ if (qual .eqs. "OPI_SUPPORT")
+$ then
+$ mydefs = mydefs + "OPI_SUPPORT#"
+$ goto def_loop
+$ endif
+$ if (qual .eqs. "COMPRESS")
+$ then
+$ compress_def = true
+$ goto def_loop
+$ endif
+$ write sys$output "Qualifier ''qual' not recognized, will be ignored"
+$ goto def_loop
+$ endif
+$FINISH_DEF:
+$ return
+$!
+$!------------------------------------------------------------------------------
+$!
+$! Look for the compiler used
+$!
+$CHECK_COMPILER:
+$ its_decc = (f$search("SYS$SYSTEM:CXX$COMPILER.EXE") .nes. "")
+$ its_gnuc = .not. its_decc .and. (f$trnlnm("gnu_cc") .nes. "")
+$!
+$! Exit if no compiler available
+$!
+$ if (.not. (its_decc .or. its_gnuc)) then goto CXX_ERR
+$!
+$! Override if requested from the commandline
+$!
+$ if (p2 .eqs. "DECC")
+$ then
+$ its_decc = true
+$ its_gnuc = false
+$ endif
+$ if (p1 .eqs. "GNUC")
+$ then
+$ its_decc = false
+$ its_gnuc = true
+$ endif
+$!
+$ if its_decc
+$ then
+$ ccomp :== "cc/decc/prefix=all ''float'"
+$!
+$! Take care of includes
+$!
+$ cc_user = f$trnlnm("DECC$USER_INCLUDE")
+$ cc_system = f$trnlnm("DECC$SYSTEM_INCLUDE")
+$ cxx_user = f$trnlnm("CXX$USER_INCLUDE")
+$ cxx_system = f$trnlnm("CXX$SYSTEM_INCLUDE")
+$ define decc$system_include 'incs'
+$ define decc$user_include 'incs'
+$ define cxx$user_include 'incs'
+$ define cxx$system_include 'incs'
+$!
+$! Check version of the C++ compiler
+$!
+$ create vms_xpdf_cc_test.cc
+$ cxx/lis=vms_xpdf_cc_test.lis/show=all vms_xpdf_cc_test.cc
+$ open list vms_xpdf_cc_test.lis
+$CXX_LIST:
+$ read/end=close_cxx list line
+$ start = f$locate("__DECCXX_VER",line)
+$ if start .ne. f$length(line)
+$ then
+$ cxx_ver = f$extract(start+13,8,line)
+$ if cxx_ver .gt. 60000000
+$ then
+$ cxxdefs = "/warn=(disable=nosimpint)"
+$ xpdf_link :== cxxlink
+$ endif
+$ goto close_cxx
+$ endif
+$ goto cxx_list
+$CLOSE_CXX:
+$ close list
+$ delete/noconfirm vms_xpdf_cc_test.*;*
+$ cxxcomp :== "cxx/prefix=all ''cxxdefs' ''float' /include=cxx$user_include"
+$ endif
+$!
+$ if its_gnuc
+$ then
+$ ccomp :== "gcc/nocase/include=(''incs')"
+$ cxxcomp :== "gcc/plusplus/nocase/include=(''incs')"
+$ write optf "gnu_cc:[000000]gcclib.olb/lib"
+$ write optf "sys$share:vaxcrtl.exe/share"
+$ endif
+$ return
+$-------------------------------------------------------------------------------
+$RESET_ENV:
+$ delete/sym/glob cxxcomp
+$ delete/sym/glob ccomp
+$ delete/sym/glob xpdf_link
+$ if (ft2def) then deassign freetype
+$ if its_decc
+$ then
+$ if cc_user .eqs. ""
+$ then
+$ deass decc$user_include
+$ else
+$ define decc$user_include 'cc_user'
+$ endif
+$ if cc_system .eqs. ""
+$ then
+$ deass decc$system_include
+$ else
+$ define decc$system_include 'cc_system'
+$ endif
+$ if cxx_user .eqs. ""
+$ then
+$ deass cxx$user_include
+$ else
+$ define cxx$user_include 'cxx_user'
+$ endif
+$ if cxx_system .eqs. ""
+$ then
+$ deass cxx$system_include
+$ else
+$ define cxx$system_include 'cxx_system'
+$ endif
+$ endif
+$ if (x11_save .nes. "") then define x11 'x11_save'
+$ return
+$!
+$!------------------------------------------------------------------------------
+$!
+$! Check for properties of C/C++ compiler
+$!
+$CC_PROP_CHECK:
+$ cc_prop = true
+$ set message/nofac/noident/nosever/notext
+$ on error then continue
+$ cc 'tmpnam'
+$ if .not. ($status) then cc_prop = false
+$ on error then continue
+$! The headers might lie about the capabilities of the RTL
+$ link/opt=tmp.opt 'tmpnam'
+$ if .not. ($status) then cc_prop = false
+$ set message/fac/ident/sever/text
+$ on error then goto err_exit
+$ delete/nolog 'tmpnam'.*;*
+$ if cc_prop
+$ then
+$ write sys$output "Checking for ''def'... yes"
+$ write aconf "#define ''def' 1"
+$ if (def .eqs. "HAVE_FSEEKO") .or. (def .eqs. "_LARGE_FILES") then -
+ write aconf "#define _LARGEFILE"
+$ else
+$ write sys$output "Checking for ''def'... no"
+$ write aconf line
+$ endif
+$ return
+$!------------------------------------------------------------------------------
+$!
+$! Check Xlibs and write to options file
+$!
+$CHECK_XLIB:
+$ If F$Type (xlibs) .nes. "STRING" Then xlibs = ""
+$ need_xt = f$locate("XT",f$edit(xlibs,"upcase")) .lt. f$length(xlibs)
+$ need_xmu = f$locate("XMU",f$edit(xlibs,"upcase")) .lt. f$length(xlibs)
+$ need_xm = f$locate("MOTIF",f$edit(xlibs,"upcase")) .lt. f$length(xlibs)
+$ On Error Then GoTo XUI
+$ @sys$update:decw$get_image_version sys$share:decw$xlibshr.exe decw$version
+$ if f$extract(4,3,decw$version).eqs."1.0"
+$ then
+$ if need_xt .or. need_xmu .or. need_xm
+$ then
+$ write optf "Sys$share:DECW$DWTLIBSHR.EXE/Share"
+$ write topt "Sys$share:DECW$DWTLIBSHR.EXE/Share"
+$ endif
+$ endif
+$ if f$extract(4,3,decw$version).eqs."1.1"
+$ then
+$ if need_xm then write optf "sys$share:decw$xmlibshr.exe/share"
+$ if need_xt then write optf "sys$share:decw$xtshr.exe/share"
+$ if nedd_xmu then write optf "sys$share:decw$xmulibshr.exe/share"
+$ if need_xm then write topt "sys$share:decw$xmlibshr.exe/share"
+$ if need_xt then write topt "sys$share:decw$xtshr.exe/share"
+$ if nedd_xmu then write topt "sys$share:decw$xmulibshr.exe/share"
+$ endif
+$ if f$extract(4,3,decw$version).eqs."1.2"
+$ then
+$ if need_xm then write optf "sys$share:decw$xmlibshr12.exe/share"
+$ if need_xt then write optf "sys$share:decw$xtlibshrr5.exe/share"
+$ if need_xmu then write optf "sys$share:decw$xmulibshrr5.exe/share"
+$ if need_xm then write topt "sys$share:decw$xmlibshr12.exe/share"
+$ if need_xt then write topt "sys$share:decw$xtlibshrr5.exe/share"
+$ if need_xmu then write topt "sys$share:decw$xmulibshrr5.exe/share"
+$ endif
+$ GoTo MAIN
+$ XUI:
+$!
+$ if need_xt .or. need_xmu
+$ then
+$ write optf "Sys$share:DECW$DWTLIBSHR.EXE/Share"
+$ write topt "Sys$share:DECW$DWTLIBSHR.EXE/Share"
+$ endif
+$ MAIN:
+$ on error then goto err_exit
+$ write optf "sys$share:decw$xlibshr.exe/share"
+$ write topt "sys$share:decw$xlibshr.exe/share"
+$ return
+$!------------------------------------------------------------------------------
+$!
+$! Check version of Xpdf to build
+$!
+$CHECK_VERSION:
+$ open/read in [.xpdf]config.h
+$ check_string = "xpdfVersionNum"
+$vloop:
+$ read/end=vdone in rec
+$ if (f$element(1," " ,rec) .nes. check_string) then goto vloop
+$ start = f$locate(check_string,rec) + f$length(check_string)
+$ len = f$length(rec) - start
+$ version = f$edit(f$extract(start,len,rec),"COLLAPSE")
+$vdone:
+$ close in
+$ return
+$!------------------------------------------------------------------------------
diff --git a/xpdf/Annot.cc b/xpdf/Annot.cc
new file mode 100644
index 0000000..1f5d8d1
--- /dev/null
+++ b/xpdf/Annot.cc
@@ -0,0 +1,1556 @@
+//========================================================================
+//
+// 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;
+}
diff --git a/xpdf/Annot.h b/xpdf/Annot.h
new file mode 100644
index 0000000..1f89d49
--- /dev/null
+++ b/xpdf/Annot.h
@@ -0,0 +1,142 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/Array.cc b/xpdf/Array.cc
new file mode 100644
index 0000000..10ded14
--- /dev/null
+++ b/xpdf/Array.cc
@@ -0,0 +1,73 @@
+//========================================================================
+//
+// 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);
+}
diff --git a/xpdf/Array.h b/xpdf/Array.h
new file mode 100644
index 0000000..20ae05f
--- /dev/null
+++ b/xpdf/Array.h
@@ -0,0 +1,58 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/BuiltinFont.cc b/xpdf/BuiltinFont.cc
new file mode 100644
index 0000000..ce98957
--- /dev/null
+++ b/xpdf/BuiltinFont.cc
@@ -0,0 +1,65 @@
+//========================================================================
+//
+// 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);
+}
diff --git a/xpdf/BuiltinFont.h b/xpdf/BuiltinFont.h
new file mode 100644
index 0000000..903ed19
--- /dev/null
+++ b/xpdf/BuiltinFont.h
@@ -0,0 +1,57 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/BuiltinFontTables.cc b/xpdf/BuiltinFontTables.cc
new file mode 100644
index 0000000..9c36238
--- /dev/null
+++ b/xpdf/BuiltinFontTables.cc
@@ -0,0 +1,4284 @@
+//========================================================================
+//
+// 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;
+ }
+}
diff --git a/xpdf/BuiltinFontTables.h b/xpdf/BuiltinFontTables.h
new file mode 100644
index 0000000..eb45549
--- /dev/null
+++ b/xpdf/BuiltinFontTables.h
@@ -0,0 +1,23 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/CMap.cc b/xpdf/CMap.cc
new file mode 100644
index 0000000..303cf09
--- /dev/null
+++ b/xpdf/CMap.cc
@@ -0,0 +1,408 @@
+//========================================================================
+//
+// 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;
+}
diff --git a/xpdf/CMap.h b/xpdf/CMap.h
new file mode 100644
index 0000000..c321a57
--- /dev/null
+++ b/xpdf/CMap.h
@@ -0,0 +1,102 @@
+//========================================================================
+//
+// 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
+ // <s>, 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
diff --git a/xpdf/Catalog.cc b/xpdf/Catalog.cc
new file mode 100644
index 0000000..de282c3
--- /dev/null
+++ b/xpdf/Catalog.cc
@@ -0,0 +1,374 @@
+//========================================================================
+//
+// 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;
+}
diff --git a/xpdf/Catalog.h b/xpdf/Catalog.h
new file mode 100644
index 0000000..6403194
--- /dev/null
+++ b/xpdf/Catalog.h
@@ -0,0 +1,97 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/CharCodeToUnicode.cc b/xpdf/CharCodeToUnicode.cc
new file mode 100644
index 0000000..3702a16
--- /dev/null
+++ b/xpdf/CharCodeToUnicode.cc
@@ -0,0 +1,540 @@
+//========================================================================
+//
+// 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"
+
+//------------------------------------------------------------------------
+
+#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;
+ 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();
+}
diff --git a/xpdf/CharCodeToUnicode.h b/xpdf/CharCodeToUnicode.h
new file mode 100644
index 0000000..04852ae
--- /dev/null
+++ b/xpdf/CharCodeToUnicode.h
@@ -0,0 +1,117 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/CharTypes.h b/xpdf/CharTypes.h
new file mode 100644
index 0000000..d0df630
--- /dev/null
+++ b/xpdf/CharTypes.h
@@ -0,0 +1,24 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/CompactFontTables.h b/xpdf/CompactFontTables.h
new file mode 100644
index 0000000..28e16e7
--- /dev/null
+++ b/xpdf/CompactFontTables.h
@@ -0,0 +1,464 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/CoreOutputDev.cc b/xpdf/CoreOutputDev.cc
new file mode 100644
index 0000000..7875110
--- /dev/null
+++ b/xpdf/CoreOutputDev.cc
@@ -0,0 +1,61 @@
+//========================================================================
+//
+// 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);
+}
diff --git a/xpdf/CoreOutputDev.h b/xpdf/CoreOutputDev.h
new file mode 100644
index 0000000..006d351
--- /dev/null
+++ b/xpdf/CoreOutputDev.h
@@ -0,0 +1,61 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/Decrypt.cc b/xpdf/Decrypt.cc
new file mode 100644
index 0000000..0320589
--- /dev/null
+++ b/xpdf/Decrypt.cc
@@ -0,0 +1,776 @@
+//========================================================================
+//
+// 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);
+}
diff --git a/xpdf/Decrypt.h b/xpdf/Decrypt.h
new file mode 100644
index 0000000..56f34b7
--- /dev/null
+++ b/xpdf/Decrypt.h
@@ -0,0 +1,95 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/Dict.cc b/xpdf/Dict.cc
new file mode 100644
index 0000000..dd1517f
--- /dev/null
+++ b/xpdf/Dict.cc
@@ -0,0 +1,95 @@
+//========================================================================
+//
+// 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);
+}
diff --git a/xpdf/Dict.h b/xpdf/Dict.h
new file mode 100644
index 0000000..08f55ec
--- /dev/null
+++ b/xpdf/Dict.h
@@ -0,0 +1,77 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/Error.cc b/xpdf/Error.cc
new file mode 100644
index 0000000..c03f75f
--- /dev/null
+++ b/xpdf/Error.cc
@@ -0,0 +1,38 @@
+//========================================================================
+//
+// 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);
+}
diff --git a/xpdf/Error.h b/xpdf/Error.h
new file mode 100644
index 0000000..0ce55e9
--- /dev/null
+++ b/xpdf/Error.h
@@ -0,0 +1,23 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/ErrorCodes.h b/xpdf/ErrorCodes.h
new file mode 100644
index 0000000..b28528d
--- /dev/null
+++ b/xpdf/ErrorCodes.h
@@ -0,0 +1,36 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/FontEncodingTables.cc b/xpdf/FontEncodingTables.cc
new file mode 100644
index 0000000..f3b9280
--- /dev/null
+++ b/xpdf/FontEncodingTables.cc
@@ -0,0 +1,1824 @@
+//========================================================================
+//
+// 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
+};
diff --git a/xpdf/FontEncodingTables.h b/xpdf/FontEncodingTables.h
new file mode 100644
index 0000000..8b0a1e7
--- /dev/null
+++ b/xpdf/FontEncodingTables.h
@@ -0,0 +1,20 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/Function.cc b/xpdf/Function.cc
new file mode 100644
index 0000000..a4d51c7
--- /dev/null
+++ b/xpdf/Function.cc
@@ -0,0 +1,1573 @@
+//========================================================================
+//
+// 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;
+ }
+ }
+}
diff --git a/xpdf/Function.h b/xpdf/Function.h
new file mode 100644
index 0000000..334a439
--- /dev/null
+++ b/xpdf/Function.h
@@ -0,0 +1,229 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/Gfx.cc b/xpdf/Gfx.cc
new file mode 100644
index 0000000..b7655b6
--- /dev/null
+++ b/xpdf/Gfx.cc
@@ -0,0 +1,4181 @@
+//========================================================================
+//
+// 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);
+ 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;
+}
diff --git a/xpdf/Gfx.h b/xpdf/Gfx.h
new file mode 100644
index 0000000..0e4263c
--- /dev/null
+++ b/xpdf/Gfx.h
@@ -0,0 +1,312 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/GfxFont.cc b/xpdf/GfxFont.cc
new file mode 100644
index 0000000..9af7efc
--- /dev/null
+++ b/xpdf/GfxFont.cc
@@ -0,0 +1,1568 @@
+//========================================================================
+//
+// 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;
+}
diff --git a/xpdf/GfxFont.h b/xpdf/GfxFont.h
new file mode 100644
index 0000000..d076692
--- /dev/null
+++ b/xpdf/GfxFont.h
@@ -0,0 +1,320 @@
+//========================================================================
+//
+// 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 <s> 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
diff --git a/xpdf/GfxState.cc b/xpdf/GfxState.cc
new file mode 100644
index 0000000..67b4f6f
--- /dev/null
+++ b/xpdf/GfxState.cc
@@ -0,0 +1,4137 @@
+//========================================================================
+//
+// 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;
+ }
+}
diff --git a/xpdf/GfxState.h b/xpdf/GfxState.h
new file mode 100644
index 0000000..f85643d
--- /dev/null
+++ b/xpdf/GfxState.h
@@ -0,0 +1,1244 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/GlobalParams.cc b/xpdf/GlobalParams.cc
new file mode 100644
index 0000000..e2f2f90
--- /dev/null
+++ b/xpdf/GlobalParams.cc
@@ -0,0 +1,2908 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/GlobalParams.h b/xpdf/GlobalParams.h
new file mode 100644
index 0000000..7fa26ca
--- /dev/null
+++ b/xpdf/GlobalParams.h
@@ -0,0 +1,463 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/ImageOutputDev.cc b/xpdf/ImageOutputDev.cc
new file mode 100644
index 0000000..3c0f478
--- /dev/null
+++ b/xpdf/ImageOutputDev.cc
@@ -0,0 +1,195 @@
+//========================================================================
+//
+// 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);
+ }
+}
diff --git a/xpdf/ImageOutputDev.h b/xpdf/ImageOutputDev.h
new file mode 100644
index 0000000..acc8a84
--- /dev/null
+++ b/xpdf/ImageOutputDev.h
@@ -0,0 +1,76 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/JArithmeticDecoder.cc b/xpdf/JArithmeticDecoder.cc
new file mode 100644
index 0000000..195b73e
--- /dev/null
+++ b/xpdf/JArithmeticDecoder.cc
@@ -0,0 +1,322 @@
+//========================================================================
+//
+// 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;
+ }
+}
diff --git a/xpdf/JArithmeticDecoder.h b/xpdf/JArithmeticDecoder.h
new file mode 100644
index 0000000..a40823d
--- /dev/null
+++ b/xpdf/JArithmeticDecoder.h
@@ -0,0 +1,109 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/JBIG2Stream.cc b/xpdf/JBIG2Stream.cc
new file mode 100644
index 0000000..a4f1b6e
--- /dev/null
+++ b/xpdf/JBIG2Stream.cc
@@ -0,0 +1,3413 @@
+//========================================================================
+//
+// 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;
+}
diff --git a/xpdf/JBIG2Stream.h b/xpdf/JBIG2Stream.h
new file mode 100644
index 0000000..210d927
--- /dev/null
+++ b/xpdf/JBIG2Stream.h
@@ -0,0 +1,145 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/JPXStream.cc b/xpdf/JPXStream.cc
new file mode 100644
index 0000000..7107854
--- /dev/null
+++ b/xpdf/JPXStream.cc
@@ -0,0 +1,3144 @@
+//========================================================================
+//
+// 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;
+}
diff --git a/xpdf/JPXStream.h b/xpdf/JPXStream.h
new file mode 100644
index 0000000..e96e7d3
--- /dev/null
+++ b/xpdf/JPXStream.h
@@ -0,0 +1,351 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/Lexer.cc b/xpdf/Lexer.cc
new file mode 100644
index 0000000..9f0c3ca
--- /dev/null
+++ b/xpdf/Lexer.cc
@@ -0,0 +1,485 @@
+//========================================================================
+//
+// 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;
+}
diff --git a/xpdf/Lexer.h b/xpdf/Lexer.h
new file mode 100644
index 0000000..f6ad9ce
--- /dev/null
+++ b/xpdf/Lexer.h
@@ -0,0 +1,80 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/Link.cc b/xpdf/Link.cc
new file mode 100644
index 0000000..ae2de53
--- /dev/null
+++ b/xpdf/Link.cc
@@ -0,0 +1,784 @@
+//========================================================================
+//
+// 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;
+}
diff --git a/xpdf/Link.h b/xpdf/Link.h
new file mode 100644
index 0000000..698f2c8
--- /dev/null
+++ b/xpdf/Link.h
@@ -0,0 +1,369 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/Makefile.dep b/xpdf/Makefile.dep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/xpdf/Makefile.dep
diff --git a/xpdf/Makefile.in b/xpdf/Makefile.in
new file mode 100644
index 0000000..b97cc6e
--- /dev/null
+++ b/xpdf/Makefile.in
@@ -0,0 +1,254 @@
+#========================================================================
+#
+# 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 = @CXXFLAGS@ @DEFS@ -I.. -I$(GOOSRCDIR) -I$(FOFISRCDIR) -I$(SPLASHSRCDIR) -I$(srcdir) @freetype2_CFLAGS@ @Sgm_CFLAGS@ @Xm_CFLAGS@ @Xt_CFLAGS@ @Xp_CFLAGS@ @Xext_CFLAGS@ @Xpm_CFLAGS@ @t1_CFLAGS@ @libpaper_CFLAGS@ @X_CFLAGS@
+
+LDFLAGS = @LDFLAGS@
+
+T1LIBS = @t1_LIBS@
+FTLIBS = @freetype2_LIBS@
+
+XLIBS = @Sgm_LIBS@ @Xm_LIBS@ @Xt_LIBS@ @Xp_LIBS@ @Xext_LIBS@ @Xpm_LIBS@ @X_PRE_LIBS@ @X_LIBS@ -lX11 @X_EXTRA_LIBS@
+
+SPLASHLIBS = -L$(SPLASHLIBDIR) -lsplash
+
+OTHERLIBS = @LIBS@ @libpaper_LIBS@ \
+ -L$(FOFILIBDIR) -lfofi \
+ -L$(GOOLIBDIR) -lGoo
+
+CXX = @CXX@
+
+LIBPREFIX = @LIBPREFIX@
+EXE = @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: xpdf$(EXE) pdftops$(EXE) pdftotext$(EXE) pdfinfo$(EXE) \
+ pdffonts$(EXE) pdftoppm$(EXE) pdfimages$(EXE)
+
+all-no-x: pdftops$(EXE) pdftotext$(EXE) pdfinfo$(EXE) pdffonts$(EXE) \
+ pdfimages$(EXE)
+
+#------------------------------------------------------------------------
+
+XPDF_OBJS = Annot.o Array.o BuiltinFont.o BuiltinFontTables.o Catalog.o \
+ CharCodeToUnicode.o CMap.o CoreOutputDev.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 PDFCore.o PDFDoc.o PDFDocEncoding.o \
+ PreScanOutputDev.o PSOutputDev.o PSTokenizer.o SecurityHandler.x.o \
+ SplashOutputDev.o Stream.o TextOutputDev.o UnicodeMap.o \
+ UnicodeTypeTable.o XPDFApp.o XPDFCore.o XPDFTree.o XPDFViewer.o \
+ XpdfPluginAPI.o XRef.o xpdf.o
+XPDF_LIBS = -L$(GOOLIBDIR) -lGoo $(SPLASHLIBS) $(T1LIBS) $(FTLIBS) \
+ $(XLIBS) $(OTHERLIBS) -lm
+
+xpdf$(EXE): $(XPDF_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
+ $(CXX) $(CXXFLAGS) $(LDFLAGS) -o xpdf$(EXE) $(XPDF_OBJS) $(XPDF_LIBS)
+
+#------------------------------------------------------------------------
+
+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) $(T1LIBS) $(FTLIBS) \
+ $(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)
+
+#------------------------------------------------------------------------
+
+PDFTOPPM_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 SplashOutputDev.o Stream.o TextOutputDev.o \
+ UnicodeMap.o UnicodeTypeTable.o XpdfPluginAPI.o XRef.o pdftoppm.o
+PDFTOPPM_LIBS = -L$(GOOLIBDIR) -lGoo $(SPLASHLIBS) $(T1LIBS) $(FTLIBS) \
+ $(OTHERLIBS) -lm
+
+pdftoppm$(EXE): $(PDFTOPPM_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
+ $(CXX) $(CXXFLAGS) $(LDFLAGS) -o pdftoppm$(EXE) $(PDFTOPPM_OBJS) \
+ $(PDFTOPPM_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)
+
+#------------------------------------------------------------------------
+
+SecurityHandler.x.o: SecurityHandler.cc
+ $(CXX) $(CXXFLAGS) -DHAVE_XPDFCORE -c -o SecurityHandler.x.o \
+ SecurityHandler.cc
+
+#------------------------------------------------------------------------
+
+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 $(PDFTOPPM_OBJS) pdftoppm$(EXE)
+ rm -f $(PDFIMAGES_OBJS) pdfimages$(EXE)
+
+#------------------------------------------------------------------------
+
+depend:
+ $(CXX) $(CXXFLAGS) -MM $(CXX_SRC) >Makefile.dep
+
+include Makefile.dep
diff --git a/xpdf/NameToCharCode.cc b/xpdf/NameToCharCode.cc
new file mode 100644
index 0000000..7ebf4e1
--- /dev/null
+++ b/xpdf/NameToCharCode.cc
@@ -0,0 +1,116 @@
+//========================================================================
+//
+// 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);
+}
diff --git a/xpdf/NameToCharCode.h b/xpdf/NameToCharCode.h
new file mode 100644
index 0000000..65453c3
--- /dev/null
+++ b/xpdf/NameToCharCode.h
@@ -0,0 +1,42 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/NameToUnicodeTable.h b/xpdf/NameToUnicodeTable.h
new file mode 100644
index 0000000..c5ecba4
--- /dev/null
+++ b/xpdf/NameToUnicodeTable.h
@@ -0,0 +1,1097 @@
+//========================================================================
+//
+// 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 }
+};
diff --git a/xpdf/Object.cc b/xpdf/Object.cc
new file mode 100644
index 0000000..71c632a
--- /dev/null
+++ b/xpdf/Object.cc
@@ -0,0 +1,231 @@
+//========================================================================
+//
+// 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
+}
diff --git a/xpdf/Object.h b/xpdf/Object.h
new file mode 100644
index 0000000..8b1807c
--- /dev/null
+++ b/xpdf/Object.h
@@ -0,0 +1,303 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/Outline.cc b/xpdf/Outline.cc
new file mode 100644
index 0000000..39e89a3
--- /dev/null
+++ b/xpdf/Outline.cc
@@ -0,0 +1,151 @@
+//========================================================================
+//
+// 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;
+ }
+}
diff --git a/xpdf/Outline.h b/xpdf/Outline.h
new file mode 100644
index 0000000..f38f8d1
--- /dev/null
+++ b/xpdf/Outline.h
@@ -0,0 +1,76 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/OutputDev.cc b/xpdf/OutputDev.cc
new file mode 100644
index 0000000..8d4763b
--- /dev/null
+++ b/xpdf/OutputDev.cc
@@ -0,0 +1,131 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/OutputDev.h b/xpdf/OutputDev.h
new file mode 100644
index 0000000..27dc0dd
--- /dev/null
+++ b/xpdf/OutputDev.h
@@ -0,0 +1,250 @@
+//========================================================================
+//
+// 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) {}
+
+ // 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; }
+
+ //----- 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
diff --git a/xpdf/PDFCore.cc b/xpdf/PDFCore.cc
new file mode 100644
index 0000000..fb3df3f
--- /dev/null
+++ b/xpdf/PDFCore.cc
@@ -0,0 +1,2044 @@
+//========================================================================
+//
+// 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);
+ }
+}
diff --git a/xpdf/PDFCore.h b/xpdf/PDFCore.h
new file mode 100644
index 0000000..013256d
--- /dev/null
+++ b/xpdf/PDFCore.h
@@ -0,0 +1,321 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/PDFDoc.cc b/xpdf/PDFDoc.cc
new file mode 100644
index 0000000..c559eef
--- /dev/null
+++ b/xpdf/PDFDoc.cc
@@ -0,0 +1,402 @@
+//========================================================================
+//
+// 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, 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,
+ 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;
+}
diff --git a/xpdf/PDFDoc.h b/xpdf/PDFDoc.h
new file mode 100644
index 0000000..208b61e
--- /dev/null
+++ b/xpdf/PDFDoc.h
@@ -0,0 +1,183 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/PDFDocEncoding.cc b/xpdf/PDFDocEncoding.cc
new file mode 100644
index 0000000..89dc382
--- /dev/null
+++ b/xpdf/PDFDocEncoding.cc
@@ -0,0 +1,44 @@
+//========================================================================
+//
+// 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
+};
diff --git a/xpdf/PDFDocEncoding.h b/xpdf/PDFDocEncoding.h
new file mode 100644
index 0000000..3259d3e
--- /dev/null
+++ b/xpdf/PDFDocEncoding.h
@@ -0,0 +1,16 @@
+//========================================================================
+//
+// PDFDocEncoding.h
+//
+// Copyright 2002-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef PDFDOCENCODING_H
+#define PDFDOCENCODING_H
+
+#include "CharTypes.h"
+
+extern Unicode pdfDocEncoding[256];
+
+#endif
diff --git a/xpdf/PSOutputDev.cc b/xpdf/PSOutputDev.cc
new file mode 100644
index 0000000..8a1a9e9
--- /dev/null
+++ b/xpdf/PSOutputDev.cc
@@ -0,0 +1,6222 @@
+//========================================================================
+//
+// 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,
+ 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,
+ 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
+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");
+}
diff --git a/xpdf/PSOutputDev.h b/xpdf/PSOutputDev.h
new file mode 100644
index 0000000..67b4d98
--- /dev/null
+++ b/xpdf/PSOutputDev.h
@@ -0,0 +1,395 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/PSTokenizer.cc b/xpdf/PSTokenizer.cc
new file mode 100644
index 0000000..0ad3761
--- /dev/null
+++ b/xpdf/PSTokenizer.cc
@@ -0,0 +1,135 @@
+//========================================================================
+//
+// 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;
+}
diff --git a/xpdf/PSTokenizer.h b/xpdf/PSTokenizer.h
new file mode 100644
index 0000000..4d5ee97
--- /dev/null
+++ b/xpdf/PSTokenizer.h
@@ -0,0 +1,41 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/Page.cc b/xpdf/Page.cc
new file mode 100644
index 0000000..c930665
--- /dev/null
+++ b/xpdf/Page.cc
@@ -0,0 +1,441 @@
+//========================================================================
+//
+// 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,
+ GBool printing, Catalog *catalog,
+ GBool (*abortCheckCbk)(void *data),
+ void *abortCheckCbkData) {
+ displaySlice(out, hDPI, vDPI, rotate, useMediaBox, crop,
+ -1, -1, -1, -1, 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,
+ GBool printing, Catalog *catalog,
+ GBool (*abortCheckCbk)(void *data),
+ void *abortCheckCbkData) {
+#ifndef PDF_PARSER_ONLY
+ PDFRectangle *mediaBox, *cropBox;
+ PDFRectangle box;
+ Gfx *gfx;
+ Object obj;
+ 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 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;
+}
diff --git a/xpdf/Page.h b/xpdf/Page.h
new file mode 100644
index 0000000..2b01175
--- /dev/null
+++ b/xpdf/Page.h
@@ -0,0 +1,187 @@
+//========================================================================
+//
+// 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,
+ 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,
+ 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
diff --git a/xpdf/Parser.cc b/xpdf/Parser.cc
new file mode 100644
index 0000000..c1fc715
--- /dev/null
+++ b/xpdf/Parser.cc
@@ -0,0 +1,227 @@
+//========================================================================
+//
+// 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);
+}
diff --git a/xpdf/Parser.h b/xpdf/Parser.h
new file mode 100644
index 0000000..a109558
--- /dev/null
+++ b/xpdf/Parser.h
@@ -0,0 +1,59 @@
+//========================================================================
+//
+// 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
+
diff --git a/xpdf/PreScanOutputDev.cc b/xpdf/PreScanOutputDev.cc
new file mode 100644
index 0000000..7dd7bf5
--- /dev/null
+++ b/xpdf/PreScanOutputDev.cc
@@ -0,0 +1,257 @@
+//========================================================================
+//
+// 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;
+}
diff --git a/xpdf/PreScanOutputDev.h b/xpdf/PreScanOutputDev.h
new file mode 100644
index 0000000..c825ddf
--- /dev/null
+++ b/xpdf/PreScanOutputDev.h
@@ -0,0 +1,130 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/SecurityHandler.cc b/xpdf/SecurityHandler.cc
new file mode 100644
index 0000000..ea0f934
--- /dev/null
+++ b/xpdf/SecurityHandler.cc
@@ -0,0 +1,390 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/SecurityHandler.h b/xpdf/SecurityHandler.h
new file mode 100644
index 0000000..a27868c
--- /dev/null
+++ b/xpdf/SecurityHandler.h
@@ -0,0 +1,160 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/SplashOutputDev.cc b/xpdf/SplashOutputDev.cc
new file mode 100644
index 0000000..e4f86ce
--- /dev/null
+++ b/xpdf/SplashOutputDev.cc
@@ -0,0 +1,2845 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/SplashOutputDev.h b/xpdf/SplashOutputDev.h
new file mode 100644
index 0000000..96f270a
--- /dev/null
+++ b/xpdf/SplashOutputDev.h
@@ -0,0 +1,247 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/Stream-CCITT.h b/xpdf/Stream-CCITT.h
new file mode 100644
index 0000000..c4458fe
--- /dev/null
+++ b/xpdf/Stream-CCITT.h
@@ -0,0 +1,459 @@
+//========================================================================
+//
+// 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}
+};
diff --git a/xpdf/Stream.cc b/xpdf/Stream.cc
new file mode 100644
index 0000000..68abafa
--- /dev/null
+++ b/xpdf/Stream.cc
@@ -0,0 +1,4627 @@
+//========================================================================
+//
+// 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;
+}
diff --git a/xpdf/Stream.h b/xpdf/Stream.h
new file mode 100644
index 0000000..85a8469
--- /dev/null
+++ b/xpdf/Stream.h
@@ -0,0 +1,858 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/TextOutputDev.cc b/xpdf/TextOutputDev.cc
new file mode 100644
index 0000000..a78f0ee
--- /dev/null
+++ b/xpdf/TextOutputDev.cc
@@ -0,0 +1,4090 @@
+//========================================================================
+//
+// 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;
+}
diff --git a/xpdf/TextOutputDev.h b/xpdf/TextOutputDev.h
new file mode 100644
index 0000000..3a424bb
--- /dev/null
+++ b/xpdf/TextOutputDev.h
@@ -0,0 +1,661 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/UTF8.h b/xpdf/UTF8.h
new file mode 100644
index 0000000..8536dbf
--- /dev/null
+++ b/xpdf/UTF8.h
@@ -0,0 +1,56 @@
+//========================================================================
+//
+// 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;
+ }
+}
diff --git a/xpdf/UnicodeMap.cc b/xpdf/UnicodeMap.cc
new file mode 100644
index 0000000..2b8cb1f
--- /dev/null
+++ b/xpdf/UnicodeMap.cc
@@ -0,0 +1,293 @@
+//========================================================================
+//
+// 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;
+}
diff --git a/xpdf/UnicodeMap.h b/xpdf/UnicodeMap.h
new file mode 100644
index 0000000..0f86101
--- /dev/null
+++ b/xpdf/UnicodeMap.h
@@ -0,0 +1,123 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/UnicodeMapTables.h b/xpdf/UnicodeMapTables.h
new file mode 100644
index 0000000..9c51034
--- /dev/null
+++ b/xpdf/UnicodeMapTables.h
@@ -0,0 +1,361 @@
+//========================================================================
+//
+// 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))
diff --git a/xpdf/UnicodeTypeTable.cc b/xpdf/UnicodeTypeTable.cc
new file mode 100644
index 0000000..b896040
--- /dev/null
+++ b/xpdf/UnicodeTypeTable.cc
@@ -0,0 +1,949 @@
+//========================================================================
+//
+// 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;
+}
+
diff --git a/xpdf/UnicodeTypeTable.h b/xpdf/UnicodeTypeTable.h
new file mode 100644
index 0000000..7103dbd
--- /dev/null
+++ b/xpdf/UnicodeTypeTable.h
@@ -0,0 +1,20 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/XPDFApp.cc b/xpdf/XPDFApp.cc
new file mode 100644
index 0000000..f9d2cd4
--- /dev/null
+++ b/xpdf/XPDFApp.cc
@@ -0,0 +1,447 @@
+//========================================================================
+//
+// 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);
+}
diff --git a/xpdf/XPDFApp.h b/xpdf/XPDFApp.h
new file mode 100644
index 0000000..9d4060a
--- /dev/null
+++ b/xpdf/XPDFApp.h
@@ -0,0 +1,114 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/XPDFCore.cc b/xpdf/XPDFCore.cc
new file mode 100644
index 0000000..827a86d
--- /dev/null
+++ b/xpdf/XPDFCore.cc
@@ -0,0 +1,1655 @@
+//========================================================================
+//
+// 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;
+}
diff --git a/xpdf/XPDFCore.h b/xpdf/XPDFCore.h
new file mode 100644
index 0000000..903e22b
--- /dev/null
+++ b/xpdf/XPDFCore.h
@@ -0,0 +1,251 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/XPDFTree.cc b/xpdf/XPDFTree.cc
new file mode 100644
index 0000000..720197f
--- /dev/null
+++ b/xpdf/XPDFTree.cc
@@ -0,0 +1,931 @@
+//========================================================================
+//
+// 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);
+}
diff --git a/xpdf/XPDFTree.h b/xpdf/XPDFTree.h
new file mode 100644
index 0000000..432b4ff
--- /dev/null
+++ b/xpdf/XPDFTree.h
@@ -0,0 +1,45 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/XPDFTreeP.h b/xpdf/XPDFTreeP.h
new file mode 100644
index 0000000..1d786f4
--- /dev/null
+++ b/xpdf/XPDFTreeP.h
@@ -0,0 +1,87 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/XPDFViewer.cc b/xpdf/XPDFViewer.cc
new file mode 100644
index 0000000..c15d3e6
--- /dev/null
+++ b/xpdf/XPDFViewer.cc
@@ -0,0 +1,3488 @@
+//========================================================================
+//
+// 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;
+}
diff --git a/xpdf/XPDFViewer.h b/xpdf/XPDFViewer.h
new file mode 100644
index 0000000..2be27e1
--- /dev/null
+++ b/xpdf/XPDFViewer.h
@@ -0,0 +1,352 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/XRef.cc b/xpdf/XRef.cc
new file mode 100644
index 0000000..6a5b6c8
--- /dev/null
+++ b/xpdf/XRef.cc
@@ -0,0 +1,896 @@
+//========================================================================
+//
+// 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;
+}
diff --git a/xpdf/XRef.h b/xpdf/XRef.h
new file mode 100644
index 0000000..8a5e66c
--- /dev/null
+++ b/xpdf/XRef.h
@@ -0,0 +1,133 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/XpdfPluginAPI.cc b/xpdf/XpdfPluginAPI.cc
new file mode 100644
index 0000000..789b45f
--- /dev/null
+++ b/xpdf/XpdfPluginAPI.cc
@@ -0,0 +1,262 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/XpdfPluginAPI.h b/xpdf/XpdfPluginAPI.h
new file mode 100644
index 0000000..22540f7
--- /dev/null
+++ b/xpdf/XpdfPluginAPI.h
@@ -0,0 +1,341 @@
+/*
+ * 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
diff --git a/xpdf/about-text.h b/xpdf/about-text.h
new file mode 100644
index 0000000..50266bd
--- /dev/null
+++ b/xpdf/about-text.h
@@ -0,0 +1,48 @@
+//========================================================================
+//
+// 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
+};
diff --git a/xpdf/about.xbm b/xpdf/about.xbm
new file mode 100644
index 0000000..86f8472
--- /dev/null
+++ b/xpdf/about.xbm
@@ -0,0 +1,6 @@
+#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};
diff --git a/xpdf/backArrow.xbm b/xpdf/backArrow.xbm
new file mode 100644
index 0000000..47f49e1
--- /dev/null
+++ b/xpdf/backArrow.xbm
@@ -0,0 +1,6 @@
+#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};
diff --git a/xpdf/backArrowDis.xbm b/xpdf/backArrowDis.xbm
new file mode 100644
index 0000000..7529639
--- /dev/null
+++ b/xpdf/backArrowDis.xbm
@@ -0,0 +1,6 @@
+#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};
diff --git a/xpdf/config.h b/xpdf/config.h
new file mode 100644
index 0000000..81d4dd0
--- /dev/null
+++ b/xpdf/config.h
@@ -0,0 +1,112 @@
+//========================================================================
+//
+// 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
diff --git a/xpdf/dblLeftArrow.xbm b/xpdf/dblLeftArrow.xbm
new file mode 100644
index 0000000..616d2bd
--- /dev/null
+++ b/xpdf/dblLeftArrow.xbm
@@ -0,0 +1,6 @@
+#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};
diff --git a/xpdf/dblLeftArrowDis.xbm b/xpdf/dblLeftArrowDis.xbm
new file mode 100644
index 0000000..3fb78d8
--- /dev/null
+++ b/xpdf/dblLeftArrowDis.xbm
@@ -0,0 +1,6 @@
+#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};
diff --git a/xpdf/dblRightArrow.xbm b/xpdf/dblRightArrow.xbm
new file mode 100644
index 0000000..0072810
--- /dev/null
+++ b/xpdf/dblRightArrow.xbm
@@ -0,0 +1,6 @@
+#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};
diff --git a/xpdf/dblRightArrowDis.xbm b/xpdf/dblRightArrowDis.xbm
new file mode 100644
index 0000000..a6c1e37
--- /dev/null
+++ b/xpdf/dblRightArrowDis.xbm
@@ -0,0 +1,6 @@
+#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};
diff --git a/xpdf/find.xbm b/xpdf/find.xbm
new file mode 100644
index 0000000..206da87
--- /dev/null
+++ b/xpdf/find.xbm
@@ -0,0 +1,6 @@
+#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};
diff --git a/xpdf/findDis.xbm b/xpdf/findDis.xbm
new file mode 100644
index 0000000..cf666f2
--- /dev/null
+++ b/xpdf/findDis.xbm
@@ -0,0 +1,6 @@
+#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};
diff --git a/xpdf/forwardArrow.xbm b/xpdf/forwardArrow.xbm
new file mode 100644
index 0000000..e387535
--- /dev/null
+++ b/xpdf/forwardArrow.xbm
@@ -0,0 +1,6 @@
+#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};
diff --git a/xpdf/forwardArrowDis.xbm b/xpdf/forwardArrowDis.xbm
new file mode 100644
index 0000000..58d0cc0
--- /dev/null
+++ b/xpdf/forwardArrowDis.xbm
@@ -0,0 +1,6 @@
+#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};
diff --git a/xpdf/leftArrow.xbm b/xpdf/leftArrow.xbm
new file mode 100644
index 0000000..367e4e6
--- /dev/null
+++ b/xpdf/leftArrow.xbm
@@ -0,0 +1,5 @@
+#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};
diff --git a/xpdf/leftArrowDis.xbm b/xpdf/leftArrowDis.xbm
new file mode 100644
index 0000000..953092a
--- /dev/null
+++ b/xpdf/leftArrowDis.xbm
@@ -0,0 +1,5 @@
+#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};
diff --git a/xpdf/pdffonts.cc b/xpdf/pdffonts.cc
new file mode 100644
index 0000000..d35825f
--- /dev/null
+++ b/xpdf/pdffonts.cc
@@ -0,0 +1,298 @@
+//========================================================================
+//
+// 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();
+}
diff --git a/xpdf/pdfimages.cc b/xpdf/pdfimages.cc
new file mode 100644
index 0000000..549f090
--- /dev/null
+++ b/xpdf/pdfimages.cc
@@ -0,0 +1,155 @@
+//========================================================================
+//
+// 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;
+}
diff --git a/xpdf/pdfinfo.cc b/xpdf/pdfinfo.cc
new file mode 100644
index 0000000..aa49273
--- /dev/null
+++ b/xpdf/pdfinfo.cc
@@ -0,0 +1,387 @@
+//========================================================================
+//
+// 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);
+}
diff --git a/xpdf/pdftoppm.cc b/xpdf/pdftoppm.cc
new file mode 100644
index 0000000..acce660
--- /dev/null
+++ b/xpdf/pdftoppm.cc
@@ -0,0 +1,203 @@
+//========================================================================
+//
+// 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;
+}
diff --git a/xpdf/pdftops.cc b/xpdf/pdftops.cc
new file mode 100644
index 0000000..9ed024e
--- /dev/null
+++ b/xpdf/pdftops.cc
@@ -0,0 +1,344 @@
+//========================================================================
+//
+// 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;
+}
diff --git a/xpdf/pdftotext.cc b/xpdf/pdftotext.cc
new file mode 100644
index 0000000..f4373f6
--- /dev/null
+++ b/xpdf/pdftotext.cc
@@ -0,0 +1,333 @@
+//========================================================================
+//
+// 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();
+}
diff --git a/xpdf/print.xbm b/xpdf/print.xbm
new file mode 100644
index 0000000..209eb43
--- /dev/null
+++ b/xpdf/print.xbm
@@ -0,0 +1,6 @@
+#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};
diff --git a/xpdf/printDis.xbm b/xpdf/printDis.xbm
new file mode 100644
index 0000000..19e962d
--- /dev/null
+++ b/xpdf/printDis.xbm
@@ -0,0 +1,6 @@
+#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};
diff --git a/xpdf/rightArrow.xbm b/xpdf/rightArrow.xbm
new file mode 100644
index 0000000..4ccb16b
--- /dev/null
+++ b/xpdf/rightArrow.xbm
@@ -0,0 +1,5 @@
+#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};
diff --git a/xpdf/rightArrowDis.xbm b/xpdf/rightArrowDis.xbm
new file mode 100644
index 0000000..1216c47
--- /dev/null
+++ b/xpdf/rightArrowDis.xbm
@@ -0,0 +1,5 @@
+#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};
diff --git a/xpdf/vms_make.com b/xpdf/vms_make.com
new file mode 100644
index 0000000..f4fb74a
--- /dev/null
+++ b/xpdf/vms_make.com
@@ -0,0 +1,129 @@
+$!========================================================================
+$!
+$! 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
diff --git a/xpdf/xpdf.cc b/xpdf/xpdf.cc
new file mode 100644
index 0000000..4305ee2
--- /dev/null
+++ b/xpdf/xpdf.cc
@@ -0,0 +1,344 @@
+//========================================================================
+//
+// 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;
+}
diff --git a/xpdf/xpdfIcon.xpm b/xpdf/xpdfIcon.xpm
new file mode 100644
index 0000000..5e05d20
--- /dev/null
+++ b/xpdf/xpdfIcon.xpm
@@ -0,0 +1,62 @@
+/* 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.",
+"................................................",
+"................................................",
+"................................................",
+"................................................"
+};