summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Cloos <cloos@jhcloos.com>2014-06-04 00:15:11 -0400
committerJames Cloos <cloos@jhcloos.com>2014-06-04 00:15:11 -0400
commitffaf11c4f9ee0c4a124ec454f96c0f99ee699f10 (patch)
treedc6c9e877d757f6774ae6d6af400660b2be91c23
parent3ed3a482f75317f50821f6a43e4afc9997d0ae83 (diff)
Signed-off-by: James Cloos <cloos@jhcloos.com>
-rw-r--r--ANNOUNCE26
-rw-r--r--CHANGES174
-rw-r--r--INSTALL44
-rw-r--r--Makefile.in2
-rw-r--r--README25
-rw-r--r--aconf-dj.h5
-rw-r--r--aconf-win32.h15
-rw-r--r--aconf.h.in11
-rw-r--r--aconf2.h7
-rwxr-xr-xconfigure1141
-rw-r--r--configure.in112
-rw-r--r--dj_make.bat8
-rw-r--r--doc/pdfdetach.110
-rw-r--r--doc/pdfdetach.cat10
-rw-r--r--doc/pdfdetach.hlp101
-rw-r--r--doc/pdffonts.125
-rw-r--r--doc/pdffonts.cat21
-rw-r--r--doc/pdffonts.hlp114
-rw-r--r--doc/pdfimages.114
-rw-r--r--doc/pdfimages.cat14
-rw-r--r--doc/pdfimages.hlp94
-rw-r--r--doc/pdfinfo.112
-rw-r--r--doc/pdfinfo.cat12
-rw-r--r--doc/pdfinfo.hlp126
-rw-r--r--doc/pdftohtml.1106
-rw-r--r--doc/pdftohtml.cat87
-rw-r--r--doc/pdftopng.1119
-rw-r--r--doc/pdftopng.cat96
-rw-r--r--doc/pdftoppm.115
-rw-r--r--doc/pdftoppm.cat20
-rw-r--r--doc/pdftoppm.hlp110
-rw-r--r--doc/pdftops.111
-rw-r--r--doc/pdftops.cat60
-rw-r--r--doc/pdftops.hlp211
-rw-r--r--doc/pdftotext.161
-rw-r--r--doc/pdftotext.cat68
-rw-r--r--doc/pdftotext.hlp127
-rw-r--r--doc/sample-xpdfrc3
-rw-r--r--doc/xpdf.130
-rw-r--r--doc/xpdf.cat152
-rw-r--r--doc/xpdf.hlp782
-rw-r--r--doc/xpdfrc.557
-rw-r--r--doc/xpdfrc.cat334
-rw-r--r--doc/xpdfrc.hlp612
-rw-r--r--fofi/FoFiBase.cc12
-rw-r--r--fofi/FoFiIdentifier.cc255
-rw-r--r--fofi/FoFiIdentifier.h11
-rw-r--r--fofi/FoFiTrueType.cc220
-rw-r--r--fofi/FoFiTrueType.h33
-rw-r--r--fofi/FoFiType1C.cc140
-rw-r--r--fofi/Makefile.in4
-rw-r--r--fofi/vms_make.com0
-rw-r--r--goo/FixedPoint.h58
-rw-r--r--goo/GMutex.h2
-rw-r--r--goo/GString.cc68
-rw-r--r--goo/GString.h10
-rw-r--r--goo/Makefile.in6
-rw-r--r--goo/gfile.cc90
-rw-r--r--goo/gfile.h37
-rw-r--r--goo/gmem.cc77
-rw-r--r--goo/gmem.h5
-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
-rw-r--r--ms_make.bat92
-rw-r--r--splash/Makefile.in10
-rw-r--r--splash/Splash.cc4548
-rw-r--r--splash/Splash.h117
-rw-r--r--splash/SplashBitmap.cc2
-rw-r--r--splash/SplashBitmap.h2
-rw-r--r--splash/SplashClip.cc547
-rw-r--r--splash/SplashClip.h55
-rw-r--r--splash/SplashErrorCodes.h2
-rw-r--r--splash/SplashFTFont.cc31
-rw-r--r--splash/SplashFTFont.h2
-rw-r--r--splash/SplashFTFontEngine.cc243
-rw-r--r--splash/SplashFTFontEngine.h56
-rw-r--r--splash/SplashFTFontFile.cc71
-rw-r--r--splash/SplashFTFontFile.h34
-rw-r--r--splash/SplashFont.cc2
-rw-r--r--splash/SplashFont.h2
-rw-r--r--splash/SplashFontEngine.cc123
-rw-r--r--splash/SplashFontEngine.h63
-rw-r--r--splash/SplashFontFile.cc21
-rw-r--r--splash/SplashFontFile.h15
-rw-r--r--splash/SplashFontFileID.cc2
-rw-r--r--splash/SplashFontFileID.h2
-rw-r--r--splash/SplashGlyphBitmap.h2
-rw-r--r--splash/SplashMath.h93
-rw-r--r--splash/SplashPath.cc3
-rw-r--r--splash/SplashPath.h4
-rw-r--r--splash/SplashPattern.cc2
-rw-r--r--splash/SplashPattern.h2
-rw-r--r--splash/SplashScreen.cc2
-rw-r--r--splash/SplashScreen.h2
-rw-r--r--splash/SplashState.cc48
-rw-r--r--splash/SplashState.h11
-rw-r--r--splash/SplashT1Font.cc290
-rw-r--r--splash/SplashT1Font.h57
-rw-r--r--splash/SplashT1FontEngine.cc124
-rw-r--r--splash/SplashT1FontEngine.h53
-rw-r--r--splash/SplashT1FontFile.cc98
-rw-r--r--splash/SplashT1FontFile.h58
-rw-r--r--splash/SplashTypes.h2
-rw-r--r--splash/SplashXPath.cc346
-rw-r--r--splash/SplashXPath.h63
-rw-r--r--splash/SplashXPathScanner.cc899
-rw-r--r--splash/SplashXPathScanner.h88
-rw-r--r--splash/vms_make.com0
-rw-r--r--vms_make.com736
-rw-r--r--xpdf/AcroForm.cc1897
-rw-r--r--xpdf/AcroForm.h128
-rw-r--r--xpdf/Annot.cc1766
-rw-r--r--xpdf/Annot.h59
-rw-r--r--xpdf/CMap.cc52
-rw-r--r--xpdf/Catalog.cc85
-rw-r--r--xpdf/Catalog.h5
-rw-r--r--xpdf/CharCodeToUnicode.cc16
-rw-r--r--xpdf/CharCodeToUnicode.h2
-rw-r--r--xpdf/Decrypt.cc853
-rw-r--r--xpdf/Decrypt.h18
-rw-r--r--xpdf/Dict.cc78
-rw-r--r--xpdf/Dict.h10
-rw-r--r--xpdf/Error.cc30
-rw-r--r--xpdf/Error.h6
-rw-r--r--xpdf/Form.cc67
-rw-r--r--xpdf/Form.h64
-rw-r--r--xpdf/Function.cc1176
-rw-r--r--xpdf/Function.h15
-rw-r--r--xpdf/Gfx.cc720
-rw-r--r--xpdf/Gfx.h59
-rw-r--r--xpdf/GfxFont.cc129
-rw-r--r--xpdf/GfxFont.h10
-rw-r--r--xpdf/GfxState.cc449
-rw-r--r--xpdf/GfxState.h79
-rw-r--r--xpdf/GlobalParams.cc506
-rw-r--r--xpdf/GlobalParams.h28
-rw-r--r--xpdf/HTMLGen.cc583
-rw-r--r--xpdf/HTMLGen.h63
-rw-r--r--xpdf/ImageOutputDev.cc84
-rw-r--r--xpdf/ImageOutputDev.h18
-rw-r--r--xpdf/JArithmeticDecoder.cc18
-rw-r--r--xpdf/JArithmeticDecoder.h1
-rw-r--r--xpdf/JBIG2Stream.cc73
-rw-r--r--xpdf/JPXStream.cc505
-rw-r--r--xpdf/JPXStream.h24
-rw-r--r--xpdf/Lexer.h9
-rw-r--r--xpdf/Link.cc132
-rw-r--r--xpdf/Link.h82
-rw-r--r--xpdf/Makefile.in580
-rw-r--r--xpdf/Object.h13
-rw-r--r--xpdf/OptionalContent.cc207
-rw-r--r--xpdf/OptionalContent.h21
-rw-r--r--xpdf/Outline.cc92
-rw-r--r--xpdf/Outline.h17
-rw-r--r--xpdf/OutputDev.cc38
-rw-r--r--xpdf/OutputDev.h14
-rw-r--r--xpdf/PDFCore.cc79
-rw-r--r--xpdf/PDFCore.h6
-rw-r--r--xpdf/PDFDoc.cc65
-rw-r--r--xpdf/PDFDoc.h9
-rw-r--r--xpdf/PSOutputDev.cc1639
-rw-r--r--xpdf/PSOutputDev.h65
-rw-r--r--xpdf/Page.cc39
-rw-r--r--xpdf/Page.h3
-rw-r--r--xpdf/Parser.cc27
-rw-r--r--xpdf/Parser.h2
-rw-r--r--xpdf/PreScanOutputDev.cc27
-rw-r--r--xpdf/PreScanOutputDev.h11
-rw-r--r--xpdf/SecurityHandler.cc14
-rw-r--r--xpdf/SplashOutputDev.cc549
-rw-r--r--xpdf/SplashOutputDev.h25
-rw-r--r--xpdf/Stream.cc889
-rw-r--r--xpdf/Stream.h121
-rw-r--r--xpdf/TextOutputDev.cc6527
-rw-r--r--xpdf/TextOutputDev.h513
-rw-r--r--xpdf/TextString.cc164
-rw-r--r--xpdf/TextString.h66
-rw-r--r--xpdf/UnicodeTypeTable.cc40
-rw-r--r--xpdf/UnicodeTypeTable.h4
-rw-r--r--xpdf/XFAForm.cc1458
-rw-r--r--xpdf/XFAForm.h126
-rw-r--r--xpdf/XPDFCore.cc19
-rw-r--r--xpdf/XPDFViewer.cc35
-rw-r--r--xpdf/XPDFViewer.h1
-rw-r--r--xpdf/XRef.cc433
-rw-r--r--xpdf/XRef.h40
-rw-r--r--xpdf/XpdfPluginAPI.cc2
-rw-r--r--xpdf/Zoox.cc839
-rw-r--r--xpdf/Zoox.h241
-rw-r--r--xpdf/config.h20
-rw-r--r--xpdf/pdfdetach.cc2
-rw-r--r--xpdf/pdffonts.cc158
-rw-r--r--xpdf/pdfinfo.cc47
-rw-r--r--xpdf/pdftohtml.cc246
-rw-r--r--xpdf/pdftopng.cc289
-rw-r--r--xpdf/pdftoppm.cc36
-rw-r--r--xpdf/pdftops.cc31
-rw-r--r--xpdf/pdftotext.cc189
-rw-r--r--xpdf/vms_make.com129
-rw-r--r--xpdf/xpdf.cc10
205 files changed, 25148 insertions, 16788 deletions
diff --git a/ANNOUNCE b/ANNOUNCE
index b4dff6c..d21dd9c 100644
--- a/ANNOUNCE
+++ b/ANNOUNCE
@@ -1,4 +1,4 @@
-Subject: ANNOUNCE: Xpdf 3.03 - a PDF viewer for X
+Subject: ANNOUNCE: Xpdf 3.04 - a PDF viewer for X
Glyph & Cog, LLC is pleased to announce a new version of Xpdf, the
@@ -6,32 +6,20 @@ 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
+Xpdf runs under the X Window System on Unix, 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:
-* Added the "fixed pitch" text extraction mode.
-* Modified "pdftops -paper match" to handle PDF files with
- different-sized pages, i.e., it will now select the matching paper
- size on a page-by-page basis.
-* Add ability for pdftoppm to write to stdout.
-* Added the pdfdetach tool.
-* Implemented 256-bit AES decryption.
-* Commented out the t1lib section in the configure script -- t1lib has
- some potential security holes, and hasn't been updated in years.
-* Redesigned the font configuration xpdfrc commands: removed the
- displayFontT1, displayFontTT, displayNamedCIDFontT1,
- displayCIDFontT1, displayNamedCIDFontTT, displayCIDFontTT, psFont,
- psNamedFont16, and psFont16 commands; added the fontFile,
- fontFileCC, psResidentFont, psResidentFont16, and psResidentFontCC
- commands.
-* Switched from GPLv2 to dual v2/v3 licensing.
+* New text extractor.
+* Added the pdftohtml tool.
+* Added the pdftopng tool.
+* New trapezoid-based rasterizer core (for performance).
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.
+compile for UNIX, OS/2, and Win32.
More information, source code, and precompiled binaries are on the
xpdf web page and ftp site:
diff --git a/CHANGES b/CHANGES
index cde9756..d2228d2 100644
--- a/CHANGES
+++ b/CHANGES
@@ -2147,3 +2147,177 @@ Implement embedding of external 16-bit fonts (without subsetting) in
PostScript output.
Added the minLineWidth xpdfrc command.
Added warning messages for font substitutions.
+
+3.04 (2014-may-28)
+------------------
+New text extractor.
+Added the pdftohtml tool.
+Added the pdftopng tool.
+New trapezoid-based rasterizer core (for performance).
+Generate appearance streams for Line, PolyLine, and Polygon
+ annotations.
+Added the closeWindowOrQuit command, and changed the default binding
+ for ctrl-W from closeWindow to closeWindowOrQuit.
+Implemented the new AES-256 mode (R=6, Acrobat X).
+Add an object cache.
+Added a small cache for object streams.
+Modify PSOutputDev to use LZW compression instead of RLE, with a
+ fallback to RLE if the "psLZW no" setting is given.
+Pdfinfo now prints page rotation info.
+Modified ImageOutputDev, used by pdfimages, to output the masks and
+ soft masks used when drawing images.
+Remove non-printable characters from error output, just in case they
+ might cause problems for the terminal program.
+Added initial support for Code3of9 bar codes in XFA forms.
+Added the mapExtTrueTypeFontsViaUnicode xpdfrc command.
+Apply stroke adjustment to rectangular images and clipping regions (in
+ addition to strokes and fills).
+Decode JPEG 2000 images at less than full resolution if the full res
+ image isn't needed (i.e., if the raw image is higher resolution than
+ the output).
+Implemented knockout groups.
+Removed t1lib support.
+Added support for images with 16-bit components.
+Rewrote the Dict class to use a hash table; as a side effect, this
+ handles dictionaries with multiple definitions for a key, which are
+ in violation of the spec, but Acrobat appears to handle.
+The transformed line width computation -- used to implement the
+ minLineWidth setting, and the hairline threshold in monochrome mode --
+ was incorrect.
+Pdftops was not correctly handling the case where it couldn't find
+ a 16-bit font -- this led to crashes and/or invalid PostScript.
+A bug in FlateStream::getBlock() was causing problems with narrow
+ images.
+Use the correct _WIN32 define instead of WIN32.
+Use copy-on-write for the clip path in SplashState (when doing gsave),
+ for performance.
+Added a Solaris-specific entry to the ghostscript font search path.
+SplashState was initializing line width to 0 instead of 1.
+Abort processing on a content stream after getting 500 errors
+ (undefined operator, wrong number of args) -- this avoids very long
+ processing time for malicious PDF files using bogus RLE encoded
+ content streams.
+Added the psUseCropBoxAsPage xpdfrc option; "pdftops -pagecrop" now
+ sets psUseCropBoxAsPage; "pdftops -pagecrop -paper match" now uses
+ the CropBox as the page size.
+Re-architected the AcroForm support code into a separate AcroForm
+ module.
+Fixed the handling of overprinting/transparency interaction, using
+ the CompatibleOverprint blend mode.
+The TIFF predictor code for the 1-bit-per-pixel case was broken.
+For triangle and patch mesh shadings (types 4-7) with color functions,
+ interpolate the function parameter not the color.
+Check the fontFile/fontDir commands before (instead of after) doing
+ Base-14 substitution in PS output.
+Correctly handle non-embedded TrueType fonts that have an Identity
+ ToUnicode mapping (display and PS output were failing).
+Added support for XFA form rendering, including an "enableXFA" xpdfrc
+ setting.
+Handle PFB Type 1 fonts when generating PostScript output.
+Unwind any extraneous saved graphics state at the end of the page
+ (before drawing annotations).
+Added some integer overflow checks in the GString class.
+Handle 16-bit components in JPEG 2000 images.
+ActualText spans can end without a valid font, in which case
+ TextPage::beginWord was crashing.
+The Domain entry in function shadings wasn't being parsed correctly.
+Fixed a bug in the JPEG decoder - successive approximation
+ (progressive mode) coefficients weren't being handled correctly.
+Added a better infinite loop test to the xref parser.
+When generating PostScript, merge reused TrueType fonts (if their
+ code-to-GID mappings are the same).
+Tweak the Gouraud triangle shaded fill code to end the recursive
+ splitting if the triangles get sufficiently small.
+Do bilinear interpolation when upsampling images.
+When skipping extraneous image data from an inline image, look for
+ EI<whitespace> instead of just EI.
+When writing to stdout on Windows, pdftoppm now sets the file mode to
+ binary. [Thanks to Robert Frunzke.]
+Accept strings as well as names for the BaseFont entry in font
+ objects.
+Removed the TEXTOUT_WORD_LIST config option (with the new text
+ extractor, this is always enabled).
+Fixed a bug in the JBIG2 decoder (the TPGD context for template #3 in
+ readGenericBitmap was incorrect).
+Rewrote the PostScriptFunction code for performance.
+Handle 8-bit OpenType CFF fonts that are missing required tables in
+ the OpenType wrapper.
+Handle tiling patterns with reversed coordinates in their bounding
+ boxes.
+Added support for 64-bit file offsets, i.e., PDF files larger than
+ 2GB.
+Optimize the code that rasterizes pattern-filled image masks.
+Added support for Mac OS X system fonts (Base-14 only).
+The backdrop color in luminosity-type soft mask groups was not being
+ handled correctly.
+Modified behavior of "pdftops -paper match -duplex ..." - it will now
+ duplex consecutive same-sized pages.
+Tweak the handling of degenerate fills ('moveto lineto fill') to
+ match Adobe.
+Don't honor the OPM=1 setting with ICCBased CMYK color spaces.
+Whole-word searches were treating certain punctuation (Unicode number
+ separators and terminators) as part of the word, e.g., searching for
+ "foo" would not match "foo,".
+Use the TextString class everywhere it makes sense.
+Removed the unnecessary segment sort in Splash (performance
+ optimization).
+Handle hyperlinks that use Widget-type annotations.
+Fix up the integer overflow checks to avoid issues with clever
+ compilers. [Thanks to Nickolai Zeldovich.]
+Correctly handle streams with missing Length entries in damaged PDF
+ files.
+Added a compile-time option (LOAD_FONTS_FROM_MEM) to load fonts from
+ memory rather than temporary files on disk.
+Added the psRasterSliceSize xpdfrc option.
+Fixed a case in the JPEG 2000 arithmetic decoder where extra data is
+ present in packet i, and needs to be saved for use in packet i+1.
+Fixed a bug in the JPEG 2000 decoder related to images with fewer than
+ 8 bits per component.
+Handle the case in PSOutputDev where slice size overflows a 32-bit
+ integer.
+Add (partial) support for TrueType cmap format 2.
+Always pass FT_LOAD_NO_BITMAP to FreeType -- bitmaps apparently fail
+ with rotated characters.
+Support fonts specified in ExtGState dictionaries.
+Annotations with empty Border arrays should not draw a border.
+Fix the CMap parser to handle large CID ranges.
+Check for Type 3 CharProcs that call q or Q before the d0/d1 operator,
+ and treat them as uncacheable.
+Invert the selection color when starting in reverse video mode.
+Device{Gray,RGB,CMYK} cannot be mapped via a resource dict.
+Changed the PS output for masked images (explicit and color key
+ masking): use a plain old clip path instead of rectclip to avoid
+ array overflows.
+Check the StemSnapH/V arrays when converting Type 1C fonts to Type 1 -
+ if there are any duplicate or out-of-order values, skip that
+ StemSnapH/V array.
+Added the psMinLineWidth xpdfrc setting.
+Fix an obscure issue in converting TrueType fonts to Type 42, related
+ to empty glyph descriptions (12 zero bytes).
+Pdftops now reports an error if there were any I/O errors writing to
+ the PS output file.
+Fix vertical text (CJK fonts) in PS output -- offset the character
+ origin correctly.
+Increased the number of digits used by pdfimages for the image number
+ from three to four.
+Handle right-to-left (e.g., Arabic) ligatures correctly in the text
+ extractor.
+Added the -loc and -locPS options to pdffonts.
+Extend the object parser recursion limit to cover Stream::addFilters()
+ / Stream::makeFilters() - to avoid another possibility of stack
+ overflow.
+Disable FreeType autohinting, because it can fail badly with font
+ subsets that use invalid glyph names -- except in the case of Type 1
+ fonts, which look much better with light autohinting.
+Modified the rasterizer pipeline functions to process a scan line at a
+ time (for performance).
+Removed VMS build support (it hasn't been updated in ages).
+Removed pdftotext's '-htmlmeta' option (use pdftohtml instead).
+PSOutputDev's font/form setup code, and pdffonts, were not scanning
+ soft mask groups in ExtGState dictionaries.
+Invalid DCT input (e.g., from a damaged PDF file) could overflow the
+ dctClip array.
+When upsampling an image mask or image with a large resulting image
+ size, do it in stream mode instead of prescaling the whole image
+ (to avoid running out of memory).
+Added infinite loop detection to pdffonts.
diff --git a/INSTALL b/INSTALL
index 366a46d..fce20fd 100644
--- a/INSTALL
+++ b/INSTALL
@@ -1,11 +1,11 @@
Xpdf
====
-version 3.03
-2011-aug-15
+version 3.04
+2014-may-28
The Xpdf software and documentation are
-copyright 1996-2011 Glyph & Cog, LLC.
+copyright 1996-2014 Glyph & Cog, LLC.
Email: derekn@foolabs.com
WWW: http://www.foolabs.com/xpdf/
@@ -36,12 +36,6 @@ different systems.
--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:
@@ -112,35 +106,6 @@ 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 ***
**************
@@ -176,8 +141,7 @@ port (thanks to Michael A. Richmond for these instructions):
* to build xpdf:
- cd xpdf-x.yy
- - ./configure --with-t1-library=/usr/local/lib
- --with-t1-includes=/usr/local/include
+ - ./configure
--with-freetype2-library=/usr/lib
--with-freetype2-includes=/usr/include/freetype2
--with-Xm-library=/usr/lib
diff --git a/Makefile.in b/Makefile.in
index e4391e6..40f7449 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -119,7 +119,7 @@ clean:
distclean: clean
rm -f config.log config.status config.cache
rm -f aconf.h
- rm -f Makefile goo/Makefile xpdf/Makefile
+ rm -f Makefile goo/Makefile fofi/Makefile splash/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
diff --git a/README b/README
index e97791c..93a1bc5 100644
--- a/README
+++ b/README
@@ -1,11 +1,11 @@
Xpdf
====
-version 3.03
-2011-aug-15
+version 3.04
+2014-may-28
The Xpdf software and documentation are
-copyright 1996-2011 Glyph & Cog, LLC.
+copyright 1996-2014 Glyph & Cog, LLC.
Email: derekn@foolabs.com
WWW: http://www.foolabs.com/xpdf/
@@ -23,7 +23,7 @@ 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
+Xpdf runs under the X Window System on UNIX and OS/2. The non-X
components (pdftops, pdftotext, etc.) also run on Windows and Mac OSX
systems and should run on pretty much any system with a decent C++
compiler. Xpdf will run on 32-bit and 64-bit machines.
@@ -68,9 +68,9 @@ Xpdf is developed and tested on Linux.
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.
+as 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,
pdfdetach, pdftoppm, and pdfimages) can also be compiled on Windows
@@ -137,7 +137,7 @@ their man pages):
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.).
+pages: xpdf(1), etc.
All of these utilities read an optional configuration file: see the
xpdfrc(5) man page.
@@ -182,6 +182,15 @@ bug listed here, please send me email, with a pointer (URL, ftp site,
etc.) to the PDF file.
+Third-Party Libraries
+---------------------
+
+Xpdf uses the following libraries:
+* FreeType [http://www.freetype.org/]
+* libpng [http://www.libpng.com/pub/png/libpng.html] (used by pdftohtml)
+* zlib [http://zlib.net/] (used by pdftohtml)
+
+
Acknowledgments
---------------
diff --git a/aconf-dj.h b/aconf-dj.h
index 24492df..4c4beff 100644
--- a/aconf-dj.h
+++ b/aconf-dj.h
@@ -55,11 +55,6 @@
#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
diff --git a/aconf-win32.h b/aconf-win32.h
index 240d046..67bbb44 100644
--- a/aconf-win32.h
+++ b/aconf-win32.h
@@ -35,11 +35,6 @@
#define USE_EXCEPTIONS 1
/*
- * Enable word list support.
- */
-#undef TEXTOUT_WORD_LIST
-
-/*
* Use fixed point (instead of floating point) arithmetic.
*/
#undef USE_FIXEDPOINT
@@ -72,6 +67,7 @@
#define HAVE_STD_SORT 1
#undef HAVE_FSEEKO
#undef HAVE_FSEEK64
+#define HAVE_FSEEKI64 1
#undef _FILE_OFFSET_BITS
#undef _LARGE_FILES
#undef _LARGEFILE_SOURCE
@@ -83,11 +79,6 @@
#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
@@ -111,11 +102,11 @@
/*
* Defined if the Splash library is avaiable.
*/
-#undef HAVE_SPLASH
+#define HAVE_SPLASH 1
/*
* Enable support for CMYK output.
*/
-#undef SPLASH_CMYK
+#define SPLASH_CMYK 1
#endif
diff --git a/aconf.h.in b/aconf.h.in
index 42c04d8..91a9ce4 100644
--- a/aconf.h.in
+++ b/aconf.h.in
@@ -35,11 +35,6 @@
#undef USE_EXCEPTIONS
/*
- * Enable word list support.
- */
-#undef TEXTOUT_WORD_LIST
-
-/*
* Use fixed point (instead of floating point) arithmetic.
*/
#undef USE_FIXEDPOINT
@@ -72,6 +67,7 @@
#undef HAVE_STD_SORT
#undef HAVE_FSEEKO
#undef HAVE_FSEEK64
+#undef HAVE_FSEEKI64
#undef _FILE_OFFSET_BITS
#undef _LARGE_FILES
#undef _LARGEFILE_SOURCE
@@ -83,11 +79,6 @@
#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
diff --git a/aconf2.h b/aconf2.h
index b51de1e..59a1740 100644
--- a/aconf2.h
+++ b/aconf2.h
@@ -28,11 +28,4 @@
# endif
#endif
-/*
- * Make sure WIN32 is defined if appropriate.
- */
-#if defined(_WIN32) && !defined(WIN32)
-# define WIN32
-#endif
-
#endif
diff --git a/configure b/configure
index 03cd2de..b6cd8bd 100755
--- a/configure
+++ b/configure
@@ -1,11 +1,9 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.65.
+# Generated by GNU Autoconf 2.69.
#
#
-# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
-# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
-# Inc.
+# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
#
#
# This configure script is free software; the Free Software Foundation
@@ -89,6 +87,7 @@ fi
IFS=" "" $as_nl"
# Find who we are. Look in the path if we contain no directory separator.
+as_myself=
case $0 in #((
*[\\/]* ) as_myself=$0 ;;
*) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -133,6 +132,31 @@ export LANGUAGE
# CDPATH.
(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+# Use a proper internal environment variable to ensure we don't fall
+ # into an infinite loop, continuously re-executing ourselves.
+ if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then
+ _as_can_reexec=no; export _as_can_reexec;
+ # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+ *v*x* | *x*v* ) as_opts=-vx ;;
+ *v* ) as_opts=-v ;;
+ *x* ) as_opts=-x ;;
+ * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+as_fn_exit 255
+ fi
+ # We don't want this to propagate to other subprocesses.
+ { _as_can_reexec=; unset _as_can_reexec;}
if test "x$CONFIG_SHELL" = x; then
as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
emulate sh
@@ -166,7 +190,8 @@ if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then :
else
exitcode=1; echo positional parameters were not saved.
fi
-test x\$exitcode = x0 || exit 1"
+test x\$exitcode = x0 || exit 1
+test -x / || exit 1"
as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
@@ -211,14 +236,25 @@ IFS=$as_save_IFS
if test "x$CONFIG_SHELL" != x; then :
- # We cannot yet assume a decent shell, so we have to provide a
- # neutralization value for shells without unset; and this also
- # works around shells that cannot unset nonexistent variables.
- BASH_ENV=/dev/null
- ENV=/dev/null
- (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
- export CONFIG_SHELL
- exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"}
+ export CONFIG_SHELL
+ # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+ *v*x* | *x*v* ) as_opts=-vx ;;
+ *v* ) as_opts=-v ;;
+ *x* ) as_opts=-x ;;
+ * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+exit 255
fi
if test x$as_have_required = xno; then :
@@ -316,10 +352,18 @@ $as_echo X"$as_dir" |
test -d "$as_dir" && break
done
test -z "$as_dirs" || eval "mkdir $as_dirs"
- } || test -d "$as_dir" || as_fn_error "cannot create directory $as_dir"
+ } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
} # as_fn_mkdir_p
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+ test -f "$1" && test -x "$1"
+} # as_fn_executable_p
# as_fn_append VAR VALUE
# ----------------------
# Append the text in VALUE to the end of the definition contained in VAR. Take
@@ -356,19 +400,19 @@ else
fi # as_fn_arith
-# as_fn_error ERROR [LINENO LOG_FD]
-# ---------------------------------
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
-# script with status $?, using 1 if that was 0.
+# script with STATUS, using 1 if that was 0.
as_fn_error ()
{
- as_status=$?; test $as_status -eq 0 && as_status=1
- if test "$3"; then
- as_lineno=${as_lineno-"$2"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
- $as_echo "$as_me:${as_lineno-$LINENO}: error: $1" >&$3
+ as_status=$1; test $as_status -eq 0 && as_status=1
+ if test "$4"; then
+ as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
fi
- $as_echo "$as_me: error: $1" >&2
+ $as_echo "$as_me: error: $2" >&2
as_fn_exit $as_status
} # as_fn_error
@@ -441,6 +485,10 @@ as_cr_alnum=$as_cr_Letters$as_cr_digits
chmod +x "$as_me.lineno" ||
{ $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
+ # If we had to re-execute with $CONFIG_SHELL, we're ensured to have
+ # already done that, so ensure we don't try to do so again and fall
+ # in an infinite loop. This has already happened in practice.
+ _as_can_reexec=no; export _as_can_reexec
# Don't try to exec as it changes $[0], causing all sort of problems
# (the dirname of $[0] is not the place where we might find the
# original and so on. Autoconf is especially sensitive to this).
@@ -475,16 +523,16 @@ if (echo >conf$$.file) 2>/dev/null; then
# ... but there are two gotchas:
# 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
# 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
- # In both cases, we have to default to `cp -p'.
+ # In both cases, we have to default to `cp -pR'.
ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
- as_ln_s='cp -p'
+ as_ln_s='cp -pR'
elif ln conf$$.file conf$$ 2>/dev/null; then
as_ln_s=ln
else
- as_ln_s='cp -p'
+ as_ln_s='cp -pR'
fi
else
- as_ln_s='cp -p'
+ as_ln_s='cp -pR'
fi
rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
rmdir conf$$.dir 2>/dev/null
@@ -496,28 +544,8 @@ else
as_mkdir_p=false
fi
-if test -x / >/dev/null 2>&1; then
- as_test_x='test -x'
-else
- if ls -dL / >/dev/null 2>&1; then
- as_ls_L_option=L
- else
- as_ls_L_option=
- fi
- as_test_x='
- eval sh -c '\''
- if test -d "$1"; then
- test -d "$1/.";
- else
- case $1 in #(
- -*)set "./$1";;
- esac;
- case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #((
- ???[sx]*):;;*)false;;esac;fi
- '\'' sh
- '
-fi
-as_executable_p=$as_test_x
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
# 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'"
@@ -530,7 +558,7 @@ test -n "$DJDIR" || exec 7<&0 </dev/null
exec 6>&1
# Name of the host.
-# hostname on some systems (SVR3.2, Linux) returns a bogus exit status,
+# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status,
# so uname gets run too.
ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
@@ -593,14 +621,16 @@ ac_includes_default="\
ac_subst_vars='LTLIBOBJS
LIBOBJS
+EXTRA_CFLAGS
+EXTRA_LIBS
XPDF_TARGET
X
libpaper_CFLAGS
libpaper_LIBS
+libpng_CFLAGS
+libpng_LIBS
freetype2_CFLAGS
freetype2_LIBS
-t1_CFLAGS
-t1_LIBS
Sgm_CFLAGS
Sgm_LIBS
Xm_CFLAGS
@@ -685,7 +715,6 @@ enable_no_text_select
enable_opi
enable_multithreaded
enable_exceptions
-enable_wordlist
enable_fixedpoint
enable_cmyk
with_appdef_dir
@@ -705,6 +734,8 @@ with_Sgm_library
with_Sgm_includes
with_freetype2_library
with_freetype2_includes
+with_libpng_library
+with_libpng_includes
with_libpaper_library
with_libpaper_includes
'
@@ -783,8 +814,9 @@ do
fi
case $ac_option in
- *=*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
- *) ac_optarg=yes ;;
+ *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
+ *=) ac_optarg= ;;
+ *) ac_optarg=yes ;;
esac
# Accept the important Cygnus configure options, so we can diagnose typos.
@@ -829,7 +861,7 @@ do
ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
# Reject names that are not valid shell variable names.
expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
- as_fn_error "invalid feature name: $ac_useropt"
+ as_fn_error $? "invalid feature name: $ac_useropt"
ac_useropt_orig=$ac_useropt
ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
case $ac_user_opts in
@@ -855,7 +887,7 @@ do
ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
# Reject names that are not valid shell variable names.
expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
- as_fn_error "invalid feature name: $ac_useropt"
+ as_fn_error $? "invalid feature name: $ac_useropt"
ac_useropt_orig=$ac_useropt
ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
case $ac_user_opts in
@@ -1059,7 +1091,7 @@ do
ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
# Reject names that are not valid shell variable names.
expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
- as_fn_error "invalid package name: $ac_useropt"
+ as_fn_error $? "invalid package name: $ac_useropt"
ac_useropt_orig=$ac_useropt
ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
case $ac_user_opts in
@@ -1075,7 +1107,7 @@ do
ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
# Reject names that are not valid shell variable names.
expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
- as_fn_error "invalid package name: $ac_useropt"
+ as_fn_error $? "invalid package name: $ac_useropt"
ac_useropt_orig=$ac_useropt
ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
case $ac_user_opts in
@@ -1105,8 +1137,8 @@ do
| --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
x_libraries=$ac_optarg ;;
- -*) as_fn_error "unrecognized option: \`$ac_option'
-Try \`$0 --help' for more information."
+ -*) as_fn_error $? "unrecognized option: \`$ac_option'
+Try \`$0 --help' for more information"
;;
*=*)
@@ -1114,7 +1146,7 @@ Try \`$0 --help' for more information."
# Reject names that are not valid shell variable names.
case $ac_envvar in #(
'' | [0-9]* | *[!_$as_cr_alnum]* )
- as_fn_error "invalid variable name: \`$ac_envvar'" ;;
+ as_fn_error $? "invalid variable name: \`$ac_envvar'" ;;
esac
eval $ac_envvar=\$ac_optarg
export $ac_envvar ;;
@@ -1124,7 +1156,7 @@ Try \`$0 --help' for more information."
$as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2
expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
$as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2
- : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}
+ : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}"
;;
esac
@@ -1132,13 +1164,13 @@ done
if test -n "$ac_prev"; then
ac_option=--`echo $ac_prev | sed 's/_/-/g'`
- as_fn_error "missing argument to $ac_option"
+ as_fn_error $? "missing argument to $ac_option"
fi
if test -n "$ac_unrecognized_opts"; then
case $enable_option_checking in
no) ;;
- fatal) as_fn_error "unrecognized options: $ac_unrecognized_opts" ;;
+ fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;;
*) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
esac
fi
@@ -1161,7 +1193,7 @@ do
[\\/$]* | ?:[\\/]* ) continue;;
NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
esac
- as_fn_error "expected an absolute directory name for --$ac_var: $ac_val"
+ as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val"
done
# There might be people who depend on the old broken behavior: `$host'
@@ -1175,8 +1207,6 @@ target=$target_alias
if test "x$host_alias" != x; then
if test "x$build_alias" = x; then
cross_compiling=maybe
- $as_echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host.
- If a cross compiler is detected then cross compile mode will be used." >&2
elif test "x$build_alias" != "x$host_alias"; then
cross_compiling=yes
fi
@@ -1191,9 +1221,9 @@ test "$silent" = yes && exec 6>/dev/null
ac_pwd=`pwd` && test -n "$ac_pwd" &&
ac_ls_di=`ls -di .` &&
ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
- as_fn_error "working directory cannot be determined"
+ as_fn_error $? "working directory cannot be determined"
test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
- as_fn_error "pwd does not report name of working directory"
+ as_fn_error $? "pwd does not report name of working directory"
# Find the source files, if location was not specified.
@@ -1232,11 +1262,11 @@ else
fi
if test ! -r "$srcdir/$ac_unique_file"; then
test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
- as_fn_error "cannot find sources ($ac_unique_file) in $srcdir"
+ as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir"
fi
ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
ac_abs_confdir=`(
- cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error "$ac_msg"
+ cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg"
pwd)`
# When building in place, set srcdir=.
if test "$ac_abs_confdir" = "$ac_pwd"; then
@@ -1276,7 +1306,7 @@ Configuration:
--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
+ -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
@@ -1333,14 +1363,14 @@ Optional Features:
--disable-option-checking ignore unrecognized --enable/--with options
--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-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-fixedpoint use fixed point (instead of floating point)
+ arithmetic
--enable-cmyk include support for CMYK rasterization
--disable-largefile omit support for large files
@@ -1377,6 +1407,10 @@ Optional Packages:
use freetype2 library (FreeType2 font rasterizer - version 2.0.5+)
--with-freetype2-includes=DIR
set directory for freetype2 headers
+ --with-libpng-library=PATH
+ use libpng library (PNG library)
+ --with-libpng-includes=DIR
+ set directory for libpng headers
--with-libpaper-library=PATH
use libpaper library (Debian libpaper)
--with-libpaper-includes=DIR
@@ -1462,9 +1496,9 @@ test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
configure
-generated by GNU Autoconf 2.65
+generated by GNU Autoconf 2.69
-Copyright (C) 2009 Free Software Foundation, Inc.
+Copyright (C) 2012 Free Software Foundation, Inc.
This configure script is free software; the Free Software Foundation
gives unlimited permission to copy, distribute and modify it.
_ACEOF
@@ -1508,7 +1542,7 @@ sed 's/^/| /' conftest.$ac_ext >&5
ac_retval=1
fi
- eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
as_fn_set_status $ac_retval
} # ac_fn_c_try_compile
@@ -1540,7 +1574,7 @@ $as_echo "$ac_try_echo"; } >&5
test ! -s conftest.err
} && test -s conftest$ac_exeext && {
test "$cross_compiling" = yes ||
- $as_test_x conftest$ac_exeext
+ test -x conftest$ac_exeext
}; then :
ac_retval=0
else
@@ -1554,7 +1588,7 @@ fi
# interfere with the next link command; also delete a directory that is
# left behind by Apple's compiler. We do this before executing the actions.
rm -rf conftest.dSYM conftest_ipa8_conftest.oo
- eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
as_fn_set_status $ac_retval
} # ac_fn_c_try_link
@@ -1592,7 +1626,7 @@ sed 's/^/| /' conftest.$ac_ext >&5
ac_retval=1
fi
- eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
as_fn_set_status $ac_retval
} # ac_fn_cxx_try_compile
@@ -1618,7 +1652,7 @@ $as_echo "$ac_try_echo"; } >&5
mv -f conftest.er1 conftest.err
fi
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
- test $ac_status = 0; } >/dev/null && {
+ test $ac_status = 0; } > conftest.i && {
test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
test ! -s conftest.err
}; then :
@@ -1629,7 +1663,7 @@ sed 's/^/| /' conftest.$ac_ext >&5
ac_retval=1
fi
- eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
as_fn_set_status $ac_retval
} # ac_fn_c_try_cpp
@@ -1642,7 +1676,7 @@ ac_fn_c_check_func ()
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
$as_echo_n "checking for $2... " >&6; }
-if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then :
+if eval \${$3+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -1697,7 +1731,7 @@ fi
eval ac_res=\$$3
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
$as_echo "$ac_res" >&6; }
- eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
} # ac_fn_c_check_func
@@ -1728,7 +1762,7 @@ $as_echo "$ac_try_echo"; } >&5
test ! -s conftest.err
} && test -s conftest$ac_exeext && {
test "$cross_compiling" = yes ||
- $as_test_x conftest$ac_exeext
+ test -x conftest$ac_exeext
}; then :
ac_retval=0
else
@@ -1742,7 +1776,7 @@ fi
# interfere with the next link command; also delete a directory that is
# left behind by Apple's compiler. We do this before executing the actions.
rm -rf conftest.dSYM conftest_ipa8_conftest.oo
- eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
as_fn_set_status $ac_retval
} # ac_fn_cxx_try_link
@@ -1755,7 +1789,7 @@ ac_fn_cxx_check_func ()
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
$as_echo_n "checking for $2... " >&6; }
-if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then :
+if eval \${$3+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -1810,7 +1844,7 @@ fi
eval ac_res=\$$3
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
$as_echo "$ac_res" >&6; }
- eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
} # ac_fn_cxx_check_func
@@ -1822,10 +1856,10 @@ $as_echo "$ac_res" >&6; }
ac_fn_c_check_header_mongrel ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
- if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then :
+ if eval \${$3+:} false; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
$as_echo_n "checking for $2... " >&6; }
-if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then :
+if eval \${$3+:} false; then :
$as_echo_n "(cached) " >&6
fi
eval ac_res=\$$3
@@ -1861,7 +1895,7 @@ if ac_fn_c_try_cpp "$LINENO"; then :
else
ac_header_preproc=no
fi
-rm -f conftest.err conftest.$ac_ext
+rm -f conftest.err conftest.i conftest.$ac_ext
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5
$as_echo "$ac_header_preproc" >&6; }
@@ -1888,7 +1922,7 @@ $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
$as_echo_n "checking for $2... " >&6; }
-if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then :
+if eval \${$3+:} false; then :
$as_echo_n "(cached) " >&6
else
eval "$3=\$ac_header_compiler"
@@ -1897,7 +1931,7 @@ eval ac_res=\$$3
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
$as_echo "$ac_res" >&6; }
fi
- eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
} # ac_fn_c_check_header_mongrel
@@ -1938,7 +1972,7 @@ sed 's/^/| /' conftest.$ac_ext >&5
ac_retval=$ac_status
fi
rm -rf conftest.dSYM conftest_ipa8_conftest.oo
- eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
as_fn_set_status $ac_retval
} # ac_fn_c_try_run
@@ -1952,7 +1986,7 @@ ac_fn_c_check_header_compile ()
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
$as_echo_n "checking for $2... " >&6; }
-if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then :
+if eval \${$3+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -1970,7 +2004,7 @@ fi
eval ac_res=\$$3
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
$as_echo "$ac_res" >&6; }
- eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
} # ac_fn_c_check_header_compile
cat >config.log <<_ACEOF
@@ -1978,7 +2012,7 @@ 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.65. Invocation command line was
+generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -2088,11 +2122,9 @@ trap 'exit_status=$?
{
echo
- cat <<\_ASBOX
-## ---------------- ##
+ $as_echo "## ---------------- ##
## Cache variables. ##
-## ---------------- ##
-_ASBOX
+## ---------------- ##"
echo
# The following way of writing the cache mishandles newlines in values,
(
@@ -2126,11 +2158,9 @@ $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
)
echo
- cat <<\_ASBOX
-## ----------------- ##
+ $as_echo "## ----------------- ##
## Output variables. ##
-## ----------------- ##
-_ASBOX
+## ----------------- ##"
echo
for ac_var in $ac_subst_vars
do
@@ -2143,11 +2173,9 @@ _ASBOX
echo
if test -n "$ac_subst_files"; then
- cat <<\_ASBOX
-## ------------------- ##
+ $as_echo "## ------------------- ##
## File substitutions. ##
-## ------------------- ##
-_ASBOX
+## ------------------- ##"
echo
for ac_var in $ac_subst_files
do
@@ -2161,11 +2189,9 @@ _ASBOX
fi
if test -s confdefs.h; then
- cat <<\_ASBOX
-## ----------- ##
+ $as_echo "## ----------- ##
## confdefs.h. ##
-## ----------- ##
-_ASBOX
+## ----------- ##"
echo
cat confdefs.h
echo
@@ -2220,7 +2246,12 @@ _ACEOF
ac_site_file1=NONE
ac_site_file2=NONE
if test -n "$CONFIG_SITE"; then
- ac_site_file1=$CONFIG_SITE
+ # We do not want a PATH search for config.site.
+ case $CONFIG_SITE in #((
+ -*) ac_site_file1=./$CONFIG_SITE;;
+ */*) ac_site_file1=$CONFIG_SITE;;
+ *) ac_site_file1=./$CONFIG_SITE;;
+ esac
elif test "x$prefix" != xNONE; then
ac_site_file1=$prefix/share/config.site
ac_site_file2=$prefix/etc/config.site
@@ -2235,7 +2266,11 @@ do
{ $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
$as_echo "$as_me: loading site script $ac_site_file" >&6;}
sed 's/^/| /' "$ac_site_file" >&5
- . "$ac_site_file"
+ . "$ac_site_file" \
+ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "failed to load site script $ac_site_file
+See \`config.log' for more details" "$LINENO" 5; }
fi
done
@@ -2311,7 +2346,7 @@ if $ac_cache_corrupted; then
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;}
- as_fn_error "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5
+ as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5
fi
## -------------------- ##
## Main body of script. ##
@@ -2327,64 +2362,90 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
ac_config_headers="$ac_config_headers aconf.h"
+
# Check whether --enable-a4-paper was given.
if test "${enable_a4_paper+set}" = set; then :
- enableval=$enable_a4_paper; $as_echo "#define A4_PAPER 1" >>confdefs.h
+ enableval=$enable_a4_paper;
+fi
+
+if test "x$enable_a4_paper" = "xyes"; then :
+ $as_echo "#define A4_PAPER 1" >>confdefs.h
fi
# Check whether --enable-no-text-select was given.
if test "${enable_no_text_select+set}" = set; then :
- enableval=$enable_no_text_select; $as_echo "#define NO_TEXT_SELECT 1" >>confdefs.h
+ enableval=$enable_no_text_select;
+fi
+
+if test "x$enable_no_text_select" = "xyes"; then :
+ $as_echo "#define NO_TEXT_SELECT 1" >>confdefs.h
fi
# Check whether --enable-opi was given.
if test "${enable_opi+set}" = set; then :
- enableval=$enable_opi; $as_echo "#define OPI_SUPPORT 1" >>confdefs.h
+ enableval=$enable_opi;
+fi
+
+if test "x$enable_opi" = "xyes"; then :
+ $as_echo "#define OPI_SUPPORT 1" >>confdefs.h
fi
# Check whether --enable-multithreaded was given.
if test "${enable_multithreaded+set}" = set; then :
- enableval=$enable_multithreaded; $as_echo "#define MULTITHREADED 1" >>confdefs.h
+ enableval=$enable_multithreaded;
+fi
+
+if test "x$enable_multithreaded" = "xyes"; then :
+ $as_echo "#define MULTITHREADED 1" >>confdefs.h
fi
# Check whether --enable-exceptions was given.
if test "${enable_exceptions+set}" = set; then :
- enableval=$enable_exceptions; $as_echo "#define USE_EXCEPTIONS 1" >>confdefs.h
-
+ enableval=$enable_exceptions;
fi
-# Check whether --enable-wordlist was given.
-if test "${enable_wordlist+set}" = set; then :
- enableval=$enable_wordlist; $as_echo "#define TEXTOUT_WORD_LIST 1" >>confdefs.h
+if test "x$enable_exceptions" = "xyes"; then :
+ $as_echo "#define USE_EXCEPTIONS 1" >>confdefs.h
fi
# Check whether --enable-fixedpoint was given.
if test "${enable_fixedpoint+set}" = set; then :
- enableval=$enable_fixedpoint; $as_echo "#define USE_FIXEDPOINT 1" >>confdefs.h
+ enableval=$enable_fixedpoint;
+fi
+
+if test "x$enable_fixedpoint" = "xyes"; then :
+ $as_echo "#define USE_FIXEDPOINT 1" >>confdefs.h
fi
# Check whether --enable-cmyk was given.
if test "${enable_cmyk+set}" = set; then :
- enableval=$enable_cmyk; $as_echo "#define SPLASH_CMYK 1" >>confdefs.h
+ enableval=$enable_cmyk;
+fi
+
+if test "x$enable_cmyk" = "xyes"; then :
+ $as_echo "#define SPLASH_CMYK 1" >>confdefs.h
fi
# Check whether --with-appdef-dir was given.
if test "${with_appdef_dir+set}" = set; then :
- withval=$with_appdef_dir; cat >>confdefs.h <<_ACEOF
+ withval=$with_appdef_dir;
+fi
+
+if test "x$with_appdef_dir" != "xno"; then :
+ 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"
@@ -2409,7 +2470,7 @@ if test -n "$ac_tool_prefix"; then
set dummy ${ac_tool_prefix}gcc; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_CC+set}" = set; then :
+if ${ac_cv_prog_CC+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$CC"; then
@@ -2421,7 +2482,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_CC="${ac_tool_prefix}gcc"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -2449,7 +2510,7 @@ if test -z "$ac_cv_prog_CC"; then
set dummy gcc; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_ac_ct_CC+set}" = set; then :
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_CC"; then
@@ -2461,7 +2522,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_CC="gcc"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -2502,7 +2563,7 @@ if test -z "$CC"; then
set dummy ${ac_tool_prefix}cc; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_CC+set}" = set; then :
+if ${ac_cv_prog_CC+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$CC"; then
@@ -2514,7 +2575,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_CC="${ac_tool_prefix}cc"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -2542,7 +2603,7 @@ if test -z "$CC"; then
set dummy cc; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_CC+set}" = set; then :
+if ${ac_cv_prog_CC+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$CC"; then
@@ -2555,7 +2616,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_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
@@ -2601,7 +2662,7 @@ if test -z "$CC"; then
set dummy $ac_tool_prefix$ac_prog; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_CC+set}" = set; then :
+if ${ac_cv_prog_CC+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$CC"; then
@@ -2613,7 +2674,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -2645,7 +2706,7 @@ do
set dummy $ac_prog; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_ac_ct_CC+set}" = set; then :
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_CC"; then
@@ -2657,7 +2718,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_CC="$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -2699,8 +2760,8 @@ fi
test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
-as_fn_error "no acceptable C compiler found in \$PATH
-See \`config.log' for more details." "$LINENO" 5; }
+as_fn_error $? "no acceptable C compiler found in \$PATH
+See \`config.log' for more details" "$LINENO" 5; }
# Provide some information about the compiler.
$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
@@ -2814,9 +2875,8 @@ sed 's/^/| /' conftest.$ac_ext >&5
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
-{ as_fn_set_status 77
-as_fn_error "C compiler cannot create executables
-See \`config.log' for more details." "$LINENO" 5; }; }
+as_fn_error 77 "C compiler cannot create executables
+See \`config.log' for more details" "$LINENO" 5; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
@@ -2858,8 +2918,8 @@ done
else
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
-as_fn_error "cannot compute suffix of executables: cannot compile and link
-See \`config.log' for more details." "$LINENO" 5; }
+as_fn_error $? "cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details" "$LINENO" 5; }
fi
rm -f conftest conftest$ac_cv_exeext
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
@@ -2916,9 +2976,9 @@ $as_echo "$ac_try_echo"; } >&5
else
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
-as_fn_error "cannot run C compiled programs.
+as_fn_error $? "cannot run C compiled programs.
If you meant to cross compile, use \`--host'.
-See \`config.log' for more details." "$LINENO" 5; }
+See \`config.log' for more details" "$LINENO" 5; }
fi
fi
fi
@@ -2929,7 +2989,7 @@ rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
ac_clean_files=$ac_clean_files_save
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
$as_echo_n "checking for suffix of object files... " >&6; }
-if test "${ac_cv_objext+set}" = set; then :
+if ${ac_cv_objext+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -2969,8 +3029,8 @@ sed 's/^/| /' conftest.$ac_ext >&5
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
-as_fn_error "cannot compute suffix of object files: cannot compile
-See \`config.log' for more details." "$LINENO" 5; }
+as_fn_error $? "cannot compute suffix of object files: cannot compile
+See \`config.log' for more details" "$LINENO" 5; }
fi
rm -f conftest.$ac_cv_objext conftest.$ac_ext
fi
@@ -2980,7 +3040,7 @@ OBJEXT=$ac_cv_objext
ac_objext=$OBJEXT
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5
$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
-if test "${ac_cv_c_compiler_gnu+set}" = set; then :
+if ${ac_cv_c_compiler_gnu+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -3017,7 +3077,7 @@ ac_test_CFLAGS=${CFLAGS+set}
ac_save_CFLAGS=$CFLAGS
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
$as_echo_n "checking whether $CC accepts -g... " >&6; }
-if test "${ac_cv_prog_cc_g+set}" = set; then :
+if ${ac_cv_prog_cc_g+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_save_c_werror_flag=$ac_c_werror_flag
@@ -3095,7 +3155,7 @@ else
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
-if test "${ac_cv_prog_cc_c89+set}" = set; then :
+if ${ac_cv_prog_cc_c89+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_cv_prog_cc_c89=no
@@ -3104,8 +3164,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <stdarg.h>
#include <stdio.h>
-#include <sys/types.h>
-#include <sys/stat.h>
+struct stat;
/* 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);
@@ -3193,7 +3252,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing strerror" >&5
$as_echo_n "checking for library containing strerror... " >&6; }
-if test "${ac_cv_search_strerror+set}" = set; then :
+if ${ac_cv_search_strerror+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_func_search_save_LIBS=$LIBS
@@ -3227,11 +3286,11 @@ for ac_lib in '' cposix; do
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext
- if test "${ac_cv_search_strerror+set}" = set; then :
+ if ${ac_cv_search_strerror+:} false; then :
break
fi
done
-if test "${ac_cv_search_strerror+set}" = set; then :
+if ${ac_cv_search_strerror+:} false; then :
else
ac_cv_search_strerror=no
@@ -3253,7 +3312,7 @@ fi
*) :
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C99" >&5
$as_echo_n "checking for $CC option to accept ISO C99... " >&6; }
-if test "${ac_cv_prog_cc_c99+set}" = set; then :
+if ${ac_cv_prog_cc_c99+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_cv_prog_cc_c99=no
@@ -3397,7 +3456,7 @@ main ()
return 0;
}
_ACEOF
-for ac_arg in '' -std=gnu99 -std=c99 -c99 -AC99 -xc99=all -qlanglvl=extc99
+for ac_arg in '' -std=gnu99 -std=c99 -c99 -AC99 -D_STDC_C99= -qlanglvl=extc99
do
CC="$ac_save_CC $ac_arg"
if ac_fn_c_try_compile "$LINENO"; then :
@@ -3428,7 +3487,7 @@ if test "x$ac_cv_prog_cc_c99" != xno; then :
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
-if test "${ac_cv_prog_cc_c89+set}" = set; then :
+if ${ac_cv_prog_cc_c89+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_cv_prog_cc_c89=no
@@ -3437,8 +3496,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <stdarg.h>
#include <stdio.h>
-#include <sys/types.h>
-#include <sys/stat.h>
+struct stat;
/* 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);
@@ -3524,7 +3582,7 @@ fi
esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO Standard C" >&5
$as_echo_n "checking for $CC option to accept ISO Standard C... " >&6; }
- if test "${ac_cv_prog_cc_stdc+set}" = set; then :
+ if ${ac_cv_prog_cc_stdc+:} false; then :
$as_echo_n "(cached) " >&6
fi
@@ -3559,7 +3617,7 @@ if test -z "$CXX"; then
set dummy $ac_tool_prefix$ac_prog; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_CXX+set}" = set; then :
+if ${ac_cv_prog_CXX+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$CXX"; then
@@ -3571,7 +3629,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_CXX="$ac_tool_prefix$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -3603,7 +3661,7 @@ do
set dummy $ac_prog; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_ac_ct_CXX+set}" = set; then :
+if ${ac_cv_prog_ac_ct_CXX+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_CXX"; then
@@ -3615,7 +3673,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_CXX="$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -3681,7 +3739,7 @@ done
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5
$as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; }
-if test "${ac_cv_cxx_compiler_gnu+set}" = set; then :
+if ${ac_cv_cxx_compiler_gnu+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -3718,7 +3776,7 @@ ac_test_CXXFLAGS=${CXXFLAGS+set}
ac_save_CXXFLAGS=$CXXFLAGS
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5
$as_echo_n "checking whether $CXX accepts -g... " >&6; }
-if test "${ac_cv_prog_cxx_g+set}" = set; then :
+if ${ac_cv_prog_cxx_g+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_save_cxx_werror_flag=$ac_cxx_werror_flag
@@ -3802,16 +3860,22 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
ac_aux_dir=
for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do
- for ac_t in install-sh install.sh shtool; do
- if test -f "$ac_dir/$ac_t"; then
- ac_aux_dir=$ac_dir
- ac_install_sh="$ac_aux_dir/$ac_t -c"
- break 2
- fi
- done
+ 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
- as_fn_error "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5
+ as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5
fi
# These three variables are undocumented and unsupported,
@@ -3840,7 +3904,7 @@ ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5
$as_echo_n "checking for a BSD-compatible install... " >&6; }
if test -z "$INSTALL"; then
-if test "${ac_cv_path_install+set}" = set; then :
+if ${ac_cv_path_install+:} false; then :
$as_echo_n "(cached) " >&6
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -3860,7 +3924,7 @@ case $as_dir/ in #((
# by default.
for ac_prog in ginstall scoinst install; do
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then
+ if as_fn_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.
@@ -3921,7 +3985,7 @@ if test -n "$ac_tool_prefix"; then
set dummy ${ac_tool_prefix}ranlib; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_RANLIB+set}" = set; then :
+if ${ac_cv_prog_RANLIB+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$RANLIB"; then
@@ -3933,7 +3997,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -3961,7 +4025,7 @@ if test -z "$ac_cv_prog_RANLIB"; then
set dummy ranlib; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then :
+if ${ac_cv_prog_ac_ct_RANLIB+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_RANLIB"; then
@@ -3973,7 +4037,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_RANLIB="ranlib"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -4016,7 +4080,7 @@ UP_DIR=""
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for OS/2 (with EMX)" >&5
$as_echo_n "checking for OS/2 (with EMX)... " >&6; }
-if test "${xpdf_cv_sys_os2+set}" = set; then :
+if ${xpdf_cv_sys_os2+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -4047,7 +4111,7 @@ fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for DOS (with DJGPP)" >&5
$as_echo_n "checking for DOS (with DJGPP)... " >&6; }
-if test "${xpdf_cv_sys_dos+set}" = set; then :
+if ${xpdf_cv_sys_dos+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -4094,7 +4158,7 @@ if test -n "$CPP" && test -d "$CPP"; then
CPP=
fi
if test -z "$CPP"; then
- if test "${ac_cv_prog_CPP+set}" = set; then :
+ if ${ac_cv_prog_CPP+:} false; then :
$as_echo_n "(cached) " >&6
else
# Double quotes because CPP needs to be expanded
@@ -4124,7 +4188,7 @@ else
# Broken: fails on valid input.
continue
fi
-rm -f conftest.err conftest.$ac_ext
+rm -f conftest.err conftest.i conftest.$ac_ext
# OK, works on sane cases. Now check whether nonexistent headers
# can be detected and how.
@@ -4140,11 +4204,11 @@ else
ac_preproc_ok=:
break
fi
-rm -f conftest.err conftest.$ac_ext
+rm -f conftest.err conftest.i conftest.$ac_ext
done
# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
-rm -f conftest.err conftest.$ac_ext
+rm -f conftest.i conftest.err conftest.$ac_ext
if $ac_preproc_ok; then :
break
fi
@@ -4183,7 +4247,7 @@ else
# Broken: fails on valid input.
continue
fi
-rm -f conftest.err conftest.$ac_ext
+rm -f conftest.err conftest.i conftest.$ac_ext
# OK, works on sane cases. Now check whether nonexistent headers
# can be detected and how.
@@ -4199,18 +4263,18 @@ else
ac_preproc_ok=:
break
fi
-rm -f conftest.err conftest.$ac_ext
+rm -f conftest.err conftest.i conftest.$ac_ext
done
# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
-rm -f conftest.err conftest.$ac_ext
+rm -f conftest.i conftest.err conftest.$ac_ext
if $ac_preproc_ok; then :
else
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
-as_fn_error "C preprocessor \"$CPP\" fails sanity check
-See \`config.log' for more details." "$LINENO" 5; }
+as_fn_error $? "C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details" "$LINENO" 5; }
fi
ac_ext=c
@@ -4235,8 +4299,8 @@ if test "x$with_x" = xno; then
have_x=disabled
else
case $x_includes,$x_libraries in #(
- *\'*) as_fn_error "cannot use X directory names containing '" "$LINENO" 5;; #(
- *,NONE | NONE,*) if test "${ac_cv_have_x+set}" = set; then :
+ *\'*) as_fn_error $? "cannot use X directory names containing '" "$LINENO" 5;; #(
+ *,NONE | NONE,*) if ${ac_cv_have_x+:} false; then :
$as_echo_n "(cached) " >&6
else
# One or both of the vars are not set, and there is no cached value.
@@ -4253,7 +4317,7 @@ libdir:
@echo libdir='${LIBDIR}'
_ACEOF
if (export CC; ${XMKMF-xmkmf}) >/dev/null 2>/dev/null && test -f Makefile; then
- # GNU make sometimes prints "make[1]: Entering...", which would confuse us.
+ # GNU make sometimes prints "make[1]: Entering ...", which would confuse us.
for ac_var in incroot usrlibdir libdir; do
eval "ac_im_$ac_var=\`\${MAKE-make} $ac_var 2>/dev/null | sed -n 's/^$ac_var=//p'\`"
done
@@ -4339,7 +4403,7 @@ else
fi
done
fi
-rm -f conftest.err conftest.$ac_ext
+rm -f conftest.err conftest.i conftest.$ac_ext
fi # $ac_x_includes = no
if test "$ac_x_libraries" = no; then
@@ -4513,7 +4577,7 @@ if ac_fn_c_try_link "$LINENO"; then :
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dnet_ntoa in -ldnet" >&5
$as_echo_n "checking for dnet_ntoa in -ldnet... " >&6; }
-if test "${ac_cv_lib_dnet_dnet_ntoa+set}" = set; then :
+if ${ac_cv_lib_dnet_dnet_ntoa+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
@@ -4547,14 +4611,14 @@ LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dnet_dnet_ntoa" >&5
$as_echo "$ac_cv_lib_dnet_dnet_ntoa" >&6; }
-if test "x$ac_cv_lib_dnet_dnet_ntoa" = x""yes; then :
+if test "x$ac_cv_lib_dnet_dnet_ntoa" = xyes; then :
X_EXTRA_LIBS="$X_EXTRA_LIBS -ldnet"
fi
if test $ac_cv_lib_dnet_dnet_ntoa = no; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dnet_ntoa in -ldnet_stub" >&5
$as_echo_n "checking for dnet_ntoa in -ldnet_stub... " >&6; }
-if test "${ac_cv_lib_dnet_stub_dnet_ntoa+set}" = set; then :
+if ${ac_cv_lib_dnet_stub_dnet_ntoa+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
@@ -4588,7 +4652,7 @@ LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dnet_stub_dnet_ntoa" >&5
$as_echo "$ac_cv_lib_dnet_stub_dnet_ntoa" >&6; }
-if test "x$ac_cv_lib_dnet_stub_dnet_ntoa" = x""yes; then :
+if test "x$ac_cv_lib_dnet_stub_dnet_ntoa" = xyes; then :
X_EXTRA_LIBS="$X_EXTRA_LIBS -ldnet_stub"
fi
@@ -4607,14 +4671,14 @@ rm -f core conftest.err conftest.$ac_objext \
# The functions gethostbyname, getservbyname, and inet_addr are
# in -lbsd on LynxOS 3.0.1/i386, according to Lars Hecking.
ac_fn_c_check_func "$LINENO" "gethostbyname" "ac_cv_func_gethostbyname"
-if test "x$ac_cv_func_gethostbyname" = x""yes; then :
+if test "x$ac_cv_func_gethostbyname" = xyes; then :
fi
if test $ac_cv_func_gethostbyname = no; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for gethostbyname in -lnsl" >&5
$as_echo_n "checking for gethostbyname in -lnsl... " >&6; }
-if test "${ac_cv_lib_nsl_gethostbyname+set}" = set; then :
+if ${ac_cv_lib_nsl_gethostbyname+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
@@ -4648,14 +4712,14 @@ LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_nsl_gethostbyname" >&5
$as_echo "$ac_cv_lib_nsl_gethostbyname" >&6; }
-if test "x$ac_cv_lib_nsl_gethostbyname" = x""yes; then :
+if test "x$ac_cv_lib_nsl_gethostbyname" = xyes; then :
X_EXTRA_LIBS="$X_EXTRA_LIBS -lnsl"
fi
if test $ac_cv_lib_nsl_gethostbyname = no; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for gethostbyname in -lbsd" >&5
$as_echo_n "checking for gethostbyname in -lbsd... " >&6; }
-if test "${ac_cv_lib_bsd_gethostbyname+set}" = set; then :
+if ${ac_cv_lib_bsd_gethostbyname+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
@@ -4689,7 +4753,7 @@ LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_bsd_gethostbyname" >&5
$as_echo "$ac_cv_lib_bsd_gethostbyname" >&6; }
-if test "x$ac_cv_lib_bsd_gethostbyname" = x""yes; then :
+if test "x$ac_cv_lib_bsd_gethostbyname" = xyes; then :
X_EXTRA_LIBS="$X_EXTRA_LIBS -lbsd"
fi
@@ -4704,14 +4768,14 @@ fi
# must be given before -lnsl if both are needed. We assume that
# if connect needs -lnsl, so does gethostbyname.
ac_fn_c_check_func "$LINENO" "connect" "ac_cv_func_connect"
-if test "x$ac_cv_func_connect" = x""yes; then :
+if test "x$ac_cv_func_connect" = xyes; then :
fi
if test $ac_cv_func_connect = no; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for connect in -lsocket" >&5
$as_echo_n "checking for connect in -lsocket... " >&6; }
-if test "${ac_cv_lib_socket_connect+set}" = set; then :
+if ${ac_cv_lib_socket_connect+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
@@ -4745,7 +4809,7 @@ LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_socket_connect" >&5
$as_echo "$ac_cv_lib_socket_connect" >&6; }
-if test "x$ac_cv_lib_socket_connect" = x""yes; then :
+if test "x$ac_cv_lib_socket_connect" = xyes; then :
X_EXTRA_LIBS="-lsocket $X_EXTRA_LIBS"
fi
@@ -4753,14 +4817,14 @@ fi
# Guillermo Gomez says -lposix is necessary on A/UX.
ac_fn_c_check_func "$LINENO" "remove" "ac_cv_func_remove"
-if test "x$ac_cv_func_remove" = x""yes; then :
+if test "x$ac_cv_func_remove" = xyes; then :
fi
if test $ac_cv_func_remove = no; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for remove in -lposix" >&5
$as_echo_n "checking for remove in -lposix... " >&6; }
-if test "${ac_cv_lib_posix_remove+set}" = set; then :
+if ${ac_cv_lib_posix_remove+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
@@ -4794,7 +4858,7 @@ LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_posix_remove" >&5
$as_echo "$ac_cv_lib_posix_remove" >&6; }
-if test "x$ac_cv_lib_posix_remove" = x""yes; then :
+if test "x$ac_cv_lib_posix_remove" = xyes; then :
X_EXTRA_LIBS="$X_EXTRA_LIBS -lposix"
fi
@@ -4802,14 +4866,14 @@ fi
# BSDI BSD/OS 2.1 needs -lipc for XOpenDisplay.
ac_fn_c_check_func "$LINENO" "shmat" "ac_cv_func_shmat"
-if test "x$ac_cv_func_shmat" = x""yes; then :
+if test "x$ac_cv_func_shmat" = xyes; then :
fi
if test $ac_cv_func_shmat = no; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for shmat in -lipc" >&5
$as_echo_n "checking for shmat in -lipc... " >&6; }
-if test "${ac_cv_lib_ipc_shmat+set}" = set; then :
+if ${ac_cv_lib_ipc_shmat+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
@@ -4843,7 +4907,7 @@ LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ipc_shmat" >&5
$as_echo "$ac_cv_lib_ipc_shmat" >&6; }
-if test "x$ac_cv_lib_ipc_shmat" = x""yes; then :
+if test "x$ac_cv_lib_ipc_shmat" = xyes; then :
X_EXTRA_LIBS="$X_EXTRA_LIBS -lipc"
fi
@@ -4861,7 +4925,7 @@ fi
# John Interrante, Karl Berry
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for IceConnectionNumber in -lICE" >&5
$as_echo_n "checking for IceConnectionNumber in -lICE... " >&6; }
-if test "${ac_cv_lib_ICE_IceConnectionNumber+set}" = set; then :
+if ${ac_cv_lib_ICE_IceConnectionNumber+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
@@ -4895,7 +4959,7 @@ LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ICE_IceConnectionNumber" >&5
$as_echo "$ac_cv_lib_ICE_IceConnectionNumber" >&6; }
-if test "x$ac_cv_lib_ICE_IceConnectionNumber" = x""yes; then :
+if test "x$ac_cv_lib_ICE_IceConnectionNumber" = xyes; then :
X_PRE_LIBS="$X_PRE_LIBS -lSM -lICE"
fi
@@ -4908,7 +4972,7 @@ for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h; do
as_ac_Header=`$as_echo "ac_cv_header_dirent_$ac_hdr" | $as_tr_sh`
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_hdr that defines DIR" >&5
$as_echo_n "checking for $ac_hdr that defines DIR... " >&6; }
-if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then :
+if eval \${$as_ac_Header+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -4935,8 +4999,7 @@ fi
eval ac_res=\$$as_ac_Header
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
$as_echo "$ac_res" >&6; }
-eval as_val=\$$as_ac_Header
- if test "x$as_val" = x""yes; then :
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
cat >>confdefs.h <<_ACEOF
#define `$as_echo "HAVE_$ac_hdr" | $as_tr_cpp` 1
_ACEOF
@@ -4949,7 +5012,7 @@ done
if test $ac_header_dirent = dirent.h; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing opendir" >&5
$as_echo_n "checking for library containing opendir... " >&6; }
-if test "${ac_cv_search_opendir+set}" = set; then :
+if ${ac_cv_search_opendir+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_func_search_save_LIBS=$LIBS
@@ -4983,11 +5046,11 @@ for ac_lib in '' dir; do
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext
- if test "${ac_cv_search_opendir+set}" = set; then :
+ if ${ac_cv_search_opendir+:} false; then :
break
fi
done
-if test "${ac_cv_search_opendir+set}" = set; then :
+if ${ac_cv_search_opendir+:} false; then :
else
ac_cv_search_opendir=no
@@ -5006,7 +5069,7 @@ fi
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing opendir" >&5
$as_echo_n "checking for library containing opendir... " >&6; }
-if test "${ac_cv_search_opendir+set}" = set; then :
+if ${ac_cv_search_opendir+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_func_search_save_LIBS=$LIBS
@@ -5040,11 +5103,11 @@ for ac_lib in '' x; do
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext
- if test "${ac_cv_search_opendir+set}" = set; then :
+ if ${ac_cv_search_opendir+:} false; then :
break
fi
done
-if test "${ac_cv_search_opendir+set}" = set; then :
+if ${ac_cv_search_opendir+:} false; then :
else
ac_cv_search_opendir=no
@@ -5072,14 +5135,14 @@ ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
ac_fn_cxx_check_func "$LINENO" "gethostbyname" "ac_cv_func_gethostbyname"
-if test "x$ac_cv_func_gethostbyname" = x""yes; then :
+if test "x$ac_cv_func_gethostbyname" = xyes; then :
fi
if test $ac_cv_func_gethostbyname = no; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for gethostbyname in -lbsd" >&5
$as_echo_n "checking for gethostbyname in -lbsd... " >&6; }
-if test "${ac_cv_lib_bsd_gethostbyname+set}" = set; then :
+if ${ac_cv_lib_bsd_gethostbyname+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
@@ -5113,7 +5176,7 @@ LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_bsd_gethostbyname" >&5
$as_echo "$ac_cv_lib_bsd_gethostbyname" >&6; }
-if test "x$ac_cv_lib_bsd_gethostbyname" = x""yes; then :
+if test "x$ac_cv_lib_bsd_gethostbyname" = xyes; then :
X_EXTRA_LIBS="$X_EXTRA_LIBS -lbsd"
fi
@@ -5300,7 +5363,7 @@ fi
for ac_func in rewinddir
do :
ac_fn_cxx_check_func "$LINENO" "rewinddir" "ac_cv_func_rewinddir"
-if test "x$ac_cv_func_rewinddir" = x""yes; then :
+if test "x$ac_cv_func_rewinddir" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_REWINDDIR 1
_ACEOF
@@ -5311,7 +5374,7 @@ done
if test $ac_cv_func_rewinddir = no; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for rewinddir in -lcposix" >&5
$as_echo_n "checking for rewinddir in -lcposix... " >&6; }
-if test "${ac_cv_lib_cposix_rewinddir+set}" = set; then :
+if ${ac_cv_lib_cposix_rewinddir+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
@@ -5345,7 +5408,7 @@ LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_cposix_rewinddir" >&5
$as_echo "$ac_cv_lib_cposix_rewinddir" >&6; }
-if test "x$ac_cv_lib_cposix_rewinddir" = x""yes; then :
+if test "x$ac_cv_lib_cposix_rewinddir" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_LIBCPOSIX 1
_ACEOF
@@ -5359,7 +5422,7 @@ fi
for ac_func in popen
do :
ac_fn_cxx_check_func "$LINENO" "popen" "ac_cv_func_popen"
-if test "x$ac_cv_func_popen" = x""yes; then :
+if test "x$ac_cv_func_popen" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_POPEN 1
_ACEOF
@@ -5369,7 +5432,7 @@ done
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for mkstemp" >&5
$as_echo_n "checking for mkstemp... " >&6; }
-if test "${xpdf_cv_func_mkstemp+set}" = set; then :
+if ${xpdf_cv_func_mkstemp+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -5400,7 +5463,7 @@ if test "$xpdf_cv_func_mkstemp" = yes; then
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for mkstemps" >&5
$as_echo_n "checking for mkstemps... " >&6; }
-if test "${xpdf_cv_func_mkstemps+set}" = set; then :
+if ${xpdf_cv_func_mkstemps+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -5432,7 +5495,7 @@ fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether select takes fd_set arguments" >&5
$as_echo_n "checking whether select takes fd_set arguments... " >&6; }
-if test "${xpdf_cv_func_select_arg+set}" = set; then :
+if ${xpdf_cv_func_select_arg+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -5468,7 +5531,7 @@ fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for std::sort" >&5
$as_echo_n "checking for std::sort... " >&6; }
-if test "${xpdf_cv_func_std_sort+set}" = set; then :
+if ${xpdf_cv_func_std_sort+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -5516,7 +5579,7 @@ if test "$enable_largefile" != no; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for special C compiler options needed for large files" >&5
$as_echo_n "checking for special C compiler options needed for large files... " >&6; }
-if test "${ac_cv_sys_largefile_CC+set}" = set; then :
+if ${ac_cv_sys_largefile_CC+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_cv_sys_largefile_CC=no
@@ -5567,7 +5630,7 @@ $as_echo "$ac_cv_sys_largefile_CC" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for _FILE_OFFSET_BITS value needed for large files" >&5
$as_echo_n "checking for _FILE_OFFSET_BITS value needed for large files... " >&6; }
-if test "${ac_cv_sys_file_offset_bits+set}" = set; then :
+if ${ac_cv_sys_file_offset_bits+:} false; then :
$as_echo_n "(cached) " >&6
else
while :; do
@@ -5636,7 +5699,7 @@ rm -rf conftest*
if test $ac_cv_sys_file_offset_bits = unknown; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for _LARGE_FILES value needed for large files" >&5
$as_echo_n "checking for _LARGE_FILES value needed for large files... " >&6; }
-if test "${ac_cv_sys_large_files+set}" = set; then :
+if ${ac_cv_sys_large_files+:} false; then :
$as_echo_n "(cached) " >&6
else
while :; do
@@ -5703,11 +5766,13 @@ _ACEOF
esac
rm -rf conftest*
fi
+
+
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for _LARGEFILE_SOURCE value needed for large files" >&5
$as_echo_n "checking for _LARGEFILE_SOURCE value needed for large files... " >&6; }
-if test "${ac_cv_sys_largefile_source+set}" = set; then :
+if ${ac_cv_sys_largefile_source+:} false; then :
$as_echo_n "(cached) " >&6
else
while :; do
@@ -5776,7 +5841,7 @@ fi
for ac_func in fseek64
do :
ac_fn_c_check_func "$LINENO" "fseek64" "ac_cv_func_fseek64"
-if test "x$ac_cv_func_fseek64" = x""yes; then :
+if test "x$ac_cv_func_fseek64" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_FSEEK64 1
_ACEOF
@@ -5789,7 +5854,7 @@ done
for ac_func in ftell64
do :
ac_fn_c_check_func "$LINENO" "ftell64" "ac_cv_func_ftell64"
-if test "x$ac_cv_func_ftell64" = x""yes; then :
+if test "x$ac_cv_func_ftell64" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_FTELL64 1
_ACEOF
@@ -5807,7 +5872,7 @@ fi
if test -z "$no_x"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5
$as_echo_n "checking for grep that handles long lines and -e... " >&6; }
-if test "${ac_cv_path_GREP+set}" = set; then :
+if ${ac_cv_path_GREP+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -z "$GREP"; then
@@ -5821,7 +5886,7 @@ do
for ac_prog in grep ggrep; do
for ac_exec_ext in '' $ac_executable_extensions; do
ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
- { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue
+ as_fn_executable_p "$ac_path_GREP" || continue
# Check for GNU ac_path_GREP and select it if it is found.
# Check for GNU $ac_path_GREP
case `"$ac_path_GREP" --version 2>&1` in
@@ -5856,7 +5921,7 @@ esac
done
IFS=$as_save_IFS
if test -z "$ac_cv_path_GREP"; then
- as_fn_error "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+ as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
fi
else
ac_cv_path_GREP=$GREP
@@ -5870,7 +5935,7 @@ $as_echo "$ac_cv_path_GREP" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5
$as_echo_n "checking for egrep... " >&6; }
-if test "${ac_cv_path_EGREP+set}" = set; then :
+if ${ac_cv_path_EGREP+:} false; then :
$as_echo_n "(cached) " >&6
else
if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
@@ -5887,7 +5952,7 @@ do
for ac_prog in egrep; do
for ac_exec_ext in '' $ac_executable_extensions; do
ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
- { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue
+ as_fn_executable_p "$ac_path_EGREP" || continue
# Check for GNU ac_path_EGREP and select it if it is found.
# Check for GNU $ac_path_EGREP
case `"$ac_path_EGREP" --version 2>&1` in
@@ -5922,7 +5987,7 @@ esac
done
IFS=$as_save_IFS
if test -z "$ac_cv_path_EGREP"; then
- as_fn_error "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+ as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
fi
else
ac_cv_path_EGREP=$EGREP
@@ -5937,7 +6002,7 @@ $as_echo "$ac_cv_path_EGREP" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
$as_echo_n "checking for ANSI C header files... " >&6; }
-if test "${ac_cv_header_stdc+set}" = set; then :
+if ${ac_cv_header_stdc+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -6054,8 +6119,7 @@ do :
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default
"
-eval as_val=\$$as_ac_Header
- if test "x$as_val" = x""yes; then :
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
cat >>confdefs.h <<_ACEOF
#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
_ACEOF
@@ -6091,7 +6155,7 @@ fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use Xpm library" >&5
$as_echo_n "checking whether to use Xpm library... " >&6; }
-if test "${smr_cv_with_Xpm_library+set}" = set; then :
+if ${smr_cv_with_Xpm_library+:} false; then :
$as_echo_n "(cached) " >&6
else
smr_cv_with_Xpm_library=maybe
@@ -6115,7 +6179,7 @@ $as_echo "$smr_cv_with_Xpm_library" >&6; }
elif test -d "$smr_cv_with_Xpm_library"; then
Xpm_LIBS="-L$smr_cv_with_Xpm_library -lXpm"
else
- as_fn_error "argument must be boolean, file, or directory" "$LINENO" 5
+ as_fn_error $? "argument must be boolean, file, or directory" "$LINENO" 5
fi
with_Xpm=yes
;;
@@ -6147,7 +6211,7 @@ fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking where to find the Xpm header files" >&5
$as_echo_n "checking where to find the Xpm header files... " >&6; }
-if test "${smr_cv_with_Xpm_includes+set}" = set; then :
+if ${smr_cv_with_Xpm_includes+:} false; then :
$as_echo_n "(cached) " >&6
else
smr_cv_with_Xpm_includes=
@@ -6159,7 +6223,7 @@ $as_echo "$smr_cv_with_Xpm_includes" >&6; }
if test -d "$smr_cv_with_Xpm_includes"; then
Xpm_CFLAGS="-I$smr_cv_with_Xpm_includes"
else
- as_fn_error "argument must be a directory" "$LINENO" 5
+ as_fn_error $? "argument must be a directory" "$LINENO" 5
fi
else
Xpm_CFLAGS=
@@ -6172,7 +6236,7 @@ $as_echo "$smr_cv_with_Xpm_includes" >&6; }
for ac_header in X11/xpm.h
do :
ac_fn_c_check_header_mongrel "$LINENO" "X11/xpm.h" "ac_cv_header_X11_xpm_h" "$ac_includes_default"
-if test "x$ac_cv_header_X11_xpm_h" = x""yes; then :
+if test "x$ac_cv_header_X11_xpm_h" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_X11_XPM_H 1
_ACEOF
@@ -6202,7 +6266,7 @@ done
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for XpmCreatePixmapFromData in -lXpm" >&5
$as_echo_n "checking for XpmCreatePixmapFromData in -lXpm... " >&6; }
-if test "${ac_cv_lib_Xpm_XpmCreatePixmapFromData+set}" = set; then :
+if ${ac_cv_lib_Xpm_XpmCreatePixmapFromData+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
@@ -6236,7 +6300,7 @@ LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_Xpm_XpmCreatePixmapFromData" >&5
$as_echo "$ac_cv_lib_Xpm_XpmCreatePixmapFromData" >&6; }
-if test "x$ac_cv_lib_Xpm_XpmCreatePixmapFromData" = x""yes; then :
+if test "x$ac_cv_lib_Xpm_XpmCreatePixmapFromData" = xyes; then :
smr_have_Xpm_library=yes
else
smr_have_Xpm_library=no
@@ -6302,7 +6366,7 @@ fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use Xext library" >&5
$as_echo_n "checking whether to use Xext library... " >&6; }
-if test "${smr_cv_with_Xext_library+set}" = set; then :
+if ${smr_cv_with_Xext_library+:} false; then :
$as_echo_n "(cached) " >&6
else
smr_cv_with_Xext_library=maybe
@@ -6326,7 +6390,7 @@ $as_echo "$smr_cv_with_Xext_library" >&6; }
elif test -d "$smr_cv_with_Xext_library"; then
Xext_LIBS="-L$smr_cv_with_Xext_library -lXext"
else
- as_fn_error "argument must be boolean, file, or directory" "$LINENO" 5
+ as_fn_error $? "argument must be boolean, file, or directory" "$LINENO" 5
fi
with_Xext=yes
;;
@@ -6358,7 +6422,7 @@ fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking where to find the Xext header files" >&5
$as_echo_n "checking where to find the Xext header files... " >&6; }
-if test "${smr_cv_with_Xext_includes+set}" = set; then :
+if ${smr_cv_with_Xext_includes+:} false; then :
$as_echo_n "(cached) " >&6
else
smr_cv_with_Xext_includes=
@@ -6370,7 +6434,7 @@ $as_echo "$smr_cv_with_Xext_includes" >&6; }
if test -d "$smr_cv_with_Xext_includes"; then
Xext_CFLAGS="-I$smr_cv_with_Xext_includes"
else
- as_fn_error "argument must be a directory" "$LINENO" 5
+ as_fn_error $? "argument must be a directory" "$LINENO" 5
fi
else
Xext_CFLAGS=
@@ -6383,7 +6447,7 @@ $as_echo "$smr_cv_with_Xext_includes" >&6; }
for ac_header in X11/Xlib.h
do :
ac_fn_c_check_header_mongrel "$LINENO" "X11/Xlib.h" "ac_cv_header_X11_Xlib_h" "$ac_includes_default"
-if test "x$ac_cv_header_X11_Xlib_h" = x""yes; then :
+if test "x$ac_cv_header_X11_Xlib_h" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_X11_XLIB_H 1
_ACEOF
@@ -6413,7 +6477,7 @@ done
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for XextAddDisplay in -lXext" >&5
$as_echo_n "checking for XextAddDisplay in -lXext... " >&6; }
-if test "${ac_cv_lib_Xext_XextAddDisplay+set}" = set; then :
+if ${ac_cv_lib_Xext_XextAddDisplay+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
@@ -6447,7 +6511,7 @@ LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_Xext_XextAddDisplay" >&5
$as_echo "$ac_cv_lib_Xext_XextAddDisplay" >&6; }
-if test "x$ac_cv_lib_Xext_XextAddDisplay" = x""yes; then :
+if test "x$ac_cv_lib_Xext_XextAddDisplay" = xyes; then :
smr_have_Xext_library=yes
else
smr_have_Xext_library=no
@@ -6510,7 +6574,7 @@ fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use Xp library" >&5
$as_echo_n "checking whether to use Xp library... " >&6; }
-if test "${smr_cv_with_Xp_library+set}" = set; then :
+if ${smr_cv_with_Xp_library+:} false; then :
$as_echo_n "(cached) " >&6
else
smr_cv_with_Xp_library=maybe
@@ -6534,7 +6598,7 @@ $as_echo "$smr_cv_with_Xp_library" >&6; }
elif test -d "$smr_cv_with_Xp_library"; then
Xp_LIBS="-L$smr_cv_with_Xp_library -lXp"
else
- as_fn_error "argument must be boolean, file, or directory" "$LINENO" 5
+ as_fn_error $? "argument must be boolean, file, or directory" "$LINENO" 5
fi
with_Xp=yes
;;
@@ -6566,7 +6630,7 @@ fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking where to find the Xp header files" >&5
$as_echo_n "checking where to find the Xp header files... " >&6; }
-if test "${smr_cv_with_Xp_includes+set}" = set; then :
+if ${smr_cv_with_Xp_includes+:} false; then :
$as_echo_n "(cached) " >&6
else
smr_cv_with_Xp_includes=
@@ -6578,7 +6642,7 @@ $as_echo "$smr_cv_with_Xp_includes" >&6; }
if test -d "$smr_cv_with_Xp_includes"; then
Xp_CFLAGS="-I$smr_cv_with_Xp_includes"
else
- as_fn_error "argument must be a directory" "$LINENO" 5
+ as_fn_error $? "argument must be a directory" "$LINENO" 5
fi
else
Xp_CFLAGS=
@@ -6591,7 +6655,7 @@ $as_echo "$smr_cv_with_Xp_includes" >&6; }
for ac_header in X11/extensions/Print.h
do :
ac_fn_c_check_header_mongrel "$LINENO" "X11/extensions/Print.h" "ac_cv_header_X11_extensions_Print_h" "$ac_includes_default"
-if test "x$ac_cv_header_X11_extensions_Print_h" = x""yes; then :
+if test "x$ac_cv_header_X11_extensions_Print_h" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_X11_EXTENSIONS_PRINT_H 1
_ACEOF
@@ -6621,7 +6685,7 @@ done
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for XpStartPage in -lXp" >&5
$as_echo_n "checking for XpStartPage in -lXp... " >&6; }
-if test "${ac_cv_lib_Xp_XpStartPage+set}" = set; then :
+if ${ac_cv_lib_Xp_XpStartPage+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
@@ -6655,7 +6719,7 @@ LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_Xp_XpStartPage" >&5
$as_echo "$ac_cv_lib_Xp_XpStartPage" >&6; }
-if test "x$ac_cv_lib_Xp_XpStartPage" = x""yes; then :
+if test "x$ac_cv_lib_Xp_XpStartPage" = xyes; then :
smr_have_Xp_library=yes
else
smr_have_Xp_library=no
@@ -6718,7 +6782,7 @@ fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use Xt library" >&5
$as_echo_n "checking whether to use Xt library... " >&6; }
-if test "${smr_cv_with_Xt_library+set}" = set; then :
+if ${smr_cv_with_Xt_library+:} false; then :
$as_echo_n "(cached) " >&6
else
smr_cv_with_Xt_library=maybe
@@ -6742,7 +6806,7 @@ $as_echo "$smr_cv_with_Xt_library" >&6; }
elif test -d "$smr_cv_with_Xt_library"; then
Xt_LIBS="-L$smr_cv_with_Xt_library -lXt"
else
- as_fn_error "argument must be boolean, file, or directory" "$LINENO" 5
+ as_fn_error $? "argument must be boolean, file, or directory" "$LINENO" 5
fi
with_Xt=yes
;;
@@ -6774,7 +6838,7 @@ fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking where to find the Xt header files" >&5
$as_echo_n "checking where to find the Xt header files... " >&6; }
-if test "${smr_cv_with_Xt_includes+set}" = set; then :
+if ${smr_cv_with_Xt_includes+:} false; then :
$as_echo_n "(cached) " >&6
else
smr_cv_with_Xt_includes=
@@ -6786,7 +6850,7 @@ $as_echo "$smr_cv_with_Xt_includes" >&6; }
if test -d "$smr_cv_with_Xt_includes"; then
Xt_CFLAGS="-I$smr_cv_with_Xt_includes"
else
- as_fn_error "argument must be a directory" "$LINENO" 5
+ as_fn_error $? "argument must be a directory" "$LINENO" 5
fi
else
Xt_CFLAGS=
@@ -6799,7 +6863,7 @@ $as_echo "$smr_cv_with_Xt_includes" >&6; }
for ac_header in X11/Intrinsic.h
do :
ac_fn_c_check_header_mongrel "$LINENO" "X11/Intrinsic.h" "ac_cv_header_X11_Intrinsic_h" "$ac_includes_default"
-if test "x$ac_cv_header_X11_Intrinsic_h" = x""yes; then :
+if test "x$ac_cv_header_X11_Intrinsic_h" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_X11_INTRINSIC_H 1
_ACEOF
@@ -6829,7 +6893,7 @@ done
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for XtAppInitialize in -lXt" >&5
$as_echo_n "checking for XtAppInitialize in -lXt... " >&6; }
-if test "${ac_cv_lib_Xt_XtAppInitialize+set}" = set; then :
+if ${ac_cv_lib_Xt_XtAppInitialize+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
@@ -6863,7 +6927,7 @@ LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_Xt_XtAppInitialize" >&5
$as_echo "$ac_cv_lib_Xt_XtAppInitialize" >&6; }
-if test "x$ac_cv_lib_Xt_XtAppInitialize" = x""yes; then :
+if test "x$ac_cv_lib_Xt_XtAppInitialize" = xyes; then :
smr_have_Xt_library=yes
else
smr_have_Xt_library=no
@@ -6926,7 +6990,7 @@ fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use Xm library" >&5
$as_echo_n "checking whether to use Xm library... " >&6; }
-if test "${smr_cv_with_Xm_library+set}" = set; then :
+if ${smr_cv_with_Xm_library+:} false; then :
$as_echo_n "(cached) " >&6
else
smr_cv_with_Xm_library=maybe
@@ -6950,7 +7014,7 @@ $as_echo "$smr_cv_with_Xm_library" >&6; }
elif test -d "$smr_cv_with_Xm_library"; then
Xm_LIBS="-L$smr_cv_with_Xm_library -lXm"
else
- as_fn_error "argument must be boolean, file, or directory" "$LINENO" 5
+ as_fn_error $? "argument must be boolean, file, or directory" "$LINENO" 5
fi
with_Xm=yes
;;
@@ -6982,7 +7046,7 @@ fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking where to find the Xm header files" >&5
$as_echo_n "checking where to find the Xm header files... " >&6; }
-if test "${smr_cv_with_Xm_includes+set}" = set; then :
+if ${smr_cv_with_Xm_includes+:} false; then :
$as_echo_n "(cached) " >&6
else
smr_cv_with_Xm_includes=
@@ -6994,7 +7058,7 @@ $as_echo "$smr_cv_with_Xm_includes" >&6; }
if test -d "$smr_cv_with_Xm_includes"; then
Xm_CFLAGS="-I$smr_cv_with_Xm_includes"
else
- as_fn_error "argument must be a directory" "$LINENO" 5
+ as_fn_error $? "argument must be a directory" "$LINENO" 5
fi
else
Xm_CFLAGS=
@@ -7007,7 +7071,7 @@ $as_echo "$smr_cv_with_Xm_includes" >&6; }
for ac_header in Xm/XmAll.h
do :
ac_fn_c_check_header_mongrel "$LINENO" "Xm/XmAll.h" "ac_cv_header_Xm_XmAll_h" "$ac_includes_default"
-if test "x$ac_cv_header_Xm_XmAll_h" = x""yes; then :
+if test "x$ac_cv_header_Xm_XmAll_h" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_XM_XMALL_H 1
_ACEOF
@@ -7037,7 +7101,7 @@ done
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for XmCreateForm in -lXm" >&5
$as_echo_n "checking for XmCreateForm in -lXm... " >&6; }
-if test "${ac_cv_lib_Xm_XmCreateForm+set}" = set; then :
+if ${ac_cv_lib_Xm_XmCreateForm+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
@@ -7071,7 +7135,7 @@ LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_Xm_XmCreateForm" >&5
$as_echo "$ac_cv_lib_Xm_XmCreateForm" >&6; }
-if test "x$ac_cv_lib_Xm_XmCreateForm" = x""yes; then :
+if test "x$ac_cv_lib_Xm_XmCreateForm" = xyes; then :
smr_have_Xm_library=yes
else
smr_have_Xm_library=no
@@ -7134,7 +7198,7 @@ fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use Sgm library" >&5
$as_echo_n "checking whether to use Sgm library... " >&6; }
-if test "${smr_cv_with_Sgm_library+set}" = set; then :
+if ${smr_cv_with_Sgm_library+:} false; then :
$as_echo_n "(cached) " >&6
else
smr_cv_with_Sgm_library=maybe
@@ -7158,7 +7222,7 @@ $as_echo "$smr_cv_with_Sgm_library" >&6; }
elif test -d "$smr_cv_with_Sgm_library"; then
Sgm_LIBS="-L$smr_cv_with_Sgm_library -lSgm"
else
- as_fn_error "argument must be boolean, file, or directory" "$LINENO" 5
+ as_fn_error $? "argument must be boolean, file, or directory" "$LINENO" 5
fi
with_Sgm=yes
;;
@@ -7190,7 +7254,7 @@ fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking where to find the Sgm header files" >&5
$as_echo_n "checking where to find the Sgm header files... " >&6; }
-if test "${smr_cv_with_Sgm_includes+set}" = set; then :
+if ${smr_cv_with_Sgm_includes+:} false; then :
$as_echo_n "(cached) " >&6
else
smr_cv_with_Sgm_includes=
@@ -7202,7 +7266,7 @@ $as_echo "$smr_cv_with_Sgm_includes" >&6; }
if test -d "$smr_cv_with_Sgm_includes"; then
Sgm_CFLAGS="-I$smr_cv_with_Sgm_includes"
else
- as_fn_error "argument must be a directory" "$LINENO" 5
+ as_fn_error $? "argument must be a directory" "$LINENO" 5
fi
else
Sgm_CFLAGS=
@@ -7215,7 +7279,7 @@ $as_echo "$smr_cv_with_Sgm_includes" >&6; }
for ac_header in Sgm/HPanedW.h
do :
ac_fn_c_check_header_mongrel "$LINENO" "Sgm/HPanedW.h" "ac_cv_header_Sgm_HPanedW_h" "$ac_includes_default"
-if test "x$ac_cv_header_Sgm_HPanedW_h" = x""yes; then :
+if test "x$ac_cv_header_Sgm_HPanedW_h" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_SGM_HPANEDW_H 1
_ACEOF
@@ -7245,7 +7309,7 @@ done
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for SgCreateHorzPanedWindow in -lSgm" >&5
$as_echo_n "checking for SgCreateHorzPanedWindow in -lSgm... " >&6; }
-if test "${ac_cv_lib_Sgm_SgCreateHorzPanedWindow+set}" = set; then :
+if ${ac_cv_lib_Sgm_SgCreateHorzPanedWindow+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
@@ -7279,7 +7343,7 @@ LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_Sgm_SgCreateHorzPanedWindow" >&5
$as_echo "$ac_cv_lib_Sgm_SgCreateHorzPanedWindow" >&6; }
-if test "x$ac_cv_lib_Sgm_SgCreateHorzPanedWindow" = x""yes; then :
+if test "x$ac_cv_lib_Sgm_SgCreateHorzPanedWindow" = xyes; then :
smr_have_Sgm_library=yes
else
smr_have_Sgm_library=no
@@ -7320,7 +7384,7 @@ $as_echo "$as_me: WARNING: requested Sgm library not found!" >&2;}
if test "x$smr_have_Xt_library" = xyes; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for XtAppSetExitFlag in -lXt" >&5
$as_echo_n "checking for XtAppSetExitFlag in -lXt... " >&6; }
-if test "${ac_cv_lib_Xt_XtAppSetExitFlag+set}" = set; then :
+if ${ac_cv_lib_Xt_XtAppSetExitFlag+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
@@ -7354,7 +7418,7 @@ LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_Xt_XtAppSetExitFlag" >&5
$as_echo "$ac_cv_lib_Xt_XtAppSetExitFlag" >&6; }
-if test "x$ac_cv_lib_Xt_XtAppSetExitFlag" = x""yes; then :
+if test "x$ac_cv_lib_Xt_XtAppSetExitFlag" = xyes; then :
$as_echo "#define HAVE_XTAPPSETEXITFLAG 1" >>confdefs.h
fi
@@ -7362,18 +7426,6 @@ fi
fi
fi
-#dnl ##### Check for t1lib.
-#smr_CHECK_LIB(t1, t1, [Type 1 font rasterizer],
-# T1_InitLib, t1lib.h,
-# -lm, $X_CFLAGS)
-# t1lib has some potential security holes, and hasn't been updated in
-# years -- if you really want to use it, uncomment the preceding lines,
-# and comment out the next two lines
-t1_LIBS=""
-t1_CFLAGS=""
-
-
-
@@ -7400,7 +7452,7 @@ fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use freetype2 library" >&5
$as_echo_n "checking whether to use freetype2 library... " >&6; }
-if test "${smr_cv_with_freetype2_library+set}" = set; then :
+if ${smr_cv_with_freetype2_library+:} false; then :
$as_echo_n "(cached) " >&6
else
smr_cv_with_freetype2_library=maybe
@@ -7424,7 +7476,7 @@ $as_echo "$smr_cv_with_freetype2_library" >&6; }
elif test -d "$smr_cv_with_freetype2_library"; then
freetype2_LIBS="-L$smr_cv_with_freetype2_library -lfreetype"
else
- as_fn_error "argument must be boolean, file, or directory" "$LINENO" 5
+ as_fn_error $? "argument must be boolean, file, or directory" "$LINENO" 5
fi
with_freetype2=yes
;;
@@ -7456,7 +7508,7 @@ fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking where to find the freetype2 header files" >&5
$as_echo_n "checking where to find the freetype2 header files... " >&6; }
-if test "${smr_cv_with_freetype2_includes+set}" = set; then :
+if ${smr_cv_with_freetype2_includes+:} false; then :
$as_echo_n "(cached) " >&6
else
smr_cv_with_freetype2_includes=
@@ -7468,7 +7520,7 @@ $as_echo "$smr_cv_with_freetype2_includes" >&6; }
if test -d "$smr_cv_with_freetype2_includes"; then
freetype2_CFLAGS="-I$smr_cv_with_freetype2_includes"
else
- as_fn_error "argument must be a directory" "$LINENO" 5
+ as_fn_error $? "argument must be a directory" "$LINENO" 5
fi
else
freetype2_CFLAGS=
@@ -7481,7 +7533,7 @@ $as_echo "$smr_cv_with_freetype2_includes" >&6; }
for ac_header in ft2build.h
do :
ac_fn_c_check_header_mongrel "$LINENO" "ft2build.h" "ac_cv_header_ft2build_h" "$ac_includes_default"
-if test "x$ac_cv_header_ft2build_h" = x""yes; then :
+if test "x$ac_cv_header_ft2build_h" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_FT2BUILD_H 1
_ACEOF
@@ -7511,7 +7563,7 @@ done
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for FT_Get_Name_Index in -lfreetype" >&5
$as_echo_n "checking for FT_Get_Name_Index in -lfreetype... " >&6; }
-if test "${ac_cv_lib_freetype_FT_Get_Name_Index+set}" = set; then :
+if ${ac_cv_lib_freetype_FT_Get_Name_Index+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
@@ -7545,7 +7597,7 @@ LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_freetype_FT_Get_Name_Index" >&5
$as_echo "$ac_cv_lib_freetype_FT_Get_Name_Index" >&6; }
-if test "x$ac_cv_lib_freetype_FT_Get_Name_Index" = x""yes; then :
+if test "x$ac_cv_lib_freetype_FT_Get_Name_Index" = xyes; then :
smr_have_freetype2_library=yes
else
smr_have_freetype2_library=no
@@ -7607,6 +7659,215 @@ fi
+# Check whether --with-libpng-library was given.
+if test "${with_libpng_library+set}" = set; then :
+ withval=$with_libpng_library; smr_cv_with_libpng_library=$withval
+fi
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use libpng library" >&5
+$as_echo_n "checking whether to use libpng library... " >&6; }
+if ${smr_cv_with_libpng_library+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ smr_cv_with_libpng_library=maybe
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $smr_cv_with_libpng_library" >&5
+$as_echo "$smr_cv_with_libpng_library" >&6; }
+
+
+ case x"$smr_cv_with_libpng_library" in
+ xyes | xmaybe)
+ libpng_LIBS="-lpng"
+ with_libpng=$smr_cv_with_libpng_library
+ ;;
+ xno)
+ libpng_LIBS=
+ with_libpng=no
+ ;;
+ *)
+ if test -f "$smr_cv_with_libpng_library"; then
+ libpng_LIBS=$smr_cv_with_libpng_library
+ elif test -d "$smr_cv_with_libpng_library"; then
+ libpng_LIBS="-L$smr_cv_with_libpng_library -lpng"
+ else
+ as_fn_error $? "argument must be boolean, file, or directory" "$LINENO" 5
+ fi
+ with_libpng=yes
+ ;;
+ esac
+
+
+
+
+
+
+
+ if test ! x"$with_libpng" = xno; then
+
+ # If we got this far, then the user didn't explicitly ask not to use
+ # the library.
+
+
+
+
+
+
+
+
+# Check whether --with-libpng-includes was given.
+if test "${with_libpng_includes+set}" = set; then :
+ withval=$with_libpng_includes; smr_cv_with_libpng_includes=$withval
+fi
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking where to find the libpng header files" >&5
+$as_echo_n "checking where to find the libpng header files... " >&6; }
+if ${smr_cv_with_libpng_includes+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ smr_cv_with_libpng_includes=
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $smr_cv_with_libpng_includes" >&5
+$as_echo "$smr_cv_with_libpng_includes" >&6; }
+
+ if test ! x"$smr_cv_with_libpng_includes" = x; then
+ if test -d "$smr_cv_with_libpng_includes"; then
+ libpng_CFLAGS="-I$smr_cv_with_libpng_includes"
+ else
+ as_fn_error $? "argument must be a directory" "$LINENO" 5
+ fi
+ else
+ libpng_CFLAGS=
+ fi
+
+ smr_test_CPPFLAGS="${CPPFLAGS+set}"
+ smr_save_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $libpng_CFLAGS "
+
+ for ac_header in png.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "png.h" "ac_cv_header_png_h" "$ac_includes_default"
+if test "x$ac_cv_header_png_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_PNG_H 1
+_ACEOF
+ smr_have_libpng_header=yes
+else
+ smr_have_libpng_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_libpng_header != no; then
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for png_write_row in -lpng" >&5
+$as_echo_n "checking for png_write_row in -lpng... " >&6; }
+if ${ac_cv_lib_png_png_write_row+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpng $libpng_CFLAGS $libpng_LIBS -lz $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char png_write_row ();
+int
+main ()
+{
+return png_write_row ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_png_png_write_row=yes
+else
+ ac_cv_lib_png_png_write_row=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_png_png_write_row" >&5
+$as_echo "$ac_cv_lib_png_png_write_row" >&6; }
+if test "x$ac_cv_lib_png_png_write_row" = xyes; then :
+ smr_have_libpng_library=yes
+else
+ smr_have_libpng_library=no
+fi
+
+ fi
+
+ if test x"$smr_have_libpng_library" = xyes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: using libpng library" >&5
+$as_echo "using libpng library" >&6; }
+ else
+ libpng_LIBS=
+ libpng_CFLAGS=
+
+ if test x"$with_libpng" = xmaybe; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: not using libpng library" >&5
+$as_echo "not using libpng library" >&6; }
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: requested libpng library not found!" >&5
+$as_echo "$as_me: WARNING: requested libpng library not found!" >&2;}
+ fi
+ fi
+ fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# Check whether --with-libpaper-library was given.
if test "${with_libpaper_library+set}" = set; then :
withval=$with_libpaper_library; smr_cv_with_libpaper_library=$withval
@@ -7615,7 +7876,7 @@ fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use libpaper library" >&5
$as_echo_n "checking whether to use libpaper library... " >&6; }
-if test "${smr_cv_with_libpaper_library+set}" = set; then :
+if ${smr_cv_with_libpaper_library+:} false; then :
$as_echo_n "(cached) " >&6
else
smr_cv_with_libpaper_library=maybe
@@ -7639,7 +7900,7 @@ $as_echo "$smr_cv_with_libpaper_library" >&6; }
elif test -d "$smr_cv_with_libpaper_library"; then
libpaper_LIBS="-L$smr_cv_with_libpaper_library -lpaper"
else
- as_fn_error "argument must be boolean, file, or directory" "$LINENO" 5
+ as_fn_error $? "argument must be boolean, file, or directory" "$LINENO" 5
fi
with_libpaper=yes
;;
@@ -7671,7 +7932,7 @@ fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking where to find the libpaper header files" >&5
$as_echo_n "checking where to find the libpaper header files... " >&6; }
-if test "${smr_cv_with_libpaper_includes+set}" = set; then :
+if ${smr_cv_with_libpaper_includes+:} false; then :
$as_echo_n "(cached) " >&6
else
smr_cv_with_libpaper_includes=
@@ -7683,7 +7944,7 @@ $as_echo "$smr_cv_with_libpaper_includes" >&6; }
if test -d "$smr_cv_with_libpaper_includes"; then
libpaper_CFLAGS="-I$smr_cv_with_libpaper_includes"
else
- as_fn_error "argument must be a directory" "$LINENO" 5
+ as_fn_error $? "argument must be a directory" "$LINENO" 5
fi
else
libpaper_CFLAGS=
@@ -7696,7 +7957,7 @@ $as_echo "$smr_cv_with_libpaper_includes" >&6; }
for ac_header in paper.h
do :
ac_fn_c_check_header_mongrel "$LINENO" "paper.h" "ac_cv_header_paper_h" "$ac_includes_default"
-if test "x$ac_cv_header_paper_h" = x""yes; then :
+if test "x$ac_cv_header_paper_h" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_PAPER_H 1
_ACEOF
@@ -7726,7 +7987,7 @@ done
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for paperinit in -lpaper" >&5
$as_echo_n "checking for paperinit in -lpaper... " >&6; }
-if test "${ac_cv_lib_paper_paperinit+set}" = set; then :
+if ${ac_cv_lib_paper_paperinit+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
@@ -7760,7 +8021,7 @@ LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_paper_paperinit" >&5
$as_echo "$ac_cv_lib_paper_paperinit" >&6; }
-if test "x$ac_cv_lib_paper_paperinit" = x""yes; then :
+if test "x$ac_cv_lib_paper_paperinit" = xyes; then :
smr_have_libpaper_library=yes
else
smr_have_libpaper_library=no
@@ -7808,6 +8069,11 @@ fi
+EXTRA_LIBS=
+EXTRA_CFLAGS=
+
+
+
ac_config_files="$ac_config_files Makefile goo/Makefile fofi/Makefile splash/Makefile xpdf/Makefile"
cat >confcache <<\_ACEOF
@@ -7874,10 +8140,21 @@ $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&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" &&
+ if test "x$cache_file" != "x/dev/null"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
$as_echo "$as_me: updating cache $cache_file" >&6;}
- cat confcache >$cache_file
+ if test ! -f "$cache_file" || test -h "$cache_file"; then
+ cat confcache >"$cache_file"
+ else
+ case $cache_file in #(
+ */* | ?:*)
+ mv -f confcache "$cache_file"$$ &&
+ mv -f "$cache_file"$$ "$cache_file" ;; #(
+ *)
+ mv -f confcache "$cache_file" ;;
+ esac
+ fi
+ fi
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}
@@ -7893,6 +8170,7 @@ DEFS=-DHAVE_CONFIG_H
ac_libobjs=
ac_ltlibobjs=
+U=
for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
# 1. Remove the extension, and $U if already installed.
ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
@@ -7908,7 +8186,7 @@ LTLIBOBJS=$ac_ltlibobjs
-: ${CONFIG_STATUS=./config.status}
+: "${CONFIG_STATUS=./config.status}"
ac_write_fail=0
ac_clean_files_save=$ac_clean_files
ac_clean_files="$ac_clean_files $CONFIG_STATUS"
@@ -8009,6 +8287,7 @@ fi
IFS=" "" $as_nl"
# Find who we are. Look in the path if we contain no directory separator.
+as_myself=
case $0 in #((
*[\\/]* ) as_myself=$0 ;;
*) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -8054,19 +8333,19 @@ export LANGUAGE
(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
-# as_fn_error ERROR [LINENO LOG_FD]
-# ---------------------------------
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
-# script with status $?, using 1 if that was 0.
+# script with STATUS, using 1 if that was 0.
as_fn_error ()
{
- as_status=$?; test $as_status -eq 0 && as_status=1
- if test "$3"; then
- as_lineno=${as_lineno-"$2"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
- $as_echo "$as_me:${as_lineno-$LINENO}: error: $1" >&$3
+ as_status=$1; test $as_status -eq 0 && as_status=1
+ if test "$4"; then
+ as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
fi
- $as_echo "$as_me: error: $1" >&2
+ $as_echo "$as_me: error: $2" >&2
as_fn_exit $as_status
} # as_fn_error
@@ -8204,16 +8483,16 @@ if (echo >conf$$.file) 2>/dev/null; then
# ... but there are two gotchas:
# 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
# 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
- # In both cases, we have to default to `cp -p'.
+ # In both cases, we have to default to `cp -pR'.
ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
- as_ln_s='cp -p'
+ as_ln_s='cp -pR'
elif ln conf$$.file conf$$ 2>/dev/null; then
as_ln_s=ln
else
- as_ln_s='cp -p'
+ as_ln_s='cp -pR'
fi
else
- as_ln_s='cp -p'
+ as_ln_s='cp -pR'
fi
rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
rmdir conf$$.dir 2>/dev/null
@@ -8262,7 +8541,7 @@ $as_echo X"$as_dir" |
test -d "$as_dir" && break
done
test -z "$as_dirs" || eval "mkdir $as_dirs"
- } || test -d "$as_dir" || as_fn_error "cannot create directory $as_dir"
+ } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
} # as_fn_mkdir_p
@@ -8273,28 +8552,16 @@ else
as_mkdir_p=false
fi
-if test -x / >/dev/null 2>&1; then
- as_test_x='test -x'
-else
- if ls -dL / >/dev/null 2>&1; then
- as_ls_L_option=L
- else
- as_ls_L_option=
- fi
- as_test_x='
- eval sh -c '\''
- if test -d "$1"; then
- test -d "$1/.";
- else
- case $1 in #(
- -*)set "./$1";;
- esac;
- case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #((
- ???[sx]*):;;*)false;;esac;fi
- '\'' sh
- '
-fi
-as_executable_p=$as_test_x
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+ test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
# 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'"
@@ -8316,7 +8583,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# values after options handling.
ac_log="
This file was extended by $as_me, which was
-generated by GNU Autoconf 2.65. Invocation command line was
+generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
CONFIG_HEADERS = $CONFIG_HEADERS
@@ -8378,10 +8645,10 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
config.status
-configured by $0, generated by GNU Autoconf 2.65,
+configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
-Copyright (C) 2009 Free Software Foundation, Inc.
+Copyright (C) 2012 Free Software Foundation, Inc.
This config.status script is free software; the Free Software Foundation
gives unlimited permission to copy, distribute and modify it."
@@ -8397,11 +8664,16 @@ 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=`expr "X$1" : 'X\([^=]*\)='`
+ ac_optarg=
+ ac_shift=:
+ ;;
*)
ac_option=$1
ac_optarg=$2
@@ -8423,6 +8695,7 @@ do
$ac_shift
case $ac_optarg in
*\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ '') as_fn_error $? "missing file argument" ;;
esac
as_fn_append CONFIG_FILES " '$ac_optarg'"
ac_need_defaults=false;;
@@ -8435,7 +8708,7 @@ do
ac_need_defaults=false;;
--he | --h)
# Conflict between --help and --header
- as_fn_error "ambiguous option: \`$1'
+ as_fn_error $? "ambiguous option: \`$1'
Try \`$0 --help' for more information.";;
--help | --hel | -h )
$as_echo "$ac_cs_usage"; exit ;;
@@ -8444,7 +8717,7 @@ Try \`$0 --help' for more information.";;
ac_cs_silent=: ;;
# This is an error.
- -*) as_fn_error "unrecognized option: \`$1'
+ -*) as_fn_error $? "unrecognized option: \`$1'
Try \`$0 --help' for more information." ;;
*) as_fn_append ac_config_targets " $1"
@@ -8464,7 +8737,7 @@ fi
_ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
if \$ac_cs_recheck; then
- set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+ set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
shift
\$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
CONFIG_SHELL='$SHELL'
@@ -8500,7 +8773,7 @@ do
"splash/Makefile") CONFIG_FILES="$CONFIG_FILES splash/Makefile" ;;
"xpdf/Makefile") CONFIG_FILES="$CONFIG_FILES xpdf/Makefile" ;;
- *) as_fn_error "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
+ *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
esac
done
@@ -8522,9 +8795,10 @@ fi
# after its creation but before its name has been assigned to `$tmp'.
$debug ||
{
- tmp=
+ tmp= ac_tmp=
trap 'exit_status=$?
- { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status
+ : "${ac_tmp:=$tmp}"
+ { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status
' 0
trap 'as_fn_exit 1' 1 2 13 15
}
@@ -8532,12 +8806,13 @@ $debug ||
{
tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
- test -n "$tmp" && test -d "$tmp"
+ test -d "$tmp"
} ||
{
tmp=./conf$$-$RANDOM
(umask 077 && mkdir "$tmp")
-} || as_fn_error "cannot create a temporary directory in ." "$LINENO" 5
+} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5
+ac_tmp=$tmp
# Set up the scripts for CONFIG_FILES section.
# No need to generate them if there are no CONFIG_FILES.
@@ -8554,12 +8829,12 @@ if test "x$ac_cr" = x; then
fi
ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
- ac_cs_awk_cr='\r'
+ ac_cs_awk_cr='\\r'
else
ac_cs_awk_cr=$ac_cr
fi
-echo 'BEGIN {' >"$tmp/subs1.awk" &&
+echo 'BEGIN {' >"$ac_tmp/subs1.awk" &&
_ACEOF
@@ -8568,18 +8843,18 @@ _ACEOF
echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
echo "_ACEOF"
} >conf$$subs.sh ||
- as_fn_error "could not make $CONFIG_STATUS" "$LINENO" 5
-ac_delim_num=`echo "$ac_subst_vars" | grep -c '$'`
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'`
ac_delim='%!_!# '
for ac_last_try in false false false false false :; do
. ./conf$$subs.sh ||
- as_fn_error "could not make $CONFIG_STATUS" "$LINENO" 5
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
if test $ac_delim_n = $ac_delim_num; then
break
elif $ac_last_try; then
- as_fn_error "could not make $CONFIG_STATUS" "$LINENO" 5
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
else
ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
fi
@@ -8587,7 +8862,7 @@ done
rm -f conf$$subs.sh
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
-cat >>"\$tmp/subs1.awk" <<\\_ACAWK &&
+cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK &&
_ACEOF
sed -n '
h
@@ -8635,7 +8910,7 @@ t delim
rm -f conf$$subs.awk
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
_ACAWK
-cat >>"\$tmp/subs1.awk" <<_ACAWK &&
+cat >>"\$ac_tmp/subs1.awk" <<_ACAWK &&
for (key in S) S_is_set[key] = 1
FS = ""
@@ -8667,21 +8942,29 @@ if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
else
cat
-fi < "$tmp/subs1.awk" > "$tmp/subs.awk" \
- || as_fn_error "could not setup config files machinery" "$LINENO" 5
+fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \
+ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5
_ACEOF
-# VPATH may cause trouble with some makes, so we remove $(srcdir),
-# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and
+# VPATH may cause trouble with some makes, so we remove sole $(srcdir),
+# ${srcdir} and @srcdir@ entries 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/
+ ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{
+h
+s///
+s/^/:/
+s/[ ]*$/:/
+s/:\$(srcdir):/:/g
+s/:\${srcdir}:/:/g
+s/:@srcdir@:/:/g
+s/^:*//
s/:*$//
+x
+s/\(=[ ]*\).*/\1/
+G
+s/\n//
s/^[^=]*=[ ]*$//
}'
fi
@@ -8693,7 +8976,7 @@ fi # test -n "$CONFIG_FILES"
# No need to generate them if there are no CONFIG_HEADERS.
# This happens for instance with `./config.status Makefile'.
if test -n "$CONFIG_HEADERS"; then
-cat >"$tmp/defines.awk" <<\_ACAWK ||
+cat >"$ac_tmp/defines.awk" <<\_ACAWK ||
BEGIN {
_ACEOF
@@ -8705,11 +8988,11 @@ _ACEOF
# handling of long lines.
ac_delim='%!_!# '
for ac_last_try in false false :; do
- ac_t=`sed -n "/$ac_delim/p" confdefs.h`
- if test -z "$ac_t"; then
+ ac_tt=`sed -n "/$ac_delim/p" confdefs.h`
+ if test -z "$ac_tt"; then
break
elif $ac_last_try; then
- as_fn_error "could not make $CONFIG_HEADERS" "$LINENO" 5
+ as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5
else
ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
fi
@@ -8794,7 +9077,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
_ACAWK
_ACEOF
cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
- as_fn_error "could not setup config headers machinery" "$LINENO" 5
+ as_fn_error $? "could not setup config headers machinery" "$LINENO" 5
fi # test -n "$CONFIG_HEADERS"
@@ -8807,7 +9090,7 @@ do
esac
case $ac_mode$ac_tag in
:[FHL]*:*);;
- :L* | :C*:*) as_fn_error "invalid tag \`$ac_tag'" "$LINENO" 5;;
+ :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;
:[FH]-) ac_tag=-:-;;
:[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
esac
@@ -8826,7 +9109,7 @@ do
for ac_f
do
case $ac_f in
- -) ac_f="$tmp/stdin";;
+ -) ac_f="$ac_tmp/stdin";;
*) # Look for the file first in the build tree, then in the source tree
# (if the path is not absolute). The absolute path cannot be DOS-style,
# because $ac_f cannot contain `:'.
@@ -8835,7 +9118,7 @@ do
[\\/$]*) false;;
*) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
esac ||
- as_fn_error "cannot find input file: \`$ac_f'" "$LINENO" 5;;
+ as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
esac
case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
as_fn_append ac_file_inputs " '$ac_f'"
@@ -8861,8 +9144,8 @@ $as_echo "$as_me: creating $ac_file" >&6;}
esac
case $ac_tag in
- *:-:* | *:-) cat >"$tmp/stdin" \
- || as_fn_error "could not create $ac_file" "$LINENO" 5 ;;
+ *:-:* | *:-) cat >"$ac_tmp/stdin" \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;
esac
;;
esac
@@ -8992,23 +9275,24 @@ s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
s&@INSTALL@&$ac_INSTALL&;t t
$ac_datarootdir_hack
"
-eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$tmp/subs.awk" >$tmp/out \
- || as_fn_error "could not create $ac_file" "$LINENO" 5
+eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \
+ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5
test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
- { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } &&
- { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } &&
+ { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } &&
+ { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \
+ "$ac_tmp/out"`; test -z "$ac_out"; } &&
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
-which seems to be undefined. Please make sure it is defined." >&5
+which seems to be undefined. Please make sure it is defined" >&5
$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
-which seems to be undefined. Please make sure it is defined." >&2;}
+which seems to be undefined. Please make sure it is defined" >&2;}
- rm -f "$tmp/stdin"
+ rm -f "$ac_tmp/stdin"
case $ac_file in
- -) cat "$tmp/out" && rm -f "$tmp/out";;
- *) rm -f "$ac_file" && mv "$tmp/out" "$ac_file";;
+ -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";;
+ *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";;
esac \
- || as_fn_error "could not create $ac_file" "$LINENO" 5
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5
;;
:H)
#
@@ -9017,21 +9301,21 @@ which seems to be undefined. Please make sure it is defined." >&2;}
if test x"$ac_file" != x-; then
{
$as_echo "/* $configure_input */" \
- && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs"
- } >"$tmp/config.h" \
- || as_fn_error "could not create $ac_file" "$LINENO" 5
- if diff "$ac_file" "$tmp/config.h" >/dev/null 2>&1; then
+ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs"
+ } >"$ac_tmp/config.h" \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5
$as_echo "$as_me: $ac_file is unchanged" >&6;}
else
rm -f "$ac_file"
- mv "$tmp/config.h" "$ac_file" \
- || as_fn_error "could not create $ac_file" "$LINENO" 5
+ mv "$ac_tmp/config.h" "$ac_file" \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5
fi
else
$as_echo "/* $configure_input */" \
- && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs" \
- || as_fn_error "could not create -" "$LINENO" 5
+ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \
+ || as_fn_error $? "could not create -" "$LINENO" 5
fi
;;
@@ -9046,7 +9330,7 @@ _ACEOF
ac_clean_files=$ac_clean_files_save
test $ac_write_fail = 0 ||
- as_fn_error "write failure creating $CONFIG_STATUS" "$LINENO" 5
+ as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5
# configure is writing to config.log, and then calls config.status.
@@ -9067,7 +9351,7 @@ if test "$no_create" != yes; then
exec 5>>config.log
# Use ||, not &&, to avoid exiting from the if with $? = 1, which
# would make configure fail if this is the last instruction.
- $ac_cs_success || as_fn_exit $?
+ $ac_cs_success || as_fn_exit 1
fi
if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
@@ -9095,3 +9379,8 @@ $as_echo "$as_me: WARNING: -- You will be able to compile pdftops, pdftotext,
pdfinfo, pdffonts, pdfdetach, and pdfimages, but not xpdf
or pdftoppm" >&2;}
fi
+
+if test "x$smr_have_libpng_library" != xyes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Couldn't find libpng -- you will not be able to build pdftohtml or pdftopng" >&5
+$as_echo "$as_me: WARNING: Couldn't find libpng -- you will not be able to build pdftohtml or pdftopng" >&2;}
+fi
diff --git a/configure.in b/configure.in
index 6592a46..47e33fc 100644
--- a/configure.in
+++ b/configure.in
@@ -1,5 +1,5 @@
dnl Process this file with autoconf to produce a configure script.
-dnl Copyright 1998-2005 Glyph & Cog, LLC
+dnl Copyright 1998-2013 Glyph & Cog, LLC
AC_PREREQ(2.57)
@@ -7,34 +7,54 @@ AC_INIT(xpdf/Gfx.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"))
+
+AC_ARG_ENABLE([a4-paper],
+ AS_HELP_STRING([--enable-a4-paper],
+ [use A4 paper size instead of Letter for PostScript output]))
+AS_IF([test "x$enable_a4_paper" = "xyes"],
+ [AC_DEFINE(A4_PAPER)])
+
+AC_ARG_ENABLE([no-text-select],
+ AS_HELP_STRING([--enable-no-text-select],
+ [do not allow text selection]))
+AS_IF([test "x$enable_no_text_select" = "xyes"],
+ [AC_DEFINE(NO_TEXT_SELECT)])
+
+AC_ARG_ENABLE([opi],
+ AS_HELP_STRING([--enable-opi],
+ [include support for OPI comments]))
+AS_IF([test "x$enable_opi" = "xyes"],
+ [AC_DEFINE(OPI_SUPPORT)])
+
+AC_ARG_ENABLE([multithreaded],
+ AS_HELP_STRING([--enable-multithreaded],
+ [include support for multithreading]))
+AS_IF([test "x$enable_multithreaded" = "xyes"],
+ [AC_DEFINE(MULTITHREADED)])
+
+AC_ARG_ENABLE([exceptions],
+ AS_HELP_STRING([--enable-exceptions],
+ [use C++ exceptions]))
+AS_IF([test "x$enable_exceptions" = "xyes"],
+ [AC_DEFINE(USE_EXCEPTIONS)])
+
+AC_ARG_ENABLE([fixedpoint],
+ AS_HELP_STRING([--enable-fixedpoint],
+ [use fixed point (instead of floating point) arithmetic]))
+AS_IF([test "x$enable_fixedpoint" = "xyes"],
+ [AC_DEFINE(USE_FIXEDPOINT)])
+
+AC_ARG_ENABLE([cmyk],
+ AS_HELP_STRING([--enable-cmyk],
+ [include support for CMYK rasterization]))
+AS_IF([test "x$enable_cmyk" = "xyes"],
+ [AC_DEFINE(SPLASH_CMYK)])
+
+AC_ARG_WITH([appdef-dir],
+ AS_HELP_STRING([--with-appdef-dir],
+ [set app-defaults directory]))
+AS_IF([test "x$with_appdef_dir" != "xno"],
+ [AC_DEFINE_UNQUOTED(APPDEFDIR, "$with_appdef_dir")])
dnl ##### Path to xpdfrc.
dnl This ugly kludge to get the sysconfdir path is needed because
@@ -241,12 +261,12 @@ dnl ##### Check for std::sort.
AC_CACHE_CHECK([for std::sort],
xpdf_cv_func_std_sort,
[AC_COMPILE_IFELSE(
- AC_LANG_PROGRAM([[#include <algorithm>
+ [AC_LANG_PROGRAM([[#include <algorithm>
struct functor {
bool operator()(const int &i0, const int &i1) { return i0 < i1; }
};]],
- [[int a[[100]];
-std::sort(a, a+100, functor());]]),
+ [[int a[100];
+std::sort(a, a+100, functor());]])],
xpdf_cv_func_std_sort=yes, xpdf_cv_func_std_sort=no)])
if test "$xpdf_cv_func_std_sort" = yes; then
AC_DEFINE(HAVE_STD_SORT)
@@ -314,18 +334,6 @@ if test -z "$no_x"; then
fi
fi
-#dnl ##### Check for t1lib.
-#smr_CHECK_LIB(t1, t1, [Type 1 font rasterizer],
-# T1_InitLib, t1lib.h,
-# -lm, $X_CFLAGS)
-# t1lib has some potential security holes, and hasn't been updated in
-# years -- if you really want to use it, uncomment the preceding lines,
-# and comment out the next two lines
-t1_LIBS=""
-t1_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+.)
@@ -338,6 +346,11 @@ if test "x$smr_have_freetype2_library" = xyes; then
AC_DEFINE(HAVE_SPLASH)
fi
+dnl ##### Check for libpng.
+smr_CHECK_LIB(libpng, png, [PNG library], png_write_row, png.h, -lz)
+AC_SUBST(libpng_LIBS)
+AC_SUBST(libpng_CFLAGS)
+
dnl ##### Check for libpaper (Debian).
smr_CHECK_LIB(libpaper, paper, [Debian libpaper], paperinit, paper.h)
AC_SUBST(libpaper_LIBS)
@@ -354,6 +367,12 @@ fi
AC_SUBST(X)
AC_SUBST(XPDF_TARGET)
+dnl ##### Extra libraries.
+EXTRA_LIBS=
+EXTRA_CFLAGS=
+AC_SUBST(EXTRA_LIBS)
+AC_SUBST(EXTRA_CFLAGS)
+
dnl ##### Write the makefiles.
AC_OUTPUT(Makefile goo/Makefile fofi/Makefile splash/Makefile xpdf/Makefile)
@@ -372,3 +391,8 @@ if test -n "$no_x" -o "x$smr_have_Xm_library" != xyes -o "x$smr_have_freetype2_l
pdfinfo, pdffonts, pdfdetach, and pdfimages, but not xpdf
or pdftoppm])
fi
+
+dnl ##### Warn user if libpng is missing.
+if test "x$smr_have_libpng_library" != xyes; then
+ AC_MSG_WARN([Couldn't find libpng -- you will not be able to build pdftohtml or pdftopng])
+fi
diff --git a/dj_make.bat b/dj_make.bat
index 13c192d..a629e17 100644
--- a/dj_make.bat
+++ b/dj_make.bat
@@ -1,5 +1,5 @@
set CC=gcc
-set CFLAGS=-g -O2 -I.. -I..\fofi -I..\goo
+set CFLAGS=-g -O2 -I.. -I..\splash -I..\fofi -I..\goo
set CXX=gpp
set CXXFLAGS=%CFLAGS%
set LIBPROG=ar
@@ -28,6 +28,7 @@ cd ..\fofi
cd ..\xpdf
del *.o
+%CXX% %CXXFLAGS% -c AcroForm.cc
%CXX% %CXXFLAGS% -c Annot.cc
%CXX% %CXXFLAGS% -c Array.cc
%CXX% %CXXFLAGS% -c BuiltinFont.cc
@@ -39,6 +40,7 @@ del *.o
%CXX% %CXXFLAGS% -c Dict.cc
%CXX% %CXXFLAGS% -c Error.cc
%CXX% %CXXFLAGS% -c FontEncodingTables.cc
+%CXX% %CXXFLAGS% -c Form.cc
%CXX% %CXXFLAGS% -c Function.cc
%CXX% %CXXFLAGS% -c Gfx.cc
%CXX% %CXXFLAGS% -c GfxFont.cc
@@ -61,12 +63,16 @@ del *.o
%CXX% %CXXFLAGS% -c PSTokenizer.cc
%CXX% %CXXFLAGS% -c Page.cc
%CXX% %CXXFLAGS% -c Parser.cc
+%CXX% %CXXFLAGS% -c PreScanOutputDev.cc
%CXX% %CXXFLAGS% -c SecurityHandler.cc
%CXX% %CXXFLAGS% -c Stream.cc
%CXX% %CXXFLAGS% -c TextOutputDev.cc
+%CXX% %CXXFLAGS% -c TextString.cc
%CXX% %CXXFLAGS% -c UnicodeMap.cc
%CXX% %CXXFLAGS% -c UnicodeTypeTable.cc
+%CXX% %CXXFLAGS% -c XFAForm.cc
%CXX% %CXXFLAGS% -c XRef.cc
+%CXX% %CXXFLAGS% -c Zoox.cc
del libxpdf.a
%LIBPROG% -rc libxpdf.a *.o
diff --git a/doc/pdfdetach.1 b/doc/pdfdetach.1
index 1e2f8aa..b0d9dd3 100644
--- a/doc/pdfdetach.1
+++ b/doc/pdfdetach.1
@@ -1,8 +1,8 @@
-.\" Copyright 2011 Glyph & Cog, LLC
-.TH pdfdetach 1 "15 August 2011"
+.\" Copyright 2013-2014 Glyph & Cog, LLC
+.TH pdfdetach 1 "28 May 2014"
.SH NAME
pdfdetach \- Portable Document Format (PDF) document embedded file
-extractor (version 3.03)
+extractor (version 3.04)
.SH SYNOPSIS
.B pdfdetach
[options]
@@ -90,15 +90,17 @@ Error related to PDF permissions.
99
Other error.
.SH AUTHOR
-The pdfinfo software and documentation are copyright 1996-2011 Glyph &
+The pdfinfo software and documentation are copyright 1996-2014 Glyph &
Cog, LLC.
.SH "SEE ALSO"
.BR xpdf (1),
.BR pdftops (1),
.BR pdftotext (1),
+.BR pdftohtml (1),
.BR pdfinfo (1),
.BR pdffonts (1),
.BR pdftoppm (1),
+.BR pdftopng (1),
.BR pdfimages (1),
.BR xpdfrc (5)
.br
diff --git a/doc/pdfdetach.cat b/doc/pdfdetach.cat
index 526bbba..8b052e2 100644
--- a/doc/pdfdetach.cat
+++ b/doc/pdfdetach.cat
@@ -4,7 +4,7 @@ pdfdetach(1) pdfdetach(1)
NAME
pdfdetach - Portable Document Format (PDF) document embedded file
- extractor (version 3.03)
+ extractor (version 3.04)
SYNOPSIS
pdfdetach [options] [PDF-file]
@@ -79,14 +79,14 @@ EXIT CODES
99 Other error.
AUTHOR
- The pdfinfo software and documentation are copyright 1996-2011 Glyph &
+ The pdfinfo software and documentation are copyright 1996-2014 Glyph &
Cog, LLC.
SEE ALSO
- xpdf(1), pdftops(1), pdftotext(1), pdfinfo(1), pdffonts(1),
- pdftoppm(1), pdfimages(1), xpdfrc(5)
+ xpdf(1), pdftops(1), pdftotext(1), pdftohtml(1), pdfinfo(1), pdf-
+ fonts(1), pdftoppm(1), pdftopng(1), pdfimages(1), xpdfrc(5)
http://www.foolabs.com/xpdf/
- 15 August 2011 pdfdetach(1)
+ 28 May 2014 pdfdetach(1)
diff --git a/doc/pdfdetach.hlp b/doc/pdfdetach.hlp
deleted file mode 100644
index f85cd0f..0000000
--- a/doc/pdfdetach.hlp
+++ /dev/null
@@ -1,101 +0,0 @@
-! Generated automatically by mantohlp
-1 pdfdetach
-
- pdfdetach - Portable Document Format (PDF) document embedded file
-
- pdfdetach [options] [PDF-file]
-
- Pdfdetach lists or extracts embedded files (attachments) from a Porta-
- ble Document Format (PDF) file.
-
- ()
-
-2 ONFIGURATION_FIL
-
- Pdfdetach 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
-
- Some of the following options can be set with configuration file com-
- mands. These are listed in square brackets with the description of the
- corresponding command line option.
-
- -list List all of the embedded files in the PDF file. File names are
- converted to the text encoding specified by the "-enc" switch.
-
- -save number
- Save the specified embedded file. By default, this uses the
- file name associated with the embedded file (as printed by the
- "-list" switch); the file name can be changed with the "-o"
- switch.
-
- -saveall
- Save all of the embedded files. This uses the file names asso-
- ciated with the embedded files (as printed by the "-list"
- switch). By default, the files are saved in the current direc-
- tory; this can be changed with the "-o" switch.
-
- -o path
- Set the file name used when saving an embedded file with the
- "-save" switch, or the directory used by "-saveall".
-
- -enc encoding-name
- Sets the encoding to use for text output (embedded file names).
- 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: textEncoding]
-
- -opw password
- Specify the owner password for the PDF file. Providing 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 system-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-2011 Glyph &
- Cog, LLC.
-
- ()
-
-2 SEE_ALSO
-
- xpdf(1), pdftops(1), pdftotext(1), pdfinfo(1), pdffonts(1),
- pdftoppm(1), pdfimages(1), xpdfrc(5)
- http://www.foolabs.com/xpdf/
-
- ()
-
diff --git a/doc/pdffonts.1 b/doc/pdffonts.1
index 6e132ac..70e1f45 100644
--- a/doc/pdffonts.1
+++ b/doc/pdffonts.1
@@ -1,8 +1,8 @@
-.\" Copyright 1999-2011 Glyph & Cog, LLC
-.TH pdffonts 1 "15 August 2011"
+.\" Copyright 1999-2014 Glyph & Cog, LLC
+.TH pdffonts 1 "28 May 2014"
.SH NAME
pdffonts \- Portable Document Format (PDF) font analyzer (version
-3.03)
+3.04)
.SH SYNOPSIS
.B pdffonts
[options]
@@ -34,6 +34,13 @@ can't be converted to Unicode)
.TP
.B object ID
the font dictionary object ID (number and generation)
+.TP
+.B location
+the font location (see the
+.B \-loc
+and
+.B \-locPS
+options).
.PP
PDF files can contain the following types of fonts:
.PP
@@ -86,6 +93,14 @@ the corresponding command line option.
.BI \-f " number"
Specifies the first page to analyze.
.TP
+.B \-loc
+Shows additional information on the location of the font that will be
+used when the PDF file is rasterized (with xpdf, pdftoppm, etc.).
+.TP
+.B \-locPS
+Shows additional information on the location of the font that will be
+used when the PDF file is converted to PostScript (with pdftops).
+.TP
.BI \-l " number"
Specifies the last page to analyze.
.TP
@@ -128,15 +143,17 @@ Error related to PDF permissions.
99
Other error.
.SH AUTHOR
-The pdffonts software and documentation are copyright 1996-2011 Glyph
+The pdffonts software and documentation are copyright 1996-2014 Glyph
& Cog, LLC.
.SH "SEE ALSO"
.BR xpdf (1),
.BR pdftops (1),
.BR pdftotext (1),
+.BR pdftohtml (1),
.BR pdfinfo (1),
.BR pdfdetach (1),
.BR pdftoppm (1),
+.BR pdftopng (1),
.BR pdfimages (1),
.BR xpdfrc (5)
.br
diff --git a/doc/pdffonts.cat b/doc/pdffonts.cat
index b2d92f3..0c10b25 100644
--- a/doc/pdffonts.cat
+++ b/doc/pdffonts.cat
@@ -3,7 +3,7 @@ pdffonts(1) pdffonts(1)
NAME
- pdffonts - Portable Document Format (PDF) font analyzer (version 3.03)
+ pdffonts - Portable Document Format (PDF) font analyzer (version 3.04)
SYNOPSIS
pdffonts [options] [PDF-file]
@@ -30,6 +30,9 @@ DESCRIPTION
object ID
the font dictionary object ID (number and generation)
+ location
+ the font location (see the -loc and -locPS options).
+
PDF files can contain the following types of fonts:
Type 1
@@ -59,6 +62,14 @@ OPTIONS
-f number
Specifies the first page to analyze.
+ -loc Shows additional information on the location of the font that
+ will be used when the PDF file is rasterized (with xpdf,
+ pdftoppm, etc.).
+
+ -locPS Shows additional information on the location of the font that
+ will be used when the PDF file is converted to PostScript (with
+ pdftops).
+
-l number
Specifies the last page to analyze.
@@ -91,14 +102,14 @@ EXIT CODES
99 Other error.
AUTHOR
- The pdffonts software and documentation are copyright 1996-2011 Glyph &
+ The pdffonts software and documentation are copyright 1996-2014 Glyph &
Cog, LLC.
SEE ALSO
- xpdf(1), pdftops(1), pdftotext(1), pdfinfo(1), pdfdetach(1),
- pdftoppm(1), pdfimages(1), xpdfrc(5)
+ xpdf(1), pdftops(1), pdftotext(1), pdftohtml(1), pdfinfo(1), pdfde-
+ tach(1), pdftoppm(1), pdftopng(1), pdfimages(1), xpdfrc(5)
http://www.foolabs.com/xpdf/
- 15 August 2011 pdffonts(1)
+ 28 May 2014 pdffonts(1)
diff --git a/doc/pdffonts.hlp b/doc/pdffonts.hlp
deleted file mode 100644
index c5a6c13..0000000
--- a/doc/pdffonts.hlp
+++ /dev/null
@@ -1,114 +0,0 @@
-! 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 Format (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 generation)
-
- 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 configuration file com-
- mands. 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. Providing 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 system-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-2011 Glyph &
- Cog, LLC.
-
- ()
-
-2 SEE_ALSO
-
- xpdf(1), pdftops(1), pdftotext(1), pdfinfo(1), pdfdetach(1),
- pdftoppm(1), pdfimages(1), xpdfrc(5)
- http://www.foolabs.com/xpdf/
-
- ()
-
diff --git a/doc/pdfimages.1 b/doc/pdfimages.1
index a68a510..0953ced 100644
--- a/doc/pdfimages.1
+++ b/doc/pdfimages.1
@@ -1,8 +1,8 @@
-.\" Copyright 1998-2011 Glyph & Cog, LLC
-.TH pdfimages 1 "15 August 2011"
+.\" Copyright 1998-2014 Glyph & Cog, LLC
+.TH pdfimages 1 "28 May 2014"
.SH NAME
pdfimages \- Portable Document Format (PDF) image extractor
-(version 3.03)
+(version 3.04)
.SH SYNOPSIS
.B pdfimages
[options]
@@ -15,9 +15,9 @@ Pixmap (PPM), Portable Bitmap (PBM), or JPEG files.
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 ,
+.IR image-root - nnnn . xxx ,
where
-.I nnn
+.I nnnn
is the image number and
.I xxx
is the image type (.ppm, .pbm, .jpg).
@@ -88,16 +88,18 @@ Error related to PDF permissions.
99
Other error.
.SH AUTHOR
-The pdfimages software and documentation are copyright 1998-2011 Glyph
+The pdfimages software and documentation are copyright 1998-2014 Glyph
& Cog, LLC.
.SH "SEE ALSO"
.BR xpdf (1),
.BR pdftops (1),
.BR pdftotext (1),
+.BR pdftohtml (1),
.BR pdfinfo (1),
.BR pdffonts (1),
.BR pdfdetach (1),
.BR pdftoppm (1),
+.BR pdftopng (1),
.BR xpdfrc (5)
.br
.B http://www.foolabs.com/xpdf/
diff --git a/doc/pdfimages.cat b/doc/pdfimages.cat
index ee8810c..a152e78 100644
--- a/doc/pdfimages.cat
+++ b/doc/pdfimages.cat
@@ -4,7 +4,7 @@ pdfimages(1) pdfimages(1)
NAME
pdfimages - Portable Document Format (PDF) image extractor (version
- 3.03)
+ 3.04)
SYNOPSIS
pdfimages [options] PDF-file image-root
@@ -14,8 +14,8 @@ DESCRIPTION
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,
+ writes one PPM, PBM, or JPEG file for each image, image-root-nnnn.xxx,
+ where nnnn 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
@@ -72,14 +72,14 @@ EXIT CODES
99 Other error.
AUTHOR
- The pdfimages software and documentation are copyright 1998-2011 Glyph
+ The pdfimages software and documentation are copyright 1998-2014 Glyph
& Cog, LLC.
SEE ALSO
- xpdf(1), pdftops(1), pdftotext(1), pdfinfo(1), pdffonts(1), pdfde-
- tach(1), pdftoppm(1), xpdfrc(5)
+ xpdf(1), pdftops(1), pdftotext(1), pdftohtml(1), pdfinfo(1), pdf-
+ fonts(1), pdfdetach(1), pdftoppm(1), pdftopng(1), xpdfrc(5)
http://www.foolabs.com/xpdf/
- 15 August 2011 pdfimages(1)
+ 28 May 2014 pdfimages(1)
diff --git a/doc/pdfimages.hlp b/doc/pdfimages.hlp
deleted file mode 100644
index 18fef58..0000000
--- a/doc/pdfimages.hlp
+++ /dev/null
@@ -1,94 +0,0 @@
-! 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 configuration file com-
- mands. 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. Providing 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-2011 Glyph
- & Cog, LLC.
-
- ()
-
-2 SEE_ALSO
-
- xpdf(1), pdftops(1), pdftotext(1), pdfinfo(1), pdffonts(1), pdfde-
- tach(1), pdftoppm(1), xpdfrc(5)
- http://www.foolabs.com/xpdf/
-
- ()
-
diff --git a/doc/pdfinfo.1 b/doc/pdfinfo.1
index b6b0498..5a02353 100644
--- a/doc/pdfinfo.1
+++ b/doc/pdfinfo.1
@@ -1,8 +1,8 @@
-.\" Copyright 1999-2011 Glyph & Cog, LLC
-.TH pdfinfo 1 "15 August 2011"
+.\" Copyright 1999-2014 Glyph & Cog, LLC
+.TH pdfinfo 1 "28 May 2014"
.SH NAME
pdfinfo \- Portable Document Format (PDF) document information
-extractor (version 3.03)
+extractor (version 3.04)
.SH SYNOPSIS
.B pdfinfo
[options]
@@ -57,7 +57,7 @@ encrypted flag (yes/no)
print and copy permissions (if encrypted)
.RE
.RS
-page size
+page size and rotation
.RE
.RS
file size
@@ -150,15 +150,17 @@ Error related to PDF permissions.
99
Other error.
.SH AUTHOR
-The pdfinfo software and documentation are copyright 1996-2011 Glyph &
+The pdfinfo software and documentation are copyright 1996-2014 Glyph &
Cog, LLC.
.SH "SEE ALSO"
.BR xpdf (1),
.BR pdftops (1),
.BR pdftotext (1),
+.BR pdftohtml (1),
.BR pdffonts (1),
.BR pdfdetach (1),
.BR pdftoppm (1),
+.BR pdftopng (1),
.BR pdfimages (1),
.BR xpdfrc (5)
.br
diff --git a/doc/pdfinfo.cat b/doc/pdfinfo.cat
index 7f74ced..8ef2bb2 100644
--- a/doc/pdfinfo.cat
+++ b/doc/pdfinfo.cat
@@ -4,7 +4,7 @@ pdfinfo(1) pdfinfo(1)
NAME
pdfinfo - Portable Document Format (PDF) document information extractor
- (version 3.03)
+ (version 3.04)
SYNOPSIS
pdfinfo [options] [PDF-file]
@@ -31,7 +31,7 @@ DESCRIPTION
page count
encrypted flag (yes/no)
print and copy permissions (if encrypted)
- page size
+ page size and rotation
file size
linearized (yes/no)
PDF version
@@ -104,14 +104,14 @@ EXIT CODES
99 Other error.
AUTHOR
- The pdfinfo software and documentation are copyright 1996-2011 Glyph &
+ The pdfinfo software and documentation are copyright 1996-2014 Glyph &
Cog, LLC.
SEE ALSO
- xpdf(1), pdftops(1), pdftotext(1), pdffonts(1), pdfdetach(1),
- pdftoppm(1), pdfimages(1), xpdfrc(5)
+ xpdf(1), pdftops(1), pdftotext(1), pdftohtml(1), pdffonts(1), pdfde-
+ tach(1), pdftoppm(1), pdftopng(1), pdfimages(1), xpdfrc(5)
http://www.foolabs.com/xpdf/
- 15 August 2011 pdfinfo(1)
+ 28 May 2014 pdfinfo(1)
diff --git a/doc/pdfinfo.hlp b/doc/pdfinfo.hlp
deleted file mode 100644
index f408266..0000000
--- a/doc/pdfinfo.hlp
+++ /dev/null
@@ -1,126 +0,0 @@
-! Generated automatically by mantohlp
-1 pdfinfo
-
- pdfinfo - Portable Document Format (PDF) document information
-
- 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)
- form (AcroForm / XFA / none)
- 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 configuration file com-
- mands. 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 exam-
- ined.
-
- -l number
- Specifies the last page to examine.
-
- -box Prints the page box bounding boxes: MediaBox, CropBox, BleedBox,
- TrimBox, and ArtBox.
-
- -meta Prints document-level metadata. (This is the "Metadata" stream
- from the PDF file's Catalog object.)
-
- -rawdates
- Prints the raw (undecoded) date strings, directly from the PDF
- file.
-
- -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). [con-
- fig file: textEncoding]
-
- -opw password
- Specify the owner password for the PDF file. Providing 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 system-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-2011 Glyph &
- Cog, LLC.
-
- ()
-
-2 SEE_ALSO
-
- xpdf(1), pdftops(1), pdftotext(1), pdffonts(1), pdfdetach(1),
- pdftoppm(1), pdfimages(1), xpdfrc(5)
- http://www.foolabs.com/xpdf/
-
- ()
-
diff --git a/doc/pdftohtml.1 b/doc/pdftohtml.1
new file mode 100644
index 0000000..a74df55
--- /dev/null
+++ b/doc/pdftohtml.1
@@ -0,0 +1,106 @@
+.\" Copyright 1997-2014 Glyph & Cog, LLC
+.TH pdftohtml 1 "28 May 2014"
+.SH NAME
+pdftohtml \- Portable Document Format (PDF) to HTML converter
+(version 3.04)
+.SH SYNOPSIS
+.B pdftohtml
+[options]
+.I PDF-file
+.I HTML-dir
+.SH DESCRIPTION
+.B Pdftohtml
+converts Portable Document Format (PDF) files to HTML.
+.PP
+Pdftohtml reads the PDF file,
+.IR PDF-file ,
+and places an HTML file for each page, along with auxiliary images
+in the directory,
+.IR HTML-dir .
+The HTML directory will be created; if it already exists, pdftohtml
+will report an error.
+.SH CONFIGURATION FILE
+Pdftohtml 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 pdftohtml
+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 \-r
+Specifies the resolution, in DPI, for background images. The default
+is 150 DPI.
+.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 pdftohtml software and documentation are copyright 1996-2014 Glyph
+& Cog, LLC.
+.SH "SEE ALSO"
+.BR xpdf (1),
+.BR pdftops (1),
+.BR pdftotext (1),
+.BR pdfinfo (1),
+.BR pdffonts (1),
+.BR pdfdetach (1),
+.BR pdftoppm (1),
+.BR pdftopng (1),
+.BR pdfimages (1),
+.BR xpdfrc (5)
+.br
+.B http://www.foolabs.com/xpdf/
diff --git a/doc/pdftohtml.cat b/doc/pdftohtml.cat
new file mode 100644
index 0000000..ed46c1a
--- /dev/null
+++ b/doc/pdftohtml.cat
@@ -0,0 +1,87 @@
+pdftohtml(1) pdftohtml(1)
+
+
+
+NAME
+ pdftohtml - Portable Document Format (PDF) to HTML converter (version
+ 3.04)
+
+SYNOPSIS
+ pdftohtml [options] PDF-file HTML-dir
+
+DESCRIPTION
+ Pdftohtml converts Portable Document Format (PDF) files to HTML.
+
+ Pdftohtml reads the PDF file, PDF-file, and places an HTML file for
+ each page, along with auxiliary images in the directory, HTML-dir. The
+ HTML directory will be created; if it already exists, pdftohtml will
+ report an error.
+
+CONFIGURATION FILE
+ Pdftohtml 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 pdftohtml is built). See the
+ xpdfrc(5) man page for details.
+
+OPTIONS
+ Many of the following options can be set with configuration file com-
+ mands. 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 Specifies the resolution, in DPI, for background images. The
+ default is 150 DPI.
+
+ -opw password
+ Specify the owner password for the PDF file. Providing 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 system-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 pdftohtml software and documentation are copyright 1996-2014 Glyph
+ & Cog, LLC.
+
+SEE ALSO
+ xpdf(1), pdftops(1), pdftotext(1), pdfinfo(1), pdffonts(1), pdfde-
+ tach(1), pdftoppm(1), pdftopng(1), pdfimages(1), xpdfrc(5)
+ http://www.foolabs.com/xpdf/
+
+
+
+ 28 May 2014 pdftohtml(1)
diff --git a/doc/pdftopng.1 b/doc/pdftopng.1
new file mode 100644
index 0000000..6d93653
--- /dev/null
+++ b/doc/pdftopng.1
@@ -0,0 +1,119 @@
+.\" Copyright 2014 Glyph & Cog, LLC
+.TH pdftopng 1 "28 May 2014"
+.SH NAME
+pdftopng \- Portable Document Format (PDF) to Portable Network Graphics
+(PNG) converter (version 3.04)
+.SH SYNOPSIS
+.B pdftopng
+[options]
+.I PDF-file PNG-root
+.SH DESCRIPTION
+.B Pdftopng
+converts Portable Document Format (PDF) files to color, grayscale, or
+monochrome image files in Portable Network Graphics (PNG) format.
+.PP
+Pdftopng reads the PDF file,
+.IR PDF-file ,
+and writes one PNG file for each page,
+.IR PNG-root - nnnnnn .png,
+where
+.I nnnnnn
+is the page number.
+If
+.I PNG-root
+is \'-', the image is sent to stdout (this is probably only useful
+when converting a single page).
+.SH CONFIGURATION FILE
+Pdftopng 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 pdftopng
+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 image (instead of a color image).
+.TP
+.B \-gray
+Generate a grayscale image (instead of a color image).
+.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 pdftopng software and documentation are copyright 1996-2014 Glyph
+& Cog, LLC.
+.SH "SEE ALSO"
+.BR xpdf (1),
+.BR pdftops (1),
+.BR pdftotext (1),
+.BR pdftohtml (1),
+.BR pdfinfo (1),
+.BR pdffonts (1),
+.BR pdfdetach (1),
+.BR pdftoppm (1),
+.BR pdfimages (1),
+.BR xpdfrc (5)
+.br
+.B http://www.foolabs.com/xpdf/
diff --git a/doc/pdftopng.cat b/doc/pdftopng.cat
new file mode 100644
index 0000000..ca1c5ed
--- /dev/null
+++ b/doc/pdftopng.cat
@@ -0,0 +1,96 @@
+pdftopng(1) pdftopng(1)
+
+
+
+NAME
+ pdftopng - Portable Document Format (PDF) to Portable Network Graphics
+ (PNG) converter (version 3.04)
+
+SYNOPSIS
+ pdftopng [options] PDF-file PNG-root
+
+DESCRIPTION
+ Pdftopng converts Portable Document Format (PDF) files to color,
+ grayscale, or monochrome image files in Portable Network Graphics (PNG)
+ format.
+
+ Pdftopng reads the PDF file, PDF-file, and writes one PNG file for each
+ page, PNG-root-nnnnnn.png, where nnnnnn is the page number. If PNG-
+ root is '-', the image is sent to stdout (this is probably only useful
+ when converting a single page).
+
+CONFIGURATION FILE
+ Pdftopng 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 pdftopng is built). See the
+ xpdfrc(5) man page for details.
+
+OPTIONS
+ Many of the following options can be set with configuration file com-
+ mands. 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 image (instead of a color image).
+
+ -gray Generate a grayscale image (instead of a color image).
+
+ -freetype yes | no
+ Enable or disable FreeType (a TrueType / Type 1 font raster-
+ izer). 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. Providing 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 pdftopng software and documentation are copyright 1996-2014 Glyph &
+ Cog, LLC.
+
+SEE ALSO
+ xpdf(1), pdftops(1), pdftotext(1), pdftohtml(1), pdfinfo(1), pdf-
+ fonts(1), pdfdetach(1), pdftoppm(1), pdfimages(1), xpdfrc(5)
+ http://www.foolabs.com/xpdf/
+
+
+
+ 28 May 2014 pdftopng(1)
diff --git a/doc/pdftoppm.1 b/doc/pdftoppm.1
index 59a2f18..4255937 100644
--- a/doc/pdftoppm.1
+++ b/doc/pdftoppm.1
@@ -1,8 +1,8 @@
-.\" Copyright 2005-2011 Glyph & Cog, LLC
-.TH pdftoppm 1 "15 August 2011"
+.\" Copyright 2005-2014 Glyph & Cog, LLC
+.TH pdftoppm 1 "28 May 2014"
.SH NAME
pdftoppm \- Portable Document Format (PDF) to Portable Pixmap (PPM)
-converter (version 3.03)
+converter (version 3.04)
.SH SYNOPSIS
.B pdftoppm
[options]
@@ -53,11 +53,6 @@ Generate a monochrome PBM file (instead of a color PPM file).
.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".
@@ -109,15 +104,17 @@ Error related to PDF permissions.
99
Other error.
.SH AUTHOR
-The pdftoppm software and documentation are copyright 1996-2011 Glyph
+The pdftoppm software and documentation are copyright 1996-2014 Glyph
& Cog, LLC.
.SH "SEE ALSO"
.BR xpdf (1),
.BR pdftops (1),
.BR pdftotext (1),
+.BR pdftohtml (1),
.BR pdfinfo (1),
.BR pdffonts (1),
.BR pdfdetach (1),
+.BR pdftopng (1),
.BR pdfimages (1),
.BR xpdfrc (5)
.br
diff --git a/doc/pdftoppm.cat b/doc/pdftoppm.cat
index dd0cc78..bfa37c7 100644
--- a/doc/pdftoppm.cat
+++ b/doc/pdftoppm.cat
@@ -4,7 +4,7 @@ pdftoppm(1) pdftoppm(1)
NAME
pdftoppm - Portable Document Format (PDF) to Portable Pixmap (PPM) con-
- verter (version 3.03)
+ verter (version 3.04)
SYNOPSIS
pdftoppm [options] PDF-file PPM-root
@@ -45,16 +45,12 @@ OPTIONS
-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 raster-
+ Enable or disable FreeType (a TrueType / Type 1 font raster-
izer). This defaults to "yes". [config file: enableFreeType]
-aa yes | no
- Enable or disable font anti-aliasing. This defaults to "yes".
+ Enable or disable font anti-aliasing. This defaults to "yes".
[config file: antialias]
-aaVector yes | no
@@ -62,7 +58,7 @@ OPTIONS
[config file: vectorAntialias]
-opw password
- Specify the owner password for the PDF file. Providing this
+ Specify the owner password for the PDF file. Providing this
will bypass all security restrictions.
-upw password
@@ -88,14 +84,14 @@ EXIT CODES
99 Other error.
AUTHOR
- The pdftoppm software and documentation are copyright 1996-2011 Glyph &
+ The pdftoppm software and documentation are copyright 1996-2014 Glyph &
Cog, LLC.
SEE ALSO
- xpdf(1), pdftops(1), pdftotext(1), pdfinfo(1), pdffonts(1), pdfde-
- tach(1), pdfimages(1), xpdfrc(5)
+ xpdf(1), pdftops(1), pdftotext(1), pdftohtml(1), pdfinfo(1), pdf-
+ fonts(1), pdfdetach(1), pdftopng(1), pdfimages(1), xpdfrc(5)
http://www.foolabs.com/xpdf/
- 15 August 2011 pdftoppm(1)
+ 28 May 2014 pdftoppm(1)
diff --git a/doc/pdftoppm.hlp b/doc/pdftoppm.hlp
deleted file mode 100644
index 6c370ed..0000000
--- a/doc/pdftoppm.hlp
+++ /dev/null
@@ -1,110 +0,0 @@
-! 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 Porta-
- ble 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. If PPM-
- root is '-', the image is sent to stdout (this is probably only useful
- when converting a single page).
-
- ()
-
-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 configuration file com-
- mands. 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 raster-
- izer). 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. Providing 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-2011 Glyph &
- Cog, LLC.
-
- ()
-
-2 SEE_ALSO
-
- xpdf(1), pdftops(1), pdftotext(1), pdfinfo(1), pdffonts(1), pdfde-
- tach(1), pdfimages(1), xpdfrc(5)
- http://www.foolabs.com/xpdf/
-
- ()
-
diff --git a/doc/pdftops.1 b/doc/pdftops.1
index 9c68827..d9daf49 100644
--- a/doc/pdftops.1
+++ b/doc/pdftops.1
@@ -1,8 +1,8 @@
-.\" Copyright 1996-2011 Glyph & Cog, LLC
-.TH pdftops 1 "15 August 2011"
+.\" Copyright 1996-2014 Glyph & Cog, LLC
+.TH pdftops 1 "28 May 2014"
.SH NAME
pdftops \- Portable Document Format (PDF) to PostScript converter
-(version 3.03)
+(version 3.04)
.SH SYNOPSIS
.B pdftops
[options]
@@ -172,6 +172,7 @@ lower-left corner of the paper instead.
.B \-pagecrop
Treat the CropBox as the PDF page size. By default, the MediaBox is
used as the page size.
+.RB "[config file: " psUseCropBoxAsPage ]
.TP
.B \-duplex
Set the Duplex pagedevice entry in the PostScript file. This tells
@@ -221,15 +222,17 @@ Error related to PDF permissions.
99
Other error.
.SH AUTHOR
-The pdftops software and documentation are copyright 1996-2011 Glyph &
+The pdftops software and documentation are copyright 1996-2014 Glyph &
Cog, LLC.
.SH "SEE ALSO"
.BR xpdf (1),
.BR pdftotext (1),
+.BR pdftohtml (1),
.BR pdfinfo (1),
.BR pdffonts (1),
.BR pdfdetach (1),
.BR pdftoppm (1),
+.BR pdftopng (1),
.BR pdfimages (1),
.BR xpdfrc (5)
.br
diff --git a/doc/pdftops.cat b/doc/pdftops.cat
index 8104b37..9e119ac 100644
--- a/doc/pdftops.cat
+++ b/doc/pdftops.cat
@@ -4,7 +4,7 @@ pdftops(1) pdftops(1)
NAME
pdftops - Portable Document Format (PDF) to PostScript converter (ver-
- sion 3.03)
+ sion 3.04)
SYNOPSIS
pdftops [options] [PDF-file [PS-file]]
@@ -73,54 +73,54 @@ OPTIONS
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
+ -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
+ -opi Generate OPI comments for all images and forms which have OPI
information. (This option is only available if pdftops was com-
piled with OPI support.) [config file: psOPI]
-noembt1
- By default, any Type 1 fonts which are embedded in the PDF file
+ 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 Post-
- Script files larger, but may be necessary for readable output.
+ to substitute base fonts instead. Embedded fonts make Post-
+ Script 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 out-
- put. Also, some PostScript interpreters do not have TrueType
+ 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 out-
+ put. Also, some PostScript interpreters do not have TrueType
rasterizers. [config file: psEmbedTrueTypeFonts]
-noembcidps
- By default, any CID PostScript fonts which are embedded in the
- PDF file are copied into the PostScript file. This option dis-
+ By default, any CID PostScript fonts which are embedded in the
+ PDF file are copied into the PostScript file. This option dis-
ables that embedding. No attempt is made to substitute for non-
- embedded CID PostScript fonts. [config file: psEmbedCID-
+ embedded CID PostScript fonts. [config file: psEmbedCID-
PostScriptFonts]
-noembcidtt
By default, any CID TrueType fonts which are embedded in the PDF
- file are copied into the PostScript file. This option disables
+ file are copied into the PostScript file. This option disables
that embedding. No attempt is made to substitute for non-embed-
ded CID TrueType fonts. [config file: psEmbedCIDTrueTypeFonts]
-preload
- Convert PDF forms to PS procedures, and preload image data.
- This uses more memory in the PostScript interpreter, but gener-
- ates significantly smaller PS files in situations where, e.g.,
+ Convert PDF forms to PS procedures, and preload image data.
+ This uses more memory in the PostScript interpreter, but gener-
+ ates significantly smaller PS files in situations where, e.g.,
the same image is drawn on every page of a long document.
-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:
+ 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:
psPaperSize]
-paperw size
@@ -130,7 +130,7 @@ OPTIONS
Set the paper height, in points. [config file: psPaperSize]
-nocrop
- By default, output is cropped to the CropBox specified in the
+ By default, output is cropped to the CropBox specified in the
PDF file. This option disables cropping. [config file: psCrop]
-expand
@@ -151,7 +151,7 @@ OPTIONS
-pagecrop
Treat the CropBox as the PDF page size. By default, the Media-
- Box is used as the page size.
+ Box is used as the page size. [config file: psUseCropBoxAsPage]
-duplex
Set the Duplex pagedevice entry in the PostScript file. This
@@ -189,14 +189,14 @@ EXIT CODES
99 Other error.
AUTHOR
- The pdftops software and documentation are copyright 1996-2011 Glyph &
+ The pdftops software and documentation are copyright 1996-2014 Glyph &
Cog, LLC.
SEE ALSO
- xpdf(1), pdftotext(1), pdfinfo(1), pdffonts(1), pdfdetach(1),
- pdftoppm(1), pdfimages(1), xpdfrc(5)
+ xpdf(1), pdftotext(1), pdftohtml(1), pdfinfo(1), pdffonts(1), pdfde-
+ tach(1), pdftoppm(1), pdftopng(1), pdfimages(1), xpdfrc(5)
http://www.foolabs.com/xpdf/
- 15 August 2011 pdftops(1)
+ 28 May 2014 pdftops(1)
diff --git a/doc/pdftops.hlp b/doc/pdftops.hlp
deleted file mode 100644
index b93febd..0000000
--- a/doc/pdftops.hlp
+++ /dev/null
@@ -1,211 +0,0 @@
-! 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 configuration file com-
- mands. 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 separation handling
- is the same as for -level2Sep. [config 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 com-
- piled with OPI support.) [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 Post-
- Script 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 out-
- put. Also, some PostScript interpreters do not have TrueType
- rasterizers. [config file: psEmbedTrueTypeFonts]
-
- -noembcidps
- By default, any CID PostScript fonts which are embedded in the
- PDF file are copied into the PostScript file. This option dis-
- ables that embedding. No attempt is made to substitute for non-
- embedded CID PostScript fonts. [config file: psEmbedCID-
- PostScriptFonts]
-
- -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-embed-
- ded CID TrueType fonts. [config file: psEmbedCIDTrueTypeFonts]
-
- -preload
- Convert PDF forms to PS procedures, and preload image data.
- This uses more memory in the PostScript interpreter, but gener-
- ates significantly smaller PS files in situations where, e.g.,
- the same image is drawn on every page of a long document.
-
- -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:
- psPaperSize]
-
- -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 specified 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: psExpandS-
- maller]
-
- -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: psCenter]
-
- -pagecrop
- Treat the CropBox as the PDF page size. By default, the Media-
- Box 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. Providing 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 system-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-2011 Glyph &
- Cog, LLC.
-
- ()
-
-2 SEE_ALSO
-
- xpdf(1), pdftotext(1), pdfinfo(1), pdffonts(1), pdfdetach(1),
- pdftoppm(1), pdfimages(1), xpdfrc(5)
- http://www.foolabs.com/xpdf/
-
- ()
-
diff --git a/doc/pdftotext.1 b/doc/pdftotext.1
index 83bf7c6..12d7500 100644
--- a/doc/pdftotext.1
+++ b/doc/pdftotext.1
@@ -1,8 +1,8 @@
-.\" Copyright 1997-2011 Glyph & Cog, LLC
-.TH pdftotext 1 "15 August 2011"
+.\" Copyright 1997-2014 Glyph & Cog, LLC
+.TH pdftotext 1 "28 May 2014"
.SH NAME
pdftotext \- Portable Document Format (PDF) to text converter
-(version 3.03)
+(version 3.04)
.SH SYNOPSIS
.B pdftotext
[options]
@@ -47,21 +47,50 @@ Specifies the last page to convert.
.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.
+hyphenation, etc.) and output the text in reading order. If the
+.B \-fixed
+option is given, character spacing within each line will be determined
+by the specified character pitch.
+.TP
+.B \-table
+Table mode is similar to physical layout mode, but optimized for
+tabular data, with the goal of keeping rows and columns aligned (at
+the expense of inserting extra whitespace). If the
+.B \-fixed
+option is given, character spacing within each line will be determined
+by the specified character pitch.
+.TP
+.B \-lineprinter
+Line printer mode uses a strict fixed-character-pitch and -height
+layout. That is, the page is broken into a grid, and characters are
+placed into that grid. If the grid spacing is too small for the
+actual characters, the result is extra whitespace. If the grid
+spacing is too large, the result is missing whitespace. The grid
+spacing can be specified using the
+.B \-fixed
+and
+.B \-linespacing
+options.
+If one or both are not given on the command line, pdftotext will
+attempt to compute appropriate value(s).
+.TP
+.B \-raw
+Keep the text in content stream order. Depending on how the PDF file
+was generated, this may or may not be useful.
.TP
.BI \-fixed " number"
-Assume fixed-pitch (or tabular) text, with the specified character
-width (in points). This forces physical layout mode.
+Specify the character pitch (character width), in points, for physical
+layout, table, or line printer mode. This is ignored in all other
+modes.
.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.
+.BI \-linespacing " number"
+Specify the line spacing, in points, for line printer mode. This is
+ignored in all other modes.
.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.
+.B \-clip
+Text which is hidden because of clipping is removed before doing
+layout, and then added back in. This can be helpful for tables where
+clipped (invisible) text would overlap the next column.
.TP
.BI \-enc " encoding-name"
Sets the encoding to use for text output. The
@@ -127,15 +156,17 @@ Error related to PDF permissions.
99
Other error.
.SH AUTHOR
-The pdftotext software and documentation are copyright 1996-2011 Glyph
+The pdftotext software and documentation are copyright 1996-2014 Glyph
& Cog, LLC.
.SH "SEE ALSO"
.BR xpdf (1),
.BR pdftops (1),
+.BR pdftohtml (1),
.BR pdfinfo (1),
.BR pdffonts (1),
.BR pdfdetach (1),
.BR pdftoppm (1),
+.BR pdftopng (1),
.BR pdfimages (1),
.BR xpdfrc (5)
.br
diff --git a/doc/pdftotext.cat b/doc/pdftotext.cat
index 5c8a709..8217743 100644
--- a/doc/pdftotext.cat
+++ b/doc/pdftotext.cat
@@ -4,27 +4,27 @@ pdftotext(1) pdftotext(1)
NAME
pdftotext - Portable Document Format (PDF) to text converter (version
- 3.03)
+ 3.04)
SYNOPSIS
pdftotext [options] [PDF-file [text-file]]
DESCRIPTION
- Pdftotext converts Portable Document Format (PDF) files to plain text.
+ 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
+ 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
+ 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
+ (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 configuration file com-
+ Many of the following options can be set with configuration file com-
mands. These are listed in square brackets with the description of the
corresponding command line option.
@@ -35,22 +35,44 @@ OPTIONS
Specifies the last page to convert.
-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.
+ 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. If the
+ -fixed option is given, character spacing within each line will
+ be determined by the specified character pitch.
+
+ -table Table mode is similar to physical layout mode, but optimized for
+ tabular data, with the goal of keeping rows and columns aligned
+ (at the expense of inserting extra whitespace). If the -fixed
+ option is given, character spacing within each line will be
+ determined by the specified character pitch.
+
+ -lineprinter
+ Line printer mode uses a strict fixed-character-pitch and
+ -height layout. That is, the page is broken into a grid, and
+ characters are placed into that grid. If the grid spacing is
+ too small for the actual characters, the result is extra white-
+ space. If the grid spacing is too large, the result is missing
+ whitespace. The grid spacing can be specified using the -fixed
+ and -linespacing options. If one or both are not given on the
+ command line, pdftotext will attempt to compute appropriate
+ value(s).
+
+ -raw Keep the text in content stream order. Depending on how the PDF
+ file was generated, this may or may not be useful.
-fixed number
- Assume fixed-pitch (or tabular) text, with the specified charac-
- ter width (in points). This forces physical layout mode.
+ Specify the character pitch (character width), in points, for
+ physical layout, table, or line printer mode. This is ignored
+ in all other modes.
- -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.
+ -linespacing number
+ Specify the line spacing, in points, for line printer mode.
+ This is ignored in all other modes.
- -htmlmeta
- Generate a simple HTML file, including the meta information.
- This simply wraps the text in <pre> and </pre> and prepends the
- meta headers.
+ -clip Text which is hidden because of clipping is removed before doing
+ layout, and then added back in. This can be helpful for tables
+ where clipped (invisible) text would overlap the next column.
-enc encoding-name
Sets the encoding to use for text output. The encoding-name
@@ -102,14 +124,14 @@ EXIT CODES
99 Other error.
AUTHOR
- The pdftotext software and documentation are copyright 1996-2011 Glyph
+ The pdftotext software and documentation are copyright 1996-2014 Glyph
& Cog, LLC.
SEE ALSO
- xpdf(1), pdftops(1), pdfinfo(1), pdffonts(1), pdfdetach(1),
- pdftoppm(1), pdfimages(1), xpdfrc(5)
+ xpdf(1), pdftops(1), pdftohtml(1), pdfinfo(1), pdffonts(1), pdfde-
+ tach(1), pdftoppm(1), pdftopng(1), pdfimages(1), xpdfrc(5)
http://www.foolabs.com/xpdf/
- 15 August 2011 pdftotext(1)
+ 28 May 2014 pdftotext(1)
diff --git a/doc/pdftotext.hlp b/doc/pdftotext.hlp
deleted file mode 100644
index 651af91..0000000
--- a/doc/pdftotext.hlp
+++ /dev/null
@@ -1,127 +0,0 @@
-! Generated automatically by mantohlp
-1 pdftotext
-
- pdftotext - Portable Document Format (PDF) to text converter
-
- 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 configuration file com-
- mands. 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 physical layout of
- the text. The default is to 'undo' physical layout (columns,
- hyphenation, etc.) and output the text in reading order.
-
- -fixed number
- Assume fixed-pitch (or tabular) text, with the specified charac-
- ter width (in points). This forces physical layout mode.
-
- -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: textEncoding]
-
- -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. Providing 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 system-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-2011 Glyph
- & Cog, LLC.
-
- ()
-
-2 SEE_ALSO
-
- xpdf(1), pdftops(1), pdfinfo(1), pdffonts(1), pdfdetach(1),
- pdftoppm(1), pdfimages(1), xpdfrc(5)
- http://www.foolabs.com/xpdf/
-
- ()
-
diff --git a/doc/sample-xpdfrc b/doc/sample-xpdfrc
index 0df70a7..0a4234f 100644
--- a/doc/sample-xpdfrc
+++ b/doc/sample-xpdfrc
@@ -79,9 +79,8 @@
#----- misc settings
-# Enable t1lib, FreeType, and anti-aliased text.
+# Enable FreeType, and anti-aliased text.
-#enableT1lib yes
#enableFreeType yes
#antialias yes
diff --git a/doc/xpdf.1 b/doc/xpdf.1
index 05c0af9..c34ad7f 100644
--- a/doc/xpdf.1
+++ b/doc/xpdf.1
@@ -1,7 +1,7 @@
-.\" Copyright 1996-2011 Glyph & Cog, LLC
-.TH xpdf 1 "15 August 2011"
+.\" Copyright 1996-2014 Glyph & Cog, LLC
+.TH xpdf 1 "28 May 2014"
.SH NAME
-xpdf \- Portable Document Format (PDF) file viewer for X (version 3.03)
+xpdf \- Portable Document Format (PDF) file viewer for X (version 3.04)
.SH SYNOPSIS
.B xpdf
[options]
@@ -102,11 +102,6 @@ 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".
@@ -638,7 +633,11 @@ Redraw the window.
Raise the window to the front.
.TP
.B closeWindow
-Close the window.
+Close the window. If this was the last open window, clear the window,
+but don't quit from Xpdf.
+.TP
+.B closeWindowOrQuit
+Close the window. If this was the last open window, quit from Xpdf.
.TP
.BI run( external-command-string )
Run an external command. The following escapes are allowed in the
@@ -668,6 +667,13 @@ command string:
%% => %
.fi
+The external command string will often contain spaces, so the whole
+command must be quoted in the xpdfrc file:
+.nf
+
+ bind x "run(ls -l)"
+
+.fi
.TP
.B openOutline
Open the outline pane.
@@ -799,7 +805,7 @@ The default key bindings are as follows:
bind w any zoomFitWidth
bind alt-f any toggleFullScreenMode
bind ctrl-l any redraw
- bind ctrl-w any closeWindow
+ bind ctrl-w any closeWindowOrQuit
bind ? any about
bind q any quit
bind Q any quit
@@ -863,15 +869,17 @@ Error related to PDF permissions.
99
Other error.
.SH AUTHOR
-The xpdf software and documentation are copyright 1996-2011 Glyph &
+The xpdf software and documentation are copyright 1996-2014 Glyph &
Cog, LLC.
.SH "SEE ALSO"
.BR pdftops (1),
.BR pdftotext (1),
+.BR pdftohtml (1),
.BR pdfinfo (1),
.BR pdffonts (1),
.BR pdfdetach (1),
.BR pdftoppm (1),
+.BR pdftopng (1),
.BR pdfimages (1),
.BR xpdfrc (5)
.br
diff --git a/doc/xpdf.cat b/doc/xpdf.cat
index eab46ab..965b310 100644
--- a/doc/xpdf.cat
+++ b/doc/xpdf.cat
@@ -3,7 +3,7 @@ xpdf(1) xpdf(1)
NAME
- xpdf - Portable Document Format (PDF) file viewer for X (version 3.03)
+ xpdf - Portable Document Format (PDF) file viewer for X (version 3.04)
SYNOPSIS
xpdf [options] [PDF-file [page | +dest]]
@@ -60,15 +60,15 @@ OPTIONS
serve color table entries. This is ignored with private col-
ormaps 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.reverseV-
+ -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.reverseV-
ideo]
-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
+ 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
@@ -87,16 +87,12 @@ OPTIONS
-cont Start in continuous view mode, i.e., with one vertical 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 raster-
+ Enable or disable FreeType (a TrueType / Type 1 font raster-
izer). This defaults to "yes". [config file: enableFreeType]
-aa yes | no
- Enable or disable font anti-aliasing. This defaults to "yes".
+ Enable or disable font anti-aliasing. This defaults to "yes".
[config file: antialias]
-aaVector yes | no
@@ -104,15 +100,15 @@ OPTIONS
[config file: vectorAntialias]
-ps PS-file
- Set the default file name for PostScript output (i.e., the name
+ 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. [con-
fig 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:
+ 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:
psPaperSize]
-paperw size
@@ -122,14 +118,14 @@ OPTIONS
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
+ 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)).
+ 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). [con-
fig file: textEncoding]
@@ -138,7 +134,7 @@ OPTIONS
file: textEOL]
-opw password
- Specify the owner password for the PDF file. Providing this
+ Specify the owner password for the PDF file. Providing this
will bypass all security restrictions.
-upw password
@@ -148,11 +144,11 @@ OPTIONS
Open xpdf in full-screen mode, useful for presentations.
-remote name
- Start/contact xpdf remote server with specified name (see the
+ 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
+ Execute a command (see the COMMANDS section below) in an xpdf
remote server window (with -remote only).
-reload
@@ -162,7 +158,7 @@ OPTIONS
-quit Kill xpdf remote server (with -remote only).
- -cmd Print commands as they're executed (useful for debugging).
+ -cmd Print commands as they're executed (useful for debugging).
[config file: printCommands]
-q Don't print any messages or errors. [config file: errQuiet]
@@ -199,7 +195,7 @@ OPTIONS
tips on the toolbar buttons.
xpdf.fullScreenMatteColor
- Sets the matte color to be used in full-screen mode. The
+ Sets the matte color to be used in full-screen mode. The
default setting is "black".
CONTROLS
@@ -214,19 +210,19 @@ CONTROLS
Move backward or forward along the history path.
'Page' entry box
- Move to a specific page number. Click in the box to activate
+ 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
+ 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
+ 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'.
@@ -240,7 +236,6 @@ CONTROLS
'Quit' button
Quit xpdf.
-
Menu
Pressing the right mouse button will post a popup menu with the follow-
ing commands:
@@ -249,11 +244,11 @@ CONTROLS
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
+ 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
+ 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...
@@ -266,50 +261,45 @@ CONTROLS
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
+ 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
+ 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
+ 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 bot-
tom 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
+ 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
+ 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
+ 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
@@ -327,14 +317,14 @@ CONTROLS
control-P
Print.
- n Move to the next page. Scrolls to the top of the page, unless
+ 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,
+ 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 bottom, move to
+ Scroll down on the current page; if already at bottom, move to
next page.
<Backspace> or <Delete> or <PageUp> or <Previous>
@@ -374,9 +364,9 @@ CONTROLS
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
+ 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
@@ -388,17 +378,17 @@ WEB BROWSERS
Make sure that xpdf is on your executable search path.
- When you click on a URL link in a PDF file, xpdf will execute the com-
- mand specified by the urlCommand config file option, replacing an
- occurrence of '%s' with the URL. For example, to call netscape with
+ When you click on a URL link in a PDF file, xpdf will execute the com-
+ mand 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
+ 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
@@ -418,14 +408,14 @@ COMMANDS
Go to the last page in the PDF file.
gotoLastPageNoScroll
- Go to the last page in the PDF file, with the current relative
+ Go to the last page in the PDF file, with the current relative
scroll position.
nextPage
Go to the next page.
nextPageNoScroll
- Go to the next page, with the current relative scroll position.
+ Go to the next page, with the current relative scroll position.
prevPage
Go to the previous page.
@@ -459,19 +449,19 @@ COMMANDS
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
+ 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 horizon-
+ Scroll to the bottom edge of the current page, with no horizon-
tal movement.
scrollToLeftEdge
- Scroll to the left edge of the current page, with no vertical
+ 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
+ Scroll to the right edge of the current page, with no vertical
movement.
scrollToTopLeft
@@ -507,7 +497,7 @@ COMMANDS
Rotate the page 90 degrees counterclockwise.
setSelection(pg,ulx,uly,lrx,lry)
- Set the selection to the specified coordinates on the specified
+ Set the selection to the specified coordinates on the specified
page.
continuousMode
@@ -540,7 +530,7 @@ COMMANDS
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 des-
+ Open a specified PDF file in this window and go to a named des-
tination.
openFileAtDestInNewWin(file,dest)
@@ -554,10 +544,15 @@ COMMANDS
raise Raise the window to the front.
closeWindow
- Close the window.
+ Close the window. If this was the last open window, clear the
+ window, but don't quit from Xpdf.
+
+ closeWindowOrQuit
+ Close the window. If this was the last open window, quit from
+ Xpdf.
run(external-command-string)
- Run an external command. The following escapes are allowed in
+ Run an external command. The following escapes are allowed in
the command string:
%f => PDF file name (or an empty string if no
@@ -582,6 +577,11 @@ COMMANDS
%k => y coordinate of the mouse pointer
%% => %
+ The external command string will often contain spaces, so the
+ whole command must be quoted in the xpdfrc file:
+
+ bind x "run(ls -l)"
+
openOutline
Open the outline pane.
@@ -703,7 +703,7 @@ COMMANDS
bind w any zoomFitWidth
bind alt-f any toggleFullScreenMode
bind ctrl-l any redraw
- bind ctrl-w any closeWindow
+ bind ctrl-w any closeWindowOrQuit
bind ? any about
bind q any quit
bind Q any quit
@@ -755,14 +755,14 @@ EXIT CODES
99 Other error.
AUTHOR
- The xpdf software and documentation are copyright 1996-2011 Glyph &
+ The xpdf software and documentation are copyright 1996-2014 Glyph &
Cog, LLC.
SEE ALSO
- pdftops(1), pdftotext(1), pdfinfo(1), pdffonts(1), pdfdetach(1),
- pdftoppm(1), pdfimages(1), xpdfrc(5)
+ pdftops(1), pdftotext(1), pdftohtml(1), pdfinfo(1), pdffonts(1), pdfde-
+ tach(1), pdftoppm(1), pdftopng(1), pdfimages(1), xpdfrc(5)
http://www.foolabs.com/xpdf/
- 15 August 2011 xpdf(1)
+ 28 May 2014 xpdf(1)
diff --git a/doc/xpdf.hlp b/doc/xpdf.hlp
deleted file mode 100644
index 9696ec7..0000000
--- a/doc/xpdf.hlp
+++ /dev/null
@@ -1,782 +0,0 @@
-! Generated automatically by mantohlp
-1 xpdf
-
- xpdf - Portable Document Format (PDF) file viewer for X (version 3.03)
-
- 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 configuration file com-
- mands 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 visu-
- als. [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 con-
- serve color table entries. This is ignored with private col-
- ormaps 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.reverseV-
- ideo]
-
- -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 background 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 percent-
- age, 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. [config file: initialZoom; or X
- resource: xpdf.initialZoom]
-
- -cont Start in continuous view mode, i.e., with one vertical 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 raster-
- izer). 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 dialog). This can also be of the
- form '|command' to pipe the PostScript through a command. [con-
- fig 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:
- psPaperSize]
-
- -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). [con-
- fig file: textEncoding]
-
- -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. Providing 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 presentations.
-
- -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 system-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 follow-
- ing 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 vertical split bar via the knob near its bot-
- tom 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', 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 bottom, move to
- next page.
-
- <Backspace> or <Delete> or <PageUp> or <Previous>
- Scroll up on the current page; if already at top, move to previ-
- ous 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 execute the com-
- mand 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 posi-
- tion.
-
- 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 current 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 posi-
- tion.
-
- 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 appropri-
- ate.
-
- 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 horizon-
- tal 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.
-
- rotateCW
- Rotate the page 90 degrees clockwise.
-
- rotateCCW
- Rotate the page 90 degrees counterclockwise.
-
- setSelection(pg,ulx,uly,lrx,lry)
- Set the selection to the specified coordinates on the specified
- page.
-
- 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 des-
- tination.
-
- openFileAtDestInNewWin(file,dest)
- Open a specified PDF file in a new window and go to a named des-
- tination.
-
- 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)
- %p => current page number (or an empty string if
- no file is open)
- %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)
- %i => page containing the mouse pointer
- %j => x coordinate of the mouse pointer
- %k => y coordinate of the mouse pointer
- %% => %
-
- 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 position:
-
- 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 window (does
- nothing if the mouse is not over a link). For links to non-PDF
- files, this command is identical 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 number). 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 spec-
- ified with or without a file name and page number.
-
- 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-2011 Glyph &
- Cog, LLC.
-
- ()
-
-2 SEE_ALSO
-
- pdftops(1), pdftotext(1), pdfinfo(1), pdffonts(1), pdfdetach(1),
- pdftoppm(1), pdfimages(1), xpdfrc(5)
- http://www.foolabs.com/xpdf/
-
- ()
-
diff --git a/doc/xpdfrc.5 b/doc/xpdfrc.5
index 676ad49..afb8d95 100644
--- a/doc/xpdfrc.5
+++ b/doc/xpdfrc.5
@@ -1,7 +1,7 @@
-.\" Copyright 2002-2011 Glyph & Cog, LLC
-.TH xpdfrc 5 "15 August 2011"
+.\" Copyright 2002-2014 Glyph & Cog, LLC
+.TH xpdfrc 5 "28 May 2014"
.SH NAME
-xpdfrc \- configuration file for Xpdf tools (version 3.03)
+xpdfrc \- configuration file for Xpdf tools (version 3.04)
.SH DESCRIPTION
All of the Xpdf tools read a single configuration file. If you have a
.I .xpdfrc
@@ -17,6 +17,9 @@ The xpdfrc file consists of a series of configuration options, one
per line. Blank lines and lines starting with a \'#' (comments) are
ignored.
.PP
+Arguments may be quoted, using "double-quote" characters, e.g., for
+file names that contain spaces.
+.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
@@ -259,6 +262,10 @@ 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 psUseCropBoxAsPage " yes | no"
+If set to "yes", PostScript output treats the CropBox as the page size.
+By default, this is "no", and the MediaBox is used as the page size.
+.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
@@ -299,12 +306,23 @@ the Xpdf tools were compiled with OPI support. This defaults to "no".
If set to "yes", the ASCIIHexEncode filter will be used instead of
ASCII85Encode for binary data. This defaults to "no".
.TP
+.BR psLZW " yes | no"
+If set to "yes", the LZWEncode filter will be used for lossless
+compression in PostScript output; if set to "no", the RunLengthEncode
+filter will be used instead. LZW generates better compression
+(smaller PS files), but may not be supported by some printers. This
+defaults to "yes".
+.TP
.BR psUncompressPreloadedImages " yes | no"
If set to "yes", all preloaded images in PS files will uncompressed.
If set to "no", the original compressed images will be used when
possible. The "yes" setting is useful to work around certain buggy
PostScript interpreters. This defaults to "no".
.TP
+.BR psMinLineWidth " float"
+Set the minimum line width, in points, for PostScript output. The
+default value is 0 (no minimum).
+.TP
.BR psRasterResolution " float"
Set the resolution (in dpi) for rasterized pages in PostScript output.
(Pdftops will rasterize pages which use transparency.) This defaults
@@ -314,6 +332,11 @@ to 300.
If set to "yes", rasterized pages in PS files will be monochrome
(8-bit gray) instead of color. This defaults to "no".
.TP
+.BR psRasterSliceSize " pixels"
+When rasterizing pages, pdftops splits the page into horizontal
+"slices", to limit memory usage. This option sets the maximum slice
+size, in pixels. This defaults to 20000000 (20 million).
+.TP
.BR psAlwaysRasterize " yes | no"
If set to "yes", all PostScript output will be rasterized. This
defaults to "no".
@@ -359,7 +382,7 @@ 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".
+cross-hatching. This defaults to "yes".
.SH MISCELLANEOUS SETTINGS
.TP
.BR initialZoom " \fIpercentage\fR | page | width"
@@ -373,12 +396,6 @@ 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
@@ -499,6 +516,21 @@ setting mapNumericCharNames to "no" is unnecessary.) 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 mapExtTrueTypeFontsViaUnicode " yes | no"
+When rasterizing text using an external TrueType font, there are two
+options for handling character codes. If
+mapExtTrueTypeFontsViaUnicode is set to "yes", Xpdf will use the font
+encoding/ToUnicode info to map character codes to Unicode, and then
+use the font's Unicode cmap to map Unicode to GIDs. If
+mapExtTrueTypeFontsViaUnicode is set to "no", Xpdf will assume the
+character codes are GIDs (i.e., use an identity mapping). This
+defaults to "yes".
+.TP
+.BI enableXFA " yes | no"
+If set to "yes", an XFA form (if present) will be rendered in place of
+an AcroForm. If "no", an XFA form will never be rendered. This
+defaults to "yes".
+.TP
.BI bind " modifiers-key context command ..."
Add a key or mouse button binding.
.I Modifiers
@@ -633,7 +665,6 @@ textEncoding UTF-8
textEOL unix
# misc options
-enableT1lib yes
enableFreeType yes
launchCommand viewer-script
urlCommand "netscape \-remote 'openURL(%s)'"
@@ -649,16 +680,18 @@ Depending on build options, it may be placed elsewhere.
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-2011 Glyph &
+The Xpdf software and documentation are copyright 1996-2014 Glyph &
Cog, LLC.
.SH "SEE ALSO"
.BR xpdf (1),
.BR pdftops (1),
.BR pdftotext (1),
+.BR pdftohtml (1),
.BR pdfinfo (1),
.BR pdffonts (1),
.BR pdfdetach (1),
.BR pdftoppm (1),
+.BR pdftopng (1),
.BR pdfimages (1)
.br
.B http://www.foolabs.com/xpdf/
diff --git a/doc/xpdfrc.cat b/doc/xpdfrc.cat
index db42866..b8faf8a 100644
--- a/doc/xpdfrc.cat
+++ b/doc/xpdfrc.cat
@@ -3,7 +3,7 @@ xpdfrc(5) xpdfrc(5)
NAME
- xpdfrc - configuration file for Xpdf tools (version 3.03)
+ xpdfrc - configuration file for Xpdf tools (version 3.04)
DESCRIPTION
All of the Xpdf tools read a single configuration file. If you have a
@@ -17,65 +17,68 @@ DESCRIPTION
line. Blank lines and lines starting with a '#' (comments) are
ignored.
- The following sections list all of the configuration options, sorted
+ Arguments may be quoted, using "double-quote" characters, e.g., for
+ file names that contain spaces.
+
+ The following sections list all of the configuration options, sorted
into functional groups. There is an examples 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.
+ 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 Uni-
- code. This is used to handle PDF fonts that have valid encod-
- ings but no ToUnicode entry. Each line of a nameToUnicode file
+ Specifies a file with the mapping from character names to Uni-
+ code. This is used to handle PDF fonts that have valid encod-
+ ings 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
+ 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 char-
+ Unicode. Each line of a cidToUnicode file represents one char-
acter:
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
+ 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 specified
file is used. There are no built-in cidToUnicode mappings.
unicodeToUnicode font-name-substring map-file
- This is used to work around PDF fonts which have incorrect Uni-
+ This is used to work around PDF fonts which have incorrect Uni-
code 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-sub-
- string. Each line of a unicodeToUnicode file represents one
+ (incorrect) Unicode indexes to the correct ones. The mapping
+ will be used for any font whose name contains font-name-sub-
+ string. Each line of a unicodeToUnicode 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
+ 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.
+ Specifies the file with mapping from Unicode to encoding-name.
These encodings are used for 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 encod-
+ of a unicodeMap file represents a range of one or more Unicode
+ characters which maps linearly to a range in the output encod-
ing:
in-start-hex in-end-hex out-start-hex
@@ -84,149 +87,154 @@ CHARACTER MAPPING
in-hex out-hex
- The in-start-hex and in-end-hex fields (or the single in-hex
- field) specify the Unicode range. The out-start-hex field (or
- the out-hex field) specifies the start of the output encoding
- range. The length of the out-start-hex (or out-hex) string
+ The in-start-hex and in-end-hex fields (or the single in-hex
+ field) specify the Unicode range. The out-start-hex field (or
+ the out-hex field) specifies 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 specified file
- is used. The Latin1, ASCII7, Symbol, ZapfDingbats, UTF-8, and
+ 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 Latin1, ASCII7, Symbol, ZapfDingbats, UTF-8, and
UCS-2 encodings are predefined.
cMapDir registry-ordering dir
- Specifies a search directory, dir, for CMaps for the reg-
- istry-ordering character collection. There can be multiple
- directories for a particular collection. There are no default
+ Specifies a search directory, dir, for CMaps for the reg-
+ istry-ordering character collection. There can be multiple
+ directories for a particular collection. There are no default
CMap directories.
toUnicodeDir dir
- Specifies a search directory, dir, for ToUnicode CMaps. There
- can be multiple ToUnicode directories. There are no default
+ Specifies a search directory, dir, for ToUnicode CMaps. There
+ can be multiple ToUnicode directories. There are no default
ToUnicode directories.
GENERAL FONT CONFIGURATION
fontFile PDF-font-name font-file
- Maps a PDF font, PDF-font-name, to a font for display or Post-
- Script output. The font file, font-file, can be any type
- allowed in a PDF file. This command can be used for 8-bit or
+ Maps a PDF font, PDF-font-name, to a font for display or Post-
+ Script output. The font file, font-file, can be any type
+ allowed in a PDF file. This command can be used for 8-bit or
16-bit (CID) fonts.
fontDir dir
- Specifies a search directory for font files. There can be mul-
+ Specifies a search directory for font files. There can be mul-
tiple fontDir commands; all of the specified directories will be
- searched in order. The font files can be Type 1 (.pfa or .pfb)
+ searched in order. The font files can be Type 1 (.pfa or .pfb)
or TrueType (.ttf or .ttc); other files in the directory will be
- ignored. The font file name (not including the extension) must
- exactly match the PDF font name. This search is performed if
- the font name doesn't match any of the fonts declared with the
+ ignored. The font file name (not including the extension) must
+ exactly match the PDF font name. This search is performed if
+ the font name doesn't match any of the fonts declared with the
fontFile command. There are no default fontDir directories.
fontFileCC registry-ordering font-file
- Maps the registry-ordering character collection to a font for
- display or PostScript output. This mapping is used if the font
- name doesn't match any of the fonts declared with the fontFile,
+ Maps the registry-ordering character collection to a font for
+ display or PostScript output. This mapping is used if the font
+ name doesn't match any of the fonts declared with the fontFile,
fontDir, psResidentFont16, or psResidentFontCC commands.
POSTSCRIPT FONT CONFIGURATION
psFontPassthrough yes | no
If set to "yes", pass 8-bit font names through to the PostScript
- output without substitution. Fonts which are not embedded in
- the PDF file are expected to be available on the printer. This
+ output without substitution. Fonts which are not embedded in
+ the PDF file are expected to be available on the printer. This
defaults to "no".
psResidentFont PDF-font-name PS-font-name
When the 8-bit font PDF-font-name is used (without embedding) 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
+ 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.
psResidentFont16 PDF-font-name wMode PS-font-name encoding
When the 16-bit (CID) font PDF-font-name with writing mode wMode
is used (without embedding) in a PDF file, it will be translated
- to the PostScript font PS-font-name, which is assumbed to be
- resident in the printer. The writing mode must be either 'H'
- for horizontal or 'V' for vertical. The resident font is
- assumed to use the specified encoding (which must have been
+ to the PostScript font PS-font-name, which is assumbed to be
+ resident in the printer. The writing mode must be either 'H'
+ for horizontal or 'V' for vertical. The resident font is
+ assumed to use the specified encoding (which must have been
defined with the unicodeMap command).
psResidentFontCC registry-ordering wMode PS-font-name encoding
- When a 16-bit (CID) font using the registry-ordering character
+ When a 16-bit (CID) font using the registry-ordering character
collection and wMode writing mode is used (without embedding) in
- a PDF file, the PostScript font, PS-font-name, is substituted
- for it. The substituted font is assumbed to be resident in the
- printer. The writing mode must be either 'H' for horizontal or
+ a PDF file, the PostScript font, PS-font-name, is substituted
+ for it. The substituted font is assumbed to be resident in the
+ printer. The writing mode must be either 'H' for horizontal or
'V' for vertical. The resident font is assumed to use the spec-
ified 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
+ 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 gener-
+ If set to "no", prevents embedding of TrueType fonts in gener-
ated PostScript. This defaults to "yes".
psEmbedCIDTrueTypeFonts yes | no
If set to "no", prevents embedding of CID TrueType fonts in gen-
erated PostScript. For Level 3 PostScript, this generates a CID
- font, for lower levels it generates a non-CID composite font.
+ font, for lower levels it generates a non-CID composite font.
This defaults to "yes".
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
+ 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. This defaults to "yes".
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 =
+ 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,
+ 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 inte-
- gers are the coordinates of the lower-left and upper-right cor-
+ Sets the imageable area for PostScript output. The four inte-
+ gers are the coordinates of the lower-left and upper-right cor-
ners of the imageable region, specified in points (with the ori-
gin being the lower-left corner of the paper). This defaults to
- the full paper size; the psPaperSize option will reset the
+ 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
+ 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".
+ psUseCropBoxAsPage yes | no
+ If set to "yes", PostScript output treats the CropBox as the
+ page size. By default, this is "no", and the MediaBox is used
+ as the page size.
+
psExpandSmaller yes | no
If set to "yes", PDF pages smaller than the PostScript imageable
- area are expanded to fill the imageable area. Otherwise, no
+ 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 scal-
+ If set to yes, PDF pages larger than the PostScript imageable
+ area are shrunk to fit the imageable area. Otherwise, no scal-
ing 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
+ 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".
psDuplex yes | no
- If set to "yes", the generated PostScript will set the "Duplex"
- pagedevice entry. This tells duplex-capable printers to enable
+ 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
@@ -234,28 +242,39 @@ POSTSCRIPT CONTROL
"level2".
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 Post-
+ If set to "yes", PDF forms are converted to PS procedures, and
+ image data is preloaded. This uses more memory in the Post-
Script 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".
psOPI yes | no
- If set to "yes", generates PostScript OPI comments for all
- images and forms which have OPI information. This option is
+ 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".
psASCIIHex yes | no
- If set to "yes", the ASCIIHexEncode filter will be used instead
+ If set to "yes", the ASCIIHexEncode filter will be used instead
of ASCII85Encode for binary data. This defaults to "no".
+ psLZW yes | no
+ If set to "yes", the LZWEncode filter will be used for lossless
+ compression in PostScript output; if set to "no", the RunLength-
+ Encode filter will be used instead. LZW generates better com-
+ pression (smaller PS files), but may not be supported by some
+ printers. This defaults to "yes".
+
psUncompressPreloadedImages yes | no
- If set to "yes", all preloaded images in PS files will uncom-
+ If set to "yes", all preloaded images in PS files will uncom-
pressed. If set to "no", the original compressed images will be
- used when possible. The "yes" setting is useful to work around
+ used when possible. The "yes" setting is useful to work around
certain buggy PostScript interpreters. This defaults to "no".
+ psMinLineWidth float
+ Set the minimum line width, in points, for PostScript output.
+ The default value is 0 (no minimum).
+
psRasterResolution float
Set the resolution (in dpi) for rasterized pages in PostScript
output. (Pdftops will rasterize pages which use transparency.)
@@ -265,6 +284,11 @@ POSTSCRIPT CONTROL
If set to "yes", rasterized pages in PS files will be monochrome
(8-bit gray) instead of color. This defaults to "no".
+ psRasterSliceSize pixels
+ When rasterizing pages, pdftops splits the page into horizontal
+ "slices", to limit memory usage. This option sets the maximum
+ slice size, in pixels. This defaults to 20000000 (20 million).
+
psAlwaysRasterize yes | no
If set to "yes", all PostScript output will be rasterized. This
defaults to "no".
@@ -307,7 +331,7 @@ TEXT CONTROL
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".
+ do shading or cross-hatching. This defaults to "yes".
MISCELLANEOUS SETTINGS
initialZoom percentage | page | width
@@ -321,136 +345,145 @@ MISCELLANEOUS SETTINGS
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
+ 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".
enableFreeType yes | no
- Enables or disables use of FreeType (a TrueType / Type 1 font
+ 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".
disableFreeTypeHinting yes | no
- If this is set to "yes", FreeType hinting will be forced off.
+ If this is set to "yes", FreeType hinting will be forced off.
This option defaults to "no".
antialias yes | no
- Enables or disables font anti-aliasing in the PDF rasterizer.
+ 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".
vectorAntialias yes | no
- Enables or disables anti-aliasing of vector graphics in the PDF
+ Enables or disables anti-aliasing of vector graphics in the PDF
rasterizer. This defaults to "yes".
antialiasPrinting yes | no
- If this is "yes", bitmaps sent to the printer will be
- antialiased (according to the "antialias" and "vectorAntialias"
- settings). If this is "no", printed bitmaps will not be
+ If this is "yes", bitmaps sent to the printer will be
+ antialiased (according to the "antialias" and "vectorAntialias"
+ settings). If this is "no", printed bitmaps will not be
antialiased. This defaults to "no".
strokeAdjust yes | no
- Enables or disables stroke adjustment. Stroke adjustment moves
+ Enables or disables stroke adjustment. Stroke adjustment moves
horizontal and vertical lines by up to half a pixel to make them
- look "cleaner" when vector anti-aliasing is enabled. This
+ look "cleaner" when vector anti-aliasing is enabled. This
defaults to "yes".
screenType dispersed | clustered | stochasticClustered
- Sets the halftone screen type, which will be used when generat-
- ing a monochrome (1-bit) bitmap. The three options are dis-
- persed-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 reso-
+ Sets the halftone screen type, which will be used when generat-
+ ing a monochrome (1-bit) bitmap. The three options are dis-
+ persed-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 reso-
lutions of 300 dpi and higher, and "dispersed" is used for reso-
lutions lower then 300 dpi.
screenSize integer
- Sets the size of the (square) halftone screen threshold matrix.
- By default, this is 4 for dispersed-dot dithering, 10 for clus-
- tered-dot dithering, and 100 for stochastic clustered-dot
+ Sets the size of the (square) halftone screen threshold matrix.
+ By default, this is 4 for dispersed-dot dithering, 10 for clus-
+ tered-dot dithering, and 100 for stochastic clustered-dot
dithering.
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
+ 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 parameter. Gamma val-
- ues greater than 1 make the output brighter; gamma values less
+ ues 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
+ 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
+ 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.
minLineWidth float
- Set the minimum line width, in device pixels. This affects the
- rasterizer only, not the PostScript converter (except when it
- uses rasterization to handle transparency). The default value
+ Set the minimum line width, in device pixels. This affects the
+ rasterizer only, not the PostScript converter (except when it
+ uses rasterization to handle transparency). The default value
is 0 (no minimum).
drawAnnotations yes | no
- If set to "no", annotations will not be drawn or printed. The
+ If set to "no", annotations will not be drawn or printed. The
default value is "yes".
overprintPreview yes | no
If set to "yes", generate overprint preview output, honoring the
- OP/op/OPM settings in the PDF file. Ignored for non-CMYK out-
+ OP/op/OPM settings in the PDF file. Ignored for non-CMYK out-
put. The default value is "no".
launchCommand command
- Sets the command executed when you click on a "launch"-type
- link. The intent is for the command to be a program/script
- which determines the file type and runs the appropriate viewer.
- The command line will consist of the file to be launched, fol-
- lowed by any parameters specified with the link. Do not use
- "%s" in "command". By default, this is unset, and Xpdf will
+ Sets the command executed when you click on a "launch"-type
+ link. The intent is for the command to be a program/script
+ which determines the file type and runs the appropriate viewer.
+ The command line will consist of the file to be launched, fol-
+ lowed by any parameters specified with the link. Do not use
+ "%s" in "command". By default, this is unset, and Xpdf will
simply try to execute the file (after prompting the user).
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
+ 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.
+ 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
+ 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
+ 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. (For
- CID fonts, setting mapNumericCharNames to "no" is unnecessary.)
- 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
+ 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. (For
+ CID fonts, setting mapNumericCharNames to "no" is unnecessary.)
+ 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".
+ mapExtTrueTypeFontsViaUnicode yes | no
+ When rasterizing text using an external TrueType font, there are
+ two options for handling character codes. If mapExtTrueType-
+ FontsViaUnicode is set to "yes", Xpdf will use the font encod-
+ ing/ToUnicode info to map character codes to Unicode, and then
+ use the font's Unicode cmap to map Unicode to GIDs. If mapExt-
+ TrueTypeFontsViaUnicode is set to "no", Xpdf will assume the
+ character codes are GIDs (i.e., use an identity mapping). This
+ defaults to "yes".
+
+ enableXFA yes | no
+ If set to "yes", an XFA form (if present) will be rendered in
+ place of an AcroForm. If "no", an XFA form will never be ren-
+ dered. This defaults to "yes".
+
bind modifiers-key context command ...
Add a key or mouse button binding. Modifiers can be zero or
more of:
@@ -572,7 +605,6 @@ EXAMPLES
textEOL unix
# misc options
- enableT1lib yes
enableFreeType yes
launchCommand viewer-script
urlCommand "netscape -remote 'openURL(%s)'"
@@ -588,14 +620,14 @@ FILES
read in place of the system-wide file.
AUTHOR
- The Xpdf software and documentation are copyright 1996-2011 Glyph &
+ The Xpdf software and documentation are copyright 1996-2014 Glyph &
Cog, LLC.
SEE ALSO
- xpdf(1), pdftops(1), pdftotext(1), pdfinfo(1), pdffonts(1), pdfde-
- tach(1), pdftoppm(1), pdfimages(1)
+ xpdf(1), pdftops(1), pdftotext(1), pdftohtml(1), pdfinfo(1), pdf-
+ fonts(1), pdfdetach(1), pdftoppm(1), pdftopng(1), pdfimages(1)
http://www.foolabs.com/xpdf/
- 15 August 2011 xpdfrc(5)
+ 28 May 2014 xpdfrc(5)
diff --git a/doc/xpdfrc.hlp b/doc/xpdfrc.hlp
deleted file mode 100644
index 224456c..0000000
--- a/doc/xpdfrc.hlp
+++ /dev/null
@@ -1,612 +0,0 @@
-! Generated automatically by mantohlp
-1 xpdfrc
-2 NCLUDE_FILE
-
- xpdfrc - configuration file for Xpdf tools (version 3.03)
-
- 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 Uni-
- code. This is used to handle PDF fonts that have valid encod-
- ings 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 char-
- acter:
-
- 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 specified
- file is used. There are no built-in cidToUnicode mappings.
-
- unicodeToUnicode font-name-substring map-file
- This is used to work around PDF fonts which have incorrect Uni-
- code 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-sub-
- string. Each line of a unicodeToUnicode 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 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 encod-
- ing:
-
- 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 single in-hex
- field) specify the Unicode range. The out-start-hex field (or
- the out-hex field) specifies 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 specified file
- is used. The Latin1, ASCII7, Symbol, ZapfDingbats, UTF-8, and
- UCS-2 encodings are predefined.
-
- cMapDir registry-ordering dir
- Specifies a search directory, dir, for CMaps for the reg-
- istry-ordering character collection. There can be multiple
- directories for a particular collection. There are no default
- CMap directories.
-
- toUnicodeDir dir
- Specifies a search directory, dir, for ToUnicode CMaps. There
- can be multiple ToUnicode directories. There are no default
- ToUnicode directories.
-
- ()
-
-2 ENERAL_FONT_CONFIGURATIO
-
- fontFile PDF-font-name font-file
- Maps a PDF font, PDF-font-name, to a font for display or Post-
- Script output. The font file, font-file, can be any type
- allowed in a PDF file. This command can be used for 8-bit or
- 16-bit (CID) fonts.
-
- fontDir dir
- Specifies a search directory for font files. There can be mul-
- tiple fontDir commands; all of the specified directories will be
- searched in order. The font files can be Type 1 (.pfa or .pfb)
- or TrueType (.ttf or .ttc); other files in the directory will be
- ignored. The font file name (not including the extension) must
- exactly match the PDF font name. This search is performed if
- the font name doesn't match any of the fonts declared with the
- fontFile command. There are no default fontDir directories.
-
- fontFileCC registry-ordering font-file
- Maps the registry-ordering character collection to a font for
- display or PostScript output. This mapping is used if the font
- name doesn't match any of the fonts declared with the fontFile,
- fontDir, psResidentFont16, or psResidentFontCC commands.
-
- ()
-
-2 OSTSCRIPT_FONT_CONFIGURATIO
-
- psFontPassthrough yes | no
- If set to "yes", pass 8-bit font names through to the PostScript
- output without substitution. Fonts which are not embedded in
- the PDF file are expected to be available on the printer. This
- defaults to "no".
-
- psResidentFont PDF-font-name PS-font-name
- When the 8-bit font PDF-font-name is used (without embedding) 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.
-
- psResidentFont16 PDF-font-name wMode PS-font-name encoding
- When the 16-bit (CID) font PDF-font-name with writing mode wMode
- is used (without embedding) in a PDF file, it will be translated
- to the PostScript font PS-font-name, which is assumbed to be
- resident in the printer. The writing mode must be either 'H'
- for horizontal or 'V' for vertical. The resident font is
- assumed to use the specified encoding (which must have been
- defined with the unicodeMap command).
-
- psResidentFontCC registry-ordering wMode PS-font-name encoding
- When a 16-bit (CID) font using the registry-ordering character
- collection and wMode writing mode is used (without embedding) in
- a PDF file, the PostScript font, PS-font-name, is substituted
- for it. The substituted font is assumbed to be resident in the
- printer. The writing mode must be either 'H' for horizontal or
- 'V' for vertical. The resident font is assumed to use the spec-
- ified 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 gener-
- ated PostScript. This defaults to "yes".
-
- psEmbedCIDTrueTypeFonts yes | no
- If set to "no", prevents embedding of CID TrueType fonts in gen-
- erated PostScript. For Level 3 PostScript, this generates a CID
- font, for lower levels it generates a non-CID composite font.
- This defaults to "yes".
-
- 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. This defaults to "yes".
-
- ()
-
-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 inte-
- gers are the coordinates of the lower-left and upper-right cor-
- ners of the imageable region, specified in points (with the ori-
- gin 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 scal-
- ing 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
- imageable 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".
-
- 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 Post-
- Script 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".
-
- 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".
-
- psASCIIHex yes | no
- If set to "yes", the ASCIIHexEncode filter will be used instead
- of ASCII85Encode for binary data. This defaults to "no".
-
- psUncompressPreloadedImages yes | no
- If set to "yes", all preloaded images in PS files will uncom-
- pressed. If set to "no", the original compressed images will be
- used when possible. The "yes" setting is useful to work around
- certain buggy PostScript interpreters. This defaults to "no".
-
- psRasterResolution float
- Set the resolution (in dpi) for rasterized pages in PostScript
- output. (Pdftops will rasterize pages which use transparency.)
- This defaults to 300.
-
- psRasterMono yes | no
- If set to "yes", rasterized pages in PS files will be monochrome
- (8-bit gray) instead of color. This defaults to "no".
-
- psAlwaysRasterize yes | no
- If set to "yes", all PostScript output will be rasterized. 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 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.
-
- 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 overrid-
- den with the "-enc" switch on the command line.) The encod-
- ing-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 per-
- centage, 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.
-
- 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
- "freetypeControl" 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
- "freetypeControl" option.) This option defaults to "yes".
-
- disableFreeTypeHinting yes | no
- If this is set to "yes", FreeType hinting will be forced off.
- This option defaults to "no".
-
- 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".
-
- vectorAntialias yes | no
- Enables or disables anti-aliasing of vector graphics in the PDF
- rasterizer. This defaults to "yes".
-
- antialiasPrinting yes | no
- If this is "yes", bitmaps sent to the printer will be
- antialiased (according to the "antialias" and "vectorAntialias"
- settings). If this is "no", printed bitmaps will not be
- antialiased. This defaults to "no".
-
- strokeAdjust yes | no
- Enables or disables stroke adjustment. Stroke adjustment moves
- horizontal and vertical lines by up to half a pixel to make them
- look "cleaner" when vector anti-aliasing is enabled. This
- defaults to "yes".
-
- screenType dispersed | clustered | stochasticClustered
- Sets the halftone screen type, which will be used when generat-
- ing a monochrome (1-bit) bitmap. The three options are dis-
- persed-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 reso-
- lutions of 300 dpi and higher, and "dispersed" is used for reso-
- lutions lower then 300 dpi.
-
- screenSize integer
- Sets the size of the (square) halftone screen threshold matrix.
- By default, this is 4 for dispersed-dot dithering, 10 for clus-
- tered-dot dithering, and 100 for stochastic clustered-dot
- dithering.
-
- 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 parameter. Gamma val-
- ues 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.
-
- minLineWidth float
- Set the minimum line width, in device pixels. This affects the
- rasterizer only, not the PostScript converter (except when it
- uses rasterization to handle transparency). The default value
- is 0 (no minimum).
-
- drawAnnotations yes | no
- If set to "no", annotations will not be drawn or printed. The
- default value is "yes".
-
- overprintPreview yes | no
- If set to "yes", generate overprint preview output, honoring the
- OP/op/OPM settings in the PDF file. Ignored for non-CMYK out-
- put. The default value is "no".
-
- launchCommand command
- Sets the command executed when you click on a "launch"-type
- link. The intent is for the command to be a program/script
- which determines the file type and runs the appropriate viewer.
- The command line will consist of the file to be launched, fol-
- lowed by any parameters specified with the link. Do not use
- "%s" in "command". By default, this is unset, and Xpdf will
- simply try to execute the file (after prompting the user).
-
- 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. (For
- CID fonts, setting mapNumericCharNames to "no" is unnecessary.)
- 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 combination 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 section of the
- 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:
-
- # 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 establish-
- ing new ones (e.g., if the default key binding is given for
- "any" context, and you want to create new key bindings for mul-
- tiple contexts).
-
- printCommands yes | no
- If set to "yes", drawing commands are printed as they're exe-
- cuted (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
- fontFile Times-Roman /usr/local/share/ghostscript/fonts/n021003l.pfb
- fontFile Times-Italic /usr/local/share/ghostscript/fonts/n021023l.pfb
- fontFile Times-Bold /usr/local/share/ghostscript/fonts/n021004l.pfb
- fontFile Times-BoldItalic /usr/local/share/ghostscript/fonts/n021024l.pfb
- fontFile Helvetica /usr/local/share/ghostscript/fonts/n019003l.pfb
- fontFile Helvetica-Oblique /usr/local/share/ghostscript/fonts/n019023l.pfb
- fontFile Helvetica-Bold /usr/local/share/ghostscript/fonts/n019004l.pfb
- fontFile Helvetica-BoldOblique /usr/local/share/ghostscript/fonts/n019024l.pfb
- fontFile Courier /usr/local/share/ghostscript/fonts/n022003l.pfb
- fontFile Courier-Oblique /usr/local/share/ghostscript/fonts/n022023l.pfb
- fontFile Courier-Bold /usr/local/share/ghostscript/fonts/n022004l.pfb
- fontFile Courier-BoldOblique /usr/local/share/ghostscript/fonts/n022024l.pfb
- fontFile Symbol /usr/local/share/ghostscript/fonts/s050000l.pfb
- fontFile 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
- psResidentFont Univers Univers
- psResidentFont Univers-Bold Univers-Bold
-
- # set the text output options
- textEncoding UTF-8
- textEOL unix
-
- # misc options
- enableT1lib yes
- enableFreeType yes
- launchCommand viewer-script
- 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-2011 Glyph &
- Cog, LLC.
-
- ()
-
-2 SEE_ALSO
-
- xpdf(1), pdftops(1), pdftotext(1), pdfinfo(1), pdffonts(1), pdfde-
- tach(1), pdftoppm(1), pdfimages(1)
- http://www.foolabs.com/xpdf/
-
- ()
-
diff --git a/fofi/FoFiBase.cc b/fofi/FoFiBase.cc
index 4adef2a..c9ca440 100644
--- a/fofi/FoFiBase.cc
+++ b/fofi/FoFiBase.cc
@@ -84,7 +84,7 @@ int FoFiBase::getU8(int pos, GBool *ok) {
int FoFiBase::getS16BE(int pos, GBool *ok) {
int x;
- if (pos < 0 || pos+1 >= len || pos > INT_MAX - 1) {
+ if (pos < 0 || pos > INT_MAX - 1 || pos+1 >= len) {
*ok = gFalse;
return 0;
}
@@ -99,7 +99,7 @@ int FoFiBase::getS16BE(int pos, GBool *ok) {
int FoFiBase::getU16BE(int pos, GBool *ok) {
int x;
- if (pos < 0 || pos+1 >= len || pos > INT_MAX - 1) {
+ if (pos < 0 || pos > INT_MAX - 1 || pos+1 >= len) {
*ok = gFalse;
return 0;
}
@@ -111,7 +111,7 @@ int FoFiBase::getU16BE(int pos, GBool *ok) {
int FoFiBase::getS32BE(int pos, GBool *ok) {
int x;
- if (pos < 0 || pos+3 >= len || pos > INT_MAX - 3) {
+ if (pos < 0 || pos > INT_MAX - 3 || pos+3 >= len) {
*ok = gFalse;
return 0;
}
@@ -128,7 +128,7 @@ int FoFiBase::getS32BE(int pos, GBool *ok) {
Guint FoFiBase::getU32BE(int pos, GBool *ok) {
Guint x;
- if (pos < 0 || pos+3 >= len || pos > INT_MAX - 3) {
+ if (pos < 0 || pos > INT_MAX - 3 || pos+3 >= len) {
*ok = gFalse;
return 0;
}
@@ -142,7 +142,7 @@ Guint FoFiBase::getU32BE(int pos, GBool *ok) {
Guint FoFiBase::getU32LE(int pos, GBool *ok) {
Guint x;
- if (pos < 0 || pos+3 >= len || pos > INT_MAX - 3) {
+ if (pos < 0 || pos > INT_MAX - 3 || pos+3 >= len) {
*ok = gFalse;
return 0;
}
@@ -157,7 +157,7 @@ Guint FoFiBase::getUVarBE(int pos, int size, GBool *ok) {
Guint x;
int i;
- if (pos < 0 || pos + size > len || pos > INT_MAX - size) {
+ if (pos < 0 || pos > INT_MAX - size || pos + size > len) {
*ok = gFalse;
return 0;
}
diff --git a/fofi/FoFiIdentifier.cc b/fofi/FoFiIdentifier.cc
index 10ac486..978cc70 100644
--- a/fofi/FoFiIdentifier.cc
+++ b/fofi/FoFiIdentifier.cc
@@ -16,6 +16,9 @@
#include <string.h>
#include <limits.h>
#include "gtypes.h"
+#include "gmem.h"
+#include "GString.h"
+#include "GList.h"
#include "FoFiIdentifier.h"
//------------------------------------------------------------------------
@@ -436,12 +439,23 @@ FoFiIdentifierType FoFiIdentifier::identifyMem(char *file, int len) {
FoFiIdentifierType FoFiIdentifier::identifyFile(char *fileName) {
FileReader *reader;
FoFiIdentifierType type;
+ int n;
if (!(reader = FileReader::make(fileName))) {
return fofiIdError;
}
type = identify(reader);
delete reader;
+
+ // Mac OS X dfont files don't have any sort of header or magic number,
+ // so look at the file name extension
+ if (type == fofiIdUnknown) {
+ n = (int)strlen(fileName);
+ if (n >= 6 && !strcmp(fileName + n - 6, ".dfont")) {
+ type = fofiIdDfont;
+ }
+ }
+
return type;
}
@@ -630,3 +644,244 @@ static FoFiIdentifierType identifyCFF(Reader *reader, int start) {
return fofiIdCFF8Bit;
}
}
+
+//------------------------------------------------------------------------
+
+static GList *getTTCFontList(FILE *f);
+static GList *getDfontFontList(FILE *f);
+
+GList *FoFiIdentifier::getFontList(char *fileName) {
+ FILE *f;
+ char buf[4];
+ GList *ret;
+
+ if (!(f = fopen(fileName, "rb"))) {
+ return NULL;
+ }
+ if (fread(buf, 1, 4, f) == 4 &&
+ buf[0] == 0x74 && // 'ttcf'
+ buf[1] == 0x74 &&
+ buf[2] == 0x63 &&
+ buf[3] == 0x66) {
+ ret = getTTCFontList(f);
+ } else {
+ ret = getDfontFontList(f);
+ }
+ fclose(f);
+ return ret;
+}
+
+static GList *getTTCFontList(FILE *f) {
+ Guchar buf[12];
+ Guchar *buf2;
+ int fileLength, nFonts;
+ int tabDirOffset, nTables, nameTabOffset, nNames, stringsOffset;
+ int stringPlatform, stringLength, stringOffset;
+ GBool stringUnicode;
+ int i, j;
+ GList *ret;
+
+ fseek(f, 0, SEEK_END);
+ fileLength = (int)ftell(f);
+ if (fileLength < 0) {
+ goto err1;
+ }
+ fseek(f, 8, SEEK_SET);
+ if (fread(buf, 1, 4, f) != 4) {
+ goto err1;
+ }
+ nFonts = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
+ if (nFonts < 0 ||
+ 12 + 4 * nFonts > fileLength) {
+ goto err1;
+ }
+ ret = new GList();
+ for (i = 0; i < nFonts; ++i) {
+ fseek(f, 12 + 4 * i, SEEK_SET);
+ if (fread(buf, 1, 4, f) != 4) {
+ goto err2;
+ }
+ tabDirOffset = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
+ if (tabDirOffset < 0 ||
+ tabDirOffset + 12 < 0 ||
+ tabDirOffset + 12 > fileLength) {
+ goto err2;
+ }
+ fseek(f, tabDirOffset, SEEK_SET);
+ if (fread(buf, 1, 12, f) != 12) {
+ goto err2;
+ }
+ nTables = (buf[4] << 8) | buf[5];
+ if (tabDirOffset + 12 + 16 * nTables < 0 ||
+ tabDirOffset + 12 + 16 * nTables > fileLength) {
+ goto err2;
+ }
+ buf2 = (Guchar *)gmallocn(nTables, 16);
+ if ((int)fread(buf2, 1, 16 * nTables, f) != 16 * nTables) {
+ goto err3;
+ }
+ nameTabOffset = 0; // make gcc happy
+ for (j = 0; j < nTables; ++j) {
+ if (buf2[16*j + 0] == 'n' &&
+ buf2[16*j + 1] == 'a' &&
+ buf2[16*j + 2] == 'm' &&
+ buf2[16*j + 3] == 'e') {
+ nameTabOffset = (buf2[16*j + 8] << 24) | (buf2[16*j + 9] << 16) |
+ (buf2[16*j + 10] << 8) | buf2[16*j + 11];
+ break;
+ }
+ }
+ gfree(buf2);
+ if (j >= nTables) {
+ goto err2;
+ }
+ if (nameTabOffset < 0 ||
+ nameTabOffset + 6 < 0 ||
+ nameTabOffset + 6 > fileLength) {
+ goto err2;
+ }
+ fseek(f, nameTabOffset, SEEK_SET);
+ if (fread(buf, 1, 6, f) != 6) {
+ goto err2;
+ }
+ nNames = (buf[2] << 8) | buf[3];
+ stringsOffset = (buf[4] << 8) | buf[5];
+ if (nameTabOffset + 6 + 12 * nNames < 0 ||
+ nameTabOffset + 6 + 12 * nNames > fileLength ||
+ nameTabOffset + stringsOffset < 0) {
+ goto err2;
+ }
+ buf2 = (Guchar *)gmallocn(nNames, 12);
+ if ((int)fread(buf2, 1, 12 * nNames, f) != 12 * nNames) {
+ goto err3;
+ }
+ for (j = 0; j < nNames; ++j) {
+ if (buf2[12*j + 6] == 0 && // 0x0004 = full name
+ buf2[12*j + 7] == 4) {
+ break;
+ }
+ }
+ if (j >= nNames) {
+ goto err3;
+ }
+ stringPlatform = (buf2[12*j] << 8) | buf2[12*j + 1];
+ // stringEncoding = (buf2[12*j + 2] << 8) | buf2[12*j + 3];
+ stringUnicode = stringPlatform == 0 || stringPlatform == 3;
+ stringLength = (buf2[12*j + 8] << 8) | buf2[12*j + 9];
+ stringOffset = nameTabOffset + stringsOffset +
+ ((buf2[12*j + 10] << 8) | buf2[12*j + 11]);
+ gfree(buf2);
+ if (stringOffset < 0 ||
+ stringOffset + stringLength < 0 ||
+ stringOffset + stringLength > fileLength) {
+ goto err2;
+ }
+ buf2 = (Guchar *)gmalloc(stringLength);
+ fseek(f, stringOffset, SEEK_SET);
+ if ((int)fread(buf2, 1, stringLength, f) != stringLength) {
+ goto err3;
+ }
+ if (stringUnicode) {
+ stringLength /= 2;
+ for (j = 0; j < stringLength; ++j) {
+ buf2[j] = buf2[2*j + 1];
+ }
+ }
+ ret->append(new GString((char *)buf2, stringLength));
+ gfree(buf2);
+ }
+ return ret;
+
+ err3:
+ gfree(buf2);
+ err2:
+ deleteGList(ret, GString);
+ err1:
+ return NULL;
+}
+
+static GList *getDfontFontList(FILE *f) {
+ Guchar buf[16];
+ Guchar *resMap;
+ int fileLength, resMapOffset, resMapLength;
+ int resTypeListOffset, resNameListOffset, nTypes;
+ int refListOffset, nFonts, nameOffset, nameLen;
+ int offset, i;
+ GList *ret;
+
+ fseek(f, 0, SEEK_END);
+ fileLength = (int)ftell(f);
+ if (fileLength < 0) {
+ goto err1;
+ }
+ fseek(f, 0, SEEK_SET);
+ if (fread((char *)buf, 1, 16, f) != 16) {
+ goto err1;
+ }
+ resMapOffset = (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
+ resMapLength = (buf[12] << 24) | (buf[13] << 16) | (buf[14] << 8) | buf[15];
+ if (resMapOffset < 0 ||
+ resMapOffset >= fileLength ||
+ resMapLength < 0 ||
+ resMapOffset + resMapLength > fileLength ||
+ resMapOffset + resMapLength < 0) {
+ goto err1;
+ }
+ if (resMapLength > 32768) {
+ // sanity check - this probably isn't a dfont file
+ goto err1;
+ }
+ resMap = (Guchar *)gmalloc(resMapLength);
+ fseek(f, resMapOffset, SEEK_SET);
+ if ((int)fread((char *)resMap, 1, resMapLength, f) != resMapLength) {
+ goto err2;
+ }
+ resTypeListOffset = (resMap[24] << 8) | resMap[25];
+ resNameListOffset = (resMap[26] << 8) | resMap[27];
+ nTypes = ((resMap[28] << 8) | resMap[29]) + 1;
+ if (resTypeListOffset + 2 + nTypes * 8 > resMapLength ||
+ resNameListOffset >= resMapLength) {
+ goto err2;
+ }
+ for (i = 0; i < nTypes; ++i) {
+ offset = resTypeListOffset + 2 + 8 * i;
+ if (resMap[offset] == 0x73 && // 'sfnt'
+ resMap[offset+1] == 0x66 &&
+ resMap[offset+2] == 0x6e &&
+ resMap[offset+3] == 0x74) {
+ nFonts = ((resMap[offset+4] << 8) | resMap[offset+5]) + 1;
+ refListOffset = (resMap[offset+6] << 8) | resMap[offset+7];
+ break;
+ }
+ }
+ if (i >= nTypes) {
+ goto err2;
+ }
+ if (resTypeListOffset + refListOffset >= resMapLength ||
+ resTypeListOffset + refListOffset + nFonts * 12 > resMapLength) {
+ goto err2;
+ }
+ ret = new GList();
+ for (i = 0; i < nFonts; ++i) {
+ offset = resTypeListOffset + refListOffset + 12 * i;
+ nameOffset = (resMap[offset+2] << 8) | resMap[offset+3];
+ offset = resNameListOffset + nameOffset;
+ if (offset >= resMapLength) {
+ goto err3;
+ }
+ nameLen = resMap[offset];
+ if (offset + 1 + nameLen > resMapLength) {
+ goto err3;
+ }
+ ret->append(new GString((char *)resMap + offset + 1, nameLen));
+ }
+ gfree(resMap);
+ return ret;
+
+ err3:
+ deleteGList(ret, GString);
+ err2:
+ gfree(resMap);
+ err1:
+ return NULL;
+}
diff --git a/fofi/FoFiIdentifier.h b/fofi/FoFiIdentifier.h
index 79da845..b0307a0 100644
--- a/fofi/FoFiIdentifier.h
+++ b/fofi/FoFiIdentifier.h
@@ -15,6 +15,8 @@
#pragma interface
#endif
+class GList;
+
//------------------------------------------------------------------------
// FoFiIdentifier
//------------------------------------------------------------------------
@@ -28,6 +30,7 @@ enum FoFiIdentifierType {
fofiIdTrueTypeCollection, // TrueType collection
fofiIdOpenTypeCFF8Bit, // OpenType wrapper with 8-bit CFF font
fofiIdOpenTypeCFFCID, // OpenType wrapper with CID CFF font
+ fofiIdDfont, // Mac OS X dfont
fofiIdUnknown, // unknown type
fofiIdError // error in reading the file
};
@@ -35,10 +38,18 @@ enum FoFiIdentifierType {
class FoFiIdentifier {
public:
+ // Identify a font file.
static FoFiIdentifierType identifyMem(char *file, int len);
static FoFiIdentifierType identifyFile(char *fileName);
static FoFiIdentifierType identifyStream(int (*getChar)(void *data),
void *data);
+
+ // Return a list of font names (GString *) in a font collection
+ // file. Indexes into the returned list are indexes into the
+ // collection. This function is only useful with TrueType
+ // collections and Mac dfont files. Returns NULL on error
+ // (including invalid font type).
+ static GList *getFontList(char *fileName);
};
#endif
diff --git a/fofi/FoFiTrueType.cc b/fofi/FoFiTrueType.cc
index 6503fda..8a1bfec 100644
--- a/fofi/FoFiTrueType.cc
+++ b/fofi/FoFiTrueType.cc
@@ -275,10 +275,11 @@ static const char *macGlyphNames[258] = {
// FoFiTrueType
//------------------------------------------------------------------------
-FoFiTrueType *FoFiTrueType::make(char *fileA, int lenA) {
+FoFiTrueType *FoFiTrueType::make(char *fileA, int lenA, int fontNum,
+ GBool allowHeadlessCFF) {
FoFiTrueType *ff;
- ff = new FoFiTrueType(fileA, lenA, gFalse);
+ ff = new FoFiTrueType(fileA, lenA, gFalse, fontNum, gFalse, allowHeadlessCFF);
if (!ff->parsedOk) {
delete ff;
return NULL;
@@ -286,15 +287,20 @@ FoFiTrueType *FoFiTrueType::make(char *fileA, int lenA) {
return ff;
}
-FoFiTrueType *FoFiTrueType::load(char *fileName) {
+FoFiTrueType *FoFiTrueType::load(char *fileName, int fontNum,
+ GBool allowHeadlessCFF) {
FoFiTrueType *ff;
char *fileA;
- int lenA;
+ int lenA, n;
+ GBool isDfontA;
if (!(fileA = FoFiBase::readFile(fileName, &lenA))) {
return NULL;
}
- ff = new FoFiTrueType(fileA, lenA, gTrue);
+ n = (int)strlen(fileName);
+ isDfontA = n >= 6 && !strcmp(fileName + n - 6, ".dfont");
+ ff = new FoFiTrueType(fileA, lenA, gTrue, fontNum, isDfontA,
+ allowHeadlessCFF);
if (!ff->parsedOk) {
delete ff;
return NULL;
@@ -302,7 +308,9 @@ FoFiTrueType *FoFiTrueType::load(char *fileName) {
return ff;
}
-FoFiTrueType::FoFiTrueType(char *fileA, int lenA, GBool freeFileDataA):
+FoFiTrueType::FoFiTrueType(char *fileA, int lenA, GBool freeFileDataA,
+ int fontNum, GBool isDfontA,
+ GBool allowHeadlessCFF):
FoFiBase(fileA, lenA, freeFileDataA)
{
tables = NULL;
@@ -310,9 +318,10 @@ FoFiTrueType::FoFiTrueType(char *fileA, int lenA, GBool freeFileDataA):
cmaps = NULL;
nCmaps = 0;
nameToGID = NULL;
+ isDfont = isDfontA;
parsedOk = gFalse;
- parse();
+ parse(fontNum, allowHeadlessCFF);
}
FoFiTrueType::~FoFiTrueType() {
@@ -363,7 +372,26 @@ int FoFiTrueType::mapCodeToGID(int i, int c) {
if (c < 0 || c >= cmaps[i].len - 6) {
return 0;
}
- gid = getU8(cmaps[i].offset + 6 + c, &ok);
+ gid = getU8(pos + 6 + c, &ok);
+ break;
+ case 2:
+ // this only handles single-byte codes
+ if (c < 0 || c > 0xff) {
+ return 0;
+ }
+ // check that: subHeaderKeys[0] = 0
+ // subHeaders[0].firstCode = 0
+ // subHeaders[0].entryCount = 256
+ // subHeaders[0].idDelta = 0
+ if (getU16BE(pos + 6, &ok) != 0 ||
+ getU16BE(pos + 518 + 0, &ok) != 0 ||
+ getU16BE(pos + 518 + 2, &ok) != 256 ||
+ getU16BE(pos + 518 + 4, &ok) != 0) {
+ return 0;
+ }
+ // subHeaders[0].idRangeOffset is a byte offset from itself
+ pos = pos + 518 + 6 + getU16BE(pos + 518 + 6, &ok);
+ gid = getU16BE(pos + 2 * c, &ok);
break;
case 4:
segCnt = getU16BE(pos + 6, &ok) / 2;
@@ -1022,7 +1050,7 @@ void FoFiTrueType::writeTTF(FoFiOutputFunc outputFunc,
if (!missingCmap && !missingName && !missingPost && !missingOS2 &&
!unsortedLoca && !emptyCmap && !badCmapLen && !abbrevHMTX &&
nZeroLengthTables == 0 && nBogusTables == 0 &&
- !name && !codeToGID) {
+ !name && !codeToGID && !isDfont) {
(*outputFunc)(outputStream, (char *)file, len);
goto done1;
}
@@ -1632,6 +1660,14 @@ void FoFiTrueType::cvtSfnts(FoFiOutputFunc outputFunc,
// 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)
+ //
+ // NB: a glyph description containing 12 zero bytes should be a
+ // valid empty glyph (from my reading of the TrueType spec), but
+ // Acrobat chokes on this (which is an issue when an Xpdf-generated
+ // PS file is converted back to PDF - with Ghostscript or
+ // Distiller), so we drop any glyph descriptions of 12 or fewer
+ // bytes -- an empty glyph description generates an empty glyph with
+ // no errors
locaTable = (TrueTypeLoca *)gmallocn(nGlyphs + 1, sizeof(TrueTypeLoca));
i = seekTable("loca");
pos = tables[i].offset;
@@ -1670,11 +1706,11 @@ void FoFiTrueType::cvtSfnts(FoFiOutputFunc outputFunc,
*maxUsedGlyph = -1;
for (i = 0; i <= nGlyphs; ++i) {
locaTable[i].newOffset = pos;
- pos += locaTable[i].len;
- if (pos & 3) {
- pos += 4 - (pos & 3);
- }
- if (locaTable[i].len > 0) {
+ if (locaTable[i].len > 12) {
+ pos += locaTable[i].len;
+ if (pos & 3) {
+ pos += 4 - (pos & 3);
+ }
*maxUsedGlyph = i;
}
}
@@ -1737,14 +1773,17 @@ void FoFiTrueType::cvtSfnts(FoFiOutputFunc outputFunc,
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);
+ if (locaTable[j].len > 12) {
+ 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 {
@@ -1858,7 +1897,7 @@ void FoFiTrueType::cvtSfnts(FoFiOutputFunc outputFunc,
} else if (i == t42GlyfTable) {
glyfPos = tables[seekTable("glyf")].offset;
for (j = 0; j < nGlyphs; ++j) {
- if (locaTable[j].len > 0 &&
+ if (locaTable[j].len > 12 &&
checkRegion(glyfPos + locaTable[j].origOffset, locaTable[j].len)) {
dumpString(file + glyfPos + locaTable[j].origOffset,
locaTable[j].len, outputFunc, outputStream);
@@ -1950,35 +1989,43 @@ Guint FoFiTrueType::computeTableChecksum(Guchar *data, int length) {
return checksum;
}
-void FoFiTrueType::parse() {
+void FoFiTrueType::parse(int fontNum, GBool allowHeadlessCFF) {
Guint topTag;
- int pos, ver, i, j;
+ int offset, 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);
+ // check for a dfont or TrueType collection (TTC)
+ // offset = start of actual TrueType font file (table positions are
+ // relative to this
+ // pos = position of table directory (relative to offset)
+ if (isDfont) {
+ parseDfont(fontNum, &offset, &pos);
+ } else {
+ offset = 0;
+ topTag = getU32BE(0, &parsedOk);
if (!parsedOk) {
return;
}
- } else {
- pos = 0;
+ if (topTag == ttcfTag) {
+ parseTTC(fontNum, &pos);
+ } else {
+ pos = 0;
+ }
+ }
+ if (!parsedOk) {
+ return;
}
// check the sfnt version
- ver = getU32BE(pos, &parsedOk);
+ ver = getU32BE(offset + pos, &parsedOk);
if (!parsedOk) {
return;
}
openTypeCFF = ver == 0x4f54544f; // 'OTTO'
// read the table directory
- nTables = getU16BE(pos + 4, &parsedOk);
+ nTables = getU16BE(offset + pos + 4, &parsedOk);
if (!parsedOk) {
return;
}
@@ -1986,10 +2033,10 @@ void FoFiTrueType::parse() {
pos += 12;
j = 0;
for (i = 0; i < nTables; ++i) {
- tables[j].tag = getU32BE(pos, &parsedOk);
- tables[j].checksum = getU32BE(pos + 4, &parsedOk);
- tables[j].offset = (int)getU32BE(pos + 8, &parsedOk);
- tables[j].len = (int)getU32BE(pos + 12, &parsedOk);
+ tables[j].tag = getU32BE(offset + pos, &parsedOk);
+ tables[j].checksum = getU32BE(offset + pos + 4, &parsedOk);
+ tables[j].offset = offset + (int)getU32BE(offset + pos + 8, &parsedOk);
+ tables[j].len = (int)getU32BE(offset + pos + 12, &parsedOk);
if (tables[j].offset + tables[j].len >= tables[j].offset &&
tables[j].offset + tables[j].len <= len) {
// ignore any bogus entries in the table directory
@@ -2002,10 +2049,23 @@ void FoFiTrueType::parse() {
return;
}
- // check for tables that are required by both the TrueType spec and
- // the Type 42 spec
- if (seekTable("head") < 0 ||
- seekTable("hhea") < 0 ||
+ // check for the head table; allow for a head-less OpenType CFF font
+ headlessCFF = gFalse;
+ if (seekTable("head") < 0) {
+ if (openTypeCFF && allowHeadlessCFF) {
+ headlessCFF = gTrue;
+ nGlyphs = 0;
+ bbox[0] = bbox[1] = bbox[2] = bbox[3] = 0;
+ locaFmt = 0;
+ return;
+ }
+ parsedOk = gFalse;
+ return;
+ }
+
+ // check for other tables that are required by both the TrueType
+ // spec and the Type 42 spec
+ if (seekTable("hhea") < 0 ||
seekTable("maxp") < 0 ||
seekTable("hmtx") < 0 ||
(!openTypeCFF && seekTable("loca") < 0) ||
@@ -2016,7 +2076,7 @@ void FoFiTrueType::parse() {
}
// read the cmaps
- if ((i = seekTable("cmap")) >= 0) {
+ if ((i = seekTable("cmap")) >= 0 && tables[i].len >= 4) {
pos = tables[i].offset + 2;
nCmaps = getU16BE(pos, &parsedOk);
pos += 2;
@@ -2035,8 +2095,6 @@ void FoFiTrueType::parse() {
if (!parsedOk) {
return;
}
- } else {
- nCmaps = 0;
}
// get the number of glyphs from the maxp table
@@ -2087,6 +2145,74 @@ void FoFiTrueType::parse() {
readPostTable();
}
+// Get the table directory position
+void FoFiTrueType::parseTTC(int fontNum, int *pos) {
+ int nFonts;
+
+ nFonts = getU32BE(8, &parsedOk);
+ if (!parsedOk) {
+ return;
+ }
+ if (fontNum < 0 || fontNum >= nFonts) {
+ parsedOk = gFalse;
+ return;
+ }
+ *pos = getU32BE(12 + 4 * fontNum, &parsedOk);
+}
+
+void FoFiTrueType::parseDfont(int fontNum, int *offset, int *startPos) {
+ int resMapOffset, resDataOffset;
+ int resTypeListOffset, nTypes, typeTag;
+ int nFonts, refListOffset, dataOffset;
+ int pos, i;
+
+ resDataOffset = getU32BE(0, &parsedOk);
+ resMapOffset = getU32BE(4, &parsedOk);
+ if (!parsedOk) {
+ return;
+ }
+
+ resTypeListOffset = getU16BE(resMapOffset + 24, &parsedOk);
+ // resNameListOffset = getU16BE(resMapOffset + 26, &parsedOk);
+ nTypes = getU16BE(resMapOffset + 28, &parsedOk) + 1;
+ if (!parsedOk) {
+ return;
+ }
+
+ pos = 0; // make gcc happy
+ for (i = 0; i < nTypes; ++i) {
+ pos = resMapOffset + resTypeListOffset + 2 + 8*i;
+ typeTag = getU32BE(pos, &parsedOk);
+ if (!parsedOk) {
+ return;
+ }
+ if (typeTag == 0x73666e74) { // 'sfnt'
+ break;
+ }
+ }
+ if (i >= nTypes) {
+ parsedOk = gFalse;
+ return;
+ }
+ nFonts = getU16BE(pos + 4, &parsedOk) + 1;
+ refListOffset = getU16BE(pos + 6, &parsedOk);
+ if (!parsedOk) {
+ return;
+ }
+ if (fontNum < 0 || fontNum >= nFonts) {
+ parsedOk = gFalse;
+ return;
+ }
+ pos = resMapOffset + resTypeListOffset + refListOffset + 12 * fontNum;
+ dataOffset = getU32BE(pos + 4, &parsedOk) & 0x00ffffff;
+ if (!parsedOk) {
+ return;
+ }
+ // the data offset points to a 4-byte length field, which we skip over
+ *offset = resDataOffset + dataOffset + 4;
+ *startPos = 0;
+}
+
void FoFiTrueType::readPostTable() {
GString *name;
int tablePos, postFmt, stringIdx, stringPos;
diff --git a/fofi/FoFiTrueType.h b/fofi/FoFiTrueType.h
index 53d5062..2cf9417 100644
--- a/fofi/FoFiTrueType.h
+++ b/fofi/FoFiTrueType.h
@@ -30,11 +30,19 @@ struct TrueTypeCmap;
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);
+ // Create a FoFiTrueType object from a memory buffer. If
+ // <allowHeadlessCFF> is true, OpenType CFF fonts without the 'head'
+ // table are permitted -- this is useful when calling the convert*
+ // functions.
+ static FoFiTrueType *make(char *fileA, int lenA, int fontNum,
+ GBool allowHeadlessCFF = gFalse);
+
+ // Create a FoFiTrueType object from a file on disk. If
+ // <allowHeadlessCFF> is true, OpenType CFF fonts without the 'head'
+ // table are permitted -- this is useful when calling the convert*
+ // functions.
+ static FoFiTrueType *load(char *fileName, int fontNum,
+ GBool allowHeadlessCFF = gFalse);
virtual ~FoFiTrueType();
@@ -42,6 +50,12 @@ public:
// if it's a TrueType font (or OpenType font with TrueType data).
GBool isOpenTypeCFF() { return openTypeCFF; }
+ // Returns true if this is an OpenType CFF font that is missing the
+ // 'head' table. This is a violation of the OpenType spec, but the
+ // embedded CFF font can be usable for some purposes (e.g., the
+ // convert* functions).
+ GBool isHeadlessCFF() { return headlessCFF; }
+
// Return the number of cmaps defined by this font.
int getNumCmaps();
@@ -148,7 +162,8 @@ public:
private:
- FoFiTrueType(char *fileA, int lenA, GBool freeFileDataA);
+ FoFiTrueType(char *fileA, int lenA, GBool freeFileDataA,
+ int fontNum, GBool isDfont, GBool allowHeadlessCFF);
void cvtEncoding(char **encoding,
FoFiOutputFunc outputFunc,
void *outputStream);
@@ -164,7 +179,9 @@ private:
FoFiOutputFunc outputFunc,
void *outputStream);
Guint computeTableChecksum(Guchar *data, int length);
- void parse();
+ void parse(int fontNum, GBool allowHeadlessCFF);
+ void parseTTC(int fontNum, int *pos);
+ void parseDfont(int fontNum, int *offset, int *pos);
void readPostTable();
int seekTable(const char *tag);
@@ -177,6 +194,8 @@ private:
int bbox[4];
GHash *nameToGID;
GBool openTypeCFF;
+ GBool headlessCFF;
+ GBool isDfont;
GBool parsedOk;
};
diff --git a/fofi/FoFiType1C.cc b/fofi/FoFiType1C.cc
index 93af207..7986cee 100644
--- a/fofi/FoFiType1C.cc
+++ b/fofi/FoFiType1C.cc
@@ -387,24 +387,42 @@ void FoFiType1C::convertToType1(char *psName, const char **newEncoding,
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;
+ // the StemSnapH array should be unique values in ascending order --
+ // if not, just skip it
+ for (i = 1; i < privateDicts[0].nStemSnapH; ++i) {
+ if (privateDicts[0].stemSnapH[i-1] >= privateDicts[0].stemSnapH[i]) {
+ break;
+ }
+ }
+ if (i == 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");
}
- 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;
+ // the StemSnapV array should be unique values in ascending order --
+ // if not, just skip it
+ for (i = 1; i < privateDicts[0].nStemSnapV; ++i) {
+ if (privateDicts[0].stemSnapV[i-1] >= privateDicts[0].stemSnapV[i]) {
+ break;
+ }
+ }
+ if (i == 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");
}
- eexecWrite(&eb, "] def\n");
}
if (privateDicts[0].hasForceBold) {
buf = GString::format("/ForceBold {0:s} def\n",
@@ -719,24 +737,42 @@ void FoFiType1C::convertToCIDType0(char *psName, int *codeMap, int nCodes,
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;
+ // the StemSnapH array should be unique values in ascending order --
+ // if not, just skip it
+ for (j = 1; j < privateDicts[i].nStemSnapH; ++j) {
+ if (privateDicts[i].stemSnapH[j-1] >= privateDicts[i].stemSnapH[j]) {
+ break;
+ }
+ }
+ if (j == privateDicts[0].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);
}
- (*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;
+ // the StemSnapV array should be unique values in ascending order --
+ // if not, just skip it
+ for (j = 1; j < privateDicts[i].nStemSnapV; ++j) {
+ if (privateDicts[i].stemSnapV[j-1] >= privateDicts[i].stemSnapV[j]) {
+ break;
+ }
+ }
+ if (j == privateDicts[0].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);
}
- (*outputFunc)(outputStream, "] def\n", 6);
}
if (privateDicts[i].hasForceBold) {
buf = GString::format("/ForceBold {0:s} def\n",
@@ -1017,24 +1053,44 @@ void FoFiType1C::convertToType0(char *psName, int *codeMap, int nCodes,
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;
+ // the StemSnapH array should be unique values in ascending order --
+ // if not, just skip it
+ for (k = 1; k < privateDicts[fd].nStemSnapH; ++k) {
+ if (privateDicts[fd].stemSnapH[k-1] >= privateDicts[fd].stemSnapH[k]) {
+ break;
+ }
+ }
+ if (k == privateDicts[0].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");
}
- 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;
+ // the StemSnapV array should be unique values in ascending order --
+ // if not, just skip it
+ for (k = 1; k < privateDicts[fd].nStemSnapV; ++k) {
+ if (privateDicts[fd].stemSnapV[k-1] >= privateDicts[fd].stemSnapV[k]) {
+ break;
+ }
+ }
+ if (k == privateDicts[0].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");
}
- eexecWrite(&eb, "] def\n");
}
if (privateDicts[fd].hasForceBold) {
buf = GString::format("/ForceBold {0:s} def\n",
diff --git a/fofi/Makefile.in b/fofi/Makefile.in
index f4f5d00..d00f83f 100644
--- a/fofi/Makefile.in
+++ b/fofi/Makefile.in
@@ -14,7 +14,7 @@ VPATH = @srcdir@
GOOSRCDIR = $(srcdir)/../goo
GOOLIBDIR = ../goo
-CXXFLAGS = @CXXFLAGS@ @DEFS@ -I.. -I$(GOOSRCDIR) -I$(srcdir)
+CXXFLAGS = @CXXFLAGS@ @DEFS@ -I.. -I$(srcdir)/.. -I$(GOOSRCDIR) -I$(srcdir)
CXX = @CXX@
AR = @AR@
@@ -68,4 +68,4 @@ clean:
depend:
$(CXX) $(CXXFLAGS) -MM $(CXX_SRC) >Makefile.dep
-include Makefile.dep
+-include Makefile.dep
diff --git a/fofi/vms_make.com b/fofi/vms_make.com
deleted file mode 100644
index e69de29..0000000
--- a/fofi/vms_make.com
+++ /dev/null
diff --git a/goo/FixedPoint.h b/goo/FixedPoint.h
index f9cdd4e..1f4be70 100644
--- a/goo/FixedPoint.h
+++ b/goo/FixedPoint.h
@@ -49,35 +49,35 @@ public:
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; }
+ int operator ==(FixedPoint x) const { return val == x.val; }
+ int operator ==(double x) const { return *this == (FixedPoint)x; }
+ int operator ==(int x) const { return *this == (FixedPoint)x; }
+ int operator ==(long x) const { return *this == (FixedPoint)x; }
+
+ int operator !=(FixedPoint x) const { return val != x.val; }
+ int operator !=(double x) const { return *this != (FixedPoint)x; }
+ int operator !=(int x) const { return *this != (FixedPoint)x; }
+ int operator !=(long x) const { return *this != (FixedPoint)x; }
+
+ int operator <(FixedPoint x) const { return val < x.val; }
+ int operator <(double x) const { return *this < (FixedPoint)x; }
+ int operator <(int x) const { return *this < (FixedPoint)x; }
+ int operator <(long x) const { return *this < (FixedPoint)x; }
+
+ int operator <=(FixedPoint x) const { return val <= x.val; }
+ int operator <=(double x) const { return *this <= (FixedPoint)x; }
+ int operator <=(int x) const { return *this <= (FixedPoint)x; }
+ int operator <=(long x) const { return *this <= (FixedPoint)x; }
+
+ int operator >(FixedPoint x) const { return val > x.val; }
+ int operator >(double x) const { return *this > (FixedPoint)x; }
+ int operator >(int x) const { return *this > (FixedPoint)x; }
+ int operator >(long x) const { return *this > (FixedPoint)x; }
+
+ int operator >=(FixedPoint x) const { return val >= x.val; }
+ int operator >=(double x) const { return *this >= (FixedPoint)x; }
+ int operator >=(int x) const { return *this >= (FixedPoint)x; }
+ int operator >=(long x) const { return *this >= (FixedPoint)x; }
FixedPoint operator -() { return make(-val); }
diff --git a/goo/GMutex.h b/goo/GMutex.h
index 7fa93d8..5d5621a 100644
--- a/goo/GMutex.h
+++ b/goo/GMutex.h
@@ -22,7 +22,7 @@
// ...
// gDestroyMutex(&m);
-#ifdef WIN32
+#ifdef _WIN32
#include <windows.h>
diff --git a/goo/GString.cc b/goo/GString.cc
index ea0b3ca..adf7739 100644
--- a/goo/GString.cc
+++ b/goo/GString.cc
@@ -19,6 +19,7 @@
#include <string.h>
#include <ctype.h>
#include <math.h>
+#include <limits.h>
#include "gmem.h"
#include "GString.h"
@@ -100,6 +101,9 @@ static const char *formatStrings[] = {
static inline int size(int len) {
int delta;
for (delta = 8; delta < len && delta < 0x100000; delta <<= 1) ;
+ if (len > INT_MAX - delta) {
+ gMemError("Integer overflow in GString::size()");
+ }
// this is ((len + 1) + (delta - 1)) & ~(delta - 1)
return (len + delta) & ~(delta - 1);
}
@@ -107,6 +111,9 @@ static inline int size(int len) {
inline void GString::resize(int length1) {
char *s1;
+ if (length1 < 0) {
+ gMemError("GString::resize() with negative length");
+ }
if (!s) {
s = new char[size(length1)];
} else if (size(length1) != size(length)) {
@@ -161,6 +168,9 @@ GString::GString(GString *str1, GString *str2) {
int n2 = str2->getLength();
s = NULL;
+ if (n1 > INT_MAX - n2) {
+ gMemError("Integer overflow in GString::GString()");
+ }
resize(length = n1 + n2);
memcpy(s, str1->getCString(), n1);
memcpy(s + n1, str2->getCString(), n2 + 1);
@@ -168,7 +178,7 @@ GString::GString(GString *str1, GString *str2) {
GString *GString::fromInt(int x) {
char buf[24]; // enough space for 64-bit ints plus a little extra
- char *p;
+ const char *p;
int len;
formatInt(x, buf, sizeof(buf), gFalse, 0, 10, &p, &len);
@@ -205,6 +215,9 @@ GString *GString::clear() {
}
GString *GString::append(char c) {
+ if (length > INT_MAX - 1) {
+ gMemError("Integer overflow in GString::append()");
+ }
resize(length + 1);
s[length++] = c;
s[length] = '\0';
@@ -214,6 +227,9 @@ GString *GString::append(char c) {
GString *GString::append(GString *str) {
int n = str->getLength();
+ if (length > INT_MAX - n) {
+ gMemError("Integer overflow in GString::append()");
+ }
resize(length + n);
memcpy(s + length, str->getCString(), n + 1);
length += n;
@@ -223,6 +239,9 @@ GString *GString::append(GString *str) {
GString *GString::append(const char *str) {
int n = (int)strlen(str);
+ if (length > INT_MAX - n) {
+ gMemError("Integer overflow in GString::append()");
+ }
resize(length + n);
memcpy(s + length, str, n + 1);
length += n;
@@ -230,6 +249,9 @@ GString *GString::append(const char *str) {
}
GString *GString::append(const char *str, int lengthA) {
+ if (lengthA < 0 || length > INT_MAX - lengthA) {
+ gMemError("Integer overflow in GString::append()");
+ }
resize(length + lengthA);
memcpy(s + length, str, lengthA);
length += lengthA;
@@ -256,7 +278,7 @@ GString *GString::appendfv(const char *fmt, va_list argList) {
char buf[65];
int len, i;
const char *p0, *p1;
- char *str;
+ const char *str;
argsLen = 0;
argsSize = 8;
@@ -491,13 +513,23 @@ GString *GString::appendfv(const char *fmt, va_list argList) {
reverseAlign = !reverseAlign;
break;
case fmtString:
- str = arg.s;
- len = (int)strlen(str);
+ if (arg.s) {
+ str = arg.s;
+ len = (int)strlen(str);
+ } else {
+ str = "(null)";
+ len = 6;
+ }
reverseAlign = !reverseAlign;
break;
case fmtGString:
- str = arg.gs->getCString();
- len = arg.gs->getLength();
+ if (arg.gs) {
+ str = arg.gs->getCString();
+ len = arg.gs->getLength();
+ } else {
+ str = "(null)";
+ len = 6;
+ }
reverseAlign = !reverseAlign;
break;
case fmtSpace:
@@ -542,11 +574,11 @@ GString *GString::appendfv(const char *fmt, va_list argList) {
#ifdef LLONG_MAX
void GString::formatInt(long long x, char *buf, int bufSize,
GBool zeroFill, int width, int base,
- char **p, int *len) {
+ const char **p, int *len) {
#else
void GString::formatInt(long x, char *buf, int bufSize,
GBool zeroFill, int width, int base,
- char **p, int *len) {
+ const char **p, int *len) {
#endif
static char vals[17] = "0123456789abcdef";
GBool neg;
@@ -580,11 +612,11 @@ void GString::formatInt(long x, char *buf, int bufSize,
#ifdef ULLONG_MAX
void GString::formatUInt(unsigned long long x, char *buf, int bufSize,
GBool zeroFill, int width, int base,
- char **p, int *len) {
+ const char **p, int *len) {
#else
void GString::formatUInt(Gulong x, char *buf, int bufSize,
GBool zeroFill, int width, int base,
- char **p, int *len) {
+ const char **p, int *len) {
#endif
static char vals[17] = "0123456789abcdef";
int i, j;
@@ -608,7 +640,7 @@ void GString::formatUInt(Gulong x, char *buf, int bufSize,
}
void GString::formatDouble(double x, char *buf, int bufSize, int prec,
- GBool trim, char **p, int *len) {
+ GBool trim, const char **p, int *len) {
GBool neg, started;
double x2;
int d, i, j;
@@ -649,6 +681,9 @@ void GString::formatDouble(double x, char *buf, int bufSize, int prec,
GString *GString::insert(int i, char c) {
int j;
+ if (length > INT_MAX - 1) {
+ gMemError("Integer overflow in GString::insert()");
+ }
resize(length + 1);
for (j = length + 1; j > i; --j)
s[j] = s[j-1];
@@ -661,6 +696,9 @@ GString *GString::insert(int i, GString *str) {
int n = str->getLength();
int j;
+ if (length > INT_MAX - n) {
+ gMemError("Integer overflow in GString::insert()");
+ }
resize(length + n);
for (j = length; j >= i; --j)
s[j+n] = s[j];
@@ -673,6 +711,9 @@ GString *GString::insert(int i, const char *str) {
int n = (int)strlen(str);
int j;
+ if (length > INT_MAX - n) {
+ gMemError("Integer overflow in GString::insert()");
+ }
resize(length + n);
for (j = length; j >= i; --j)
s[j+n] = s[j];
@@ -684,6 +725,9 @@ GString *GString::insert(int i, const char *str) {
GString *GString::insert(int i, const char *str, int lengthA) {
int j;
+ if (lengthA < 0 || length > INT_MAX - lengthA) {
+ gMemError("Integer overflow in GString::insert()");
+ }
resize(length + lengthA);
for (j = length; j >= i; --j)
s[j+lengthA] = s[j];
@@ -695,7 +739,7 @@ GString *GString::insert(int i, const char *str, int lengthA) {
GString *GString::del(int i, int n) {
int j;
- if (i >= 0 && n > 0 && i + n > 0) {
+ if (i >= 0 && n > 0 && i <= INT_MAX - n) {
if (i + n > length) {
n = length - i;
}
diff --git a/goo/GString.h b/goo/GString.h
index 611efda..6b342f8 100644
--- a/goo/GString.h
+++ b/goo/GString.h
@@ -129,23 +129,23 @@ private:
#ifdef LLONG_MAX
static void formatInt(long long x, char *buf, int bufSize,
GBool zeroFill, int width, int base,
- char **p, int *len);
+ const char **p, int *len);
#else
static void formatInt(long x, char *buf, int bufSize,
GBool zeroFill, int width, int base,
- char **p, int *len);
+ const char **p, int *len);
#endif
#ifdef ULLONG_MAX
static void formatUInt(unsigned long long x, char *buf, int bufSize,
GBool zeroFill, int width, int base,
- char **p, int *len);
+ const char **p, int *len);
#else
static void formatUInt(Gulong x, char *buf, int bufSize,
GBool zeroFill, int width, int base,
- char **p, int *len);
+ const char **p, int *len);
#endif
static void formatDouble(double x, char *buf, int bufSize, int prec,
- GBool trim, char **p, int *len);
+ GBool trim, const char **p, int *len);
};
#endif
diff --git a/goo/Makefile.in b/goo/Makefile.in
index 0baddbb..f61d403 100644
--- a/goo/Makefile.in
+++ b/goo/Makefile.in
@@ -11,8 +11,8 @@ SHELL = /bin/sh
srcdir = @srcdir@
VPATH = @srcdir@
-CFLAGS = @CFLAGS@ @DEFS@ -I.. -I$(srcdir)
-CXXFLAGS = @CXXFLAGS@ @DEFS@ -I.. -I$(srcdir)
+CFLAGS = @CFLAGS@ @DEFS@ -I.. -I$(srcdir)/.. -I$(srcdir)
+CXXFLAGS = @CXXFLAGS@ @DEFS@ -I.. -I$(srcdir)/.. -I$(srcdir)
CC = @CC@
CXX = @CXX@
@@ -68,4 +68,4 @@ depend:
$(CXX) $(CXXFLAGS) -MM $(CXX_SRC) >Makefile.dep
$(CC) $(CFLAGS) -MM $(C_SRC) >>Makefile.dep
-include Makefile.dep
+-include Makefile.dep
diff --git a/goo/gfile.cc b/goo/gfile.cc
index 93d0941..ed34409 100644
--- a/goo/gfile.cc
+++ b/goo/gfile.cc
@@ -10,8 +10,9 @@
#include <aconf.h>
-#ifdef WIN32
+#ifdef _WIN32
# include <time.h>
+# include <direct.h>
#else
# if defined(MACOS)
# include <sys/stat.h>
@@ -29,7 +30,7 @@
# if defined(VMS) && (__DECCXX_VER < 50200000)
# include <unixlib.h>
# endif
-#endif // WIN32
+#endif // _WIN32
#include "GString.h"
#include "gfile.h"
@@ -46,7 +47,7 @@ GString *getHomeDir() {
//---------- VMS ----------
return new GString("SYS$LOGIN:");
-#elif defined(__EMX__) || defined(WIN32)
+#elif defined(__EMX__) || defined(_WIN32)
//---------- OS/2+EMX and Win32 ----------
char *s;
GString *ret;
@@ -92,8 +93,8 @@ GString *getCurrentDir() {
#if defined(__EMX__)
if (_getcwd2(buf, sizeof(buf)))
-#elif defined(WIN32)
- if (GetCurrentDirectory(sizeof(buf), buf))
+#elif defined(_WIN32)
+ if (GetCurrentDirectoryA(sizeof(buf), buf))
#elif defined(ACORN)
if (strcpy(buf, "@"))
#elif defined(MACOS)
@@ -146,7 +147,7 @@ GString *appendToPath(GString *path, const char *fileName) {
}
return path;
-#elif defined(WIN32)
+#elif defined(_WIN32)
//---------- Win32 ----------
GString *tmp;
char buf[256];
@@ -155,7 +156,7 @@ GString *appendToPath(GString *path, const char *fileName) {
tmp = new GString(path);
tmp->append('/');
tmp->append(fileName);
- GetFullPathName(tmp->getCString(), sizeof(buf), buf, &fp);
+ GetFullPathNameA(tmp->getCString(), sizeof(buf), buf, &fp);
delete tmp;
path->clear();
path->append(buf);
@@ -282,7 +283,7 @@ GString *grabPath(char *fileName) {
return new GString(fileName, p + 1 - fileName);
return new GString();
-#elif defined(__EMX__) || defined(WIN32)
+#elif defined(__EMX__) || defined(_WIN32)
//---------- OS/2+EMX and Win32 ----------
char *p;
@@ -326,7 +327,7 @@ GBool isAbsolutePath(char *path) {
return strchr(path, ':') ||
(path[0] == '[' && path[1] != '.' && path[1] != '-');
-#elif defined(__EMX__) || defined(WIN32)
+#elif defined(__EMX__) || defined(_WIN32)
//---------- OS/2+EMX and Win32 ----------
return path[0] == '/' || path[0] == '\\' || path[1] == ':';
@@ -356,13 +357,13 @@ GString *makePathAbsolute(GString *path) {
}
return path;
-#elif defined(WIN32)
+#elif defined(_WIN32)
//---------- Win32 ----------
char buf[_MAX_PATH];
char *fp;
buf[0] = '\0';
- if (!GetFullPathName(path->getCString(), _MAX_PATH, buf, &fp)) {
+ if (!GetFullPathNameA(path->getCString(), _MAX_PATH, buf, &fp)) {
path->clear();
return path;
}
@@ -427,7 +428,7 @@ GString *makePathAbsolute(GString *path) {
}
time_t getModTime(char *fileName) {
-#ifdef WIN32
+#ifdef _WIN32
//~ should implement this, but it's (currently) only used in xpdf
return 0;
#else
@@ -440,8 +441,9 @@ time_t getModTime(char *fileName) {
#endif
}
-GBool openTempFile(GString **name, FILE **f, const char *mode, char *ext) {
-#if defined(WIN32)
+GBool openTempFile(GString **name, FILE **f,
+ const char *mode, const char *ext) {
+#if defined(_WIN32)
//---------- Win32 ----------
char *tempDir;
GString *s, *s2;
@@ -550,6 +552,14 @@ GBool openTempFile(GString **name, FILE **f, const char *mode, char *ext) {
#endif
}
+GBool createDir(char *path, int mode) {
+#ifdef _WIN32
+ return !mkdir(path);
+#else
+ return !mkdir(path, mode);
+#endif
+}
+
GBool executeCommand(char *cmd) {
#ifdef VMS
return system(cmd) ? gTrue : gFalse;
@@ -558,7 +568,7 @@ GBool executeCommand(char *cmd) {
#endif
}
-#ifdef WIN32
+#ifdef _WIN32
GString *fileNameToUTF8(char *path) {
GString *s;
char *p;
@@ -597,7 +607,7 @@ GString *fileNameToUTF8(wchar_t *path) {
#endif
FILE *openFile(const char *path, const char *mode) {
-#ifdef WIN32
+#ifdef _WIN32
OSVERSIONINFO version;
wchar_t wPath[_MAX_PATH + 1];
char nPath[_MAX_PATH + 1];
@@ -688,6 +698,30 @@ char *getLine(char *buf, int size, FILE *f) {
return buf;
}
+int gfseek(FILE *f, GFileOffset offset, int whence) {
+#if HAVE_FSEEKO
+ return fseeko(f, offset, whence);
+#elif HAVE_FSEEK64
+ return fseek64(f, offset, whence);
+#elif HAVE_FSEEKI64
+ return _fseeki64(f, offset, whence);
+#else
+ return fseek(f, offset, whence);
+#endif
+}
+
+GFileOffset gftell(FILE *f) {
+#if HAVE_FSEEKO
+ return ftello(f);
+#elif HAVE_FSEEK64
+ return ftell64(f);
+#elif HAVE_FSEEKI64
+ return _ftelli64(f);
+#else
+ return ftell(f);
+#endif
+}
+
//------------------------------------------------------------------------
// GDir and GDirEntry
//------------------------------------------------------------------------
@@ -695,7 +729,7 @@ char *getLine(char *buf, int size, FILE *f) {
GDirEntry::GDirEntry(char *dirPath, char *nameA, GBool doStat) {
#ifdef VMS
char *p;
-#elif defined(WIN32)
+#elif defined(_WIN32)
int fa;
GString *s;
#elif defined(ACORN)
@@ -715,8 +749,8 @@ GDirEntry::GDirEntry(char *dirPath, char *nameA, GBool doStat) {
#else
s = new GString(dirPath);
appendToPath(s, nameA);
-#ifdef WIN32
- fa = GetFileAttributes(s->getCString());
+#ifdef _WIN32
+ fa = GetFileAttributesA(s->getCString());
dir = (fa != 0xFFFFFFFF && (fa & FILE_ATTRIBUTE_DIRECTORY));
#else
if (stat(s->getCString(), &st) == 0)
@@ -734,15 +768,16 @@ GDirEntry::~GDirEntry() {
GDir::GDir(char *name, GBool doStatA) {
path = new GString(name);
doStat = doStatA;
-#if defined(WIN32)
+#if defined(_WIN32)
GString *tmp;
tmp = path->copy();
tmp->append("/*.*");
- hnd = FindFirstFile(tmp->getCString(), &ffd);
+ hnd = FindFirstFileA(tmp->getCString(), &ffd);
delete tmp;
#elif defined(ACORN)
#elif defined(MACOS)
+#elif defined(ANDROID)
#else
dir = opendir(name);
#ifdef VMS
@@ -753,13 +788,14 @@ GDir::GDir(char *name, GBool doStatA) {
GDir::~GDir() {
delete path;
-#if defined(WIN32)
+#if defined(_WIN32)
if (hnd) {
FindClose(hnd);
hnd = NULL;
}
#elif defined(ACORN)
#elif defined(MACOS)
+#elif defined(ANDROID)
#else
if (dir)
closedir(dir);
@@ -769,10 +805,10 @@ GDir::~GDir() {
GDirEntry *GDir::getNextEntry() {
GDirEntry *e;
-#if defined(WIN32)
+#if defined(_WIN32)
if (hnd) {
e = new GDirEntry(path->getCString(), ffd.cFileName, doStat);
- if (hnd && !FindNextFile(hnd, &ffd)) {
+ if (hnd && !FindNextFileA(hnd, &ffd)) {
FindClose(hnd);
hnd = NULL;
}
@@ -781,6 +817,7 @@ GDirEntry *GDir::getNextEntry() {
}
#elif defined(ACORN)
#elif defined(MACOS)
+#elif defined(ANDROID)
#elif defined(VMS)
struct dirent *ent;
e = NULL;
@@ -813,17 +850,18 @@ GDirEntry *GDir::getNextEntry() {
}
void GDir::rewind() {
-#ifdef WIN32
+#ifdef _WIN32
GString *tmp;
if (hnd)
FindClose(hnd);
tmp = path->copy();
tmp->append("/*.*");
- hnd = FindFirstFile(tmp->getCString(), &ffd);
+ hnd = FindFirstFileA(tmp->getCString(), &ffd);
delete tmp;
#elif defined(ACORN)
#elif defined(MACOS)
+#elif defined(ANDROID)
#else
if (dir)
rewinddir(dir);
diff --git a/goo/gfile.h b/goo/gfile.h
index 4f8c65f..5b171e6 100644
--- a/goo/gfile.h
+++ b/goo/gfile.h
@@ -14,7 +14,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
-#if defined(WIN32)
+#if defined(_WIN32)
# include <sys/stat.h>
# ifdef FPTEX
# include <win32lib.h>
@@ -24,6 +24,7 @@
#elif defined(ACORN)
#elif defined(MACOS)
# include <ctime.h>
+#elif defined(ANDROID)
#else
# include <unistd.h>
# include <sys/types.h>
@@ -84,12 +85,15 @@ extern time_t getModTime(char *fileName);
// 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,
- const char *mode, char *ext);
+ const char *mode, const char *ext);
+
+// Create a directory. Returns true on success.
+extern GBool createDir(char *path, int mode);
// Execute <command>. Returns true on success.
extern GBool executeCommand(char *cmd);
-#ifdef WIN32
+#ifdef _WIN32
// Convert a file name from Latin-1 to UTF-8.
extern GString *fileNameToUTF8(char *path);
@@ -106,6 +110,28 @@ extern FILE *openFile(const char *path, const char *mode);
// conventions.
extern char *getLine(char *buf, int size, FILE *f);
+// Type used by gfseek/gftell for file offsets. This will be 64 bits
+// on systems that support it.
+#if HAVE_FSEEKO
+typedef off_t GFileOffset;
+#define GFILEOFFSET_MAX 0x7fffffffffffffffLL
+#elif HAVE_FSEEK64
+typedef long long GFileOffset;
+#define GFILEOFFSET_MAX 0x7fffffffffffffffLL
+#elif HAVE_FSEEKI64
+typedef __int64 GFileOffset;
+#define GFILEOFFSET_MAX 0x7fffffffffffffffLL
+#else
+typedef long GFileOffset;
+#define GFILEOFFSET_MAX LONG_MAX
+#endif
+
+// Like fseek, but uses a 64-bit file offset if available.
+extern int gfseek(FILE *f, GFileOffset offset, int whence);
+
+// Like ftell, but returns a 64-bit file offset if available.
+extern GFileOffset gftell(FILE *f);
+
//------------------------------------------------------------------------
// GDir and GDirEntry
//------------------------------------------------------------------------
@@ -136,11 +162,12 @@ private:
GString *path; // directory path
GBool doStat; // call stat() for each entry?
-#if defined(WIN32)
- WIN32_FIND_DATA ffd;
+#if defined(_WIN32)
+ WIN32_FIND_DATAA ffd;
HANDLE hnd;
#elif defined(ACORN)
#elif defined(MACOS)
+#elif defined(ANDROID)
#else
DIR *dir; // the DIR structure from opendir()
#ifdef VMS
diff --git a/goo/gmem.cc b/goo/gmem.cc
index af85dd0..6fd5136 100644
--- a/goo/gmem.cc
+++ b/goo/gmem.cc
@@ -44,6 +44,7 @@ static GMemHdr *gMemTail = NULL;
static int gMemIndex = 0;
static int gMemAlloc = 0;
static int gMemInUse = 0;
+static int gMaxMemInUse = 0;
#endif /* DEBUG_MEM */
@@ -56,24 +57,14 @@ void *gmalloc(int size) GMEM_EXCEP {
unsigned long *trl, *p;
if (size < 0) {
-#if USE_EXCEPTIONS
- throw GMemException();
-#else
- fprintf(stderr, "Invalid memory allocation size\n");
- exit(1);
-#endif
+ gMemError("Invalid memory allocation size");
}
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
+ gMemError("Out of memory");
}
hdr = (GMemHdr *)mem;
data = (void *)(mem + gMemHdrSize);
@@ -92,6 +83,9 @@ void *gmalloc(int size) GMEM_EXCEP {
hdr->next = NULL;
++gMemAlloc;
gMemInUse += size;
+ if (gMemInUse > gMaxMemInUse) {
+ gMaxMemInUse = gMemInUse;
+ }
for (p = (unsigned long *)data; p <= trl; ++p) {
*p = gMemDeadVal;
}
@@ -100,23 +94,13 @@ void *gmalloc(int size) GMEM_EXCEP {
void *p;
if (size < 0) {
-#if USE_EXCEPTIONS
- throw GMemException();
-#else
- fprintf(stderr, "Invalid memory allocation size\n");
- exit(1);
-#endif
+ gMemError("Invalid memory allocation size");
}
if (size == 0) {
return NULL;
}
if (!(p = malloc(size))) {
-#if USE_EXCEPTIONS
- throw GMemException();
-#else
- fprintf(stderr, "Out of memory\n");
- exit(1);
-#endif
+ gMemError("Out of memory");
}
return p;
#endif
@@ -129,12 +113,7 @@ void *grealloc(void *p, int size) GMEM_EXCEP {
int oldSize;
if (size < 0) {
-#if USE_EXCEPTIONS
- throw GMemException();
-#else
- fprintf(stderr, "Invalid memory allocation size\n");
- exit(1);
-#endif
+ gMemError("Invalid memory allocation size");
}
if (size == 0) {
if (p) {
@@ -156,12 +135,7 @@ void *grealloc(void *p, int size) GMEM_EXCEP {
void *q;
if (size < 0) {
-#if USE_EXCEPTIONS
- throw GMemException();
-#else
- fprintf(stderr, "Invalid memory allocation size\n");
- exit(1);
-#endif
+ gMemError("Invalid memory allocation size");
}
if (size == 0) {
if (p) {
@@ -175,12 +149,7 @@ void *grealloc(void *p, int size) GMEM_EXCEP {
q = malloc(size);
}
if (!q) {
-#if USE_EXCEPTIONS
- throw GMemException();
-#else
- fprintf(stderr, "Out of memory\n");
- exit(1);
-#endif
+ gMemError("Out of memory");
}
return q;
#endif
@@ -194,12 +163,7 @@ void *gmallocn(int nObjs, int objSize) GMEM_EXCEP {
}
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
+ gMemError("Bogus memory allocation size");
}
return gmalloc(n);
}
@@ -215,12 +179,7 @@ void *greallocn(void *p, int nObjs, int objSize) GMEM_EXCEP {
}
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
+ gMemError("Bogus memory allocation size");
}
return grealloc(p, n);
}
@@ -269,11 +228,21 @@ void gfree(void *p) {
#endif
}
+void gMemError(const char *msg) GMEM_EXCEP {
+#if USE_EXCEPTIONS
+ throw GMemException();
+#else
+ fprintf(stderr, "%s\n", msg);
+ exit(1);
+#endif
+}
+
#ifdef DEBUG_MEM
void gMemReport(FILE *f) {
GMemHdr *p;
fprintf(f, "%d memory allocations in all\n", gMemIndex);
+ fprintf(f, "maximum memory in use: %d bytes\n", gMaxMemInUse);
if (gMemAlloc > 0) {
fprintf(f, "%d memory blocks left allocated:\n", gMemAlloc);
fprintf(f, " index size\n");
diff --git a/goo/gmem.h b/goo/gmem.h
index de32453..bfa9658 100644
--- a/goo/gmem.h
+++ b/goo/gmem.h
@@ -58,6 +58,11 @@ extern void *greallocn(void *p, int nObjs, int objSize) GMEM_EXCEP;
*/
extern void gfree(void *p);
+/*
+ * Report a memory error.
+ */
+extern void gMemError(const char *msg) GMEM_EXCEP;
+
#ifdef DEBUG_MEM
/*
* Report on unfreed memory.
diff --git a/goo/vms_directory.c b/goo/vms_directory.c
deleted file mode 100644
index 92d9493..0000000
--- a/goo/vms_directory.c
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 13e21a0..0000000
--- a/goo/vms_dirent.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/* @(#)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
deleted file mode 100644
index 676643f..0000000
--- a/goo/vms_make.com
+++ /dev/null
@@ -1,82 +0,0 @@
-$!========================================================================
-$!
-$! 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
deleted file mode 100644
index 2c20d71..0000000
--- a/goo/vms_sys_dirent.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/* @(#)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
deleted file mode 100644
index f8e8382..0000000
--- a/goo/vms_unix_time.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/* @(#)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
deleted file mode 100644
index 004c0d0..0000000
--- a/goo/vms_unix_times.c
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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
deleted file mode 100644
index e2cf687..0000000
--- a/goo/vms_unlink.c
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * 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/ms_make.bat b/ms_make.bat
index c4b01a4..684b26f 100644
--- a/ms_make.bat
+++ b/ms_make.bat
@@ -1,6 +1,10 @@
set CC=cl
-set CFLAGS=/DWIN32 /I.. /I..\goo /I..\fofi /I..\splash /O2 /nologo
set CXX=cl
+set FT2DIR=..\freetype-2.5.3
+rem *** Set PNGDIR and ZLIBDIR to build pdftohtml
+rem set PNGDIR=..\libpng-1.5.12
+rem set ZLIBDIR=..\zlib-1.2.7
+set CFLAGS=/I.. /I..\goo /I..\fofi /I..\splash /O2 /nologo /I%FT2DIR%\include
set CXXFLAGS=%CFLAGS% /TP
set LIBPROG=lib
set LINKFLAGS=/MT /nologo
@@ -26,7 +30,27 @@ cd ..\fofi
%CXX% %CXXFLAGS% /c FoFiType1C.cc
%LIBPROG% /nologo /out:fofi.lib FoFiBase.obj FoFiEncodings.obj FoFiIdentifier.obj FoFiTrueType.obj FoFiType1.obj FoFiType1C.obj
+cd ..\splash
+%CXX% %CXXFLAGS% /c Splash.cc
+%CXX% %CXXFLAGS% /c SplashBitmap.cc
+%CXX% %CXXFLAGS% /c SplashClip.cc
+%CXX% %CXXFLAGS% /c SplashFTFont.cc
+%CXX% %CXXFLAGS% /c SplashFTFontEngine.cc
+%CXX% %CXXFLAGS% /c SplashFTFontFile.cc
+%CXX% %CXXFLAGS% /c SplashFont.cc
+%CXX% %CXXFLAGS% /c SplashFontEngine.cc
+%CXX% %CXXFLAGS% /c SplashFontFile.cc
+%CXX% %CXXFLAGS% /c SplashFontFileID.cc
+%CXX% %CXXFLAGS% /c SplashPath.cc
+%CXX% %CXXFLAGS% /c SplashPattern.cc
+%CXX% %CXXFLAGS% /c SplashScreen.cc
+%CXX% %CXXFLAGS% /c SplashState.cc
+%CXX% %CXXFLAGS% /c 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 SplashXPath.obj SplashXPathScanner.obj
+
cd ..\xpdf
+%CXX% %CXXFLAGS% /c AcroForm.cc
%CXX% %CXXFLAGS% /c Annot.cc
%CXX% %CXXFLAGS% /c Array.cc
%CXX% %CXXFLAGS% /c BuiltinFont.cc
@@ -38,6 +62,7 @@ cd ..\xpdf
%CXX% %CXXFLAGS% /c Dict.cc
%CXX% %CXXFLAGS% /c Error.cc
%CXX% %CXXFLAGS% /c FontEncodingTables.cc
+%CXX% %CXXFLAGS% /c Form.cc
%CXX% %CXXFLAGS% /c Function.cc
%CXX% %CXXFLAGS% /c Gfx.cc
%CXX% %CXXFLAGS% /c GfxFont.cc
@@ -62,71 +87,48 @@ cd ..\xpdf
%CXX% %CXXFLAGS% /c Parser.cc
%CXX% %CXXFLAGS% /c PreScanOutputDev.cc
%CXX% %CXXFLAGS% /c SecurityHandler.cc
+%CXX% %CXXFLAGS% /c SplashOutputDev.cc
%CXX% %CXXFLAGS% /c Stream.cc
%CXX% %CXXFLAGS% /c TextOutputDev.cc
+%CXX% %CXXFLAGS% /c TextString.cc
%CXX% %CXXFLAGS% /c UnicodeMap.cc
%CXX% %CXXFLAGS% /c UnicodeTypeTable.cc
+%CXX% %CXXFLAGS% /c XFAForm.cc
%CXX% %CXXFLAGS% /c XRef.cc
+%CXX% %CXXFLAGS% /c Zoox.cc
%CXX% %CXXFLAGS% /c pdftops.cc
%CXX% %CXXFLAGS% /c pdftotext.cc
+%CXX% %CXXFLAGS% /c pdftoppm.cc
%CXX% %CXXFLAGS% /c pdfinfo.cc
%CXX% %CXXFLAGS% /c pdffonts.cc
%CXX% %CXXFLAGS% /c pdfdetach.cc
%CXX% %CXXFLAGS% /c pdfimages.cc
-rem --- Comment out this line, and uncomment the other pdftops build line
-rem (see below) to build pdftops with the Splash rasterizer.
-rem You'll also need to define HAVE_SPLASH in aconf-win32.h.
-%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 OptionalContent.obj Outline.obj OutputDev.obj Page.obj Parser.obj PDFDoc.obj PDFDocEncoding.obj PSOutputDev.obj PSTokenizer.obj PreScanOutputDev.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% /Fepdftops.exe AcroForm.obj Annot.obj Array.obj BuiltinFont.obj BuiltinFontTables.obj Catalog.obj CharCodeToUnicode.obj CMap.obj Decrypt.obj Dict.obj Error.obj FontEncodingTables.obj Form.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 OptionalContent.obj Outline.obj OutputDev.obj Page.obj Parser.obj PDFDoc.obj PDFDocEncoding.obj PSOutputDev.obj PSTokenizer.obj PreScanOutputDev.obj SecurityHandler.obj SplashOutputDev.obj Stream.obj TextString.obj UnicodeMap.obj XFAForm.obj XRef.obj Zoox.obj pdftops.obj ..\splash\splash.lib ..\fofi\fofi.lib ..\goo\Goo.lib %FT2DIR%\freetype2.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 OptionalContent.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% /Fepdftotext.exe AcroForm.obj Annot.obj Array.obj BuiltinFont.obj BuiltinFontTables.obj Catalog.obj CharCodeToUnicode.obj CMap.obj Decrypt.obj Dict.obj Error.obj FontEncodingTables.obj Form.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 OptionalContent.obj Outline.obj OutputDev.obj Page.obj Parser.obj PDFDoc.obj PDFDocEncoding.obj PSTokenizer.obj SecurityHandler.obj Stream.obj TextOutputDev.obj TextString.obj UnicodeMap.obj UnicodeTypeTable.obj XFAForm.obj XRef.obj Zoox.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 OptionalContent.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% /Fepdftoppm.exe AcroForm.obj Annot.obj Array.obj BuiltinFont.obj BuiltinFontTables.obj Catalog.obj CharCodeToUnicode.obj CMap.obj Decrypt.obj Dict.obj Error.obj FontEncodingTables.obj Form.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 OptionalContent.obj Outline.obj OutputDev.obj Page.obj Parser.obj PDFDoc.obj PDFDocEncoding.obj PSTokenizer.obj SecurityHandler.obj SplashOutputDev.obj Stream.obj TextString.obj UnicodeMap.obj UnicodeTypeTable.obj XFAForm.obj XRef.obj Zoox.obj pdftoppm.obj ..\splash\splash.lib ..\fofi\fofi.lib ..\goo\Goo.lib %FT2DIR%\freetype2.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 OptionalContent.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% /Fepdfinfo.exe AcroForm.obj Annot.obj Array.obj BuiltinFont.obj BuiltinFontTables.obj Catalog.obj CharCodeToUnicode.obj CMap.obj Decrypt.obj Dict.obj Error.obj FontEncodingTables.obj Form.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 OptionalContent.obj Outline.obj OutputDev.obj Page.obj Parser.obj PDFDoc.obj PDFDocEncoding.obj PSTokenizer.obj SecurityHandler.obj Stream.obj TextString.obj UnicodeMap.obj XFAForm.obj XRef.obj Zoox.obj pdfinfo.obj ..\fofi\fofi.lib ..\goo\Goo.lib shell32.lib user32.lib gdi32.lib advapi32.lib
-%CXX% %LINKFLAGS% /Fepdfdetach.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 OptionalContent.obj Outline.obj OutputDev.obj Page.obj Parser.obj PDFDoc.obj PDFDocEncoding.obj PSTokenizer.obj SecurityHandler.obj Stream.obj UnicodeMap.obj XRef.obj pdfdetach.obj ..\fofi\fofi.lib ..\goo\Goo.lib shell32.lib user32.lib gdi32.lib advapi32.lib
+%CXX% %LINKFLAGS% /Fepdffonts.exe AcroForm.obj Annot.obj Array.obj BuiltinFont.obj BuiltinFontTables.obj Catalog.obj CharCodeToUnicode.obj CMap.obj Decrypt.obj Dict.obj Error.obj FontEncodingTables.obj Form.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 OptionalContent.obj Outline.obj OutputDev.obj Page.obj Parser.obj PDFDoc.obj PDFDocEncoding.obj PSTokenizer.obj SecurityHandler.obj Stream.obj TextString.obj UnicodeMap.obj XFAForm.obj XRef.obj Zoox.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 OptionalContent.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 ..
+%CXX% %LINKFLAGS% /Fepdfdetach.exe AcroForm.obj Annot.obj Array.obj BuiltinFont.obj BuiltinFontTables.obj Catalog.obj CharCodeToUnicode.obj CMap.obj Decrypt.obj Dict.obj Error.obj FontEncodingTables.obj Form.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 OptionalContent.obj Outline.obj OutputDev.obj Page.obj Parser.obj PDFDoc.obj PDFDocEncoding.obj PSTokenizer.obj SecurityHandler.obj Stream.obj TextString.obj UnicodeMap.obj XFAForm.obj XRef.obj Zoox.obj pdfdetach.obj ..\fofi\fofi.lib ..\goo\Goo.lib shell32.lib user32.lib gdi32.lib advapi32.lib
-rem --- This part will only work if you have FreeType installed ---
+%CXX% %LINKFLAGS% /Fepdfimages.exe AcroForm.obj Annot.obj Array.obj BuiltinFont.obj BuiltinFontTables.obj Catalog.obj CharCodeToUnicode.obj CMap.obj Decrypt.obj Dict.obj Error.obj FontEncodingTables.obj Form.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 OptionalContent.obj Outline.obj OutputDev.obj Page.obj Parser.obj PDFDoc.obj PDFDocEncoding.obj PSTokenizer.obj SecurityHandler.obj Stream.obj TextString.obj UnicodeMap.obj XFAForm.obj XRef.obj Zoox.obj pdfimages.obj ..\fofi\fofi.lib ..\goo\Goo.lib shell32.lib user32.lib gdi32.lib advapi32.lib
-set FT2DIR=..\freetype-2.4.6
-set CXXFLAGS=%CXXFLAGS% /I%FT2DIR%\include
+if x%PNGDIR% == x goto noHTML
+if x%ZLIBDIR% == x goto noHTML
-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% %CXXFLAGS% /I%PNGDIR% /I%ZLIBDIR% /c pdftopng.cc
+%CXX% %LINKFLAGS% /Fepdftopng.exe AcroForm.obj Annot.obj Array.obj BuiltinFont.obj BuiltinFontTables.obj Catalog.obj CharCodeToUnicode.obj CMap.obj Decrypt.obj Dict.obj Error.obj FontEncodingTables.obj Form.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 OptionalContent.obj Outline.obj OutputDev.obj Page.obj Parser.obj PDFDoc.obj PDFDocEncoding.obj PSTokenizer.obj SecurityHandler.obj SplashOutputDev.obj Stream.obj TextString.obj UnicodeMap.obj UnicodeTypeTable.obj XFAForm.obj XRef.obj Zoox.obj pdftopng.obj ..\splash\splash.lib ..\fofi\fofi.lib ..\goo\Goo.lib %FT2DIR%\freetype2.lib %PNGDIR%\libpng.lib %ZLIBDIR%\zlib.lib shell32.lib user32.lib gdi32.lib advapi32.lib
-%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 OptionalContent.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
+echo "building pdftohtml"
+%CXX% %CXXFLAGS% /I%PNGDIR% /I%ZLIBDIR% /c HTMLGen.cc
+%CXX% %CXXFLAGS% /I%PNGDIR% /I%ZLIBDIR% /c pdftohtml.cc
+%CXX% %LINKFLAGS% /Fepdftohtml.exe AcroForm.obj Annot.obj Array.obj BuiltinFont.obj BuiltinFontTables.obj Catalog.obj CharCodeToUnicode.obj CMap.obj Decrypt.obj Dict.obj Error.obj FontEncodingTables.obj Form.obj Function.obj Gfx.obj GfxFont.obj GfxState.obj GlobalParams.obj HTMLGen.obj JArithmeticDecoder.obj JBIG2Stream.obj JPXStream.obj Lexer.obj Link.obj NameToCharCode.obj Object.obj OptionalContent.obj Outline.obj OutputDev.obj Page.obj Parser.obj PDFDoc.obj PDFDocEncoding.obj PSTokenizer.obj SecurityHandler.obj SplashOutputDev.obj Stream.obj TextOutputDev.obj TextString.obj UnicodeMap.obj UnicodeTypeTable.obj XFAForm.obj XRef.obj Zoox.obj pdftohtml.obj ..\splash\splash.lib ..\fofi\fofi.lib ..\goo\Goo.lib %FT2DIR%\freetype2.lib %PNGDIR%\libpng.lib %ZLIBDIR%\zlib.lib shell32.lib user32.lib gdi32.lib advapi32.lib
-rem --- Uncomment this line, and comment out the other pdftops build
-rem line (see above) to build pdftops with the Splash rasterizer.
-rem You'll also need to define HAVE_SPLASH in aconf-win32.h.
-rem %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 OptionalContent.obj Outline.obj OutputDev.obj Page.obj Parser.obj PDFDoc.obj PDFDocEncoding.obj PSOutputDev.obj PSTokenizer.obj PreScanOutputDev.obj SecurityHandler.obj SplashOutputDev.obj Stream.obj UnicodeMap.obj XRef.obj pdftops.obj ..\splash\splash.lib ..\fofi\fofi.lib ..\goo\Goo.lib %FT2DIR%\freetype2.lib shell32.lib user32.lib gdi32.lib advapi32.lib
+:noHTML
cd ..
diff --git a/splash/Makefile.in b/splash/Makefile.in
index 66c449b..6479b15 100644
--- a/splash/Makefile.in
+++ b/splash/Makefile.in
@@ -16,7 +16,7 @@ GOOLIBDIR = ../goo
FOFISRCDIR = $(srcdir)/../fofi
FOFILIBDIR = ../fofi
-CXXFLAGS = @CXXFLAGS@ @DEFS@ -I.. -I$(GOOSRCDIR) -I$(FOFISRCDIR) -I$(srcdir) @t1_CFLAGS@ @freetype2_CFLAGS@
+CXXFLAGS = @CXXFLAGS@ @DEFS@ -I.. -I$(srcdir)/.. -I$(GOOSRCDIR) -I$(FOFISRCDIR) -I$(srcdir) @freetype2_CFLAGS@
CXX = @CXX@
AR = @AR@
@@ -48,9 +48,6 @@ CXX_SRC = \
$(srcdir)/SplashPattern.cc \
$(srcdir)/SplashScreen.cc \
$(srcdir)/SplashState.cc \
- $(srcdir)/SplashT1Font.cc \
- $(srcdir)/SplashT1FontEngine.cc \
- $(srcdir)/SplashT1FontFile.cc \
$(srcdir)/SplashXPath.cc \
$(srcdir)/SplashXPathScanner.cc
@@ -75,9 +72,6 @@ SPLASH_OBJS = \
SplashPattern.o \
SplashScreen.o \
SplashState.o \
- SplashT1Font.o \
- SplashT1FontEngine.o \
- SplashT1FontFile.o \
SplashXPath.o \
SplashXPathScanner.o
@@ -96,4 +90,4 @@ clean:
depend:
$(CXX) $(CXXFLAGS) -MM $(CXX_SRC) >Makefile.dep
-include Makefile.dep
+-include Makefile.dep
diff --git a/splash/Splash.cc b/splash/Splash.cc
index 879a4f2..d9035b3 100644
--- a/splash/Splash.cc
+++ b/splash/Splash.cc
@@ -2,6 +2,8 @@
//
// Splash.cc
//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
//========================================================================
#include <aconf.h>
@@ -29,7 +31,7 @@
//------------------------------------------------------------------------
-#define splashAAGamma 1.5
+#define splashAAGamma 0.67
// distance of Bezier control point from center for circle approximation
// = (4 * (sqrt(2) - 1) / 3) * r
@@ -46,41 +48,12 @@ static inline Guchar clip255(int x) {
return x < 0 ? 0 : x > 255 ? 255 : x;
}
-// The PDF spec says that all pixels whose *centers* lie within the
-// image target region get painted, so we want to round n+0.5 down to
-// n. But this causes problems, e.g., with PDF files that fill a
-// rectangle with black and then draw an image to the exact same
-// rectangle, so we instead use the fill scan conversion rule.
-// However, the correct rule works better for glyphs, so we also
-// provide that option in fillImageMask.
-#if 0
-static inline int imgCoordMungeLower(SplashCoord x) {
- return splashCeil(x + 0.5) - 1;
-}
-static inline int imgCoordMungeUpper(SplashCoord x) {
- return splashCeil(x + 0.5) - 1;
-}
-#else
-static inline int imgCoordMungeLower(SplashCoord x) {
- return splashFloor(x);
-}
-static inline int imgCoordMungeUpper(SplashCoord x) {
- return splashFloor(x) + 1;
-}
-static inline int imgCoordMungeLowerC(SplashCoord x, GBool glyphMode) {
- return glyphMode ? (splashCeil(x + 0.5) - 1) : splashFloor(x);
-}
-static inline int imgCoordMungeUpperC(SplashCoord x, GBool glyphMode) {
- return glyphMode ? (splashCeil(x + 0.5) - 1) : (splashFloor(x) + 1);
-}
-#endif
-
// Used by drawImage and fillImageMask to divide the target
// quadrilateral into sections.
struct ImageSection {
int y0, y1; // actual y range
int ia0, ia1; // vertex indices for edge A
- int ib0, ib1; // vertex indices for edge A
+ int ib0, ib1; // vertex indices for edge B
SplashCoord xa0, ya0, xa1, ya1; // edge A
SplashCoord dxdya; // slope of edge A
SplashCoord xb0, yb0, xb1, yb1; // edge B
@@ -94,41 +67,26 @@ struct ImageSection {
#define splashPipeMaxStages 9
struct SplashPipe {
- // pixel coordinates
- int x, y;
-
// source pattern
SplashPattern *pattern;
// source alpha and color
Guchar aInput;
- GBool usesShape;
- 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
- Guchar shape;
-
- // result alpha and color
+ // special cases and result color
GBool noTransparency;
+ GBool shapeOnly;
SplashPipeResultColorCtrl resultColorCtrl;
// non-isolated group correction
+ // (this is only used when Splash::composite() is called to composite
+ // a non-isolated group onto the backdrop)
GBool nonIsolatedGroup;
// the "run" function
- void (Splash::*run)(SplashPipe *pipe);
+ void (Splash::*run)(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr);
};
SplashPipeResultColorCtrl Splash::pipeResultColorNoAlphaBlend[] = {
@@ -208,36 +166,37 @@ inline void Splash::updateModY(int y) {
// pipeline
//------------------------------------------------------------------------
-inline void Splash::pipeInit(SplashPipe *pipe, int x, int y,
- SplashPattern *pattern, SplashColorPtr cSrc,
+inline void Splash::pipeInit(SplashPipe *pipe, SplashPattern *pattern,
Guchar 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;
+ if (pattern && pattern->isStatic()) {
+ pattern->getColor(0, 0, pipe->cSrcVal);
+ pipe->pattern = NULL;
} else {
- pipe->cSrc = cSrc;
+ pipe->pattern = pattern;
}
// source alpha
pipe->aInput = aInput;
- pipe->usesShape = usesShape;
- // result alpha
- if (aInput == 255 && !state->softMask && !usesShape &&
- !state->inNonIsolatedGroup && !nonIsolatedGroup) {
- pipe->noTransparency = gTrue;
- } else {
- pipe->noTransparency = gFalse;
- }
+ // special cases
+ pipe->noTransparency = aInput == 255 &&
+ !state->softMask &&
+ !usesShape &&
+ !state->inNonIsolatedGroup &&
+ !state->inKnockoutGroup &&
+ !nonIsolatedGroup &&
+ state->overprintMask == 0xffffffff;
+ pipe->shapeOnly = aInput == 255 &&
+ !state->softMask &&
+ usesShape &&
+ !state->inNonIsolatedGroup &&
+ !state->inKnockoutGroup &&
+ !nonIsolatedGroup &&
+ state->overprintMask == 0xffffffff;
// result color
if (pipe->noTransparency) {
@@ -255,33 +214,48 @@ inline void Splash::pipeInit(SplashPipe *pipe, int x, int y,
// select the 'run' function
pipe->run = &Splash::pipeRun;
if (!pipe->pattern && pipe->noTransparency && !state->blendFunc) {
- if (bitmap->mode == splashModeMono1 && !pipe->destAlphaPtr) {
+ if (bitmap->mode == splashModeMono1 && !bitmap->alpha) {
pipe->run = &Splash::pipeRunSimpleMono1;
- } else if (bitmap->mode == splashModeMono8 && pipe->destAlphaPtr) {
+ } else if (bitmap->mode == splashModeMono8 && bitmap->alpha) {
pipe->run = &Splash::pipeRunSimpleMono8;
- } else if (bitmap->mode == splashModeRGB8 && pipe->destAlphaPtr) {
+ } else if (bitmap->mode == splashModeRGB8 && bitmap->alpha) {
pipe->run = &Splash::pipeRunSimpleRGB8;
- } else if (bitmap->mode == splashModeBGR8 && pipe->destAlphaPtr) {
+ } else if (bitmap->mode == splashModeBGR8 && bitmap->alpha) {
pipe->run = &Splash::pipeRunSimpleBGR8;
#if SPLASH_CMYK
- } else if (bitmap->mode == splashModeCMYK8 && pipe->destAlphaPtr) {
+ } else if (bitmap->mode == splashModeCMYK8 && bitmap->alpha) {
pipe->run = &Splash::pipeRunSimpleCMYK8;
#endif
}
+ } else if (!pipe->pattern && pipe->shapeOnly && !state->blendFunc) {
+ if (bitmap->mode == splashModeMono1 && !bitmap->alpha) {
+ pipe->run = &Splash::pipeRunShapeMono1;
+ } else if (bitmap->mode == splashModeMono8 && bitmap->alpha) {
+ pipe->run = &Splash::pipeRunShapeMono8;
+ } else if (bitmap->mode == splashModeRGB8 && bitmap->alpha) {
+ pipe->run = &Splash::pipeRunShapeRGB8;
+ } else if (bitmap->mode == splashModeBGR8 && bitmap->alpha) {
+ pipe->run = &Splash::pipeRunShapeBGR8;
+#if SPLASH_CMYK
+ } else if (bitmap->mode == splashModeCMYK8 && bitmap->alpha) {
+ pipe->run = &Splash::pipeRunShapeCMYK8;
+#endif
+ }
} else if (!pipe->pattern && !pipe->noTransparency && !state->softMask &&
- pipe->usesShape &&
- !(state->inNonIsolatedGroup && alpha0Bitmap->alpha) &&
+ usesShape &&
+ !(state->inNonIsolatedGroup && groupBackBitmap->alpha) &&
+ !state->inKnockoutGroup &&
!state->blendFunc && !pipe->nonIsolatedGroup) {
- if (bitmap->mode == splashModeMono1 && !pipe->destAlphaPtr) {
+ if (bitmap->mode == splashModeMono1 && !bitmap->alpha) {
pipe->run = &Splash::pipeRunAAMono1;
- } else if (bitmap->mode == splashModeMono8 && pipe->destAlphaPtr) {
+ } else if (bitmap->mode == splashModeMono8 && bitmap->alpha) {
pipe->run = &Splash::pipeRunAAMono8;
- } else if (bitmap->mode == splashModeRGB8 && pipe->destAlphaPtr) {
+ } else if (bitmap->mode == splashModeRGB8 && bitmap->alpha) {
pipe->run = &Splash::pipeRunAARGB8;
- } else if (bitmap->mode == splashModeBGR8 && pipe->destAlphaPtr) {
+ } else if (bitmap->mode == splashModeBGR8 && bitmap->alpha) {
pipe->run = &Splash::pipeRunAABGR8;
#if SPLASH_CMYK
- } else if (bitmap->mode == splashModeCMYK8 && pipe->destAlphaPtr) {
+ } else if (bitmap->mode == splashModeCMYK8 && bitmap->alpha) {
pipe->run = &Splash::pipeRunAACMYK8;
#endif
}
@@ -289,932 +263,1654 @@ inline void Splash::pipeInit(SplashPipe *pipe, int x, int y,
}
// general case
-void Splash::pipeRun(SplashPipe *pipe) {
- Guchar aSrc, aDest, alphaI, alphaIm1, alpha0, aResult;
- SplashColor cSrcNonIso, cDest, cBlend;
- SplashColorPtr cSrc;
- Guchar cResult0, cResult1, cResult2, cResult3;
- int t;
+void Splash::pipeRun(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar *shapePtr2;
+ Guchar shape, aSrc, aDest, alphaI, alphaIm1, alpha0, aResult;
+ SplashColor cSrc, cDest, cBlend;
+ Guchar shapeVal, cResult0, cResult1, cResult2, cResult3;
+ int cSrcStride, shapeStride, x, lastX, t, i;
+ SplashColorPtr destColorPtr;
+ Guchar destColorMask;
+ Guchar *destAlphaPtr;
+ SplashColorPtr color0Ptr;
+ Guchar color0Mask;
+ Guchar *alpha0Ptr;
+ SplashColorPtr softMaskPtr;
#if SPLASH_CMYK
SplashColor cSrc2, cDest2;
#endif
- //----- source color
+ if (cSrcPtr && !pipe->pattern) {
+ cSrcStride = bitmapComps;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
- // static pattern: handled in pipeInit
- // fixed color: handled in pipeInit
+ if (shapePtr) {
+ shapePtr2 = shapePtr;
+ shapeStride = 1;
+ for (; x0 <= x1; ++x0) {
+ if (*shapePtr2) {
+ break;
+ }
+ cSrcPtr += cSrcStride;
+ ++shapePtr2;
+ }
+ } else {
+ shapeVal = 0xff;
+ shapePtr2 = &shapeVal;
+ shapeStride = 0;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModY(y);
+ lastX = x0;
- // dynamic pattern
- if (pipe->pattern) {
- pipe->pattern->getColor(pipe->x, pipe->y, pipe->cSrcVal);
+ if (bitmap->mode == splashModeMono1) {
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)];
+ destColorMask = 0x80 >> (x0 & 7);
+ } else {
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + x0 * bitmapComps];
+ destColorMask = 0; // make gcc happy
+ }
+ if (bitmap->alpha) {
+ destAlphaPtr = &bitmap->alpha[y * bitmap->width + x0];
+ } else {
+ destAlphaPtr = NULL;
+ }
+ if (state->softMask) {
+ softMaskPtr = &state->softMask->data[y * state->softMask->rowSize + x0];
+ } else {
+ softMaskPtr = NULL;
+ }
+ if (state->inKnockoutGroup) {
+ if (bitmap->mode == splashModeMono1) {
+ color0Ptr =
+ &groupBackBitmap->data[(groupBackY + y) * groupBackBitmap->rowSize +
+ ((groupBackX + x0) >> 3)];
+ color0Mask = 0x80 >> ((groupBackX + x0) & 7);
+ } else {
+ color0Ptr =
+ &groupBackBitmap->data[(groupBackY + y) * groupBackBitmap->rowSize +
+ (groupBackX + x0) * bitmapComps];
+ color0Mask = 0; // make gcc happy
+ }
+ } else {
+ color0Ptr = NULL;
+ color0Mask = 0; // make gcc happy
+ }
+ if (state->inNonIsolatedGroup && groupBackBitmap->alpha) {
+ alpha0Ptr =
+ &groupBackBitmap->alpha[(groupBackY + y) * groupBackBitmap->width +
+ (groupBackX + x0)];
+ } else {
+ alpha0Ptr = NULL;
}
- if (pipe->noTransparency && !state->blendFunc) {
+ for (x = x0; x <= x1; ++x) {
- //----- write destination pixel
+ //----- shape
- switch (bitmap->mode) {
- case splashModeMono1:
- cResult0 = state->grayTransfer[pipe->cSrc[0]];
- if (state->screen->test(pipe->x, pipe->y, cResult0)) {
- *pipe->destColorPtr |= pipe->destColorMask;
+ shape = *shapePtr2;
+ if (!shape) {
+ if (bitmap->mode == splashModeMono1) {
+ destColorPtr += destColorMask & 1;
+ destColorMask = (destColorMask << 7) | (destColorMask >> 1);
} else {
- *pipe->destColorPtr &= ~pipe->destColorMask;
+ destColorPtr += bitmapComps;
}
- if (!(pipe->destColorMask >>= 1)) {
- pipe->destColorMask = 0x80;
- ++pipe->destColorPtr;
- }
- break;
- case splashModeMono8:
- *pipe->destColorPtr++ = state->grayTransfer[pipe->cSrc[0]];
- break;
- case splashModeRGB8:
- *pipe->destColorPtr++ = state->rgbTransferR[pipe->cSrc[0]];
- *pipe->destColorPtr++ = state->rgbTransferG[pipe->cSrc[1]];
- *pipe->destColorPtr++ = state->rgbTransferB[pipe->cSrc[2]];
- break;
- case splashModeBGR8:
- *pipe->destColorPtr++ = state->rgbTransferB[pipe->cSrc[2]];
- *pipe->destColorPtr++ = state->rgbTransferG[pipe->cSrc[1]];
- *pipe->destColorPtr++ = state->rgbTransferR[pipe->cSrc[0]];
- break;
-#if SPLASH_CMYK
- case splashModeCMYK8:
- if (state->overprintMask & 1) {
- pipe->destColorPtr[0] = state->cmykTransferC[pipe->cSrc[0]];
+ if (destAlphaPtr) {
+ ++destAlphaPtr;
}
- if (state->overprintMask & 2) {
- pipe->destColorPtr[1] = state->cmykTransferM[pipe->cSrc[1]];
+ if (softMaskPtr) {
+ ++softMaskPtr;
}
- if (state->overprintMask & 4) {
- pipe->destColorPtr[2] = state->cmykTransferY[pipe->cSrc[2]];
+ if (color0Ptr) {
+ if (bitmap->mode == splashModeMono1) {
+ color0Ptr += color0Mask & 1;
+ color0Mask = (color0Mask << 7) | (color0Mask >> 1);
+ } else {
+ color0Ptr += bitmapComps;
+ }
}
- if (state->overprintMask & 8) {
- pipe->destColorPtr[3] = state->cmykTransferK[pipe->cSrc[3]];
+ if (alpha0Ptr) {
+ ++alpha0Ptr;
}
- pipe->destColorPtr += 4;
- break;
-#endif
+ cSrcPtr += cSrcStride;
+ shapePtr2 += shapeStride;
+ continue;
}
- if (pipe->destAlphaPtr) {
- *pipe->destAlphaPtr++ = 255;
+ lastX = x;
+
+ //----- source color
+
+ // static pattern: handled in pipeInit
+ // fixed color: handled in pipeInit
+
+ // dynamic pattern
+ if (pipe->pattern) {
+ pipe->pattern->getColor(x, y, pipe->cSrcVal);
}
- } else {
+ if (pipe->noTransparency && !state->blendFunc) {
- //----- read destination pixel
+ //----- write 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;
+ switch (bitmap->mode) {
+ case splashModeMono1:
+ cResult0 = state->grayTransfer[cSrcPtr[0]];
+ if (state->screen->test(x, y, cResult0)) {
+ *destColorPtr |= destColorMask;
+ } else {
+ *destColorPtr &= ~destColorMask;
+ }
+ destColorPtr += destColorMask & 1;
+ destColorMask = (destColorMask << 7) | (destColorMask >> 1);
+ break;
+ case splashModeMono8:
+ *destColorPtr++ = state->grayTransfer[cSrcPtr[0]];
+ break;
+ case splashModeRGB8:
+ destColorPtr[0] = state->rgbTransferR[cSrcPtr[0]];
+ destColorPtr[1] = state->rgbTransferG[cSrcPtr[1]];
+ destColorPtr[2] = state->rgbTransferB[cSrcPtr[2]];
+ destColorPtr += 3;
+ break;
+ case splashModeBGR8:
+ destColorPtr[0] = state->rgbTransferB[cSrcPtr[2]];
+ destColorPtr[1] = state->rgbTransferG[cSrcPtr[1]];
+ destColorPtr[2] = state->rgbTransferR[cSrcPtr[0]];
+ destColorPtr += 3;
+ 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;
+ case splashModeCMYK8:
+ destColorPtr[0] = state->cmykTransferC[cSrcPtr[0]];
+ destColorPtr[1] = state->cmykTransferM[cSrcPtr[1]];
+ destColorPtr[2] = state->cmykTransferY[cSrcPtr[2]];
+ destColorPtr[3] = state->cmykTransferK[cSrcPtr[3]];
+ destColorPtr += 4;
+ break;
#endif
- }
- if (pipe->destAlphaPtr) {
- aDest = *pipe->destAlphaPtr;
- } else {
- aDest = 0xff;
- }
+ }
+ if (destAlphaPtr) {
+ *destAlphaPtr++ = 255;
+ }
- //----- source alpha
+ } else { // if (noTransparency && !blendFunc)
+
+ //----- read destination pixel
+ // (or backdrop color, for knockout groups)
+
+ if (color0Ptr) {
+
+ switch (bitmap->mode) {
+ case splashModeMono1:
+ cDest[0] = (*color0Ptr & color0Mask) ? 0xff : 0x00;
+ color0Ptr += color0Mask & 1;
+ color0Mask = (color0Mask << 7) | (color0Mask >> 1);
+ break;
+ case splashModeMono8:
+ cDest[0] = *color0Ptr++;
+ break;
+ case splashModeRGB8:
+ cDest[0] = color0Ptr[0];
+ cDest[1] = color0Ptr[1];
+ cDest[2] = color0Ptr[2];
+ color0Ptr += 3;
+ break;
+ case splashModeBGR8:
+ cDest[2] = color0Ptr[0];
+ cDest[1] = color0Ptr[1];
+ cDest[0] = color0Ptr[2];
+ color0Ptr += 3;
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ cDest[0] = color0Ptr[0];
+ cDest[1] = color0Ptr[1];
+ cDest[2] = color0Ptr[2];
+ cDest[3] = color0Ptr[3];
+ color0Ptr += 4;
+ break;
+#endif
+ }
- if (state->softMask) {
- if (pipe->usesShape) {
- aSrc = div255(div255(pipe->aInput * *pipe->softMaskPtr++) *
- pipe->shape);
} else {
- aSrc = div255(pipe->aInput * *pipe->softMaskPtr++);
+
+ switch (bitmap->mode) {
+ case splashModeMono1:
+ cDest[0] = (*destColorPtr & destColorMask) ? 0xff : 0x00;
+ break;
+ case splashModeMono8:
+ cDest[0] = *destColorPtr;
+ break;
+ case splashModeRGB8:
+ cDest[0] = destColorPtr[0];
+ cDest[1] = destColorPtr[1];
+ cDest[2] = destColorPtr[2];
+ break;
+ case splashModeBGR8:
+ cDest[0] = destColorPtr[2];
+ cDest[1] = destColorPtr[1];
+ cDest[2] = destColorPtr[0];
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ cDest[0] = destColorPtr[0];
+ cDest[1] = destColorPtr[1];
+ cDest[2] = destColorPtr[2];
+ cDest[3] = destColorPtr[3];
+ break;
+#endif
+ }
+
}
- } else if (pipe->usesShape) {
- aSrc = div255(pipe->aInput * pipe->shape);
- } else {
- aSrc = pipe->aInput;
- }
- //----- non-isolated group correction
+ if (destAlphaPtr) {
+ aDest = *destAlphaPtr;
+ } else {
+ aDest = 0xff;
+ }
+
+ //----- overprint
- if (pipe->nonIsolatedGroup) {
- // This path is only used when Splash::composite() is called to
- // composite a non-isolated group onto the backdrop. In this
- // case, pipe->shape is the source (group) alpha.
- if (pipe->shape == 0) {
- // this value will be multiplied by zero later, so it doesn't
- // matter what we use
- cSrc = pipe->cSrc;
+ for (i = 0; i < bitmapComps; ++i) {
+#if SPLASH_CMYK
+ if (state->overprintMask & (1 << i)) {
+ cSrc[i] = cSrcPtr[i];
+ } else {
+ cSrc[i] = div255(aDest * cDest[i]);
+ }
+#else
+ cSrc[i] = cSrcPtr[i];
+#endif
+ }
+
+ //----- source alpha
+
+ if (softMaskPtr) {
+ if (shapePtr) {
+ aSrc = div255(div255(pipe->aInput * *softMaskPtr++) * shape);
+ } else {
+ aSrc = div255(pipe->aInput * *softMaskPtr++);
+ }
+ } else if (shapePtr) {
+ aSrc = div255(pipe->aInput * shape);
} else {
- t = (aDest * 255) / pipe->shape - aDest;
+ aSrc = pipe->aInput;
+ }
+
+ //----- non-isolated group correction
+
+ if (pipe->nonIsolatedGroup) {
+ // This path is only used when Splash::composite() is called to
+ // composite a non-isolated group onto the backdrop. In this
+ // case, shape is the source (group) alpha.
+ t = (aDest * 255) / shape - aDest;
switch (bitmap->mode) {
#if SPLASH_CMYK
case splashModeCMYK8:
- cSrcNonIso[3] = clip255(pipe->cSrc[3] +
- ((pipe->cSrc[3] - cDest[3]) * t) / 255);
+ cSrc[3] = clip255(cSrc[3] + ((cSrc[3] - cDest[3]) * t) / 255);
#endif
case splashModeRGB8:
case splashModeBGR8:
- cSrcNonIso[2] = clip255(pipe->cSrc[2] +
- ((pipe->cSrc[2] - cDest[2]) * t) / 255);
- cSrcNonIso[1] = clip255(pipe->cSrc[1] +
- ((pipe->cSrc[1] - cDest[1]) * t) / 255);
+ cSrc[2] = clip255(cSrc[2] + ((cSrc[2] - cDest[2]) * t) / 255);
+ cSrc[1] = clip255(cSrc[1] + ((cSrc[1] - cDest[1]) * t) / 255);
case splashModeMono1:
case splashModeMono8:
- cSrcNonIso[0] = clip255(pipe->cSrc[0] +
- ((pipe->cSrc[0] - cDest[0]) * t) / 255);
+ cSrc[0] = clip255(cSrc[0] + ((cSrc[0] - cDest[0]) * t) / 255);
break;
}
- cSrc = cSrcNonIso;
}
- } else {
- cSrc = pipe->cSrc;
- }
- //----- blend function
+ //----- blend function
- if (state->blendFunc) {
+ if (state->blendFunc) {
#if SPLASH_CMYK
- if (bitmap->mode == splashModeCMYK8) {
- // convert colors to additive
- cSrc2[0] = 0xff - cSrc[0];
- cSrc2[1] = 0xff - cSrc[1];
- cSrc2[2] = 0xff - cSrc[2];
- cSrc2[3] = 0xff - cSrc[3];
- cDest2[0] = 0xff - cDest[0];
- cDest2[1] = 0xff - cDest[1];
- cDest2[2] = 0xff - cDest[2];
- cDest2[3] = 0xff - cDest[3];
- (*state->blendFunc)(cSrc2, cDest2, cBlend, bitmap->mode);
- // convert result back to subtractive
- cBlend[0] = 0xff - cBlend[0];
- cBlend[1] = 0xff - cBlend[1];
- cBlend[2] = 0xff - cBlend[2];
- cBlend[3] = 0xff - cBlend[3];
- } else
+ if (bitmap->mode == splashModeCMYK8) {
+ // convert colors to additive
+ cSrc2[0] = 0xff - cSrc[0];
+ cSrc2[1] = 0xff - cSrc[1];
+ cSrc2[2] = 0xff - cSrc[2];
+ cSrc2[3] = 0xff - cSrc[3];
+ cDest2[0] = 0xff - cDest[0];
+ cDest2[1] = 0xff - cDest[1];
+ cDest2[2] = 0xff - cDest[2];
+ cDest2[3] = 0xff - cDest[3];
+ (*state->blendFunc)(cSrc2, cDest2, cBlend, bitmap->mode);
+ // convert result back to subtractive
+ cBlend[0] = 0xff - cBlend[0];
+ cBlend[1] = 0xff - cBlend[1];
+ cBlend[2] = 0xff - cBlend[2];
+ cBlend[3] = 0xff - cBlend[3];
+ } else
#endif
- (*state->blendFunc)(cSrc, cDest, cBlend, bitmap->mode);
- }
-
- //----- result alpha and non-isolated group element correction
+ (*state->blendFunc)(cSrc, cDest, cBlend, bitmap->mode);
+ }
- if (pipe->noTransparency) {
- alphaI = alphaIm1 = aResult = 255;
- } else {
- aResult = aSrc + aDest - div255(aSrc * aDest);
+ //----- result alpha and non-isolated group element correction
// alphaI = alpha_i
// alphaIm1 = alpha_(i-1)
- if (pipe->alpha0Ptr) {
- alpha0 = *pipe->alpha0Ptr++;
- alphaI = aResult + alpha0 - div255(aResult * alpha0);
- alphaIm1 = alpha0 + aDest - div255(alpha0 * aDest);
+
+ if (pipe->noTransparency) {
+ alphaI = alphaIm1 = aResult = 255;
+ } else if (alpha0Ptr) {
+ if (color0Ptr) {
+ // non-isolated, knockout
+ aResult = aSrc;
+ alpha0 = *alpha0Ptr++;
+ alphaI = aSrc + alpha0 - div255(aSrc * alpha0);
+ alphaIm1 = alpha0;
+ } else {
+ // non-isolated, non-knockout
+ aResult = aSrc + aDest - div255(aSrc * aDest);
+ alpha0 = *alpha0Ptr++;
+ alphaI = aResult + alpha0 - div255(aResult * alpha0);
+ alphaIm1 = alpha0 + aDest - div255(alpha0 * aDest);
+ }
} else {
- alphaI = aResult;
- alphaIm1 = aDest;
+ if (color0Ptr) {
+ // isolated, knockout
+ aResult = aSrc;
+ alphaI = aSrc;
+ alphaIm1 = 0;
+ } else {
+ // isolated, non-knockout
+ aResult = aSrc + aDest - div255(aSrc * aDest);
+ alphaI = aResult;
+ alphaIm1 = aDest;
+ }
}
- }
- //----- result color
+ //----- result color
- cResult0 = cResult1 = cResult2 = cResult3 = 0; // make gcc happy
+ cResult0 = cResult1 = cResult2 = cResult3 = 0; // make gcc happy
- switch (pipe->resultColorCtrl) {
+ switch (pipe->resultColorCtrl) {
- case splashPipeResultColorNoAlphaBlendMono:
- cResult0 = state->grayTransfer[div255((255 - aDest) * cSrc[0] +
- aDest * cBlend[0])];
- break;
- case splashPipeResultColorNoAlphaBlendRGB:
- cResult0 = state->rgbTransferR[div255((255 - aDest) * cSrc[0] +
- aDest * cBlend[0])];
- cResult1 = state->rgbTransferG[div255((255 - aDest) * cSrc[1] +
- aDest * cBlend[1])];
- cResult2 = state->rgbTransferB[div255((255 - aDest) * cSrc[2] +
- aDest * cBlend[2])];
- break;
+ case splashPipeResultColorNoAlphaBlendMono:
+ cResult0 = state->grayTransfer[div255((255 - aDest) * cSrc[0] +
+ aDest * cBlend[0])];
+ break;
+ case splashPipeResultColorNoAlphaBlendRGB:
+ cResult0 = state->rgbTransferR[div255((255 - aDest) * cSrc[0] +
+ aDest * cBlend[0])];
+ cResult1 = state->rgbTransferG[div255((255 - aDest) * cSrc[1] +
+ aDest * cBlend[1])];
+ cResult2 = state->rgbTransferB[div255((255 - aDest) * cSrc[2] +
+ aDest * cBlend[2])];
+ break;
#if SPLASH_CMYK
- case splashPipeResultColorNoAlphaBlendCMYK:
- cResult0 = state->cmykTransferC[div255((255 - aDest) * cSrc[0] +
- aDest * cBlend[0])];
- cResult1 = state->cmykTransferM[div255((255 - aDest) * cSrc[1] +
- aDest * cBlend[1])];
- cResult2 = state->cmykTransferY[div255((255 - aDest) * cSrc[2] +
- aDest * cBlend[2])];
- cResult3 = state->cmykTransferK[div255((255 - aDest) * cSrc[3] +
- aDest * cBlend[3])];
- break;
+ case splashPipeResultColorNoAlphaBlendCMYK:
+ cResult0 = state->cmykTransferC[div255((255 - aDest) * cSrc[0] +
+ aDest * cBlend[0])];
+ cResult1 = state->cmykTransferM[div255((255 - aDest) * cSrc[1] +
+ aDest * cBlend[1])];
+ cResult2 = state->cmykTransferY[div255((255 - aDest) * cSrc[2] +
+ aDest * cBlend[2])];
+ cResult3 = state->cmykTransferK[div255((255 - aDest) * cSrc[3] +
+ aDest * cBlend[3])];
+ break;
#endif
- case splashPipeResultColorAlphaNoBlendMono:
- if (alphaI == 0) {
- cResult0 = 0;
- } else {
- cResult0 = state->grayTransfer[((alphaI - aSrc) * cDest[0] +
- aSrc * cSrc[0]) / alphaI];
- }
- break;
- case splashPipeResultColorAlphaNoBlendRGB:
- if (alphaI == 0) {
- cResult0 = 0;
- cResult1 = 0;
- cResult2 = 0;
- } else {
- cResult0 = state->rgbTransferR[((alphaI - aSrc) * cDest[0] +
- aSrc * cSrc[0]) / alphaI];
- cResult1 = state->rgbTransferG[((alphaI - aSrc) * cDest[1] +
- aSrc * cSrc[1]) / alphaI];
- cResult2 = state->rgbTransferB[((alphaI - aSrc) * cDest[2] +
- aSrc * cSrc[2]) / alphaI];
- }
- break;
+ case splashPipeResultColorAlphaNoBlendMono:
+ if (alphaI == 0) {
+ cResult0 = 0;
+ } else {
+ cResult0 = state->grayTransfer[((alphaI - aSrc) * cDest[0] +
+ aSrc * cSrc[0]) / alphaI];
+ }
+ break;
+ case splashPipeResultColorAlphaNoBlendRGB:
+ if (alphaI == 0) {
+ cResult0 = 0;
+ cResult1 = 0;
+ cResult2 = 0;
+ } else {
+ cResult0 = state->rgbTransferR[((alphaI - aSrc) * cDest[0] +
+ aSrc * cSrc[0]) / alphaI];
+ cResult1 = state->rgbTransferG[((alphaI - aSrc) * cDest[1] +
+ aSrc * cSrc[1]) / alphaI];
+ cResult2 = state->rgbTransferB[((alphaI - aSrc) * cDest[2] +
+ aSrc * cSrc[2]) / alphaI];
+ }
+ break;
#if SPLASH_CMYK
- case splashPipeResultColorAlphaNoBlendCMYK:
- if (alphaI == 0) {
- cResult0 = 0;
- cResult1 = 0;
- cResult2 = 0;
- cResult3 = 0;
- } else {
- cResult0 = state->cmykTransferC[((alphaI - aSrc) * cDest[0] +
- aSrc * cSrc[0]) / alphaI];
- cResult1 = state->cmykTransferM[((alphaI - aSrc) * cDest[1] +
- aSrc * cSrc[1]) / alphaI];
- cResult2 = state->cmykTransferY[((alphaI - aSrc) * cDest[2] +
- aSrc * cSrc[2]) / alphaI];
- cResult3 = state->cmykTransferK[((alphaI - aSrc) * cDest[3] +
- aSrc * cSrc[3]) / alphaI];
- }
- break;
+ case splashPipeResultColorAlphaNoBlendCMYK:
+ if (alphaI == 0) {
+ cResult0 = 0;
+ cResult1 = 0;
+ cResult2 = 0;
+ cResult3 = 0;
+ } else {
+ cResult0 = state->cmykTransferC[((alphaI - aSrc) * cDest[0] +
+ aSrc * cSrc[0]) / alphaI];
+ cResult1 = state->cmykTransferM[((alphaI - aSrc) * cDest[1] +
+ aSrc * cSrc[1]) / alphaI];
+ cResult2 = state->cmykTransferY[((alphaI - aSrc) * cDest[2] +
+ aSrc * cSrc[2]) / alphaI];
+ cResult3 = state->cmykTransferK[((alphaI - aSrc) * cDest[3] +
+ aSrc * cSrc[3]) / alphaI];
+ }
+ break;
#endif
- case splashPipeResultColorAlphaBlendMono:
- if (alphaI == 0) {
- cResult0 = 0;
- } else {
- cResult0 = state->grayTransfer[((alphaI - aSrc) * cDest[0] +
- aSrc * ((255 - alphaIm1) * cSrc[0] +
- alphaIm1 * cBlend[0]) / 255) /
- alphaI];
- }
- break;
- case splashPipeResultColorAlphaBlendRGB:
- if (alphaI == 0) {
- cResult0 = 0;
- cResult1 = 0;
- cResult2 = 0;
- } else {
- cResult0 = state->rgbTransferR[((alphaI - aSrc) * cDest[0] +
- aSrc * ((255 - alphaIm1) * cSrc[0] +
- alphaIm1 * cBlend[0]) / 255) /
- alphaI];
- cResult1 = state->rgbTransferG[((alphaI - aSrc) * cDest[1] +
- aSrc * ((255 - alphaIm1) * cSrc[1] +
- alphaIm1 * cBlend[1]) / 255) /
- alphaI];
- cResult2 = state->rgbTransferB[((alphaI - aSrc) * cDest[2] +
- aSrc * ((255 - alphaIm1) * cSrc[2] +
- alphaIm1 * cBlend[2]) / 255) /
- alphaI];
- }
- break;
+ case splashPipeResultColorAlphaBlendMono:
+ if (alphaI == 0) {
+ cResult0 = 0;
+ } else {
+ cResult0 = state->grayTransfer[((alphaI - aSrc) * cDest[0] +
+ aSrc * ((255 - alphaIm1) * cSrc[0] +
+ alphaIm1 * cBlend[0]) / 255) /
+ alphaI];
+ }
+ break;
+ case splashPipeResultColorAlphaBlendRGB:
+ if (alphaI == 0) {
+ cResult0 = 0;
+ cResult1 = 0;
+ cResult2 = 0;
+ } else {
+ cResult0 = state->rgbTransferR[((alphaI - aSrc) * cDest[0] +
+ aSrc * ((255 - alphaIm1) * cSrc[0] +
+ alphaIm1 * cBlend[0]) / 255) /
+ alphaI];
+ cResult1 = state->rgbTransferG[((alphaI - aSrc) * cDest[1] +
+ aSrc * ((255 - alphaIm1) * cSrc[1] +
+ alphaIm1 * cBlend[1]) / 255) /
+ alphaI];
+ cResult2 = state->rgbTransferB[((alphaI - aSrc) * cDest[2] +
+ aSrc * ((255 - alphaIm1) * cSrc[2] +
+ alphaIm1 * cBlend[2]) / 255) /
+ alphaI];
+ }
+ break;
#if SPLASH_CMYK
- case splashPipeResultColorAlphaBlendCMYK:
- if (alphaI == 0) {
- cResult0 = 0;
- cResult1 = 0;
- cResult2 = 0;
- cResult3 = 0;
- } else {
- cResult0 = state->cmykTransferC[((alphaI - aSrc) * cDest[0] +
- aSrc * ((255 - alphaIm1) * cSrc[0] +
- alphaIm1 * cBlend[0]) / 255) /
- alphaI];
- cResult1 = state->cmykTransferM[((alphaI - aSrc) * cDest[1] +
- aSrc * ((255 - alphaIm1) * cSrc[1] +
- alphaIm1 * cBlend[1]) / 255) /
- alphaI];
- cResult2 = state->cmykTransferY[((alphaI - aSrc) * cDest[2] +
- aSrc * ((255 - alphaIm1) * cSrc[2] +
- alphaIm1 * cBlend[2]) / 255) /
- alphaI];
- cResult3 = state->cmykTransferK[((alphaI - aSrc) * cDest[3] +
- aSrc * ((255 - alphaIm1) * cSrc[3] +
- alphaIm1 * cBlend[3]) / 255) /
- alphaI];
- }
- break;
+ case splashPipeResultColorAlphaBlendCMYK:
+ if (alphaI == 0) {
+ cResult0 = 0;
+ cResult1 = 0;
+ cResult2 = 0;
+ cResult3 = 0;
+ } else {
+ cResult0 =
+ state->cmykTransferC[((alphaI - aSrc) * cDest[0] +
+ aSrc * ((255 - alphaIm1) * cSrc[0] +
+ alphaIm1 * cBlend[0]) / 255) /
+ alphaI];
+ cResult1 =
+ state->cmykTransferM[((alphaI - aSrc) * cDest[1] +
+ aSrc * ((255 - alphaIm1) * cSrc[1] +
+ alphaIm1 * cBlend[1]) / 255) /
+ alphaI];
+ cResult2 =
+ state->cmykTransferY[((alphaI - aSrc) * cDest[2] +
+ aSrc * ((255 - alphaIm1) * cSrc[2] +
+ alphaIm1 * cBlend[2]) / 255) /
+ alphaI];
+ cResult3 =
+ state->cmykTransferK[((alphaI - aSrc) * cDest[3] +
+ aSrc * ((255 - alphaIm1) * cSrc[3] +
+ alphaIm1 * cBlend[3]) / 255) /
+ alphaI];
+ }
+ break;
#endif
- }
+ }
- //----- write destination pixel
+ //----- 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;
+ switch (bitmap->mode) {
+ case splashModeMono1:
+ if (state->screen->test(x, y, cResult0)) {
+ *destColorPtr |= destColorMask;
+ } else {
+ *destColorPtr &= ~destColorMask;
+ }
+ destColorPtr += destColorMask & 1;
+ destColorMask = (destColorMask << 7) | (destColorMask >> 1);
+ break;
+ case splashModeMono8:
+ *destColorPtr++ = cResult0;
+ break;
+ case splashModeRGB8:
+ destColorPtr[0] = cResult0;
+ destColorPtr[1] = cResult1;
+ destColorPtr[2] = cResult2;
+ destColorPtr += 3;
+ break;
+ case splashModeBGR8:
+ destColorPtr[0] = cResult2;
+ destColorPtr[1] = cResult1;
+ destColorPtr[2] = cResult0;
+ destColorPtr += 3;
+ break;
#if SPLASH_CMYK
- case splashModeCMYK8:
- if (state->overprintMask & 1) {
- pipe->destColorPtr[0] = cResult0;
- }
- if (state->overprintMask & 2) {
- pipe->destColorPtr[1] = cResult1;
- }
- if (state->overprintMask & 4) {
- pipe->destColorPtr[2] = cResult2;
+ case splashModeCMYK8:
+ destColorPtr[0] = cResult0;
+ destColorPtr[1] = cResult1;
+ destColorPtr[2] = cResult2;
+ destColorPtr[3] = cResult3;
+ destColorPtr += 4;
+ break;
+#endif
}
- if (state->overprintMask & 8) {
- pipe->destColorPtr[3] = cResult3;
+ if (destAlphaPtr) {
+ *destAlphaPtr++ = aResult;
}
- pipe->destColorPtr += 4;
- break;
-#endif
- }
- if (pipe->destAlphaPtr) {
- *pipe->destAlphaPtr++ = aResult;
- }
- }
+ } // if (noTransparency && !blendFunc)
+
+ cSrcPtr += cSrcStride;
+ shapePtr2 += shapeStride;
+ } // for (x ...)
- ++pipe->x;
+ updateModX(lastX);
}
// special case:
// !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
-// bitmap->mode == splashModeMono1 && !pipe->destAlphaPtr) {
-void Splash::pipeRunSimpleMono1(SplashPipe *pipe) {
+// bitmap->mode == splashModeMono1 && !bitmap->alpha) {
+void Splash::pipeRunSimpleMono1(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
Guchar cResult0;
+ SplashColorPtr destColorPtr;
+ Guchar destColorMask;
+ int cSrcStride, x;
- //----- write destination pixel
- cResult0 = state->grayTransfer[pipe->cSrc[0]];
- if (state->screen->test(pipe->x, pipe->y, cResult0)) {
- *pipe->destColorPtr |= pipe->destColorMask;
+ if (cSrcPtr) {
+ cSrcStride = 1;
} else {
- *pipe->destColorPtr &= ~pipe->destColorMask;
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
}
- if (!(pipe->destColorMask >>= 1)) {
- pipe->destColorMask = 0x80;
- ++pipe->destColorPtr;
+ if (x0 > x1) {
+ return;
}
+ updateModX(x0);
+ updateModX(x1);
+ updateModY(y);
- ++pipe->x;
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)];
+ destColorMask = 0x80 >> (x0 & 7);
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- write destination pixel
+ cResult0 = state->grayTransfer[cSrcPtr[0]];
+ if (state->screen->test(x, y, cResult0)) {
+ *destColorPtr |= destColorMask;
+ } else {
+ *destColorPtr &= ~destColorMask;
+ }
+ destColorPtr += destColorMask & 1;
+ destColorMask = (destColorMask << 7) | (destColorMask >> 1);
+
+ cSrcPtr += cSrcStride;
+ }
}
// special case:
// !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
-// bitmap->mode == splashModeMono8 && pipe->destAlphaPtr) {
-void Splash::pipeRunSimpleMono8(SplashPipe *pipe) {
- //----- write destination pixel
- *pipe->destColorPtr++ = state->grayTransfer[pipe->cSrc[0]];
- *pipe->destAlphaPtr++ = 255;
+// bitmap->mode == splashModeMono8 && bitmap->alpha) {
+void Splash::pipeRunSimpleMono8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ int cSrcStride, x;
+
+ if (cSrcPtr) {
+ cSrcStride = 1;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModX(x1);
+ updateModY(y);
+
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + x0];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->width + x0];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- write destination pixel
+ *destColorPtr++ = state->grayTransfer[cSrcPtr[0]];
+ *destAlphaPtr++ = 255;
- ++pipe->x;
+ cSrcPtr += cSrcStride;
+ }
}
// special case:
// !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
-// bitmap->mode == splashModeRGB8 && pipe->destAlphaPtr) {
-void Splash::pipeRunSimpleRGB8(SplashPipe *pipe) {
- //----- write destination pixel
- *pipe->destColorPtr++ = state->rgbTransferR[pipe->cSrc[0]];
- *pipe->destColorPtr++ = state->rgbTransferG[pipe->cSrc[1]];
- *pipe->destColorPtr++ = state->rgbTransferB[pipe->cSrc[2]];
- *pipe->destAlphaPtr++ = 255;
-
- ++pipe->x;
+// bitmap->mode == splashModeRGB8 && bitmap->alpha) {
+void Splash::pipeRunSimpleRGB8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ int cSrcStride, x;
+
+ if (cSrcPtr) {
+ cSrcStride = 3;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModX(x1);
+ updateModY(y);
+
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->width + x0];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- write destination pixel
+ destColorPtr[0] = state->rgbTransferR[cSrcPtr[0]];
+ destColorPtr[1] = state->rgbTransferG[cSrcPtr[1]];
+ destColorPtr[2] = state->rgbTransferB[cSrcPtr[2]];
+ destColorPtr += 3;
+ *destAlphaPtr++ = 255;
+
+ cSrcPtr += cSrcStride;
+ }
}
// special case:
// !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
-// bitmap->mode == splashModeBGR8 && pipe->destAlphaPtr) {
-void Splash::pipeRunSimpleBGR8(SplashPipe *pipe) {
- //----- write destination pixel
- *pipe->destColorPtr++ = state->rgbTransferB[pipe->cSrc[2]];
- *pipe->destColorPtr++ = state->rgbTransferG[pipe->cSrc[1]];
- *pipe->destColorPtr++ = state->rgbTransferR[pipe->cSrc[0]];
- *pipe->destAlphaPtr++ = 255;
-
- ++pipe->x;
+// bitmap->mode == splashModeBGR8 && bitmap->alpha) {
+void Splash::pipeRunSimpleBGR8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ int cSrcStride, x;
+
+ if (cSrcPtr) {
+ cSrcStride = 3;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModX(x1);
+ updateModY(y);
+
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->width + x0];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- write destination pixel
+ destColorPtr[0] = state->rgbTransferB[cSrcPtr[2]];
+ destColorPtr[1] = state->rgbTransferG[cSrcPtr[1]];
+ destColorPtr[2] = state->rgbTransferR[cSrcPtr[0]];
+ destColorPtr += 3;
+ *destAlphaPtr++ = 255;
+
+ cSrcPtr += cSrcStride;
+ }
}
#if SPLASH_CMYK
// special case:
// !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
-// bitmap->mode == splashModeCMYK8 && pipe->destAlphaPtr) {
-void Splash::pipeRunSimpleCMYK8(SplashPipe *pipe) {
- //----- write destination pixel
- if (state->overprintMask & 1) {
- pipe->destColorPtr[0] = state->cmykTransferC[pipe->cSrc[0]];
- }
- if (state->overprintMask & 2) {
- pipe->destColorPtr[1] = state->cmykTransferM[pipe->cSrc[1]];
- }
- if (state->overprintMask & 4) {
- pipe->destColorPtr[2] = state->cmykTransferY[pipe->cSrc[2]];
+// bitmap->mode == splashModeCMYK8 && bitmap->alpha) {
+void Splash::pipeRunSimpleCMYK8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ int cSrcStride, x;
+
+ if (cSrcPtr) {
+ cSrcStride = 4;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
}
- if (state->overprintMask & 8) {
- pipe->destColorPtr[3] = state->cmykTransferK[pipe->cSrc[3]];
+ if (x0 > x1) {
+ return;
}
- pipe->destColorPtr += 4;
- *pipe->destAlphaPtr++ = 255;
+ updateModX(x0);
+ updateModX(x1);
+ updateModY(y);
+
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x0];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->width + x0];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- write destination pixel
+ destColorPtr[0] = state->cmykTransferC[cSrcPtr[0]];
+ destColorPtr[1] = state->cmykTransferM[cSrcPtr[1]];
+ destColorPtr[2] = state->cmykTransferY[cSrcPtr[2]];
+ destColorPtr[3] = state->cmykTransferK[cSrcPtr[3]];
+ destColorPtr += 4;
+ *destAlphaPtr++ = 255;
- ++pipe->x;
+ cSrcPtr += cSrcStride;
+ }
}
#endif
// special case:
-// !pipe->pattern && !pipe->noTransparency && !state->softMask &&
-// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
-// !pipe->nonIsolatedGroup &&
-// bitmap->mode == splashModeMono1 && !pipe->destAlphaPtr
-void Splash::pipeRunAAMono1(SplashPipe *pipe) {
- Guchar aSrc;
- SplashColor cDest;
- Guchar cResult0;
+// !pipe->pattern && pipe->shapeOnly && !state->blendFunc &&
+// bitmap->mode == splashModeMono1 && !bitmap->alpha
+void Splash::pipeRunShapeMono1(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar shape, aSrc, cDest0, cResult0;
+ SplashColorPtr destColorPtr;
+ Guchar destColorMask;
+ int cSrcStride, x, lastX;
+
+ if (cSrcPtr) {
+ cSrcStride = 1;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+ for (; x0 <= x1; ++x0) {
+ if (*shapePtr) {
+ break;
+ }
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModY(y);
+ lastX = x0;
- //----- read destination pixel
- cDest[0] = (*pipe->destColorPtr & pipe->destColorMask) ? 0xff : 0x00;
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)];
+ destColorMask = 0x80 >> (x0 & 7);
- //----- source alpha
- aSrc = div255(pipe->aInput * pipe->shape);
+ for (x = x0; x <= x1; ++x) {
- //----- result color
- // note: aDest = alpha2 = aResult = 0xff
- cResult0 = state->grayTransfer[(Guchar)div255((0xff - aSrc) * cDest[0] +
- aSrc * pipe->cSrc[0])];
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ destColorPtr += destColorMask & 1;
+ destColorMask = (destColorMask << 7) | (destColorMask >> 1);
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
- //----- write destination pixel
- 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;
+ //----- read destination pixel
+ cDest0 = (*destColorPtr & destColorMask) ? 0xff : 0x00;
+
+ //----- source alpha
+ aSrc = shape;
+
+ //----- result color
+ // note: aDest = alphaI = aResult = 0xff
+ cResult0 = state->grayTransfer[(Guchar)div255((0xff - aSrc) * cDest0 +
+ aSrc * cSrcPtr[0])];
+
+ //----- write destination pixel
+ if (state->screen->test(x, y, cResult0)) {
+ *destColorPtr |= destColorMask;
+ } else {
+ *destColorPtr &= ~destColorMask;
+ }
+ destColorPtr += destColorMask & 1;
+ destColorMask = (destColorMask << 7) | (destColorMask >> 1);
+
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
}
- ++pipe->x;
+ updateModX(lastX);
}
// special case:
-// !pipe->pattern && !pipe->noTransparency && !state->softMask &&
-// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
-// !pipe->nonIsolatedGroup &&
-// bitmap->mode == splashModeMono8 && pipe->destAlphaPtr
-void Splash::pipeRunAAMono8(SplashPipe *pipe) {
- Guchar aSrc, aDest, alpha2, aResult;
- SplashColor cDest;
- Guchar cResult0;
+// !pipe->pattern && pipe->shapeOnly && !state->blendFunc &&
+// bitmap->mode == splashModeMono8 && bitmap->alpha
+void Splash::pipeRunShapeMono8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar shape, aSrc, aDest, alphaI, aResult, cDest0, cResult0;
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ int cSrcStride, x, lastX;
- //----- read destination pixel
- cDest[0] = *pipe->destColorPtr;
- aDest = *pipe->destAlphaPtr;
+ if (cSrcPtr) {
+ cSrcStride = 1;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+ for (; x0 <= x1; ++x0) {
+ if (*shapePtr) {
+ break;
+ }
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModY(y);
+ lastX = x0;
- //----- source alpha
- aSrc = div255(pipe->aInput * pipe->shape);
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + x0];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->width + x0];
- //----- result alpha and non-isolated group element correction
- aResult = aSrc + aDest - div255(aSrc * aDest);
- alpha2 = aResult;
+ for (x = x0; x <= x1; ++x) {
- //----- result color
- if (alpha2 == 0) {
- cResult0 = 0;
- } else {
- cResult0 = state->grayTransfer[(Guchar)(((alpha2 - aSrc) * cDest[0] +
- aSrc * pipe->cSrc[0]) / alpha2)];
- }
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ ++destColorPtr;
+ ++destAlphaPtr;
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
- //----- write destination pixel
- *pipe->destColorPtr++ = cResult0;
- *pipe->destAlphaPtr++ = aResult;
+ //----- read destination pixel
+ cDest0 = *destColorPtr;
+ aDest = *destAlphaPtr;
+
+ //----- source alpha
+ aSrc = shape;
+
+ //----- result alpha and non-isolated group element correction
+ aResult = aSrc + aDest - div255(aSrc * aDest);
+ alphaI = aResult;
- ++pipe->x;
+ //----- result color
+ if (alphaI == 0) {
+ cResult0 = 0;
+ } else {
+ cResult0 = state->grayTransfer[(Guchar)(((alphaI - aSrc) * cDest0 +
+ aSrc * cSrcPtr[0]) / alphaI)];
+ }
+
+ //----- write destination pixel
+ *destColorPtr++ = cResult0;
+ *destAlphaPtr++ = aResult;
+
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+
+ updateModX(lastX);
}
// special case:
-// !pipe->pattern && !pipe->noTransparency && !state->softMask &&
-// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
-// !pipe->nonIsolatedGroup &&
-// bitmap->mode == splashModeRGB8 && pipe->destAlphaPtr
-void Splash::pipeRunAARGB8(SplashPipe *pipe) {
- Guchar aSrc, aDest, alpha2, aResult;
- SplashColor cDest;
+// !pipe->pattern && pipe->shapeOnly && !state->blendFunc &&
+// bitmap->mode == splashModeRGB8 && bitmap->alpha
+void Splash::pipeRunShapeRGB8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar shape, aSrc, aDest, alphaI, aResult;
+ Guchar cDest0, cDest1, cDest2;
Guchar cResult0, cResult1, cResult2;
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ int cSrcStride, x, lastX;
+
+ if (cSrcPtr) {
+ cSrcStride = 3;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+ for (; x0 <= x1; ++x0) {
+ if (*shapePtr) {
+ break;
+ }
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModY(y);
+ lastX = x0;
- //----- read destination pixel
- cDest[0] = pipe->destColorPtr[0];
- cDest[1] = pipe->destColorPtr[1];
- cDest[2] = pipe->destColorPtr[2];
- aDest = *pipe->destAlphaPtr;
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->width + x0];
- //----- source alpha
- aSrc = div255(pipe->aInput * pipe->shape);
+ for (x = x0; x <= x1; ++x) {
- //----- result alpha and non-isolated group element correction
- aResult = aSrc + aDest - div255(aSrc * aDest);
- alpha2 = aResult;
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ destColorPtr += 3;
+ ++destAlphaPtr;
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
- //----- result color
- if (alpha2 == 0) {
- cResult0 = 0;
- cResult1 = 0;
- cResult2 = 0;
- } else {
- cResult0 = state->rgbTransferR[(Guchar)(((alpha2 - aSrc) * cDest[0] +
- aSrc * pipe->cSrc[0]) / alpha2)];
- cResult1 = state->rgbTransferG[(Guchar)(((alpha2 - aSrc) * cDest[1] +
- aSrc * pipe->cSrc[1]) / alpha2)];
- cResult2 = state->rgbTransferB[(Guchar)(((alpha2 - aSrc) * cDest[2] +
- aSrc * pipe->cSrc[2]) / alpha2)];
- }
+ //----- read destination pixel
+ cDest0 = destColorPtr[0];
+ cDest1 = destColorPtr[1];
+ cDest2 = destColorPtr[2];
+ aDest = *destAlphaPtr;
- //----- write destination pixel
- *pipe->destColorPtr++ = cResult0;
- *pipe->destColorPtr++ = cResult1;
- *pipe->destColorPtr++ = cResult2;
- *pipe->destAlphaPtr++ = aResult;
+ //----- source alpha
+ aSrc = shape;
- ++pipe->x;
+ //----- result alpha and non-isolated group element correction
+ aResult = aSrc + aDest - div255(aSrc * aDest);
+ alphaI = aResult;
+
+ //----- result color
+ if (alphaI == 0) {
+ cResult0 = 0;
+ cResult1 = 0;
+ cResult2 = 0;
+ } else {
+ cResult0 = state->rgbTransferR[(Guchar)(((alphaI - aSrc) * cDest0 +
+ aSrc * cSrcPtr[0]) / alphaI)];
+ cResult1 = state->rgbTransferG[(Guchar)(((alphaI - aSrc) * cDest1 +
+ aSrc * cSrcPtr[1]) / alphaI)];
+ cResult2 = state->rgbTransferB[(Guchar)(((alphaI - aSrc) * cDest2 +
+ aSrc * cSrcPtr[2]) / alphaI)];
+ }
+
+ //----- write destination pixel
+ destColorPtr[0] = cResult0;
+ destColorPtr[1] = cResult1;
+ destColorPtr[2] = cResult2;
+ destColorPtr += 3;
+ *destAlphaPtr++ = aResult;
+
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+
+ updateModX(lastX);
}
// special case:
-// !pipe->pattern && !pipe->noTransparency && !state->softMask &&
-// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
-// !pipe->nonIsolatedGroup &&
-// bitmap->mode == splashModeBGR8 && pipe->destAlphaPtr
-void Splash::pipeRunAABGR8(SplashPipe *pipe) {
- Guchar aSrc, aDest, alpha2, aResult;
- SplashColor cDest;
+// !pipe->pattern && pipe->shapeOnly && !state->blendFunc &&
+// bitmap->mode == splashModeBGR8 && bitmap->alpha
+void Splash::pipeRunShapeBGR8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar shape, aSrc, aDest, alphaI, aResult;
+ Guchar cDest0, cDest1, cDest2;
Guchar cResult0, cResult1, cResult2;
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ int cSrcStride, x, lastX;
- //----- read destination pixel
- cDest[0] = pipe->destColorPtr[2];
- cDest[1] = pipe->destColorPtr[1];
- cDest[2] = pipe->destColorPtr[0];
- aDest = *pipe->destAlphaPtr;
+ if (cSrcPtr) {
+ cSrcStride = 3;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+ for (; x0 <= x1; ++x0) {
+ if (*shapePtr) {
+ break;
+ }
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModY(y);
+ lastX = x0;
- //----- source alpha
- aSrc = div255(pipe->aInput * pipe->shape);
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->width + x0];
- //----- result alpha and non-isolated group element correction
- aResult = aSrc + aDest - div255(aSrc * aDest);
- alpha2 = aResult;
+ for (x = x0; x <= x1; ++x) {
- //----- result color
- if (alpha2 == 0) {
- cResult0 = 0;
- cResult1 = 0;
- cResult2 = 0;
- } else {
- cResult0 = state->rgbTransferR[(Guchar)(((alpha2 - aSrc) * cDest[0] +
- aSrc * pipe->cSrc[0]) / alpha2)];
- cResult1 = state->rgbTransferG[(Guchar)(((alpha2 - aSrc) * cDest[1] +
- aSrc * pipe->cSrc[1]) / alpha2)];
- cResult2 = state->rgbTransferB[(Guchar)(((alpha2 - aSrc) * cDest[2] +
- aSrc * pipe->cSrc[2]) / alpha2)];
- }
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ destColorPtr += 3;
+ ++destAlphaPtr;
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
+
+ //----- read destination pixel
+ cDest0 = destColorPtr[2];
+ cDest1 = destColorPtr[1];
+ cDest2 = destColorPtr[0];
+ aDest = *destAlphaPtr;
- //----- write destination pixel
- *pipe->destColorPtr++ = cResult2;
- *pipe->destColorPtr++ = cResult1;
- *pipe->destColorPtr++ = cResult0;
- *pipe->destAlphaPtr++ = aResult;
+ //----- source alpha
+ aSrc = shape;
+
+ //----- result alpha and non-isolated group element correction
+ aResult = aSrc + aDest - div255(aSrc * aDest);
+ alphaI = aResult;
- ++pipe->x;
+ //----- result color
+ if (alphaI == 0) {
+ cResult0 = 0;
+ cResult1 = 0;
+ cResult2 = 0;
+ } else {
+ cResult0 = state->rgbTransferR[(Guchar)(((alphaI - aSrc) * cDest0 +
+ aSrc * cSrcPtr[0]) / alphaI)];
+ cResult1 = state->rgbTransferG[(Guchar)(((alphaI - aSrc) * cDest1 +
+ aSrc * cSrcPtr[1]) / alphaI)];
+ cResult2 = state->rgbTransferB[(Guchar)(((alphaI - aSrc) * cDest2 +
+ aSrc * cSrcPtr[2]) / alphaI)];
+ }
+
+ //----- write destination pixel
+ destColorPtr[0] = cResult2;
+ destColorPtr[1] = cResult1;
+ destColorPtr[2] = cResult0;
+ destColorPtr += 3;
+ *destAlphaPtr++ = aResult;
+
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+
+ updateModX(lastX);
}
#if SPLASH_CMYK
// special case:
-// !pipe->pattern && !pipe->noTransparency && !state->softMask &&
-// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
-// !pipe->nonIsolatedGroup &&
-// bitmap->mode == splashModeCMYK8 && pipe->destAlphaPtr
-void Splash::pipeRunAACMYK8(SplashPipe *pipe) {
- Guchar aSrc, aDest, alpha2, aResult;
- SplashColor cDest;
+// !pipe->pattern && pipe->shapeOnly && !state->blendFunc &&
+// bitmap->mode == splashModeCMYK8 && bitmap->alpha
+void Splash::pipeRunShapeCMYK8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar shape, aSrc, aDest, alphaI, aResult;
+ Guchar cSrc0, cSrc1, cSrc2, cSrc3;
+ Guchar cDest0, cDest1, cDest2, cDest3;
Guchar cResult0, cResult1, cResult2, cResult3;
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ int cSrcStride, x, lastX;
- //----- read destination pixel
- cDest[0] = pipe->destColorPtr[0];
- cDest[1] = pipe->destColorPtr[1];
- cDest[2] = pipe->destColorPtr[2];
- cDest[3] = pipe->destColorPtr[3];
- aDest = *pipe->destAlphaPtr;
-
- //----- source alpha
- aSrc = div255(pipe->aInput * pipe->shape);
-
- //----- result alpha and non-isolated group element correction
- aResult = aSrc + aDest - div255(aSrc * aDest);
- alpha2 = aResult;
-
- //----- result color
- if (alpha2 == 0) {
- cResult0 = 0;
- cResult1 = 0;
- cResult2 = 0;
- cResult3 = 0;
+ if (cSrcPtr) {
+ cSrcStride = 4;
} else {
- cResult0 = state->cmykTransferC[(Guchar)(((alpha2 - aSrc) * cDest[0] +
- aSrc * pipe->cSrc[0]) / alpha2)];
- cResult1 = state->cmykTransferM[(Guchar)(((alpha2 - aSrc) * cDest[1] +
- aSrc * pipe->cSrc[1]) / alpha2)];
- cResult2 = state->cmykTransferY[(Guchar)(((alpha2 - aSrc) * cDest[2] +
- aSrc * pipe->cSrc[2]) / alpha2)];
- cResult3 = state->cmykTransferK[(Guchar)(((alpha2 - aSrc) * cDest[3] +
- aSrc * pipe->cSrc[3]) / alpha2)];
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
}
-
- //----- write destination pixel
- if (state->overprintMask & 1) {
- pipe->destColorPtr[0] = cResult0;
- }
- if (state->overprintMask & 2) {
- pipe->destColorPtr[1] = cResult1;
+ for (; x0 <= x1; ++x0) {
+ if (*shapePtr) {
+ break;
+ }
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
}
- if (state->overprintMask & 4) {
- pipe->destColorPtr[2] = cResult2;
+ if (x0 > x1) {
+ return;
}
- if (state->overprintMask & 8) {
- pipe->destColorPtr[3] = cResult3;
+ updateModX(x0);
+ updateModY(y);
+ lastX = x0;
+
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x0];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->width + x0];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ destColorPtr += 4;
+ ++destAlphaPtr;
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
+
+ //----- read destination pixel
+ cDest0 = destColorPtr[0];
+ cDest1 = destColorPtr[1];
+ cDest2 = destColorPtr[2];
+ cDest3 = destColorPtr[3];
+ aDest = *destAlphaPtr;
+
+ //----- overprint
+ if (state->overprintMask & 1) {
+ cSrc0 = cSrcPtr[0];
+ } else {
+ cSrc0 = div255(aDest * cDest0);
+ }
+ if (state->overprintMask & 2) {
+ cSrc1 = cSrcPtr[1];
+ } else {
+ cSrc1 = div255(aDest * cDest1);
+ }
+ if (state->overprintMask & 4) {
+ cSrc2 = cSrcPtr[2];
+ } else {
+ cSrc2 = div255(aDest * cDest2);
+ }
+ if (state->overprintMask & 8) {
+ cSrc3 = cSrcPtr[3];
+ } else {
+ cSrc3 = div255(aDest * cDest3);
+ }
+
+ //----- source alpha
+ aSrc = shape;
+
+ //----- result alpha and non-isolated group element correction
+ aResult = aSrc + aDest - div255(aSrc * aDest);
+ alphaI = aResult;
+
+ //----- result color
+ if (alphaI == 0) {
+ cResult0 = 0;
+ cResult1 = 0;
+ cResult2 = 0;
+ cResult3 = 0;
+ } else {
+ cResult0 = state->cmykTransferC[(Guchar)(((alphaI - aSrc) * cDest0 +
+ aSrc * cSrc0) / alphaI)];
+ cResult1 = state->cmykTransferM[(Guchar)(((alphaI - aSrc) * cDest1 +
+ aSrc * cSrc1) / alphaI)];
+ cResult2 = state->cmykTransferY[(Guchar)(((alphaI - aSrc) * cDest2 +
+ aSrc * cSrc2) / alphaI)];
+ cResult3 = state->cmykTransferK[(Guchar)(((alphaI - aSrc) * cDest3 +
+ aSrc * cSrc3) / alphaI)];
+ }
+
+ //----- write destination pixel
+ destColorPtr[0] = cResult0;
+ destColorPtr[1] = cResult1;
+ destColorPtr[2] = cResult2;
+ destColorPtr[3] = cResult3;
+ destColorPtr += 4;
+ *destAlphaPtr++ = aResult;
+
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
}
- pipe->destColorPtr += 4;
- *pipe->destAlphaPtr++ = aResult;
- ++pipe->x;
+ updateModX(lastX);
}
#endif
-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];
+// special case:
+// !pipe->pattern && !pipe->noTransparency && !state->softMask &&
+// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
+// !pipe->nonIsolatedGroup &&
+// bitmap->mode == splashModeMono1 && !bitmap->alpha
+void Splash::pipeRunAAMono1(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar shape, aSrc, cDest0, cResult0;
+ SplashColorPtr destColorPtr;
+ Guchar destColorMask;
+ int cSrcStride, x, lastX;
+
+ if (cSrcPtr) {
+ cSrcStride = 1;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
}
- 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
+ for (; x0 <= x1; ++x0) {
+ if (*shapePtr) {
+ break;
+ }
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
}
- if (bitmap->alpha) {
- pipe->destAlphaPtr = &bitmap->alpha[y * bitmap->width + x];
- } else {
- pipe->destAlphaPtr = NULL;
+ if (x0 > x1) {
+ return;
}
- if (state->inNonIsolatedGroup && alpha0Bitmap->alpha) {
- pipe->alpha0Ptr =
- &alpha0Bitmap->alpha[(alpha0Y + y) * alpha0Bitmap->width +
- (alpha0X + x)];
- } else {
- pipe->alpha0Ptr = NULL;
+ updateModX(x0);
+ updateModY(y);
+ lastX = x0;
+
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)];
+ destColorMask = 0x80 >> (x0 & 7);
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ destColorPtr += destColorMask & 1;
+ destColorMask = (destColorMask << 7) | (destColorMask >> 1);
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
+
+ //----- read destination pixel
+ cDest0 = (*destColorPtr & destColorMask) ? 0xff : 0x00;
+
+ //----- source alpha
+ aSrc = div255(pipe->aInput * shape);
+
+ //----- result color
+ // note: aDest = alphaI = aResult = 0xff
+ cResult0 = state->grayTransfer[(Guchar)div255((0xff - aSrc) * cDest0 +
+ aSrc * cSrcPtr[0])];
+
+ //----- write destination pixel
+ if (state->screen->test(x, y, cResult0)) {
+ *destColorPtr |= destColorMask;
+ } else {
+ *destColorPtr &= ~destColorMask;
+ }
+ destColorPtr += destColorMask & 1;
+ destColorMask = (destColorMask << 7) | (destColorMask >> 1);
+
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
}
+
+ updateModX(lastX);
}
-inline void Splash::pipeIncX(SplashPipe *pipe) {
- ++pipe->x;
- if (state->softMask) {
- ++pipe->softMaskPtr;
+// special case:
+// !pipe->pattern && !pipe->noTransparency && !state->softMask &&
+// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
+// !pipe->nonIsolatedGroup &&
+// bitmap->mode == splashModeMono8 && bitmap->alpha
+void Splash::pipeRunAAMono8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar shape, aSrc, aDest, alphaI, aResult, cDest0, cResult0;
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ int cSrcStride, x, lastX;
+
+ if (cSrcPtr) {
+ cSrcStride = 1;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
}
- switch (bitmap->mode) {
- case splashModeMono1:
- if (!(pipe->destColorMask >>= 1)) {
- pipe->destColorMask = 0x80;
- ++pipe->destColorPtr;
+ for (; x0 <= x1; ++x0) {
+ if (*shapePtr) {
+ break;
}
- 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;
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
}
- if (pipe->alpha0Ptr) {
- ++pipe->alpha0Ptr;
+ if (x0 > x1) {
+ return;
}
-}
+ updateModX(x0);
+ updateModY(y);
+ lastX = x0;
+
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + x0];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->width + x0];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ ++destColorPtr;
+ ++destAlphaPtr;
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
+
+ //----- read destination pixel
+ cDest0 = *destColorPtr;
+ aDest = *destAlphaPtr;
+
+ //----- source alpha
+ aSrc = div255(pipe->aInput * shape);
-inline void Splash::drawPixel(SplashPipe *pipe, int x, int y, GBool noClip) {
- if (noClip || state->clip->test(x, y)) {
- pipeSetXY(pipe, x, y);
- (this->*pipe->run)(pipe);
- updateModX(x);
- updateModY(y);
+ //----- result alpha and non-isolated group element correction
+ aResult = aSrc + aDest - div255(aSrc * aDest);
+ alphaI = aResult;
+
+ //----- result color
+ if (alphaI == 0) {
+ cResult0 = 0;
+ } else {
+ cResult0 = state->grayTransfer[(Guchar)(((alphaI - aSrc) * cDest0 +
+ aSrc * cSrcPtr[0]) / alphaI)];
+ }
+
+ //----- write destination pixel
+ *destColorPtr++ = cResult0;
+ *destAlphaPtr++ = aResult;
+
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
}
-}
-inline void Splash::drawAAPixelInit() {
- aaBufY = -1;
+ updateModX(lastX);
}
-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;
+// special case:
+// !pipe->pattern && !pipe->noTransparency && !state->softMask &&
+// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
+// !pipe->nonIsolatedGroup &&
+// bitmap->mode == splashModeRGB8 && bitmap->alpha
+void Splash::pipeRunAARGB8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar shape, aSrc, aDest, alphaI, aResult;
+ Guchar cDest0, cDest1, cDest2;
+ Guchar cResult0, cResult1, cResult2;
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ int cSrcStride, x, lastX;
- if (x < 0 || x >= bitmap->width ||
- y < state->clip->getYMinI() || y > state->clip->getYMaxI()) {
+ if (cSrcPtr) {
+ cSrcStride = 3;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+ for (; x0 <= x1; ++x0) {
+ if (*shapePtr) {
+ break;
+ }
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+ if (x0 > x1) {
return;
}
+ updateModX(x0);
+ updateModY(y);
+ lastX = x0;
- // 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];
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->width + x0];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ destColorPtr += 3;
+ ++destAlphaPtr;
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
+
+ //----- read destination pixel
+ cDest0 = destColorPtr[0];
+ cDest1 = destColorPtr[1];
+ cDest2 = destColorPtr[2];
+ aDest = *destAlphaPtr;
+
+ //----- source alpha
+ aSrc = div255(pipe->aInput * shape);
+
+ //----- result alpha and non-isolated group element correction
+ aResult = aSrc + aDest - div255(aSrc * aDest);
+ alphaI = aResult;
+
+ //----- result color
+ if (alphaI == 0) {
+ cResult0 = 0;
+ cResult1 = 0;
+ cResult2 = 0;
+ } else {
+ cResult0 = state->rgbTransferR[(Guchar)(((alphaI - aSrc) * cDest0 +
+ aSrc * cSrcPtr[0]) / alphaI)];
+ cResult1 = state->rgbTransferG[(Guchar)(((alphaI - aSrc) * cDest1 +
+ aSrc * cSrcPtr[1]) / alphaI)];
+ cResult2 = state->rgbTransferB[(Guchar)(((alphaI - aSrc) * cDest2 +
+ aSrc * cSrcPtr[2]) / alphaI)];
+ }
+
+ //----- write destination pixel
+ destColorPtr[0] = cResult0;
+ destColorPtr[1] = cResult1;
+ destColorPtr[2] = cResult2;
+ destColorPtr += 3;
+ *destAlphaPtr++ = aResult;
+
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+
+ updateModX(lastX);
+}
+
+// special case:
+// !pipe->pattern && !pipe->noTransparency && !state->softMask &&
+// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
+// !pipe->nonIsolatedGroup &&
+// bitmap->mode == splashModeBGR8 && bitmap->alpha
+void Splash::pipeRunAABGR8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar shape, aSrc, aDest, alphaI, aResult;
+ Guchar cDest0, cDest1, cDest2;
+ Guchar cResult0, cResult1, cResult2;
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ int cSrcStride, x, lastX;
+
+ if (cSrcPtr) {
+ cSrcStride = 3;
} else {
- t = bitCount4[*p >> 4] + bitCount4[p[w] >> 4] +
- bitCount4[p[2*w] >> 4] + bitCount4[p[3*w] >> 4];
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
}
-#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;
+ for (; x0 <= x1; ++x0) {
+ if (*shapePtr) {
+ break;
}
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
}
-#endif
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModY(y);
+ lastX = x0;
+
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->width + x0];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ destColorPtr += 3;
+ ++destAlphaPtr;
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
+
+ //----- read destination pixel
+ cDest0 = destColorPtr[2];
+ cDest1 = destColorPtr[1];
+ cDest2 = destColorPtr[0];
+ aDest = *destAlphaPtr;
+
+ //----- source alpha
+ aSrc = div255(pipe->aInput * shape);
+
+ //----- result alpha and non-isolated group element correction
+ aResult = aSrc + aDest - div255(aSrc * aDest);
+ alphaI = aResult;
+
+ //----- result color
+ if (alphaI == 0) {
+ cResult0 = 0;
+ cResult1 = 0;
+ cResult2 = 0;
+ } else {
+ cResult0 = state->rgbTransferR[(Guchar)(((alphaI - aSrc) * cDest0 +
+ aSrc * cSrcPtr[0]) / alphaI)];
+ cResult1 = state->rgbTransferG[(Guchar)(((alphaI - aSrc) * cDest1 +
+ aSrc * cSrcPtr[1]) / alphaI)];
+ cResult2 = state->rgbTransferB[(Guchar)(((alphaI - aSrc) * cDest2 +
+ aSrc * cSrcPtr[2]) / alphaI)];
+ }
- // draw the pixel
- if (t != 0) {
- pipeSetXY(pipe, x, y);
- pipe->shape = div255(aaGamma[t] * pipe->shape);
- (this->*pipe->run)(pipe);
- updateModX(x);
- updateModY(y);
+ //----- write destination pixel
+ destColorPtr[0] = cResult2;
+ destColorPtr[1] = cResult1;
+ destColorPtr[2] = cResult0;
+ destColorPtr += 3;
+ *destAlphaPtr++ = aResult;
+
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
}
+
+ updateModX(lastX);
}
-inline void Splash::drawSpan(SplashPipe *pipe, int x0, int x1, int y,
- GBool noClip) {
- int x;
+#if SPLASH_CMYK
+// special case:
+// !pipe->pattern && !pipe->noTransparency && !state->softMask &&
+// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
+// !pipe->nonIsolatedGroup &&
+// bitmap->mode == splashModeCMYK8 && bitmap->alpha
+void Splash::pipeRunAACMYK8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar shape, aSrc, aDest, alphaI, aResult;
+ Guchar cSrc0, cSrc1, cSrc2, cSrc3;
+ Guchar cDest0, cDest1, cDest2, cDest3;
+ Guchar cResult0, cResult1, cResult2, cResult3;
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ int cSrcStride, x, lastX;
- if (noClip) {
- pipeSetXY(pipe, x0, y);
- for (x = x0; x <= x1; ++x) {
- (this->*pipe->run)(pipe);
- }
- updateModX(x0);
- updateModX(x1);
- updateModY(y);
+ if (cSrcPtr) {
+ cSrcStride = 4;
} else {
- if (x0 < state->clip->getXMinI()) {
- x0 = state->clip->getXMinI();
- }
- if (x1 > state->clip->getXMaxI()) {
- x1 = state->clip->getXMaxI();
- }
- pipeSetXY(pipe, x0, y);
- for (x = x0; x <= x1; ++x) {
- if (state->clip->test(x, y)) {
- (this->*pipe->run)(pipe);
- updateModX(x);
- updateModY(y);
- } else {
- pipeIncX(pipe);
- }
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+ for (; x0 <= x1; ++x0) {
+ if (*shapePtr) {
+ break;
}
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
}
-}
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModY(y);
+ lastX = x0;
-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;
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x0];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->width + x0];
-#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;
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ destColorPtr += 4;
+ ++destAlphaPtr;
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
+
+ //----- read destination pixel
+ cDest0 = destColorPtr[0];
+ cDest1 = destColorPtr[1];
+ cDest2 = destColorPtr[2];
+ cDest3 = destColorPtr[3];
+ aDest = *destAlphaPtr;
+
+ //----- overprint
+ if (state->overprintMask & 1) {
+ cSrc0 = cSrcPtr[0];
} else {
- t = bitCount4[*p0 >> 4] + bitCount4[*p1 >> 4] +
- bitCount4[*p2 >> 4] + bitCount4[*p3 >> 4];
+ cSrc0 = div255(aDest * cDest0);
}
-#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;
- }
+ if (state->overprintMask & 2) {
+ cSrc1 = cSrcPtr[1];
+ } else {
+ cSrc1 = div255(aDest * cDest1);
}
-#endif
+ if (state->overprintMask & 4) {
+ cSrc2 = cSrcPtr[2];
+ } else {
+ cSrc2 = div255(aDest * cDest2);
+ }
+ if (state->overprintMask & 8) {
+ cSrc3 = cSrcPtr[3];
+ } else {
+ cSrc3 = div255(aDest * cDest3);
+ }
+
+ //----- source alpha
+ aSrc = div255(pipe->aInput * shape);
+
+ //----- result alpha and non-isolated group element correction
+ aResult = aSrc + aDest - div255(aSrc * aDest);
+ alphaI = aResult;
- if (t != 0) {
- pipe->shape = aaGamma[t];
- (this->*pipe->run)(pipe);
- updateModX(x);
- updateModY(y);
+ //----- result color
+ if (alphaI == 0) {
+ cResult0 = 0;
+ cResult1 = 0;
+ cResult2 = 0;
+ cResult3 = 0;
} else {
- pipeIncX(pipe);
+ cResult0 = state->cmykTransferC[(Guchar)(((alphaI - aSrc) * cDest0 +
+ aSrc * cSrc0) / alphaI)];
+ cResult1 = state->cmykTransferM[(Guchar)(((alphaI - aSrc) * cDest1 +
+ aSrc * cSrc1) / alphaI)];
+ cResult2 = state->cmykTransferY[(Guchar)(((alphaI - aSrc) * cDest2 +
+ aSrc * cSrc2) / alphaI)];
+ cResult3 = state->cmykTransferK[(Guchar)(((alphaI - aSrc) * cDest3 +
+ aSrc * cSrc3) / alphaI)];
}
+
+ //----- write destination pixel
+ destColorPtr[0] = cResult0;
+ destColorPtr[1] = cResult1;
+ destColorPtr[2] = cResult2;
+ destColorPtr[3] = cResult3;
+ destColorPtr += 4;
+ *destAlphaPtr++ = aResult;
+
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
}
+
+ updateModX(lastX);
}
+#endif
+
//------------------------------------------------------------------------
@@ -1238,21 +1934,18 @@ Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA,
int i;
bitmap = bitmapA;
+ bitmapComps = splashColorModeNComps[bitmap->mode];
vectorAntialias = vectorAntialiasA;
inShading = gFalse;
state = new SplashState(bitmap->width, bitmap->height, vectorAntialias,
screenParams);
+ scanBuf = (Guchar *)gmalloc(bitmap->width);
if (vectorAntialias) {
- aaBuf = new SplashBitmap(splashAASize * bitmap->width, splashAASize,
- 1, splashModeMono1, gFalse);
- for (i = 0; i <= splashAASize * splashAASize; ++i) {
+ for (i = 0; i <= 255; ++i) {
aaGamma[i] = (Guchar)splashRound(
- splashPow((SplashCoord)i /
- (SplashCoord)(splashAASize * splashAASize),
- splashAAGamma) * 255);
+ splashPow((SplashCoord)i / (SplashCoord)255,
+ splashAAGamma) * 255);
}
- } else {
- aaBuf = NULL;
}
minLineWidth = 0;
clearModRegion();
@@ -1264,21 +1957,18 @@ Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA,
int i;
bitmap = bitmapA;
+ bitmapComps = splashColorModeNComps[bitmap->mode];
vectorAntialias = vectorAntialiasA;
inShading = gFalse;
state = new SplashState(bitmap->width, bitmap->height, vectorAntialias,
screenA);
+ scanBuf = (Guchar *)gmalloc(bitmap->width);
if (vectorAntialias) {
- aaBuf = new SplashBitmap(splashAASize * bitmap->width, splashAASize,
- 1, splashModeMono1, gFalse);
- for (i = 0; i <= splashAASize * splashAASize; ++i) {
+ for (i = 0; i <= 255; ++i) {
aaGamma[i] = (Guchar)splashRound(
- splashPow((SplashCoord)i /
- (SplashCoord)(splashAASize * splashAASize),
- splashAAGamma) * 255);
+ splashPow((SplashCoord)i / (SplashCoord)255,
+ splashAAGamma) * 255);
}
- } else {
- aaBuf = NULL;
}
minLineWidth = 0;
clearModRegion();
@@ -1290,9 +1980,7 @@ Splash::~Splash() {
restoreState();
}
delete state;
- if (vectorAntialias) {
- delete aaBuf;
- }
+ gfree(scanBuf);
}
//------------------------------------------------------------------------
@@ -1375,6 +2063,10 @@ GBool Splash::getInNonIsolatedGroup() {
return state->inNonIsolatedGroup;
}
+GBool Splash::getInKnockoutGroup() {
+ return state->inKnockoutGroup;
+}
+
//------------------------------------------------------------------------
// state write
//------------------------------------------------------------------------
@@ -1442,28 +2134,30 @@ void Splash::setStrokeAdjust(GBool strokeAdjust) {
void Splash::clipResetToRect(SplashCoord x0, SplashCoord y0,
SplashCoord x1, SplashCoord y1) {
- state->clip->resetToRect(x0, y0, x1, y1);
+ state->clipResetToRect(x0, y0, x1, y1);
}
SplashError Splash::clipToRect(SplashCoord x0, SplashCoord y0,
SplashCoord x1, SplashCoord y1) {
- return state->clip->clipToRect(x0, y0, x1, y1);
+ return state->clipToRect(x0, y0, x1, y1);
}
SplashError Splash::clipToPath(SplashPath *path, GBool eo) {
- return state->clip->clipToPath(path, state->matrix, state->flatness, eo);
+ return state->clipToPath(path, 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;
+void Splash::setInTransparencyGroup(SplashBitmap *groupBackBitmapA,
+ int groupBackXA, int groupBackYA,
+ GBool nonIsolated, GBool knockout) {
+ groupBackBitmap = groupBackBitmapA;
+ groupBackX = groupBackXA;
+ groupBackY = groupBackYA;
+ state->inNonIsolatedGroup = nonIsolated;
+ state->inKnockoutGroup = knockout;
}
void Splash::setTransfer(Guchar *red, Guchar *green, Guchar *blue,
@@ -1539,9 +2233,9 @@ void Splash::clear(SplashColorPtr color, Guchar alpha) {
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];
+ *p++ = color[1];
+ *p++ = color[2];
}
row += bitmap->rowSize;
}
@@ -1560,9 +2254,9 @@ void Splash::clear(SplashColorPtr color, Guchar alpha) {
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[1];
+ *p++ = color[0];
}
row += bitmap->rowSize;
}
@@ -1606,7 +2300,7 @@ void Splash::clear(SplashColorPtr color, Guchar alpha) {
SplashError Splash::stroke(SplashPath *path) {
SplashPath *path2, *dPath;
- SplashCoord d1, d2, t1, t2, w;
+ SplashCoord t0, t1, t2, t3, w, w2;
if (debugMode) {
printf("stroke [dash:%d] [width:%.2f]:\n",
@@ -1628,31 +2322,41 @@ SplashError Splash::stroke(SplashPath *path) {
}
}
- // transform a unit square, and take the half the max of the two
- // diagonals; the product of this number and the line width is the
- // (approximate) transformed line width
- t1 = state->matrix[0] + state->matrix[2];
- t2 = state->matrix[1] + state->matrix[3];
- d1 = t1 * t1 + t2 * t2;
- t1 = state->matrix[0] - state->matrix[2];
- t2 = state->matrix[1] - state->matrix[3];
- d2 = t1 * t1 + t2 * t2;
- if (d2 > d1) {
- d1 = d2;
- }
- d1 *= 0.5;
- if (d1 > 0 &&
- d1 * state->lineWidth * state->lineWidth < minLineWidth * minLineWidth) {
- w = minLineWidth / splashSqrt(d1);
- strokeWide(path2, w);
+ // Compute an approximation of the transformed line width.
+ // Given a CTM of [m0 m1],
+ // [m2 m3]
+ // if |m0|*|m3| >= |m1|*|m2| then use min{|m0|,|m3|}, else
+ // use min{|m1|,|m2|}.
+ // This handles the common cases -- [s 0 ] and [0 s] --
+ // [0 +/-s] [+/-s 0]
+ // well, and still does something reasonable for the uncommon
+ // case transforms.
+ t0 = splashAbs(state->matrix[0]);
+ t1 = splashAbs(state->matrix[1]);
+ t2 = splashAbs(state->matrix[2]);
+ t3 = splashAbs(state->matrix[3]);
+ if (t0 * t3 >= t1 * t2) {
+ w = (t0 < t3) ? t0 : t3;
+ } else {
+ w = (t1 < t2) ? t1 : t2;
+ }
+ w2 = w * state->lineWidth;
+ // if there is a min line width set, and the transformed line width
+ // is smaller, use the min line width
+ if (w > 0 && w2 < minLineWidth) {
+ strokeWide(path2, minLineWidth / w);
} else if (bitmap->mode == splashModeMono1) {
- // this gets close to Adobe's behavior in mono mode
- if (d1 <= 2) {
+ // in monochrome mode, use 0-width lines for any transformed line
+ // width <= 1 -- lines less than 1 pixel wide look too fat without
+ // antialiasing
+ if (w2 < 1.001) {
strokeNarrow(path2);
} else {
strokeWide(path2, state->lineWidth);
}
} else {
+ // in gray and color modes, only use 0-width lines if the line
+ // width is explicitly set to 0
if (state->lineWidth == 0) {
strokeNarrow(path2);
} else {
@@ -1678,9 +2382,9 @@ void Splash::strokeNarrow(SplashPath *path) {
xPath = new SplashXPath(path, state->matrix, state->flatness, gFalse);
- pipeInit(&pipe, 0, 0, state->strokePattern, NULL,
+ pipeInit(&pipe, state->strokePattern,
(Guchar)splashRound(state->strokeAlpha * 255),
- gFalse, gFalse);
+ gTrue, gFalse);
for (i = 0, seg = xPath->segs; i < xPath->length; ++i, ++seg) {
if (seg->y0 <= seg->y1) {
@@ -1695,22 +2399,25 @@ void Splash::strokeNarrow(SplashPath *path) {
x1 = splashFloor(seg->x0);
}
if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0,
- x0 <= x1 ? x1 : x0, y1))
+ x0 <= x1 ? x1 : x0, y1,
+ state->strokeAdjust))
!= splashClipAllOutside) {
if (y0 == y1) {
if (x0 <= x1) {
- drawSpan(&pipe, x0, x1, y0, clipRes == splashClipAllInside);
+ drawStrokeSpan(&pipe, x0, x1, y0, clipRes == splashClipAllInside);
} else {
- drawSpan(&pipe, x1, x0, y0, clipRes == splashClipAllInside);
+ drawStrokeSpan(&pipe, x1, x0, y0, clipRes == splashClipAllInside);
}
} else {
dxdy = seg->dxdy;
- if (y0 < state->clip->getYMinI()) {
- y0 = state->clip->getYMinI();
+ y = state->clip->getYMinI(state->strokeAdjust);
+ if (y0 < y) {
+ y0 = y;
x0 = splashFloor(seg->x0 + ((SplashCoord)y0 - seg->y0) * dxdy);
}
- if (y1 > state->clip->getYMaxI()) {
- y1 = state->clip->getYMaxI();
+ y = state->clip->getYMaxI(state->strokeAdjust);
+ if (y1 > y) {
+ y1 = y;
x1 = splashFloor(seg->x0 + ((SplashCoord)y1 - seg->y0) * dxdy);
}
if (x0 <= x1) {
@@ -1723,9 +2430,10 @@ void Splash::strokeNarrow(SplashPath *path) {
xb = x1 + 1;
}
if (xa == xb) {
- drawPixel(&pipe, xa, y, clipRes == splashClipAllInside);
+ drawStrokeSpan(&pipe, xa, xa, y, clipRes == splashClipAllInside);
} else {
- drawSpan(&pipe, xa, xb - 1, y, clipRes == splashClipAllInside);
+ drawStrokeSpan(&pipe, xa, xb - 1, y,
+ clipRes == splashClipAllInside);
}
xa = xb;
}
@@ -1739,9 +2447,10 @@ void Splash::strokeNarrow(SplashPath *path) {
xb = x1 - 1;
}
if (xa == xb) {
- drawPixel(&pipe, xa, y, clipRes == splashClipAllInside);
+ drawStrokeSpan(&pipe, xa, xa, y, clipRes == splashClipAllInside);
} else {
- drawSpan(&pipe, xb + 1, xa, y, clipRes == splashClipAllInside);
+ drawStrokeSpan(&pipe, xb + 1, xa, y,
+ clipRes == splashClipAllInside);
}
xa = xb;
}
@@ -1762,6 +2471,32 @@ void Splash::strokeNarrow(SplashPath *path) {
delete xPath;
}
+void Splash::drawStrokeSpan(SplashPipe *pipe, int x0, int x1, int y,
+ GBool noClip) {
+ int x;
+
+ x = state->clip->getXMinI(state->strokeAdjust);
+ if (x > x0) {
+ x0 = x;
+ }
+ x = state->clip->getXMaxI(state->strokeAdjust);
+ if (x < x1) {
+ x1 = x;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ for (x = x0; x <= x1; ++x) {
+ scanBuf[x] = 0xff;
+ }
+ if (!noClip) {
+ if (!state->clip->clipSpanBinary(scanBuf, y, x0, x1, state->strokeAdjust)) {
+ return;
+ }
+ }
+ (this->*pipe->run)(pipe, x0, x1, y, scanBuf + x0, NULL);
+}
+
void Splash::strokeWide(SplashPath *path, SplashCoord w) {
SplashPath *path2;
@@ -2012,10 +2747,11 @@ SplashError Splash::fillWithPattern(SplashPath *path, GBool eo,
SplashPattern *pattern,
SplashCoord alpha) {
SplashPipe pipe;
+ SplashPath *path2;
SplashXPath *xPath;
SplashXPathScanner *scanner;
- int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y;
- SplashClipResult clipRes, clipRes2;
+ int xMin, yMin, xMax, yMax, x, y, t;
+ SplashClipResult clipRes;
if (path->length == 0) {
return splashErrEmptyPath;
@@ -2025,85 +2761,68 @@ SplashError Splash::fillWithPattern(SplashPath *path, GBool eo,
return splashOk;
}
- // add stroke adjustment hints for filled rectangles -- this only
- // applies to paths that consist of a single subpath
- // (this appears to match Acrobat's behavior)
- if (state->strokeAdjust && !path->hints) {
- int n;
- n = path->getLength();
- if (n == 4 &&
- !(path->flags[0] & splashPathClosed) &&
- !(path->flags[1] & splashPathLast) &&
- !(path->flags[2] & splashPathLast)) {
- path->close(gTrue);
- path->addStrokeAdjustHint(0, 2, 0, 4);
- path->addStrokeAdjustHint(1, 3, 0, 4);
- } else if (n == 5 &&
- (path->flags[0] & splashPathClosed) &&
- !(path->flags[1] & splashPathLast) &&
- !(path->flags[2] & splashPathLast) &&
- !(path->flags[3] & splashPathLast)) {
- path->addStrokeAdjustHint(0, 2, 0, 4);
- path->addStrokeAdjustHint(1, 3, 0, 4);
- }
- }
+ path2 = tweakFillPath(path);
- xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue);
- if (vectorAntialias && !inShading) {
- xPath->aaScale();
- }
- xPath->sort();
- yMinI = state->clip->getYMinI();
- yMaxI = state->clip->getYMaxI();
- if (vectorAntialias && !inShading) {
- yMinI = yMinI * splashAASize;
- yMaxI = (yMaxI + 1) * splashAASize - 1;
+ xPath = new SplashXPath(path2, state->matrix, state->flatness, gTrue);
+ if (path2 != path) {
+ delete path2;
}
- scanner = new SplashXPathScanner(xPath, eo, yMinI, yMaxI);
-
- // get the min and max x and y values
- if (vectorAntialias && !inShading) {
- scanner->getBBoxAA(&xMinI, &yMinI, &xMaxI, &yMaxI);
- } else {
- scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI);
+ xMin = xPath->getXMin();
+ yMin = xPath->getYMin();
+ xMax = xPath->getXMax();
+ yMax = xPath->getYMax();
+ if (xMin > xMax || yMin > yMax) {
+ delete xPath;
+ return splashOk;
}
+ scanner = new SplashXPathScanner(xPath, eo, yMin, yMax);
// check clipping
- if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI))
+ if ((clipRes = state->clip->testRect(xMin, yMin, xMax, yMax,
+ state->strokeAdjust))
!= splashClipAllOutside) {
- if (scanner->hasPartialClip()) {
- clipRes = splashClipPartial;
+
+ if ((t = state->clip->getXMinI(state->strokeAdjust)) > xMin) {
+ xMin = t;
+ }
+ if ((t = state->clip->getXMaxI(state->strokeAdjust)) < xMax) {
+ xMax = t;
+ }
+ if ((t = state->clip->getYMinI(state->strokeAdjust)) > yMin) {
+ yMin = t;
+ }
+ if ((t = state->clip->getYMaxI(state->strokeAdjust)) < yMax) {
+ yMax = t;
+ }
+ if (xMin > xMax || yMin > yMax) {
+ delete scanner;
+ delete xPath;
+ return splashOk;
}
- pipeInit(&pipe, 0, yMinI, pattern, NULL, (Guchar)splashRound(alpha * 255),
- vectorAntialias && !inShading, gFalse);
+ pipeInit(&pipe, pattern, (Guchar)splashRound(alpha * 255),
+ gTrue, gFalse);
// draw the spans
if (vectorAntialias && !inShading) {
- for (y = yMinI; y <= yMaxI; ++y) {
- scanner->renderAALine(aaBuf, &x0, &x1, y);
+ for (y = yMin; y <= yMax; ++y) {
+ scanner->getSpan(scanBuf, y, xMin, xMax);
if (clipRes != splashClipAllInside) {
- state->clip->clipAALine(aaBuf, &x0, &x1, y);
+ state->clip->clipSpan(scanBuf, y, xMin, xMax, state->strokeAdjust);
+ }
+ for (x = xMin; x <= xMax; ++x) {
+ scanBuf[x] = aaGamma[scanBuf[x]];
}
- drawAALine(&pipe, x0, x1, y);
+ (this->*pipe.run)(&pipe, xMin, xMax, y, scanBuf + xMin, NULL);
}
} 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);
- }
+ for (y = yMin; y <= yMax; ++y) {
+ scanner->getSpanBinary(scanBuf, y, xMin, xMax);
+ if (clipRes != splashClipAllInside) {
+ state->clip->clipSpanBinary(scanBuf, y, xMin, xMax,
+ state->strokeAdjust);
}
+ (this->*pipe.run)(&pipe, xMin, xMax, y, scanBuf + xMin, NULL);
}
}
}
@@ -2114,6 +2833,110 @@ SplashError Splash::fillWithPattern(SplashPath *path, GBool eo,
return splashOk;
}
+// Applies various tweaks to a fill path:
+// (1) add stroke adjust hints to a filled rectangle
+// (2) applies a minimum width to a zero-width filled rectangle (so
+// stroke adjustment works correctly
+// (3) convert a degenerate fill ('moveto lineto fill' and 'moveto
+// lineto closepath fill') to a minimum-width filled rectangle
+//
+// These tweaks only apply to paths with a single subpath.
+//
+// Returns either the unchanged input path or a new path (in which
+// case the returned path must be deleted by the caller).
+SplashPath *Splash::tweakFillPath(SplashPath *path) {
+ SplashPath *path2;
+ SplashCoord xx0, yy0, xx1, yy1, dx, dy, d, wx, wy, w;
+ int n;
+
+ if (!state->strokeAdjust || path->hints) {
+ return path;
+ }
+
+ n = path->getLength();
+ if (!((n == 2) ||
+ (n == 3 &&
+ path->flags[1] == 0) ||
+ (n == 4 &&
+ path->flags[1] == 0 &&
+ path->flags[2] == 0) ||
+ (n == 5 &&
+ path->flags[1] == 0 &&
+ path->flags[2] == 0 &&
+ path->flags[3] == 0))) {
+ return path;
+ }
+
+ path2 = path;
+
+ // degenerate fill (2 or 3 points) or rectangle of (nearly) zero
+ // width --> replace with a min-width rectangle and hint
+ if (n == 2 ||
+ (n == 3 && (path->flags[0] & splashPathClosed)) ||
+ (n == 3 && (splashAbs(path->pts[0].x - path->pts[2].x) < 0.001 &&
+ splashAbs(path->pts[0].y - path->pts[2].y) < 0.001)) ||
+ ((n == 4 ||
+ (n == 5 && (path->flags[0] & splashPathClosed))) &&
+ ((splashAbs(path->pts[0].x - path->pts[1].x) < 0.001 &&
+ splashAbs(path->pts[0].y - path->pts[1].y) < 0.001 &&
+ splashAbs(path->pts[2].x - path->pts[3].x) < 0.001 &&
+ splashAbs(path->pts[2].y - path->pts[3].y) < 0.001) ||
+ (splashAbs(path->pts[0].x - path->pts[3].x) < 0.001 &&
+ splashAbs(path->pts[0].y - path->pts[3].y) < 0.001 &&
+ splashAbs(path->pts[1].x - path->pts[2].x) < 0.001 &&
+ splashAbs(path->pts[1].y - path->pts[2].y) < 0.001)))) {
+ wx = state->matrix[0] + state->matrix[2];
+ wy = state->matrix[1] + state->matrix[3];
+ w = sqrt(wx*wx + wy*wy);
+ if (w < 0.001) {
+ w = 0;
+ } else {
+ // min width is 0.1 -- this constant is minWidth * sqrt(2)
+ w = (SplashCoord)0.1414 / w;
+ }
+ xx0 = path->pts[0].x;
+ yy0 = path->pts[0].y;
+ if (n <= 3) {
+ xx1 = path->pts[1].x;
+ yy1 = path->pts[1].y;
+ } else {
+ xx1 = path->pts[2].x;
+ yy1 = path->pts[2].y;
+ }
+ dx = xx1 - xx0;
+ dy = yy1 - yy0;
+ d = sqrt(dx * dx + dy * dy);
+ if (d < 0.001) {
+ d = 0;
+ } else {
+ d = w / d;
+ }
+ dx *= d;
+ dy *= d;
+ path2 = new SplashPath();
+ path2->moveTo(xx0 + dy, yy0 - dx);
+ path2->lineTo(xx1 + dy, yy1 - dx);
+ path2->lineTo(xx1 - dy, yy1 + dx);
+ path2->lineTo(xx0 - dy, yy0 + dx);
+ path2->close(gTrue);
+ path2->addStrokeAdjustHint(0, 2, 0, 4);
+ path2->addStrokeAdjustHint(1, 3, 0, 4);
+
+ // unclosed rectangle --> close and hint
+ } else if (n == 4 && !(path->flags[0] & splashPathClosed)) {
+ path2->close(gTrue);
+ path2->addStrokeAdjustHint(0, 2, 0, 4);
+ path2->addStrokeAdjustHint(1, 3, 0, 4);
+
+ // closed rectangle --> hint
+ } else if (n == 5 && (path->flags[0] & splashPathClosed)) {
+ path2->addStrokeAdjustHint(0, 2, 0, 4);
+ path2->addStrokeAdjustHint(1, 3, 0, 4);
+ }
+
+ return path2;
+}
+
GBool Splash::pathAllOutside(SplashPath *path) {
SplashCoord xMin1, yMin1, xMax1, yMax1;
SplashCoord xMin2, yMin2, xMax2, yMax2;
@@ -2177,7 +3000,8 @@ GBool Splash::pathAllOutside(SplashPath *path) {
xMaxI = splashFloor(xMax2);
yMaxI = splashFloor(yMax2);
- return state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI) ==
+ return state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI,
+ state->strokeAdjust) ==
splashClipAllOutside;
}
@@ -2185,49 +3009,63 @@ SplashError Splash::xorFill(SplashPath *path, GBool eo) {
SplashPipe pipe;
SplashXPath *xPath;
SplashXPathScanner *scanner;
- int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y;
- SplashClipResult clipRes, clipRes2;
+ int xMin, yMin, xMax, yMax, y, t;
+ SplashClipResult clipRes;
SplashBlendFunc origBlendFunc;
if (path->length == 0) {
return splashErrEmptyPath;
}
+ if (pathAllOutside(path)) {
+ opClipRes = splashClipAllOutside;
+ return splashOk;
+ }
xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue);
- xPath->sort();
- scanner = new SplashXPathScanner(xPath, eo, state->clip->getYMinI(),
- state->clip->getYMaxI());
-
- // get the min and max x and y values
- scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI);
+ xMin = xPath->getXMin();
+ yMin = xPath->getYMin();
+ xMax = xPath->getXMax();
+ yMax = xPath->getYMax();
+ if (xMin > xMax || yMin > yMax) {
+ delete xPath;
+ return splashOk;
+ }
+ scanner = new SplashXPathScanner(xPath, eo, yMin, yMax);
// check clipping
- if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI))
+ if ((clipRes = state->clip->testRect(xMin, yMin, xMax, yMax,
+ state->strokeAdjust))
!= splashClipAllOutside) {
- if (scanner->hasPartialClip()) {
- clipRes = splashClipPartial;
+
+ if ((t = state->clip->getXMinI(state->strokeAdjust)) > xMin) {
+ xMin = t;
+ }
+ if ((t = state->clip->getXMaxI(state->strokeAdjust)) < xMax) {
+ xMax = t;
+ }
+ if ((t = state->clip->getYMinI(state->strokeAdjust)) > yMin) {
+ yMin = t;
+ }
+ if ((t = state->clip->getYMaxI(state->strokeAdjust)) < yMax) {
+ yMax = t;
+ }
+ if (xMin > xMax || yMin > yMax) {
+ delete scanner;
+ delete xPath;
+ return splashOk;
}
origBlendFunc = state->blendFunc;
state->blendFunc = &blendXor;
- pipeInit(&pipe, 0, yMinI, state->fillPattern, NULL, 255, gFalse, gFalse);
+ pipeInit(&pipe, state->fillPattern, 255, gTrue, 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);
- }
+ for (y = yMin; y <= yMax; ++y) {
+ scanner->getSpanBinary(scanBuf, y, xMin, xMax);
+ if (clipRes != splashClipAllInside) {
+ state->clip->clipSpanBinary(scanBuf, y, xMin, xMax,
+ state->strokeAdjust);
}
+ (this->*pipe.run)(&pipe, xMin, xMax, y, scanBuf + xMin, NULL);
}
state->blendFunc = origBlendFunc;
}
@@ -2278,105 +3116,86 @@ SplashError Splash::fillGlyph(SplashCoord x, SplashCoord y,
SplashError Splash::fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph) {
SplashPipe pipe;
SplashClipResult clipRes;
- GBool noClip;
- int alpha0;
Guchar 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))
+ int xMin, yMin, xMax, yMax;
+ int x, y, xg, yg, xx, t;
+
+ xg = x0 - glyph->x;
+ yg = y0 - glyph->y;
+ xMin = xg;
+ xMax = xg + glyph->w - 1;
+ yMin = yg;
+ yMax = yg + glyph->h - 1;
+ if ((clipRes = state->clip->testRect(xMin, yMin, xMax, yMax,
+ state->strokeAdjust))
!= splashClipAllOutside) {
- noClip = clipRes == splashClipAllInside;
-
- if (noClip) {
+ pipeInit(&pipe, state->fillPattern,
+ (Guchar)splashRound(state->fillAlpha * 255),
+ gTrue, gFalse);
+ if (clipRes == splashClipAllInside) {
if (glyph->aa) {
- pipeInit(&pipe, x0 - glyph->x, y0 - glyph->y, state->fillPattern, NULL,
- (Guchar)splashRound(state->fillAlpha * 255), 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 = alpha;
- (this->*pipe.run)(&pipe);
- updateModX(x1);
- updateModY(y1);
- } else {
- pipeIncX(&pipe);
- }
- }
+ for (y = yMin; y <= yMax; ++y) {
+ (this->*pipe.run)(&pipe, xMin, xMax, y,
+ glyph->data + (y - yMin) * glyph->w, NULL);
}
} else {
- pipeInit(&pipe, x0 - glyph->x, y0 - glyph->y, state->fillPattern, NULL,
- (Guchar)splashRound(state->fillAlpha * 255), 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) {
- (this->*pipe.run)(&pipe);
- updateModX(x1);
- updateModY(y1);
- } else {
- pipeIncX(&pipe);
- }
- alpha0 <<= 1;
+ for (y = yMin; y <= yMax; ++y) {
+ for (x = xMin; x <= xMax; x += 8) {
+ alpha = *p++;
+ for (xx = 0; xx < 8 && x + xx <= xMax; ++xx) {
+ scanBuf[x + xx] = (alpha & 0x80) ? 0xff : 0x00;
+ alpha <<= 1;
}
}
+ (this->*pipe.run)(&pipe, xMin, xMax, y, scanBuf + xMin, NULL);
}
}
} else {
- if (glyph->aa) {
- pipeInit(&pipe, x0 - glyph->x, y0 - glyph->y, state->fillPattern, NULL,
- (Guchar)splashRound(state->fillAlpha * 255), 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 = alpha;
- (this->*pipe.run)(&pipe);
- updateModX(x1);
- updateModY(y1);
- } else {
- pipeIncX(&pipe);
- }
- } else {
- pipeIncX(&pipe);
- ++p;
- }
+ if ((t = state->clip->getXMinI(state->strokeAdjust)) > xMin) {
+ xMin = t;
+ }
+ if ((t = state->clip->getXMaxI(state->strokeAdjust)) < xMax) {
+ xMax = t;
+ }
+ if ((t = state->clip->getYMinI(state->strokeAdjust)) > yMin) {
+ yMin = t;
+ }
+ if ((t = state->clip->getYMaxI(state->strokeAdjust)) < yMax) {
+ yMax = t;
+ }
+ if (xMin <= xMax && yMin <= yMax) {
+ if (glyph->aa) {
+ for (y = yMin; y <= yMax; ++y) {
+ p = glyph->data + (y - yg) * glyph->w + (xMin - xg);
+ memcpy(scanBuf + xMin, p, xMax - xMin + 1);
+ state->clip->clipSpan(scanBuf, y, xMin, xMax,
+ state->strokeAdjust);
+ (this->*pipe.run)(&pipe, xMin, xMax, y, scanBuf + xMin, NULL);
}
- }
- } else {
- pipeInit(&pipe, x0 - glyph->x, y0 - glyph->y, state->fillPattern, NULL,
- (Guchar)splashRound(state->fillAlpha * 255), 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) {
- (this->*pipe.run)(&pipe);
- updateModX(x1);
- updateModY(y1);
- } else {
- pipeIncX(&pipe);
- }
- } else {
- pipeIncX(&pipe);
+ } else {
+ for (y = yMin; y <= yMax; ++y) {
+ p = glyph->data + (y - yg) * ((glyph->w + 7) >> 3)
+ + ((xMin - xg) >> 3);
+ alpha = *p++;
+ xx = (xMin - xg) & 7;
+ alpha <<= xx;
+ for (x = xMin; xx < 8 && x <= xMax; ++x, ++xx) {
+ scanBuf[x] = (alpha & 0x80) ? 255 : 0;
+ alpha <<= 1;
+ }
+ for (; x <= xMax; x += 8) {
+ alpha = *p++;
+ for (xx = 0; xx < 8 && x + xx <= xMax; ++xx) {
+ scanBuf[x + xx] = (alpha & 0x80) ? 255 : 0;
+ alpha <<= 1;
}
- alpha0 <<= 1;
}
+ state->clip->clipSpanBinary(scanBuf, y, xMin, xMax,
+ state->strokeAdjust);
+ (this->*pipe.run)(&pipe, xMin, xMax, y, scanBuf + xMin, NULL);
}
}
}
@@ -2387,12 +3206,28 @@ SplashError Splash::fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph) {
return splashOk;
}
+void Splash::getImageBounds(SplashCoord xyMin, SplashCoord xyMax,
+ int *xyMinI, int *xyMaxI) {
+ if (state->strokeAdjust) {
+ splashStrokeAdjust(xyMin, xyMax, xyMinI, xyMaxI);
+ } else {
+ *xyMinI = splashFloor(xyMin);
+ *xyMaxI = splashFloor(xyMax);
+ if (*xyMaxI <= *xyMinI) {
+ *xyMaxI = *xyMinI + 1;
+ }
+ }
+}
+
+// The glyphMode flag is not currently used, but may be useful if the
+// stroke adjustment behavior is changed.
SplashError Splash::fillImageMask(SplashImageMaskSource src, void *srcData,
int w, int h, SplashCoord *mat,
- GBool glyphMode) {
+ GBool glyphMode, GBool interpolate) {
SplashBitmap *scaledMask;
SplashClipResult clipRes;
GBool minorAxisZero;
+ SplashCoord wSize, hSize, t0, t1;
int x0, y0, x1, y1, scaledWidth, scaledHeight;
if (debugMode) {
@@ -2406,68 +3241,269 @@ SplashError Splash::fillImageMask(SplashImageMaskSource src, void *srcData,
return splashErrSingularMatrix;
}
- minorAxisZero = mat[1] == 0 && mat[2] == 0;
+ minorAxisZero = splashAbs(mat[1]) <= 0.0001 && splashAbs(mat[2]) <= 0.0001;
+
+ // rough estimate of size of scaled mask
+ t0 = splashAbs(mat[0]);
+ t1 = splashAbs(mat[1]);
+ wSize = t0 > t1 ? t0 : t1;
+ t0 = splashAbs(mat[2]);
+ t1 = splashAbs(mat[3]);
+ hSize = t0 > t1 ? t0 : t1;
+
+ // stream-mode upscaling -- this is slower, so we only use it if the
+ // upscaled mask is large (in which case clipping should remove many
+ // pixels)
+ if (wSize > 2 * w && hSize > 2 * h && wSize * hSize > 1000000) {
+ upscaleMask(src, srcData, w, h, mat, glyphMode, interpolate);
// scaling only
- if (mat[0] > 0 && minorAxisZero && mat[3] > 0) {
- x0 = imgCoordMungeLowerC(mat[4], glyphMode);
- y0 = imgCoordMungeLowerC(mat[5], glyphMode);
- x1 = imgCoordMungeUpperC(mat[0] + mat[4], glyphMode);
- y1 = imgCoordMungeUpperC(mat[3] + mat[5], glyphMode);
- // make sure narrow images cover at least one pixel
- if (x0 == x1) {
- ++x1;
- }
- if (y0 == y1) {
- ++y1;
- }
- clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1);
+ } else if (mat[0] > 0 && minorAxisZero && mat[3] > 0) {
+ getImageBounds(mat[4], mat[0] + mat[4], &x0, &x1);
+ getImageBounds(mat[5], mat[3] + mat[5], &y0, &y1);
+ clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1,
+ state->strokeAdjust);
opClipRes = clipRes;
if (clipRes != splashClipAllOutside) {
scaledWidth = x1 - x0;
scaledHeight = y1 - y0;
- scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight);
+ scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight,
+ interpolate);
blitMask(scaledMask, x0, y0, clipRes);
delete scaledMask;
}
// scaling plus vertical flip
} else if (mat[0] > 0 && minorAxisZero && mat[3] < 0) {
- x0 = imgCoordMungeLowerC(mat[4], glyphMode);
- y0 = imgCoordMungeLowerC(mat[3] + mat[5], glyphMode);
- x1 = imgCoordMungeUpperC(mat[0] + mat[4], glyphMode);
- y1 = imgCoordMungeUpperC(mat[5], glyphMode);
- // make sure narrow images cover at least one pixel
- if (x0 == x1) {
- ++x1;
- }
- if (y0 == y1) {
- ++y1;
- }
- clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1);
+ getImageBounds(mat[4], mat[0] + mat[4], &x0, &x1);
+ getImageBounds(mat[3] + mat[5], mat[5], &y0, &y1);
+ clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1,
+ state->strokeAdjust);
+ opClipRes = clipRes;
+ if (clipRes != splashClipAllOutside) {
+ scaledWidth = x1 - x0;
+ scaledHeight = y1 - y0;
+ scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight,
+ interpolate);
+ vertFlipImage(scaledMask, scaledWidth, scaledHeight, 1);
+ blitMask(scaledMask, x0, y0, clipRes);
+ delete scaledMask;
+ }
+
+ // scaling plus horizontal flip
+ } else if (mat[0] < 0 && minorAxisZero && mat[3] > 0) {
+ getImageBounds(mat[0] + mat[4], mat[4], &x0, &x1);
+ getImageBounds(mat[5], mat[3] + mat[5], &y0, &y1);
+ clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1,
+ state->strokeAdjust);
+ opClipRes = clipRes;
+ if (clipRes != splashClipAllOutside) {
+ scaledWidth = x1 - x0;
+ scaledHeight = y1 - y0;
+ scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight,
+ interpolate);
+ horizFlipImage(scaledMask, scaledWidth, scaledHeight, 1);
+ blitMask(scaledMask, x0, y0, clipRes);
+ delete scaledMask;
+ }
+
+ // scaling plus horizontal and vertical flips
+ } else if (mat[0] < 0 && minorAxisZero && mat[3] < 0) {
+ getImageBounds(mat[0] + mat[4], mat[4], &x0, &x1);
+ getImageBounds(mat[3] + mat[5], mat[5], &y0, &y1);
+ clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1,
+ state->strokeAdjust);
opClipRes = clipRes;
if (clipRes != splashClipAllOutside) {
scaledWidth = x1 - x0;
scaledHeight = y1 - y0;
- scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight);
+ scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight,
+ interpolate);
vertFlipImage(scaledMask, scaledWidth, scaledHeight, 1);
+ horizFlipImage(scaledMask, scaledWidth, scaledHeight, 1);
blitMask(scaledMask, x0, y0, clipRes);
delete scaledMask;
}
// all other cases
} else {
- arbitraryTransformMask(src, srcData, w, h, mat, glyphMode);
+ arbitraryTransformMask(src, srcData, w, h, mat, glyphMode, interpolate);
}
return splashOk;
}
+// The glyphMode flag is not currently used, but may be useful if the
+// stroke adjustment behavior is changed.
+void Splash::upscaleMask(SplashImageMaskSource src, void *srcData,
+ int srcWidth, int srcHeight,
+ SplashCoord *mat, GBool glyphMode,
+ GBool interpolate) {
+ SplashClipResult clipRes;
+ SplashPipe pipe;
+ Guchar *unscaledImage, *p;
+ SplashCoord xMin, yMin, xMax, yMax, t;
+ SplashCoord mi0, mi1, mi2, mi3, mi4, mi5, det;
+ SplashCoord ix, iy, sx, sy, pix0, pix1;
+ int xMinI, yMinI, xMaxI, yMaxI, x, y, x0, y0, x1, y1, tt;
+
+ // compute the bbox of the target quadrilateral
+ xMin = xMax = mat[4];
+ t = mat[2] + mat[4];
+ if (t < xMin) {
+ xMin = t;
+ } else if (t > xMax) {
+ xMax = t;
+ }
+ t = mat[0] + mat[2] + mat[4];
+ if (t < xMin) {
+ xMin = t;
+ } else if (t > xMax) {
+ xMax = t;
+ }
+ t = mat[0] + mat[4];
+ if (t < xMin) {
+ xMin = t;
+ } else if (t > xMax) {
+ xMax = t;
+ }
+ getImageBounds(xMin, xMax, &xMinI, &xMaxI);
+ yMin = yMax = mat[5];
+ t = mat[3] + mat[5];
+ if (t < yMin) {
+ yMin = t;
+ } else if (t > yMax) {
+ yMax = t;
+ }
+ t = mat[1] + mat[3] + mat[5];
+ if (t < yMin) {
+ yMin = t;
+ } else if (t > yMax) {
+ yMax = t;
+ }
+ t = mat[1] + mat[5];
+ if (t < yMin) {
+ yMin = t;
+ } else if (t > yMax) {
+ yMax = t;
+ }
+ getImageBounds(yMin, yMax, &yMinI, &yMaxI);
+
+ // clipping
+ clipRes = state->clip->testRect(xMinI, yMinI, xMaxI - 1, yMaxI - 1,
+ state->strokeAdjust);
+ opClipRes = clipRes;
+ if (clipRes == splashClipAllOutside) {
+ return;
+ }
+ if (clipRes != splashClipAllInside) {
+ if ((tt = state->clip->getXMinI(state->strokeAdjust)) > xMinI) {
+ xMinI = tt;
+ }
+ if ((tt = state->clip->getXMaxI(state->strokeAdjust) + 1) < xMaxI) {
+ xMaxI = tt;
+ }
+ if ((tt = state->clip->getYMinI(state->strokeAdjust)) > yMinI) {
+ yMinI = tt;
+ }
+ if ((tt = state->clip->getYMaxI(state->strokeAdjust) + 1) < yMaxI) {
+ yMaxI = tt;
+ }
+ }
+
+ // invert the matrix
+ det = mat[0] * mat[3] - mat[1] * mat[2];
+ if (splashAbs(det) < 1e-6) {
+ // this should be caught by the singular matrix check in fillImageMask
+ return;
+ }
+ det = (SplashCoord)1 / det;
+ mi0 = det * mat[3] * srcWidth;
+ mi1 = -det * mat[1] * srcHeight;
+ mi2 = -det * mat[2] * srcWidth;
+ mi3 = det * mat[0] * srcHeight;
+ mi4 = det * (mat[2] * mat[5] - mat[3] * mat[4]) * srcWidth;
+ mi5 = -det * (mat[0] * mat[5] - mat[1] * mat[4]) * srcHeight;
+
+ // grab the image
+ unscaledImage = (Guchar *)gmallocn(srcWidth, srcHeight);
+ for (y = 0, p = unscaledImage; y < srcHeight; ++y, p += srcWidth) {
+ (*src)(srcData, p);
+ for (x = 0; x < srcWidth; ++x) {
+ p[x] *= 255;
+ }
+ }
+
+ // draw it
+ pipeInit(&pipe, state->fillPattern,
+ (Guchar)splashRound(state->fillAlpha * 255),
+ gTrue, gFalse);
+ for (y = yMinI; y < yMaxI; ++y) {
+ for (x = xMinI; x < xMaxI; ++x) {
+ ix = ((SplashCoord)x + 0.5) * mi0 + ((SplashCoord)y + 0.5) * mi2 + mi4;
+ iy = ((SplashCoord)x + 0.5) * mi1 + ((SplashCoord)y + 0.5) * mi3 + mi5;
+ if (interpolate) {
+ if (ix >= 0 && ix < srcWidth && iy >= 0 && iy < srcHeight) {
+ x0 = splashFloor(ix - 0.5);
+ x1 = x0 + 1;
+ sx = (ix - 0.5) - x0;
+ y0 = splashFloor(iy - 0.5);
+ y1 = y0 + 1;
+ sy = (iy - 0.5) - y0;
+ if (x0 < 0) {
+ x0 = 0;
+ }
+ if (x1 >= srcWidth) {
+ x1 = srcWidth - 1;
+ }
+ if (y0 < 0) {
+ y0 = 0;
+ }
+ if (y1 >= srcHeight) {
+ y1 = srcHeight - 1;
+ }
+ pix0 = ((SplashCoord)1 - sx) * unscaledImage[y0 * srcWidth + x0]
+ + sx * unscaledImage[y0 * srcWidth + x1];
+ pix1 = ((SplashCoord)1 - sx) * unscaledImage[y1 * srcWidth + x0]
+ + sx * unscaledImage[y1 * srcWidth + x1];
+ scanBuf[x] = (Guchar)splashRound(((SplashCoord)1 - sy) * pix0
+ + sy * pix1);
+ } else {
+ scanBuf[x] = 0;
+ }
+ } else {
+ x0 = splashFloor(ix);
+ y0 = splashFloor(iy);
+ if (x0 >= 0 && x0 < srcWidth && y0 >= 0 && y0 < srcHeight) {
+ scanBuf[x] = unscaledImage[y0 * srcWidth + x0];
+ } else {
+ scanBuf[x] = 0;
+ }
+ }
+ }
+ if (clipRes != splashClipAllInside) {
+ if (vectorAntialias) {
+ state->clip->clipSpan(scanBuf, y, xMinI, xMaxI - 1,
+ state->strokeAdjust);
+ } else {
+ state->clip->clipSpanBinary(scanBuf, y, xMinI, xMaxI - 1,
+ state->strokeAdjust);
+ }
+ }
+ (this->*pipe.run)(&pipe, xMinI, xMaxI - 1, y, scanBuf + xMinI, NULL);
+ }
+
+ gfree(unscaledImage);
+}
+
+// The glyphMode flag is not currently used, but may be useful if the
+// stroke adjustment behavior is changed.
void Splash::arbitraryTransformMask(SplashImageMaskSource src, void *srcData,
int srcWidth, int srcHeight,
- SplashCoord *mat, GBool glyphMode) {
+ SplashCoord *mat, GBool glyphMode,
+ GBool interpolate) {
SplashBitmap *scaledMask;
- SplashClipResult clipRes, clipRes2;
+ SplashClipResult clipRes;
SplashPipe pipe;
int scaledWidth, scaledHeight, t0, t1;
SplashCoord r00, r01, r10, r11, det, ir00, ir01, ir10, ir11;
@@ -2484,29 +3520,26 @@ void Splash::arbitraryTransformMask(SplashImageMaskSource src, void *srcData,
vx[3] = mat[0] + mat[4]; vy[3] = mat[1] + mat[5];
// clipping
- xMin = imgCoordMungeLowerC(vx[0], glyphMode);
- xMax = imgCoordMungeUpperC(vx[0], glyphMode);
- yMin = imgCoordMungeLowerC(vy[0], glyphMode);
- yMax = imgCoordMungeUpperC(vy[0], glyphMode);
+ xMin = splashRound(vx[0]);
+ xMax = splashRound(vx[0]);
+ yMin = splashRound(vy[0]);
+ yMax = splashRound(vy[0]);
for (i = 1; i < 4; ++i) {
- t0 = imgCoordMungeLowerC(vx[i], glyphMode);
+ t0 = splashRound(vx[i]);
if (t0 < xMin) {
xMin = t0;
- }
- t0 = imgCoordMungeUpperC(vx[i], glyphMode);
- if (t0 > xMax) {
+ } else if (t0 > xMax) {
xMax = t0;
}
- t1 = imgCoordMungeLowerC(vy[i], glyphMode);
+ t1 = splashRound(vy[i]);
if (t1 < yMin) {
yMin = t1;
- }
- t1 = imgCoordMungeUpperC(vy[i], glyphMode);
- if (t1 > yMax) {
+ } else if (t1 > yMax) {
yMax = t1;
}
}
- clipRes = state->clip->testRect(xMin, yMin, xMax - 1, yMax - 1);
+ clipRes = state->clip->testRect(xMin, yMin, xMax - 1, yMax - 1,
+ state->strokeAdjust);
opClipRes = clipRes;
if (clipRes == splashClipAllOutside) {
return;
@@ -2514,33 +3547,25 @@ void Splash::arbitraryTransformMask(SplashImageMaskSource src, void *srcData,
// compute the scale factors
if (mat[0] >= 0) {
- t0 = imgCoordMungeUpperC(mat[0] + mat[4], glyphMode) -
- imgCoordMungeLowerC(mat[4], glyphMode);
+ t0 = splashRound(mat[0] + mat[4]) - splashRound(mat[4]);
} else {
- t0 = imgCoordMungeUpperC(mat[4], glyphMode) -
- imgCoordMungeLowerC(mat[0] + mat[4], glyphMode);
+ t0 = splashRound(mat[4]) - splashRound(mat[0] + mat[4]);
}
if (mat[1] >= 0) {
- t1 = imgCoordMungeUpperC(mat[1] + mat[5], glyphMode) -
- imgCoordMungeLowerC(mat[5], glyphMode);
+ t1 = splashRound(mat[1] + mat[5]) - splashRound(mat[5]);
} else {
- t1 = imgCoordMungeUpperC(mat[5], glyphMode) -
- imgCoordMungeLowerC(mat[1] + mat[5], glyphMode);
+ t1 = splashRound(mat[5]) - splashRound(mat[1] + mat[5]);
}
scaledWidth = t0 > t1 ? t0 : t1;
if (mat[2] >= 0) {
- t0 = imgCoordMungeUpperC(mat[2] + mat[4], glyphMode) -
- imgCoordMungeLowerC(mat[4], glyphMode);
+ t0 = splashRound(mat[2] + mat[4]) - splashRound(mat[4]);
} else {
- t0 = imgCoordMungeUpperC(mat[4], glyphMode) -
- imgCoordMungeLowerC(mat[2] + mat[4], glyphMode);
+ t0 = splashRound(mat[4]) - splashRound(mat[2] + mat[4]);
}
if (mat[3] >= 0) {
- t1 = imgCoordMungeUpperC(mat[3] + mat[5], glyphMode) -
- imgCoordMungeLowerC(mat[5], glyphMode);
+ t1 = splashRound(mat[3] + mat[5]) - splashRound(mat[5]);
} else {
- t1 = imgCoordMungeUpperC(mat[5], glyphMode) -
- imgCoordMungeLowerC(mat[3] + mat[5], glyphMode);
+ t1 = splashRound(mat[5]) - splashRound(mat[3] + mat[5]);
}
scaledHeight = t0 > t1 ? t0 : t1;
if (scaledWidth == 0) {
@@ -2567,19 +3592,28 @@ void Splash::arbitraryTransformMask(SplashImageMaskSource src, void *srcData,
// scale the input image
scaledMask = scaleMask(src, srcData, srcWidth, srcHeight,
- scaledWidth, scaledHeight);
+ scaledWidth, scaledHeight, interpolate);
// construct the three sections
- i = (vy[2] <= vy[3]) ? 2 : 3;
- if (vy[1] <= vy[i]) {
+ i = 0;
+ if (vy[1] < vy[i]) {
i = 1;
}
- if (vy[0] < vy[i] || (i != 3 && vy[0] == vy[i])) {
- i = 0;
+ if (vy[2] < vy[i]) {
+ i = 2;
}
- if (vy[i] == vy[(i+1) & 3]) {
- section[0].y0 = imgCoordMungeLowerC(vy[i], glyphMode);
- section[0].y1 = imgCoordMungeUpperC(vy[(i+2) & 3], glyphMode) - 1;
+ if (vy[3] < vy[i]) {
+ i = 3;
+ }
+ // NB: if using fixed point, 0.000001 will be truncated to zero,
+ // so these two comparisons must be <=, not <
+ if (splashAbs(vy[i] - vy[(i-1) & 3]) <= 0.000001 &&
+ vy[(i-1) & 3] < vy[(i+1) & 3]) {
+ i = (i-1) & 3;
+ }
+ if (splashAbs(vy[i] - vy[(i+1) & 3]) <= 0.000001) {
+ section[0].y0 = splashRound(vy[i]);
+ section[0].y1 = splashRound(vy[(i+2) & 3]) - 1;
if (vx[i] < vx[(i+1) & 3]) {
section[0].ia0 = i;
section[0].ia1 = (i+3) & 3;
@@ -2593,8 +3627,8 @@ void Splash::arbitraryTransformMask(SplashImageMaskSource src, void *srcData,
}
nSections = 1;
} else {
- section[0].y0 = imgCoordMungeLowerC(vy[i], glyphMode);
- section[2].y1 = imgCoordMungeUpperC(vy[(i+2) & 3], glyphMode) - 1;
+ section[0].y0 = splashRound(vy[i]);
+ section[2].y1 = splashRound(vy[(i+2) & 3]) - 1;
section[0].ia0 = section[0].ib0 = i;
section[2].ia1 = section[2].ib1 = (i+2) & 3;
if (vx[(i+1) & 3] < vx[(i+3) & 3]) {
@@ -2605,8 +3639,8 @@ void Splash::arbitraryTransformMask(SplashImageMaskSource src, void *srcData,
section[0].ib1 = section[2].ib0 = (i+1) & 3;
}
if (vy[(i+1) & 3] < vy[(i+3) & 3]) {
- section[1].y0 = imgCoordMungeLowerC(vy[(i+1) & 3], glyphMode);
- section[2].y0 = imgCoordMungeUpperC(vy[(i+3) & 3], glyphMode);
+ section[1].y0 = splashRound(vy[(i+1) & 3]);
+ section[2].y0 = splashRound(vy[(i+3) & 3]);
if (vx[(i+1) & 3] < vx[(i+3) & 3]) {
section[1].ia0 = (i+1) & 3;
section[1].ia1 = (i+2) & 3;
@@ -2619,8 +3653,8 @@ void Splash::arbitraryTransformMask(SplashImageMaskSource src, void *srcData,
section[1].ib1 = (i+2) & 3;
}
} else {
- section[1].y0 = imgCoordMungeLowerC(vy[(i+3) & 3], glyphMode);
- section[2].y0 = imgCoordMungeUpperC(vy[(i+1) & 3], glyphMode);
+ section[1].y0 = splashRound(vy[(i+3) & 3]);
+ section[2].y0 = splashRound(vy[(i+1) & 3]);
if (vx[(i+1) & 3] < vx[(i+3) & 3]) {
section[1].ia0 = i;
section[1].ia1 = (i+1) & 3;
@@ -2653,11 +3687,9 @@ void Splash::arbitraryTransformMask(SplashImageMaskSource src, void *srcData,
}
// initialize the pixel pipe
- pipeInit(&pipe, 0, 0, state->fillPattern, NULL,
- (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse);
- if (vectorAntialias) {
- drawAAPixelInit();
- }
+ pipeInit(&pipe, state->fillPattern,
+ (Guchar)splashRound(state->fillAlpha * 255),
+ gTrue, gFalse);
// make sure narrow images cover at least one pixel
if (nSections == 1) {
@@ -2675,23 +3707,30 @@ void Splash::arbitraryTransformMask(SplashImageMaskSource src, void *srcData,
// scan all pixels inside the target region
for (i = 0; i < nSections; ++i) {
for (y = section[i].y0; y <= section[i].y1; ++y) {
- xa = imgCoordMungeLowerC(section[i].xa0 +
- ((SplashCoord)y + 0.5 - section[i].ya0) *
- section[i].dxdya,
- glyphMode);
- xb = imgCoordMungeUpperC(section[i].xb0 +
- ((SplashCoord)y + 0.5 - section[i].yb0) *
- section[i].dxdyb,
- glyphMode);
+ xa = splashRound(section[i].xa0 +
+ ((SplashCoord)y + 0.5 - section[i].ya0) *
+ section[i].dxdya);
+ xb = splashRound(section[i].xb0 +
+ ((SplashCoord)y + 0.5 - section[i].yb0) *
+ section[i].dxdyb);
+ if (xa > xb) {
+ continue;
+ }
// make sure narrow images cover at least one pixel
if (xa == xb) {
++xb;
}
- if (clipRes != splashClipAllInside) {
- clipRes2 = state->clip->testSpan(xa, xb - 1, y);
- } else {
- clipRes2 = clipRes;
+ // check the scanBuf bounds
+ if (xa >= bitmap->width || xb < 0) {
+ continue;
+ }
+ if (xa < 0) {
+ xa = 0;
}
+ if (xb > bitmap->width) {
+ xb = bitmap->width;
+ }
+ // get the scan line
for (x = xa; x < xb; ++x) {
// map (x+0.5, y+0.5) back to the scaled image
xx = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir00 +
@@ -2710,13 +3749,19 @@ void Splash::arbitraryTransformMask(SplashImageMaskSource src, void *srcData,
} else if (yy >= scaledHeight) {
yy = scaledHeight - 1;
}
- pipe.shape = scaledMask->data[yy * scaledWidth + xx];
- if (vectorAntialias && clipRes2 != splashClipAllInside) {
- drawAAPixel(&pipe, x, y);
+ scanBuf[x] = scaledMask->data[yy * scaledWidth + xx];
+ }
+ // clip the scan line
+ if (clipRes != splashClipAllInside) {
+ if (vectorAntialias) {
+ state->clip->clipSpan(scanBuf, y, xa, xb - 1, state->strokeAdjust);
} else {
- drawPixel(&pipe, x, y, clipRes2 == splashClipAllInside);
+ state->clip->clipSpanBinary(scanBuf, y, xa, xb - 1,
+ state->strokeAdjust);
}
}
+ // draw the scan line
+ (this->*pipe.run)(&pipe, xa, xb - 1, y, scanBuf + xa, NULL);
}
}
@@ -2726,7 +3771,8 @@ void Splash::arbitraryTransformMask(SplashImageMaskSource src, void *srcData,
// Scale an image mask into a SplashBitmap.
SplashBitmap *Splash::scaleMask(SplashImageMaskSource src, void *srcData,
int srcWidth, int srcHeight,
- int scaledWidth, int scaledHeight) {
+ int scaledWidth, int scaledHeight,
+ GBool interpolate) {
SplashBitmap *dest;
dest = new SplashBitmap(scaledWidth, scaledHeight, 1, splashModeMono8,
@@ -2744,8 +3790,13 @@ SplashBitmap *Splash::scaleMask(SplashImageMaskSource src, void *srcData,
scaleMaskYuXd(src, srcData, srcWidth, srcHeight,
scaledWidth, scaledHeight, dest);
} else {
- scaleMaskYuXu(src, srcData, srcWidth, srcHeight,
- scaledWidth, scaledHeight, dest);
+ if (interpolate) {
+ scaleMaskYuXuI(src, srcData, srcWidth, srcHeight,
+ scaledWidth, scaledHeight, dest);
+ } else {
+ scaleMaskYuXu(src, srcData, srcWidth, srcHeight,
+ scaledWidth, scaledHeight, dest);
+ }
}
}
return dest;
@@ -3057,60 +4108,158 @@ void Splash::scaleMaskYuXu(SplashImageMaskSource src, void *srcData,
gfree(lineBuf);
}
+void Splash::scaleMaskYuXuI(SplashImageMaskSource src, void *srcData,
+ int srcWidth, int srcHeight,
+ int scaledWidth, int scaledHeight,
+ SplashBitmap *dest) {
+ Guchar *lineBuf0, *lineBuf1, *tBuf;
+ Guchar pix;
+ SplashCoord yr, xr, ys, xs, ySrc, xSrc;
+ int ySrc0, ySrc1, yBuf, xSrc0, xSrc1, y, x;
+ Guchar *destPtr;
+
+ // ratios
+ yr = (SplashCoord)srcHeight / (SplashCoord)scaledHeight;
+ xr = (SplashCoord)srcWidth / (SplashCoord)scaledWidth;
+
+ // allocate buffers
+ lineBuf0 = (Guchar *)gmalloc(scaledWidth);
+ lineBuf1 = (Guchar *)gmalloc(scaledWidth);
+
+ // read first two rows
+ (*src)(srcData, lineBuf0);
+ if (srcHeight > 1) {
+ (*src)(srcData, lineBuf1);
+ yBuf = 1;
+ } else {
+ memcpy(lineBuf1, lineBuf0, srcWidth);
+ yBuf = 0;
+ }
+
+ // interpolate first two rows
+ for (x = scaledWidth - 1; x >= 0; --x) {
+ xSrc = xr * x;
+ xSrc0 = splashFloor(xSrc + xr * 0.5 - 0.5);
+ xSrc1 = xSrc0 + 1;
+ xs = ((SplashCoord)xSrc1 + 0.5) - (xSrc + xr * 0.5);
+ if (xSrc0 < 0) {
+ xSrc0 = 0;
+ }
+ if (xSrc1 >= srcWidth) {
+ xSrc1 = srcWidth - 1;
+ }
+ lineBuf0[x] = (Guchar)(int)
+ ((xs * lineBuf0[xSrc0] +
+ ((SplashCoord)1 - xs) * lineBuf0[xSrc1]) * 255);
+ lineBuf1[x] = (Guchar)(int)
+ ((xs * lineBuf1[xSrc0] +
+ ((SplashCoord)1 - xs) * lineBuf1[xSrc1]) * 255);
+ }
+
+ destPtr = dest->data;
+ for (y = 0; y < scaledHeight; ++y) {
+
+ // compute vertical interpolation parameters
+ ySrc = yr * y;
+ ySrc0 = splashFloor(ySrc + yr * 0.5 - 0.5);
+ ySrc1 = ySrc0 + 1;
+ ys = ((SplashCoord)ySrc1 + 0.5) - (ySrc + yr * 0.5);
+ if (ySrc0 < 0) {
+ ySrc0 = 0;
+ ys = 1;
+ }
+ if (ySrc1 >= srcHeight) {
+ ySrc1 = srcHeight - 1;
+ ys = 0;
+ }
+
+ // read another row (if necessary)
+ if (ySrc1 > yBuf) {
+ tBuf = lineBuf0;
+ lineBuf0 = lineBuf1;
+ lineBuf1 = tBuf;
+ (*src)(srcData, lineBuf1);
+
+ // interpolate the row
+ for (x = scaledWidth - 1; x >= 0; --x) {
+ xSrc = xr * x;
+ xSrc0 = splashFloor(xSrc + xr * 0.5 - 0.5);
+ xSrc1 = xSrc0 + 1;
+ xs = ((SplashCoord)xSrc1 + 0.5) - (xSrc + xr * 0.5);
+ if (xSrc0 < 0) {
+ xSrc0 = 0;
+ }
+ if (xSrc1 >= srcWidth) {
+ xSrc1 = srcWidth - 1;
+ }
+ lineBuf1[x] = (Guchar)(int)
+ ((xs * lineBuf1[xSrc0] +
+ ((SplashCoord)1 - xs) * lineBuf1[xSrc1]) * 255);
+ }
+
+ ++yBuf;
+ }
+
+ // do the vertical interpolation
+ for (x = 0; x < scaledWidth; ++x) {
+
+ pix = (Guchar)(int)(ys * lineBuf0[x] +
+ ((SplashCoord)1 - ys) * lineBuf1[x]);
+
+ // store the pixel
+ *destPtr++ = pix;
+ }
+ }
+
+ gfree(lineBuf1);
+ gfree(lineBuf0);
+}
+
void Splash::blitMask(SplashBitmap *src, int xDest, int yDest,
SplashClipResult clipRes) {
SplashPipe pipe;
- Guchar *p;
- int w, h, x, y;
+ int w, h, x0, x1, y0, y1, y, t;
w = src->getWidth();
h = src->getHeight();
- if (vectorAntialias && clipRes != splashClipAllInside) {
- pipeInit(&pipe, xDest, yDest, state->fillPattern, NULL,
- (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse);
- drawAAPixelInit();
- p = src->getDataPtr();
+ pipeInit(&pipe, state->fillPattern,
+ (Guchar)splashRound(state->fillAlpha * 255),
+ gTrue, gFalse);
+ if (clipRes == splashClipAllInside) {
for (y = 0; y < h; ++y) {
- for (x = 0; x < w; ++x) {
- pipe.shape = *p++;
- drawAAPixel(&pipe, xDest + x, yDest + y);
- }
+ (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y,
+ src->getDataPtr() + y * w, NULL);
}
} else {
- pipeInit(&pipe, xDest, yDest, state->fillPattern, NULL,
- (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse);
- p = src->getDataPtr();
- if (clipRes == splashClipAllInside) {
- for (y = 0; y < h; ++y) {
- pipeSetXY(&pipe, xDest, yDest + y);
- for (x = 0; x < w; ++x) {
- if (*p) {
- pipe.shape = *p;
- (this->*pipe.run)(&pipe);
- } else {
- pipeIncX(&pipe);
- }
- ++p;
- }
- }
- updateModX(xDest);
- updateModX(xDest + w - 1);
- updateModY(yDest);
- updateModY(yDest + h - 1);
- } else {
- for (y = 0; y < h; ++y) {
- pipeSetXY(&pipe, xDest, yDest + y);
- for (x = 0; x < w; ++x) {
- if (*p && state->clip->test(xDest + x, yDest + y)) {
- pipe.shape = *p;
- (this->*pipe.run)(&pipe);
- updateModX(xDest + x);
- updateModY(yDest + y);
- } else {
- pipeIncX(&pipe);
- }
- ++p;
+ x0 = xDest;
+ if ((t = state->clip->getXMinI(state->strokeAdjust)) > x0) {
+ x0 = t;
+ }
+ x1 = xDest + w;
+ if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < x1) {
+ x1 = t;
+ }
+ y0 = yDest;
+ if ((t = state->clip->getYMinI(state->strokeAdjust)) > y0) {
+ y0 = t;
+ }
+ y1 = yDest + h;
+ if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < y1) {
+ y1 = t;
+ }
+ if (x0 < x1 && y0 < y1) {
+ for (y = y0; y < y1; ++y) {
+ memcpy(scanBuf + x0,
+ src->getDataPtr() + (y - yDest) * w + (x0 - xDest),
+ x1 - x0);
+ if (vectorAntialias) {
+ state->clip->clipSpan(scanBuf, y, x0, x1 - 1,
+ state->strokeAdjust);
+ } else {
+ state->clip->clipSpanBinary(scanBuf, y, x0, x1 - 1,
+ state->strokeAdjust);
}
+ (this->*pipe.run)(&pipe, x0, x1 - 1, y, scanBuf + x0, NULL);
}
}
}
@@ -3118,11 +4267,13 @@ void Splash::blitMask(SplashBitmap *src, int xDest, int yDest,
SplashError Splash::drawImage(SplashImageSource src, void *srcData,
SplashColorMode srcMode, GBool srcAlpha,
- int w, int h, SplashCoord *mat) {
+ int w, int h, SplashCoord *mat,
+ GBool interpolate) {
GBool ok;
SplashBitmap *scaledImg;
SplashClipResult clipRes;
GBool minorAxisZero;
+ SplashCoord wSize, hSize, t0, t1;
int x0, y0, x1, y1, scaledWidth, scaledHeight;
int nComps;
@@ -3142,11 +4293,8 @@ SplashError Splash::drawImage(SplashImageSource src, void *srcData,
nComps = 1;
break;
case splashModeRGB8:
- ok = srcMode == splashModeRGB8;
- nComps = 3;
- break;
case splashModeBGR8:
- ok = srcMode == splashModeBGR8;
+ ok = srcMode == splashModeRGB8;
nComps = 3;
break;
#if SPLASH_CMYK
@@ -3168,60 +4316,87 @@ SplashError Splash::drawImage(SplashImageSource src, void *srcData,
return splashErrSingularMatrix;
}
- minorAxisZero = mat[1] == 0 && mat[2] == 0;
+ minorAxisZero = splashAbs(mat[1]) <= 0.0001 && splashAbs(mat[2]) <= 0.0001;
+
+ // rough estimate of size of scaled image
+ t0 = splashAbs(mat[0]);
+ t1 = splashAbs(mat[1]);
+ wSize = t0 > t1 ? t0 : t1;
+ t0 = splashAbs(mat[2]);
+ t1 = splashAbs(mat[3]);
+ hSize = t0 > t1 ? t0 : t1;
+
+ // stream-mode upscaling -- this is slower, so we only use it if the
+ // upscaled image is large (in which case clipping should remove
+ // many pixels)
+ if (wSize > 2 * w && hSize > 2 * h && wSize * hSize > 1000000) {
+ upscaleImage(src, srcData, srcMode, nComps, srcAlpha,
+ w, h, mat, interpolate);
// scaling only
- if (mat[0] > 0 && minorAxisZero && mat[3] > 0) {
- x0 = imgCoordMungeLower(mat[4]);
- y0 = imgCoordMungeLower(mat[5]);
- x1 = imgCoordMungeUpper(mat[0] + mat[4]);
- y1 = imgCoordMungeUpper(mat[3] + mat[5]);
- // make sure narrow images cover at least one pixel
- if (x0 == x1) {
- ++x1;
- }
- if (y0 == y1) {
- ++y1;
- }
- clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1);
+ } else if (mat[0] > 0 && minorAxisZero && mat[3] > 0) {
+ getImageBounds(mat[4], mat[0] + mat[4], &x0, &x1);
+ getImageBounds(mat[5], mat[3] + mat[5], &y0, &y1);
+ clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1,
+ state->strokeAdjust);
opClipRes = clipRes;
if (clipRes != splashClipAllOutside) {
scaledWidth = x1 - x0;
scaledHeight = y1 - y0;
scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h,
- scaledWidth, scaledHeight);
+ scaledWidth, scaledHeight, interpolate);
blitImage(scaledImg, srcAlpha, x0, y0, clipRes);
delete scaledImg;
}
// scaling plus vertical flip
} else if (mat[0] > 0 && minorAxisZero && mat[3] < 0) {
- x0 = imgCoordMungeLower(mat[4]);
- y0 = imgCoordMungeLower(mat[3] + mat[5]);
- x1 = imgCoordMungeUpper(mat[0] + mat[4]);
- y1 = imgCoordMungeUpper(mat[5]);
- if (x0 == x1) {
- if (mat[4] + mat[0] * 0.5 < x0) {
- --x0;
- } else {
- ++x1;
- }
+ getImageBounds(mat[4], mat[0] + mat[4], &x0, &x1);
+ getImageBounds(mat[3] + mat[5], mat[5], &y0, &y1);
+ clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1,
+ state->strokeAdjust);
+ opClipRes = clipRes;
+ if (clipRes != splashClipAllOutside) {
+ scaledWidth = x1 - x0;
+ scaledHeight = y1 - y0;
+ scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h,
+ scaledWidth, scaledHeight, interpolate);
+ vertFlipImage(scaledImg, scaledWidth, scaledHeight, nComps);
+ blitImage(scaledImg, srcAlpha, x0, y0, clipRes);
+ delete scaledImg;
}
- if (y0 == y1) {
- if (mat[5] + mat[1] * 0.5 < y0) {
- --y0;
- } else {
- ++y1;
- }
+
+ // scaling plus horizontal flip
+ } else if (mat[0] > 0 && minorAxisZero && mat[3] > 0) {
+ getImageBounds(mat[0] + mat[4], mat[4], &x0, &x1);
+ getImageBounds(mat[5], mat[3] + mat[5], &y0, &y1);
+ clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1,
+ state->strokeAdjust);
+ opClipRes = clipRes;
+ if (clipRes != splashClipAllOutside) {
+ scaledWidth = x1 - x0;
+ scaledHeight = y1 - y0;
+ scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h,
+ scaledWidth, scaledHeight, interpolate);
+ horizFlipImage(scaledImg, scaledWidth, scaledHeight, nComps);
+ blitImage(scaledImg, srcAlpha, x0, y0, clipRes);
+ delete scaledImg;
}
- clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1);
+
+ // scaling plus horizontal and vertical flips
+ } else if (mat[0] > 0 && minorAxisZero && mat[3] < 0) {
+ getImageBounds(mat[0] + mat[4], mat[4], &x0, &x1);
+ getImageBounds(mat[3] + mat[5], mat[5], &y0, &y1);
+ clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1,
+ state->strokeAdjust);
opClipRes = clipRes;
if (clipRes != splashClipAllOutside) {
scaledWidth = x1 - x0;
scaledHeight = y1 - y0;
scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h,
- scaledWidth, scaledHeight);
+ scaledWidth, scaledHeight, interpolate);
vertFlipImage(scaledImg, scaledWidth, scaledHeight, nComps);
+ horizFlipImage(scaledImg, scaledWidth, scaledHeight, nComps);
blitImage(scaledImg, srcAlpha, x0, y0, clipRes);
delete scaledImg;
}
@@ -3229,21 +4404,227 @@ SplashError Splash::drawImage(SplashImageSource src, void *srcData,
// all other cases
} else {
arbitraryTransformImage(src, srcData, srcMode, nComps, srcAlpha,
- w, h, mat);
+ w, h, mat, interpolate);
}
return splashOk;
}
+void Splash::upscaleImage(SplashImageSource src, void *srcData,
+ SplashColorMode srcMode, int nComps,
+ GBool srcAlpha, int srcWidth, int srcHeight,
+ SplashCoord *mat, GBool interpolate) {
+ SplashClipResult clipRes;
+ SplashPipe pipe;
+ SplashColorPtr unscaledImage, pixelBuf, p, q, q00, q01, q10, q11;
+ Guchar *unscaledAlpha, *alphaPtr;
+ SplashCoord xMin, yMin, xMax, yMax, t;
+ SplashCoord mi0, mi1, mi2, mi3, mi4, mi5, det;
+ SplashCoord ix, iy, sx, sy, pix0, pix1;
+ int rowSize, xMinI, yMinI, xMaxI, yMaxI, x, y, x0, y0, x1, y1, tt, i;
+
+ // compute the bbox of the target quadrilateral
+ xMin = xMax = mat[4];
+ t = mat[2] + mat[4];
+ if (t < xMin) {
+ xMin = t;
+ } else if (t > xMax) {
+ xMax = t;
+ }
+ t = mat[0] + mat[2] + mat[4];
+ if (t < xMin) {
+ xMin = t;
+ } else if (t > xMax) {
+ xMax = t;
+ }
+ t = mat[0] + mat[4];
+ if (t < xMin) {
+ xMin = t;
+ } else if (t > xMax) {
+ xMax = t;
+ }
+ getImageBounds(xMin, xMax, &xMinI, &xMaxI);
+ yMin = yMax = mat[5];
+ t = mat[3] + mat[5];
+ if (t < yMin) {
+ yMin = t;
+ } else if (t > yMax) {
+ yMax = t;
+ }
+ t = mat[1] + mat[3] + mat[5];
+ if (t < yMin) {
+ yMin = t;
+ } else if (t > yMax) {
+ yMax = t;
+ }
+ t = mat[1] + mat[5];
+ if (t < yMin) {
+ yMin = t;
+ } else if (t > yMax) {
+ yMax = t;
+ }
+ getImageBounds(yMin, yMax, &yMinI, &yMaxI);
+
+ // clipping
+ clipRes = state->clip->testRect(xMinI, yMinI, xMaxI - 1, yMaxI - 1,
+ state->strokeAdjust);
+ opClipRes = clipRes;
+ if (clipRes == splashClipAllOutside) {
+ return;
+ }
+ if (clipRes != splashClipAllInside) {
+ if ((tt = state->clip->getXMinI(state->strokeAdjust)) > xMinI) {
+ xMinI = tt;
+ }
+ if ((tt = state->clip->getXMaxI(state->strokeAdjust) + 1) < xMaxI) {
+ xMaxI = tt;
+ }
+ if ((tt = state->clip->getYMinI(state->strokeAdjust)) > yMinI) {
+ yMinI = tt;
+ }
+ if ((tt = state->clip->getYMaxI(state->strokeAdjust) + 1) < yMaxI) {
+ yMaxI = tt;
+ }
+ }
+
+ // invert the matrix
+ det = mat[0] * mat[3] - mat[1] * mat[2];
+ if (splashAbs(det) < 1e-6) {
+ // this should be caught by the singular matrix check in fillImageMask
+ return;
+ }
+ det = (SplashCoord)1 / det;
+ mi0 = det * mat[3] * srcWidth;
+ mi1 = -det * mat[1] * srcHeight;
+ mi2 = -det * mat[2] * srcWidth;
+ mi3 = det * mat[0] * srcHeight;
+ mi4 = det * (mat[2] * mat[5] - mat[3] * mat[4]) * srcWidth;
+ mi5 = -det * (mat[0] * mat[5] - mat[1] * mat[4]) * srcHeight;
+
+ // grab the image
+ if (srcWidth > INT_MAX / nComps) {
+ rowSize = -1;
+ } else {
+ rowSize = srcWidth * nComps;
+ }
+ unscaledImage = (SplashColorPtr)gmallocn(srcHeight, rowSize);
+ if (srcAlpha) {
+ unscaledAlpha = (Guchar *)gmallocn(srcHeight, srcWidth);
+ for (y = 0, p = unscaledImage, alphaPtr = unscaledAlpha;
+ y < srcHeight;
+ ++y, p += rowSize, alphaPtr += srcWidth) {
+ (*src)(srcData, p, alphaPtr);
+ }
+ } else {
+ unscaledAlpha = NULL;
+ for (y = 0, p = unscaledImage; y < srcHeight; ++y, p += srcWidth * nComps) {
+ (*src)(srcData, p, NULL);
+ }
+ }
+
+ // draw it
+ pixelBuf = (SplashColorPtr)gmallocn(xMaxI - xMinI, nComps);
+ pipeInit(&pipe, NULL,
+ (Guchar)splashRound(state->fillAlpha * 255),
+ gTrue, gFalse);
+ for (y = yMinI; y < yMaxI; ++y) {
+ p = pixelBuf;
+ for (x = xMinI; x < xMaxI; ++x) {
+ ix = ((SplashCoord)x + 0.5) * mi0 + ((SplashCoord)y + 0.5) * mi2 + mi4;
+ iy = ((SplashCoord)x + 0.5) * mi1 + ((SplashCoord)y + 0.5) * mi3 + mi5;
+ if (interpolate) {
+ if (ix >= 0 && ix < srcWidth && iy >= 0 && iy < srcHeight) {
+ x0 = splashFloor(ix - 0.5);
+ x1 = x0 + 1;
+ sx = (ix - 0.5) - x0;
+ y0 = splashFloor(iy - 0.5);
+ y1 = y0 + 1;
+ sy = (iy - 0.5) - y0;
+ if (x0 < 0) {
+ x0 = 0;
+ }
+ if (x1 >= srcWidth) {
+ x1 = srcWidth - 1;
+ }
+ if (y0 < 0) {
+ y0 = 0;
+ }
+ if (y1 >= srcHeight) {
+ y1 = srcHeight - 1;
+ }
+ q00 = &unscaledImage[(y0 * srcWidth + x0) * nComps];
+ q01 = &unscaledImage[(y0 * srcWidth + x1) * nComps];
+ q10 = &unscaledImage[(y1 * srcWidth + x0) * nComps];
+ q11 = &unscaledImage[(y1 * srcWidth + x1) * nComps];
+ for (i = 0; i < nComps; ++i) {
+ pix0 = ((SplashCoord)1 - sx) * *q00++ + sx * *q01++;
+ pix1 = ((SplashCoord)1 - sx) * *q10++ + sx * *q11++;
+ *p++ = (Guchar)splashRound(((SplashCoord)1 - sy) * pix0
+ + sy * pix1);
+ }
+ if (srcAlpha) {
+ pix0 = ((SplashCoord)1 - sx) * unscaledAlpha[y0 * srcWidth + x0]
+ + sx * unscaledAlpha[y0 * srcWidth + x1];
+ pix1 = ((SplashCoord)1 - sx) * unscaledAlpha[y1 * srcWidth + x0]
+ + sx * unscaledAlpha[y1 * srcWidth + x1];
+ scanBuf[x] = (Guchar)splashRound(((SplashCoord)1 - sy) * pix0
+ + sy * pix1);
+ } else {
+ scanBuf[x] = 0xff;
+ }
+ } else {
+ for (i = 0; i < nComps; ++i) {
+ *p++ = 0;
+ }
+ scanBuf[x] = 0;
+ }
+ } else {
+ x0 = splashFloor(ix);
+ y0 = splashFloor(iy);
+ if (x0 >= 0 && x0 < srcWidth && y0 >= 0 && y0 < srcHeight) {
+ q = &unscaledImage[(y0 * srcWidth + x0) * nComps];
+ for (i = 0; i < nComps; ++i) {
+ *p++ = *q++;
+ }
+ if (srcAlpha) {
+ scanBuf[x] = unscaledAlpha[y0 * srcWidth + x0];
+ } else {
+ scanBuf[x] = 0xff;
+ }
+ } else {
+ for (i = 0; i < nComps; ++i) {
+ *p++ = 0;
+ }
+ scanBuf[x] = 0;
+ }
+ }
+ }
+ if (clipRes != splashClipAllInside) {
+ if (vectorAntialias) {
+ state->clip->clipSpan(scanBuf, y, xMinI, xMaxI - 1,
+ state->strokeAdjust);
+ } else {
+ state->clip->clipSpanBinary(scanBuf, y, xMinI, xMaxI - 1,
+ state->strokeAdjust);
+ }
+ }
+ (this->*pipe.run)(&pipe, xMinI, xMaxI - 1, y, scanBuf + xMinI, pixelBuf);
+ }
+
+ gfree(pixelBuf);
+ gfree(unscaledImage);
+ gfree(unscaledAlpha);
+}
+
void Splash::arbitraryTransformImage(SplashImageSource src, void *srcData,
SplashColorMode srcMode, int nComps,
GBool srcAlpha,
int srcWidth, int srcHeight,
- SplashCoord *mat) {
+ SplashCoord *mat, GBool interpolate) {
SplashBitmap *scaledImg;
- SplashClipResult clipRes, clipRes2;
+ SplashClipResult clipRes;
SplashPipe pipe;
- SplashColor pixel;
+ SplashColorPtr pixelBuf;
int scaledWidth, scaledHeight, t0, t1;
SplashCoord r00, r01, r10, r11, det, ir00, ir01, ir10, ir11;
SplashCoord vx[4], vy[4];
@@ -3259,29 +4640,26 @@ void Splash::arbitraryTransformImage(SplashImageSource src, void *srcData,
vx[3] = mat[0] + mat[4]; vy[3] = mat[1] + mat[5];
// clipping
- xMin = imgCoordMungeLower(vx[0]);
- xMax = imgCoordMungeUpper(vx[0]);
- yMin = imgCoordMungeLower(vy[0]);
- yMax = imgCoordMungeUpper(vy[0]);
+ xMin = splashRound(vx[0]);
+ xMax = splashRound(vx[0]);
+ yMin = splashRound(vy[0]);
+ yMax = splashRound(vy[0]);
for (i = 1; i < 4; ++i) {
- t0 = imgCoordMungeLower(vx[i]);
+ t0 = splashRound(vx[i]);
if (t0 < xMin) {
xMin = t0;
- }
- t0 = imgCoordMungeUpper(vx[i]);
- if (t0 > xMax) {
+ } else if (t0 > xMax) {
xMax = t0;
}
- t1 = imgCoordMungeLower(vy[i]);
+ t1 = splashRound(vy[i]);
if (t1 < yMin) {
yMin = t1;
- }
- t1 = imgCoordMungeUpper(vy[i]);
- if (t1 > yMax) {
+ } else if (t1 > yMax) {
yMax = t1;
}
}
- clipRes = state->clip->testRect(xMin, yMin, xMax - 1, yMax - 1);
+ clipRes = state->clip->testRect(xMin, yMin, xMax - 1, yMax - 1,
+ state->strokeAdjust);
opClipRes = clipRes;
if (clipRes == splashClipAllOutside) {
return;
@@ -3289,25 +4667,25 @@ void Splash::arbitraryTransformImage(SplashImageSource src, void *srcData,
// compute the scale factors
if (mat[0] >= 0) {
- t0 = imgCoordMungeUpper(mat[0] + mat[4]) - imgCoordMungeLower(mat[4]);
+ t0 = splashRound(mat[0] + mat[4]) - splashRound(mat[4]);
} else {
- t0 = imgCoordMungeUpper(mat[4]) - imgCoordMungeLower(mat[0] + mat[4]);
+ t0 = splashRound(mat[4]) - splashRound(mat[0] + mat[4]);
}
if (mat[1] >= 0) {
- t1 = imgCoordMungeUpper(mat[1] + mat[5]) - imgCoordMungeLower(mat[5]);
+ t1 = splashRound(mat[1] + mat[5]) - splashRound(mat[5]);
} else {
- t1 = imgCoordMungeUpper(mat[5]) - imgCoordMungeLower(mat[1] + mat[5]);
+ t1 = splashRound(mat[5]) - splashRound(mat[1] + mat[5]);
}
scaledWidth = t0 > t1 ? t0 : t1;
if (mat[2] >= 0) {
- t0 = imgCoordMungeUpper(mat[2] + mat[4]) - imgCoordMungeLower(mat[4]);
+ t0 = splashRound(mat[2] + mat[4]) - splashRound(mat[4]);
} else {
- t0 = imgCoordMungeUpper(mat[4]) - imgCoordMungeLower(mat[2] + mat[4]);
+ t0 = splashRound(mat[4]) - splashRound(mat[2] + mat[4]);
}
if (mat[3] >= 0) {
- t1 = imgCoordMungeUpper(mat[3] + mat[5]) - imgCoordMungeLower(mat[5]);
+ t1 = splashRound(mat[3] + mat[5]) - splashRound(mat[5]);
} else {
- t1 = imgCoordMungeUpper(mat[5]) - imgCoordMungeLower(mat[3] + mat[5]);
+ t1 = splashRound(mat[5]) - splashRound(mat[3] + mat[5]);
}
scaledHeight = t0 > t1 ? t0 : t1;
if (scaledWidth == 0) {
@@ -3334,7 +4712,8 @@ void Splash::arbitraryTransformImage(SplashImageSource src, void *srcData,
// scale the input image
scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha,
- srcWidth, srcHeight, scaledWidth, scaledHeight);
+ srcWidth, srcHeight, scaledWidth, scaledHeight,
+ interpolate);
// construct the three sections
i = 0;
@@ -3354,8 +4733,8 @@ void Splash::arbitraryTransformImage(SplashImageSource src, void *srcData,
i = (i-1) & 3;
}
if (splashAbs(vy[i] - vy[(i+1) & 3]) <= 0.000001) {
- section[0].y0 = imgCoordMungeLower(vy[i]);
- section[0].y1 = imgCoordMungeUpper(vy[(i+2) & 3]) - 1;
+ section[0].y0 = splashRound(vy[i]);
+ section[0].y1 = splashRound(vy[(i+2) & 3]) - 1;
if (vx[i] < vx[(i+1) & 3]) {
section[0].ia0 = i;
section[0].ia1 = (i+3) & 3;
@@ -3369,8 +4748,8 @@ void Splash::arbitraryTransformImage(SplashImageSource src, void *srcData,
}
nSections = 1;
} else {
- section[0].y0 = imgCoordMungeLower(vy[i]);
- section[2].y1 = imgCoordMungeUpper(vy[(i+2) & 3]) - 1;
+ section[0].y0 = splashRound(vy[i]);
+ section[2].y1 = splashRound(vy[(i+2) & 3]) - 1;
section[0].ia0 = section[0].ib0 = i;
section[2].ia1 = section[2].ib1 = (i+2) & 3;
if (vx[(i+1) & 3] < vx[(i+3) & 3]) {
@@ -3381,8 +4760,8 @@ void Splash::arbitraryTransformImage(SplashImageSource src, void *srcData,
section[0].ib1 = section[2].ib0 = (i+1) & 3;
}
if (vy[(i+1) & 3] < vy[(i+3) & 3]) {
- section[1].y0 = imgCoordMungeLower(vy[(i+1) & 3]);
- section[2].y0 = imgCoordMungeUpper(vy[(i+3) & 3]);
+ section[1].y0 = splashRound(vy[(i+1) & 3]);
+ section[2].y0 = splashRound(vy[(i+3) & 3]);
if (vx[(i+1) & 3] < vx[(i+3) & 3]) {
section[1].ia0 = (i+1) & 3;
section[1].ia1 = (i+2) & 3;
@@ -3395,8 +4774,8 @@ void Splash::arbitraryTransformImage(SplashImageSource src, void *srcData,
section[1].ib1 = (i+2) & 3;
}
} else {
- section[1].y0 = imgCoordMungeLower(vy[(i+3) & 3]);
- section[2].y0 = imgCoordMungeUpper(vy[(i+1) & 3]);
+ section[1].y0 = splashRound(vy[(i+3) & 3]);
+ section[2].y0 = splashRound(vy[(i+1) & 3]);
if (vx[(i+1) & 3] < vx[(i+3) & 3]) {
section[1].ia0 = i;
section[1].ia1 = (i+1) & 3;
@@ -3429,13 +4808,9 @@ void Splash::arbitraryTransformImage(SplashImageSource src, void *srcData,
}
// initialize the pixel pipe
- pipeInit(&pipe, 0, 0, NULL, pixel,
+ pipeInit(&pipe, NULL,
(Guchar)splashRound(state->fillAlpha * 255),
- srcAlpha || (vectorAntialias && clipRes != splashClipAllInside),
- gFalse);
- if (vectorAntialias) {
- drawAAPixelInit();
- }
+ gTrue, gFalse);
// make sure narrow images cover at least one pixel
if (nSections == 1) {
@@ -3450,24 +4825,46 @@ void Splash::arbitraryTransformImage(SplashImageSource src, void *srcData,
}
}
+ pixelBuf = (SplashColorPtr)gmallocn(xMax - xMin + 1, bitmapComps);
+
// scan all pixels inside the target region
for (i = 0; i < nSections; ++i) {
for (y = section[i].y0; y <= section[i].y1; ++y) {
- xa = imgCoordMungeLower(section[i].xa0 +
- ((SplashCoord)y + 0.5 - section[i].ya0) *
- section[i].dxdya);
- xb = imgCoordMungeUpper(section[i].xb0 +
- ((SplashCoord)y + 0.5 - section[i].yb0) *
- section[i].dxdyb);
+ xa = splashRound(section[i].xa0 +
+ ((SplashCoord)y + 0.5 - section[i].ya0) *
+ section[i].dxdya);
+ xb = splashRound(section[i].xb0 +
+ ((SplashCoord)y + 0.5 - section[i].yb0) *
+ section[i].dxdyb);
+ if (xa > xb) {
+ continue;
+ }
// make sure narrow images cover at least one pixel
if (xa == xb) {
++xb;
}
+ // check the scanBuf bounds
+ if (xa >= bitmap->width || xb < 0) {
+ continue;
+ }
+ if (xa < 0) {
+ xa = 0;
+ }
+ if (xb > bitmap->width) {
+ xb = bitmap->width;
+ }
+ // clip the scan line
+ memset(scanBuf + xa, 0xff, xb - xa);
if (clipRes != splashClipAllInside) {
- clipRes2 = state->clip->testSpan(xa, xb - 1, y);
- } else {
- clipRes2 = clipRes;
+ if (vectorAntialias) {
+ state->clip->clipSpan(scanBuf, y, xa, xb - 1,
+ state->strokeAdjust);
+ } else {
+ state->clip->clipSpanBinary(scanBuf, y, xa, xb - 1,
+ state->strokeAdjust);
+ }
}
+ // draw the scan line
for (x = xa; x < xb; ++x) {
// map (x+0.5, y+0.5) back to the scaled image
xx = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir00 +
@@ -3486,21 +4883,19 @@ void Splash::arbitraryTransformImage(SplashImageSource src, void *srcData,
} else if (yy >= scaledHeight) {
yy = scaledHeight - 1;
}
- scaledImg->getPixel(xx, yy, pixel);
+ // get the color
+ scaledImg->getPixel(xx, yy, pixelBuf + (x - xa) * bitmapComps);
+ // apply alpha
if (srcAlpha) {
- pipe.shape = scaledImg->alpha[yy * scaledWidth + xx];
- } else {
- pipe.shape = 255;
- }
- if (vectorAntialias && clipRes2 != splashClipAllInside) {
- drawAAPixel(&pipe, x, y);
- } else {
- drawPixel(&pipe, x, y, clipRes2 == splashClipAllInside);
+ scanBuf[x] = div255(scanBuf[x] *
+ scaledImg->alpha[yy * scaledWidth + xx]);
}
}
+ (this->*pipe.run)(&pipe, xa, xb - 1, y, scanBuf + xa, pixelBuf);
}
}
+ gfree(pixelBuf);
delete scaledImg;
}
@@ -3508,7 +4903,8 @@ void Splash::arbitraryTransformImage(SplashImageSource src, void *srcData,
SplashBitmap *Splash::scaleImage(SplashImageSource src, void *srcData,
SplashColorMode srcMode, int nComps,
GBool srcAlpha, int srcWidth, int srcHeight,
- int scaledWidth, int scaledHeight) {
+ int scaledWidth, int scaledHeight,
+ GBool interpolate) {
SplashBitmap *dest;
dest = new SplashBitmap(scaledWidth, scaledHeight, 1, srcMode, srcAlpha);
@@ -3525,8 +4921,13 @@ SplashBitmap *Splash::scaleImage(SplashImageSource src, void *srcData,
scaleImageYuXd(src, srcData, srcMode, nComps, srcAlpha,
srcWidth, srcHeight, scaledWidth, scaledHeight, dest);
} else {
- scaleImageYuXu(src, srcData, srcMode, nComps, srcAlpha,
- srcWidth, srcHeight, scaledWidth, scaledHeight, dest);
+ if (interpolate) {
+ scaleImageYuXuI(src, srcData, srcMode, nComps, srcAlpha,
+ srcWidth, srcHeight, scaledWidth, scaledHeight, dest);
+ } else {
+ scaleImageYuXu(src, srcData, srcMode, nComps, srcAlpha,
+ srcWidth, srcHeight, scaledWidth, scaledHeight, dest);
+ }
}
}
return dest;
@@ -3654,27 +5055,6 @@ void Splash::scaleImageYdXd(SplashImageSource src, void *srcData,
*destPtr++ = (Guchar)pix2;
break;
- case splashModeBGR8:
-
- // compute the final pixel
- pix0 = pix1 = pix2 = 0;
- for (i = 0; i < xStep; ++i) {
- pix0 += pixBuf[xx];
- pix1 += pixBuf[xx+1];
- pix2 += pixBuf[xx+2];
- xx += 3;
- }
- // pix / xStep * yStep
- pix0 = (pix0 * d) >> 23;
- pix1 = (pix1 * d) >> 23;
- pix2 = (pix2 * d) >> 23;
-
- // store the pixel
- *destPtr++ = (Guchar)pix2;
- *destPtr++ = (Guchar)pix1;
- *destPtr++ = (Guchar)pix0;
- break;
-
#if SPLASH_CMYK
case splashModeCMYK8:
@@ -3703,6 +5083,7 @@ void Splash::scaleImageYdXd(SplashImageSource src, void *srcData,
case splashModeMono1: // mono1 is not allowed
+ case splashModeBGR8: // bgr8 is not allowed
default:
break;
}
@@ -3812,8 +5193,6 @@ void Splash::scaleImageYdXu(SplashImageSource src, void *srcData,
// store the pixel
switch (srcMode) {
- case splashModeMono1: // mono1 is not allowed
- break;
case splashModeMono8:
for (i = 0; i < xStep; ++i) {
*destPtr++ = (Guchar)pix[0];
@@ -3826,13 +5205,6 @@ void Splash::scaleImageYdXu(SplashImageSource src, void *srcData,
*destPtr++ = (Guchar)pix[2];
}
break;
- case splashModeBGR8:
- for (i = 0; i < xStep; ++i) {
- *destPtr++ = (Guchar)pix[2];
- *destPtr++ = (Guchar)pix[1];
- *destPtr++ = (Guchar)pix[0];
- }
- break;
#if SPLASH_CMYK
case splashModeCMYK8:
for (i = 0; i < xStep; ++i) {
@@ -3843,6 +5215,10 @@ void Splash::scaleImageYdXu(SplashImageSource src, void *srcData,
}
break;
#endif
+ case splashModeMono1: // mono1 is not allowed
+ case splashModeBGR8: // BGR8 is not allowed
+ default:
+ break;
}
// process alpha
@@ -3942,8 +5318,6 @@ void Splash::scaleImageYuXd(SplashImageSource src, void *srcData,
// store the pixel
switch (srcMode) {
- case splashModeMono1: // mono1 is not allowed
- break;
case splashModeMono8:
for (i = 0; i < yStep; ++i) {
destPtr = destPtr0 + (i * scaledWidth + x) * nComps;
@@ -3958,14 +5332,6 @@ void Splash::scaleImageYuXd(SplashImageSource src, void *srcData,
*destPtr++ = (Guchar)pix[2];
}
break;
- case splashModeBGR8:
- for (i = 0; i < yStep; ++i) {
- destPtr = destPtr0 + (i * scaledWidth + x) * nComps;
- *destPtr++ = (Guchar)pix[2];
- *destPtr++ = (Guchar)pix[1];
- *destPtr++ = (Guchar)pix[0];
- }
- break;
#if SPLASH_CMYK
case splashModeCMYK8:
for (i = 0; i < yStep; ++i) {
@@ -3977,6 +5343,10 @@ void Splash::scaleImageYuXd(SplashImageSource src, void *srcData,
}
break;
#endif
+ case splashModeMono1: // mono1 is not allowed
+ case splashModeBGR8: // BGR8 is not allowed
+ default:
+ break;
}
// process alpha
@@ -4071,8 +5441,6 @@ void Splash::scaleImageYuXu(SplashImageSource src, void *srcData,
// store the pixel
switch (srcMode) {
- case splashModeMono1: // mono1 is not allowed
- break;
case splashModeMono8:
for (i = 0; i < yStep; ++i) {
for (j = 0; j < xStep; ++j) {
@@ -4091,16 +5459,6 @@ void Splash::scaleImageYuXu(SplashImageSource src, void *srcData,
}
}
break;
- case splashModeBGR8:
- for (i = 0; i < yStep; ++i) {
- for (j = 0; j < xStep; ++j) {
- destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps;
- *destPtr++ = (Guchar)pix[2];
- *destPtr++ = (Guchar)pix[1];
- *destPtr++ = (Guchar)pix[0];
- }
- }
- break;
#if SPLASH_CMYK
case splashModeCMYK8:
for (i = 0; i < yStep; ++i) {
@@ -4114,6 +5472,10 @@ void Splash::scaleImageYuXu(SplashImageSource src, void *srcData,
}
break;
#endif
+ case splashModeMono1: // mono1 is not allowed
+ case splashModeBGR8: // BGR8 is not allowed
+ default:
+ break;
}
// process alpha
@@ -4140,6 +5502,177 @@ void Splash::scaleImageYuXu(SplashImageSource src, void *srcData,
gfree(lineBuf);
}
+void Splash::scaleImageYuXuI(SplashImageSource src, void *srcData,
+ SplashColorMode srcMode, int nComps,
+ GBool srcAlpha, int srcWidth, int srcHeight,
+ int scaledWidth, int scaledHeight,
+ SplashBitmap *dest) {
+ Guchar *lineBuf0, *lineBuf1, *alphaLineBuf0, *alphaLineBuf1, *tBuf;
+ Guchar pix[splashMaxColorComps];
+ SplashCoord yr, xr, ys, xs, ySrc, xSrc;
+ int ySrc0, ySrc1, yBuf, xSrc0, xSrc1, y, x, i;
+ Guchar *destPtr, *destAlphaPtr;
+
+ // ratios
+ yr = (SplashCoord)srcHeight / (SplashCoord)scaledHeight;
+ xr = (SplashCoord)srcWidth / (SplashCoord)scaledWidth;
+
+ // allocate buffers
+ lineBuf0 = (Guchar *)gmallocn(scaledWidth, nComps);
+ lineBuf1 = (Guchar *)gmallocn(scaledWidth, nComps);
+ if (srcAlpha) {
+ alphaLineBuf0 = (Guchar *)gmalloc(scaledWidth);
+ alphaLineBuf1 = (Guchar *)gmalloc(scaledWidth);
+ } else {
+ alphaLineBuf0 = NULL;
+ alphaLineBuf1 = NULL;
+ }
+
+ // read first two rows
+ (*src)(srcData, lineBuf0, alphaLineBuf0);
+ if (srcHeight > 1) {
+ (*src)(srcData, lineBuf1, alphaLineBuf1);
+ yBuf = 1;
+ } else {
+ memcpy(lineBuf1, lineBuf0, srcWidth * nComps);
+ if (srcAlpha) {
+ memcpy(alphaLineBuf1, alphaLineBuf0, srcWidth);
+ }
+ yBuf = 0;
+ }
+
+ // interpolate first two rows
+ for (x = scaledWidth - 1; x >= 0; --x) {
+ xSrc = xr * x;
+ xSrc0 = splashFloor(xSrc + xr * 0.5 - 0.5);
+ xSrc1 = xSrc0 + 1;
+ xs = ((SplashCoord)xSrc1 + 0.5) - (xSrc + xr * 0.5);
+ if (xSrc0 < 0) {
+ xSrc0 = 0;
+ }
+ if (xSrc1 >= srcWidth) {
+ xSrc1 = srcWidth - 1;
+ }
+ for (i = 0; i < nComps; ++i) {
+ lineBuf0[x*nComps+i] = (Guchar)(int)
+ (xs * lineBuf0[xSrc0*nComps+i] +
+ ((SplashCoord)1 - xs) * lineBuf0[xSrc1*nComps+i]);
+ lineBuf1[x*nComps+i] = (Guchar)(int)
+ (xs * lineBuf1[xSrc0*nComps+i] +
+ ((SplashCoord)1 - xs) * lineBuf1[xSrc1*nComps+i]);
+ }
+ if (srcAlpha) {
+ alphaLineBuf0[x] = (Guchar)(int)
+ (xs * alphaLineBuf0[xSrc0] +
+ ((SplashCoord)1 - xs) * alphaLineBuf0[xSrc1]);
+ alphaLineBuf1[x] = (Guchar)(int)
+ (xs * alphaLineBuf1[xSrc0] +
+ ((SplashCoord)1 - xs) * alphaLineBuf1[xSrc1]);
+ }
+ }
+
+ destPtr = dest->data;
+ destAlphaPtr = dest->alpha;
+ for (y = 0; y < scaledHeight; ++y) {
+
+ // compute vertical interpolation parameters
+ ySrc = yr * y;
+ ySrc0 = splashFloor(ySrc + yr * 0.5 - 0.5);
+ ySrc1 = ySrc0 + 1;
+ ys = ((SplashCoord)ySrc1 + 0.5) - (ySrc + yr * 0.5);
+ if (ySrc0 < 0) {
+ ySrc0 = 0;
+ ys = 1;
+ }
+ if (ySrc1 >= srcHeight) {
+ ySrc1 = srcHeight - 1;
+ ys = 0;
+ }
+
+ // read another row (if necessary)
+ if (ySrc1 > yBuf) {
+ tBuf = lineBuf0;
+ lineBuf0 = lineBuf1;
+ lineBuf1 = tBuf;
+ tBuf = alphaLineBuf0;
+ alphaLineBuf0 = alphaLineBuf1;
+ alphaLineBuf1 = tBuf;
+ (*src)(srcData, lineBuf1, alphaLineBuf1);
+
+ // interpolate the row
+ for (x = scaledWidth - 1; x >= 0; --x) {
+ xSrc = xr * x;
+ xSrc0 = splashFloor(xSrc + xr * 0.5 - 0.5);
+ xSrc1 = xSrc0 + 1;
+ xs = ((SplashCoord)xSrc1 + 0.5) - (xSrc + xr * 0.5);
+ if (xSrc0 < 0) {
+ xSrc0 = 0;
+ }
+ if (xSrc1 >= srcWidth) {
+ xSrc1 = srcWidth - 1;
+ }
+ for (i = 0; i < nComps; ++i) {
+ lineBuf1[x*nComps+i] =
+ (Guchar)(int)(xs * lineBuf1[xSrc0*nComps+i] +
+ ((SplashCoord)1 - xs) * lineBuf1[xSrc1*nComps+i]);
+ }
+ if (srcAlpha) {
+ alphaLineBuf1[x] =
+ (Guchar)(int)(xs * alphaLineBuf1[xSrc0] +
+ ((SplashCoord)1 - xs) * alphaLineBuf1[xSrc1]);
+ }
+ }
+
+ ++yBuf;
+ }
+
+ // do the vertical interpolation
+ for (x = 0; x < scaledWidth; ++x) {
+
+ for (i = 0; i < nComps; ++i) {
+ pix[i] = (Guchar)(int)(ys * lineBuf0[x*nComps+i] +
+ ((SplashCoord)1 - ys) * lineBuf1[x*nComps+i]);
+ }
+
+ // store the pixel
+ switch (srcMode) {
+ case splashModeMono8:
+ *destPtr++ = pix[0];
+ break;
+ case splashModeRGB8:
+ *destPtr++ = pix[0];
+ *destPtr++ = pix[1];
+ *destPtr++ = pix[2];
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ *destPtr++ = pix[0];
+ *destPtr++ = pix[1];
+ *destPtr++ = pix[2];
+ *destPtr++ = pix[3];
+ break;
+#endif
+ case splashModeMono1: // mono1 is not allowed
+ case splashModeBGR8: // BGR8 is not allowed
+ default:
+ break;
+ }
+
+ // process alpha
+ if (srcAlpha) {
+ *destAlphaPtr++ = (Guchar)(int)
+ (ys * alphaLineBuf0[x] +
+ ((SplashCoord)1 - ys) * alphaLineBuf1[x]);
+ }
+ }
+ }
+
+ gfree(alphaLineBuf1);
+ gfree(alphaLineBuf0);
+ gfree(lineBuf1);
+ gfree(lineBuf0);
+}
+
void Splash::vertFlipImage(SplashBitmap *img, int width, int height,
int nComps) {
Guchar *lineBuf;
@@ -4167,12 +5700,43 @@ void Splash::vertFlipImage(SplashBitmap *img, int width, int height,
gfree(lineBuf);
}
+void Splash::horizFlipImage(SplashBitmap *img, int width, int height,
+ int nComps) {
+ Guchar *lineBuf;
+ SplashColorPtr p0, p1, p2;
+ int w, x, y, i;
+
+ w = width * nComps;
+ lineBuf = (Guchar *)gmalloc(w);
+ for (y = 0, p0 = img->data; y < height; ++y, p0 += img->rowSize) {
+ memcpy(lineBuf, p0, w);
+ p1 = p0;
+ p2 = lineBuf + (w - nComps);
+ for (x = 0; x < width; ++x) {
+ for (i = 0; i < nComps; ++i) {
+ p1[i] = p2[i];
+ }
+ p1 += nComps;
+ p2 -= nComps;
+ }
+ }
+ if (img->alpha) {
+ for (y = 0, p0 = img->alpha; y < height; ++y, p0 += width) {
+ memcpy(lineBuf, p0, width);
+ p1 = p0;
+ p2 = lineBuf + (width - 1);
+ for (x = 0; x < width; ++x) {
+ *p1++ = *p2--;
+ }
+ }
+ }
+ gfree(lineBuf);
+}
+
void Splash::blitImage(SplashBitmap *src, GBool srcAlpha, int xDest, int yDest,
SplashClipResult clipRes) {
SplashPipe pipe;
- SplashColor pixel;
- Guchar *ap;
- int w, h, x0, y0, x1, y1, x, y;
+ int w, h, x0, y0, x1, y1, y;
// split the image into clipped and unclipped regions
w = src->getWidth();
@@ -4200,8 +5764,8 @@ void Splash::blitImage(SplashBitmap *src, GBool srcAlpha, int xDest, int yDest,
x1 = x0;
}
if ((y1 = splashFloor(state->clip->getYMax()) - yDest) > h) {
- y1 = h;
- }
+ y1 = h;
+ }
if (y1 < y0) {
y1 = y0;
}
@@ -4210,31 +5774,24 @@ void Splash::blitImage(SplashBitmap *src, GBool srcAlpha, int xDest, int yDest,
// draw the unclipped region
if (x0 < w && y0 < h && x0 < x1 && y0 < y1) {
- pipeInit(&pipe, xDest + x0, yDest + y0, NULL, pixel,
- (Guchar)splashRound(state->fillAlpha * 255), srcAlpha, gFalse);
+ pipeInit(&pipe, NULL,
+ (Guchar)splashRound(state->fillAlpha * 255),
+ srcAlpha, gFalse);
if (srcAlpha) {
for (y = y0; y < y1; ++y) {
- pipeSetXY(&pipe, xDest + x0, yDest + y);
- ap = src->getAlphaPtr() + y * w + x0;
- for (x = x0; x < x1; ++x) {
- src->getPixel(x, y, pixel);
- pipe.shape = *ap++;
- (this->*pipe.run)(&pipe);
- }
+ (this->*pipe.run)(&pipe, xDest + x0, xDest + x1 - 1, yDest + y,
+ src->getAlphaPtr() + y * w + x0,
+ src->getDataPtr() + y * src->getRowSize() +
+ x0 * bitmapComps);
}
} else {
for (y = y0; y < y1; ++y) {
- pipeSetXY(&pipe, xDest + x0, yDest + y);
- for (x = x0; x < x1; ++x) {
- src->getPixel(x, y, pixel);
- (this->*pipe.run)(&pipe);
- }
+ (this->*pipe.run)(&pipe, xDest + x0, xDest + x1 - 1, yDest + y,
+ NULL,
+ src->getDataPtr() + y * src->getRowSize() +
+ x0 * bitmapComps);
}
}
- updateModX(xDest + x0);
- updateModX(xDest + x1 - 1);
- updateModY(yDest + y0);
- updateModY(yDest + y1 - 1);
}
// draw the clipped regions
@@ -4257,66 +5814,62 @@ void Splash::blitImageClipped(SplashBitmap *src, GBool srcAlpha,
int xSrc, int ySrc, int xDest, int yDest,
int w, int h) {
SplashPipe pipe;
- SplashColor pixel;
- Guchar *ap;
- int x, y;
+ int y;
- if (vectorAntialias) {
- pipeInit(&pipe, xDest, yDest, NULL, pixel,
- (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse);
- drawAAPixelInit();
- if (srcAlpha) {
- for (y = 0; y < h; ++y) {
- ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc;
- for (x = 0; x < w; ++x) {
- src->getPixel(xSrc + x, ySrc + y, pixel);
- pipe.shape = *ap++;
- drawAAPixel(&pipe, xDest + x, yDest + y);
- }
- }
- } else {
- for (y = 0; y < h; ++y) {
- for (x = 0; x < w; ++x) {
- src->getPixel(xSrc + x, ySrc + y, pixel);
- pipe.shape = 255;
- drawAAPixel(&pipe, xDest + x, yDest + y);
- }
+ if (xDest < 0) {
+ xSrc -= xDest;
+ w += xDest;
+ xDest = 0;
+ }
+ if (xDest + w > bitmap->width) {
+ w = bitmap->width - xDest;
+ }
+ if (yDest < 0) {
+ ySrc -= yDest;
+ h += yDest;
+ yDest = 0;
+ }
+ if (yDest + h > bitmap->height) {
+ h = bitmap->height - yDest;
+ }
+ if (w <= 0 || h <= 0) {
+ return;
+ }
+
+ pipeInit(&pipe, NULL,
+ (Guchar)splashRound(state->fillAlpha * 255),
+ gTrue, gFalse);
+ if (srcAlpha) {
+ for (y = 0; y < h; ++y) {
+ memcpy(scanBuf + xDest,
+ src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc,
+ w);
+ if (vectorAntialias) {
+ state->clip->clipSpan(scanBuf, yDest + y, xDest, xDest + w - 1,
+ state->strokeAdjust);
+ } else {
+ state->clip->clipSpanBinary(scanBuf, yDest + y, xDest, xDest + w - 1,
+ state->strokeAdjust);
}
+ (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y,
+ scanBuf + xDest,
+ src->getDataPtr() + (ySrc + y) * src->getRowSize() +
+ xSrc * bitmapComps);
}
} else {
- pipeInit(&pipe, xDest, yDest, NULL, pixel,
- (Guchar)splashRound(state->fillAlpha * 255), srcAlpha, gFalse);
- if (srcAlpha) {
- for (y = 0; y < h; ++y) {
- ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc;
- pipeSetXY(&pipe, xDest, yDest + y);
- for (x = 0; x < w; ++x) {
- if (state->clip->test(xDest + x, yDest + y)) {
- src->getPixel(xSrc + x, ySrc + y, pixel);
- pipe.shape = *ap++;
- (this->*pipe.run)(&pipe);
- updateModX(xDest + x);
- updateModY(yDest + y);
- } else {
- pipeIncX(&pipe);
- ++ap;
- }
- }
- }
- } else {
- for (y = 0; y < h; ++y) {
- pipeSetXY(&pipe, xDest, yDest + y);
- for (x = 0; x < w; ++x) {
- if (state->clip->test(xDest + x, yDest + y)) {
- src->getPixel(xSrc + x, ySrc + y, pixel);
- (this->*pipe.run)(&pipe);
- updateModX(xDest + x);
- updateModY(yDest + y);
- } else {
- pipeIncX(&pipe);
- }
- }
+ for (y = 0; y < h; ++y) {
+ memset(scanBuf + xDest, 0xff, w);
+ if (vectorAntialias) {
+ state->clip->clipSpan(scanBuf, yDest + y, xDest, xDest + w - 1,
+ state->strokeAdjust);
+ } else {
+ state->clip->clipSpanBinary(scanBuf, yDest + y, xDest, xDest + w - 1,
+ state->strokeAdjust);
}
+ (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y,
+ scanBuf + xDest,
+ src->getDataPtr() + (ySrc + y) * src->getRowSize() +
+ xSrc * bitmapComps);
}
}
}
@@ -4325,82 +5878,82 @@ 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;
+ int x0, x1, y0, y1, y, t;
if (src->mode != bitmap->mode) {
return splashErrModeMismatch;
}
- if (src->alpha) {
- pipeInit(&pipe, xDest, yDest, NULL, pixel,
- (Guchar)splashRound(state->fillAlpha * 255), gTrue, nonIsolated);
- if (noClip) {
+ pipeInit(&pipe, NULL,
+ (Guchar)splashRound(state->fillAlpha * 255),
+ !noClip || src->alpha != NULL, nonIsolated);
+ if (noClip) {
+ if (src->alpha) {
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++;
- // this uses shape instead of alpha, which isn't technically
- // correct, but works out the same
- pipe.shape = alpha;
- (this->*pipe.run)(&pipe);
- }
+ // this uses shape instead of alpha, which isn't technically
+ // correct, but works out the same
+ (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y,
+ src->getAlphaPtr() +
+ (ySrc + y) * src->getWidth() + xSrc,
+ src->getDataPtr() + (ySrc + y) * src->getRowSize() +
+ xSrc * bitmapComps);
}
- updateModX(xDest);
- updateModX(xDest + w - 1);
- updateModY(yDest);
- updateModY(yDest + h - 1);
} else {
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 (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 = alpha;
- (this->*pipe.run)(&pipe);
- updateModX(xDest + x);
- updateModY(yDest + y);
- } else {
- pipeIncX(&pipe);
- }
- }
+ (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y,
+ NULL,
+ src->getDataPtr() + (ySrc + y) * src->getRowSize() +
+ xSrc * bitmapComps);
}
}
} else {
- pipeInit(&pipe, xDest, yDest, NULL, pixel,
- (Guchar)splashRound(state->fillAlpha * 255), gFalse, nonIsolated);
- if (noClip) {
- for (y = 0; y < h; ++y) {
- pipeSetXY(&pipe, xDest, yDest + y);
- for (x = 0; x < w; ++x) {
- src->getPixel(xSrc + x, ySrc + y, pixel);
- (this->*pipe.run)(&pipe);
+ x0 = xDest;
+ if ((t = state->clip->getXMinI(state->strokeAdjust)) > x0) {
+ x0 = t;
+ }
+ x1 = xDest + w;
+ if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < x1) {
+ x1 = t;
+ }
+ y0 = yDest;
+ if ((t = state->clip->getYMinI(state->strokeAdjust)) > y0) {
+ y0 = t;
+ }
+ y1 = yDest + h;
+ if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < y1) {
+ y1 = t;
+ }
+ if (x0 < x1 && y0 < y1) {
+ if (src->alpha) {
+ for (y = y0; y < y1; ++y) {
+ memcpy(scanBuf + x0,
+ src->getAlphaPtr() + (ySrc + y - yDest) * src->getWidth() +
+ (xSrc + x0 - xDest),
+ x1 - x0);
+ if (!state->clip->clipSpanBinary(scanBuf, y, x0, x1 - 1,
+ state->strokeAdjust)) {
+ continue;
+ }
+ // this uses shape instead of alpha, which isn't technically
+ // correct, but works out the same
+ (this->*pipe.run)(&pipe, x0, x1 - 1, y,
+ scanBuf + x0,
+ src->getDataPtr() +
+ (ySrc + y - yDest) * src->getRowSize() +
+ (xSrc + x0 - xDest) * bitmapComps);
}
- }
- updateModX(xDest);
- updateModX(xDest + w - 1);
- updateModY(yDest);
- updateModY(yDest + h - 1);
- } else {
- 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 (state->clip->test(xDest + x, yDest + y)) {
- (this->*pipe.run)(&pipe);
- updateModX(xDest + x);
- updateModY(yDest + y);
- } else {
- pipeIncX(&pipe);
+ } else {
+ for (y = y0; y < y1; ++y) {
+ memset(scanBuf + x0, 0xff, x1 - x0);
+ if (!state->clip->clipSpanBinary(scanBuf, y, x0, x1 - 1,
+ state->strokeAdjust)) {
+ continue;
}
+ (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y,
+ scanBuf + x0,
+ src->getDataPtr() +
+ (ySrc + y - yDest) * src->getRowSize() +
+ (xSrc - xDest) * bitmapComps);
}
}
}
@@ -4535,9 +6088,7 @@ SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc,
for (y = 0; y < h; ++y) {
p = &bitmap->data[(yDest + y) * bitmap->rowSize + xDest];
q = &src->data[(ySrc + y) * src->rowSize + xSrc];
- for (x = 0; x < w; ++x) {
- *p++ = *q++;
- }
+ memcpy(p, q, w);
}
break;
case splashModeRGB8:
@@ -4545,11 +6096,7 @@ SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc,
for (y = 0; y < h; ++y) {
p = &bitmap->data[(yDest + y) * bitmap->rowSize + 3 * xDest];
q = &src->data[(ySrc + y) * src->rowSize + 3 * xSrc];
- for (x = 0; x < w; ++x) {
- *p++ = *q++;
- *p++ = *q++;
- *p++ = *q++;
- }
+ memcpy(p, q, 3 * w);
}
break;
#if SPLASH_CMYK
@@ -4557,12 +6104,7 @@ SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc,
for (y = 0; y < h; ++y) {
p = &bitmap->data[(yDest + y) * bitmap->rowSize + 4 * xDest];
q = &src->data[(ySrc + y) * src->rowSize + 4 * xSrc];
- for (x = 0; x < w; ++x) {
- *p++ = *q++;
- *p++ = *q++;
- *p++ = *q++;
- *p++ = *q++;
- }
+ memcpy(p, q, 4 * w);
}
break;
#endif
@@ -4571,9 +6113,7 @@ SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc,
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;
- }
+ memset(q, 0, w);
}
}
@@ -5002,11 +6542,9 @@ 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\n",
+ printf(" %4d: x0=%8.2f y0=%8.2f x1=%8.2f y1=%8.2f count=%d\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 & splashXPathHoriz) ? "H" : " ",
- (path->segs[i].flags & splashXPathVert) ? "V" : " ",
- (path->segs[i].flags & splashXPathFlip) ? "P" : " ");
+ path->segs[i].count);
}
}
diff --git a/splash/Splash.h b/splash/Splash.h
index 30761fb..7409e0c 100644
--- a/splash/Splash.h
+++ b/splash/Splash.h
@@ -2,6 +2,8 @@
//
// Splash.h
//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
//========================================================================
#ifndef SPLASH_H
@@ -97,6 +99,7 @@ public:
SplashClip *getClip();
SplashBitmap *getSoftMask();
GBool getInNonIsolatedGroup();
+ GBool getInKnockoutGroup();
//----- state write
@@ -125,8 +128,9 @@ public:
// NB: uses untransformed coordinates.
SplashError clipToPath(SplashPath *path, GBool eo);
void setSoftMask(SplashBitmap *softMask);
- void setInNonIsolatedGroup(SplashBitmap *alpha0BitmapA,
- int alpha0XA, int alpha0YA);
+ void setInTransparencyGroup(SplashBitmap *groupBackBitmapA,
+ int groupBackXA, int groupBackYA,
+ GBool nonIsolated, GBool knockout);
void setTransfer(Guchar *red, Guchar *green, Guchar *blue, Guchar *gray);
void setOverprintMask(Guint overprintMask);
@@ -172,7 +176,7 @@ public:
// top line.
SplashError fillImageMask(SplashImageMaskSource src, void *srcData,
int w, int h, SplashCoord *mat,
- GBool glyphMode);
+ GBool glyphMode, GBool interpolate);
// Draw an image. This will read <h> lines of <w> pixels from
// <src>, starting with the top line. These pixels are assumed to
@@ -182,16 +186,16 @@ public:
// are supported:
// source target
// ------ ------
- // Mono1 Mono1
// Mono8 Mono1 -- with dithering
// Mono8 Mono8
// RGB8 RGB8
- // BGR8 BGR8
+ // BGR8 RGB8
// CMYK8 CMYK8
// The matrix behaves as for fillImageMask.
SplashError drawImage(SplashImageSource src, void *srcData,
SplashColorMode srcMode, GBool srcAlpha,
- int w, int h, SplashCoord *mat);
+ int w, int h, SplashCoord *mat,
+ GBool interpolate);
// Composite a rectangular region from <src> onto this Splash
// object.
@@ -245,37 +249,53 @@ public:
private:
- void pipeInit(SplashPipe *pipe, int x, int y,
- SplashPattern *pattern, SplashColorPtr cSrc,
+ void pipeInit(SplashPipe *pipe, SplashPattern *pattern,
Guchar aInput, GBool usesShape,
GBool nonIsolatedGroup);
- void pipeRun(SplashPipe *pipe);
- void pipeRunSimpleMono1(SplashPipe *pipe);
- void pipeRunSimpleMono8(SplashPipe *pipe);
- void pipeRunSimpleRGB8(SplashPipe *pipe);
- void pipeRunSimpleBGR8(SplashPipe *pipe);
+ void pipeRun(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr);
+ void pipeRunSimpleMono1(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr);
+ void pipeRunSimpleMono8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr);
+ void pipeRunSimpleRGB8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr);
+ void pipeRunSimpleBGR8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr);
+#if SPLASH_CMYK
+ void pipeRunSimpleCMYK8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr);
+#endif
+ void pipeRunShapeMono1(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr);
+ void pipeRunShapeMono8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr);
+ void pipeRunShapeRGB8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr);
+ void pipeRunShapeBGR8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr);
#if SPLASH_CMYK
- void pipeRunSimpleCMYK8(SplashPipe *pipe);
+ void pipeRunShapeCMYK8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr);
#endif
- void pipeRunAAMono1(SplashPipe *pipe);
- void pipeRunAAMono8(SplashPipe *pipe);
- void pipeRunAARGB8(SplashPipe *pipe);
- void pipeRunAABGR8(SplashPipe *pipe);
+ void pipeRunAAMono1(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr);
+ void pipeRunAAMono8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr);
+ void pipeRunAARGB8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr);
+ void pipeRunAABGR8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr);
#if SPLASH_CMYK
- void pipeRunAACMYK8(SplashPipe *pipe);
+ void pipeRunAACMYK8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr);
#endif
- 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 drawStrokeSpan(SplashPipe *pipe, int x0, int x1, int y, GBool noClip);
void strokeWide(SplashPath *path, SplashCoord w);
SplashPath *flattenPath(SplashPath *path, SplashCoord *matrix,
SplashCoord flatness);
@@ -288,14 +308,23 @@ private:
SplashPath *makeDashedPath(SplashPath *xPath);
SplashError fillWithPattern(SplashPath *path, GBool eo,
SplashPattern *pattern, SplashCoord alpha);
+ SplashPath *tweakFillPath(SplashPath *path);
GBool pathAllOutside(SplashPath *path);
SplashError fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph);
+ void getImageBounds(SplashCoord xyMin, SplashCoord xyMax,
+ int *xyMinI, int *xyMaxI);
+ void upscaleMask(SplashImageMaskSource src, void *srcData,
+ int srcWidth, int srcHeight,
+ SplashCoord *mat, GBool glyphMode,
+ GBool interpolate);
void arbitraryTransformMask(SplashImageMaskSource src, void *srcData,
int srcWidth, int srcHeight,
- SplashCoord *mat, GBool glyphMode);
+ SplashCoord *mat, GBool glyphMode,
+ GBool interpolate);
SplashBitmap *scaleMask(SplashImageMaskSource src, void *srcData,
int srcWidth, int srcHeight,
- int scaledWidth, int scaledHeight);
+ int scaledWidth, int scaledHeight,
+ GBool interpolate);
void scaleMaskYdXd(SplashImageMaskSource src, void *srcData,
int srcWidth, int srcHeight,
int scaledWidth, int scaledHeight,
@@ -312,17 +341,26 @@ private:
int srcWidth, int srcHeight,
int scaledWidth, int scaledHeight,
SplashBitmap *dest);
+ void scaleMaskYuXuI(SplashImageMaskSource src, void *srcData,
+ int srcWidth, int srcHeight,
+ int scaledWidth, int scaledHeight,
+ SplashBitmap *dest);
void blitMask(SplashBitmap *src, int xDest, int yDest,
SplashClipResult clipRes);
+ void upscaleImage(SplashImageSource src, void *srcData,
+ SplashColorMode srcMode, int nComps,
+ GBool srcAlpha, int srcWidth, int srcHeight,
+ SplashCoord *mat, GBool interpolate);
void arbitraryTransformImage(SplashImageSource src, void *srcData,
SplashColorMode srcMode, int nComps,
GBool srcAlpha,
int srcWidth, int srcHeight,
- SplashCoord *mat);
+ SplashCoord *mat, GBool interpolate);
SplashBitmap *scaleImage(SplashImageSource src, void *srcData,
SplashColorMode srcMode, int nComps,
GBool srcAlpha, int srcWidth, int srcHeight,
- int scaledWidth, int scaledHeight);
+ int scaledWidth, int scaledHeight,
+ GBool interpolate);
void scaleImageYdXd(SplashImageSource src, void *srcData,
SplashColorMode srcMode, int nComps,
GBool srcAlpha, int srcWidth, int srcHeight,
@@ -343,8 +381,15 @@ private:
GBool srcAlpha, int srcWidth, int srcHeight,
int scaledWidth, int scaledHeight,
SplashBitmap *dest);
+ void scaleImageYuXuI(SplashImageSource src, void *srcData,
+ SplashColorMode srcMode, int nComps,
+ GBool srcAlpha, int srcWidth, int srcHeight,
+ int scaledWidth, int scaledHeight,
+ SplashBitmap *dest);
void vertFlipImage(SplashBitmap *img, int width, int height,
int nComps);
+ void horizFlipImage(SplashBitmap *img, int width, int height,
+ int nComps);
void blitImage(SplashBitmap *src, GBool srcAlpha, int xDest, int yDest,
SplashClipResult clipRes);
void blitImageClipped(SplashBitmap *src, GBool srcAlpha,
@@ -359,13 +404,13 @@ private:
static int pipeNonIsoGroupCorrection[];
SplashBitmap *bitmap;
+ int bitmapComps;
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
- Guchar aaGamma[splashAASize * splashAASize + 1];
+ Guchar *scanBuf;
+ SplashBitmap // for transparency groups, this is the bitmap
+ *groupBackBitmap; // containing the alpha0/color0 values
+ int groupBackX, groupBackY; // offset within groupBackBitmap
+ Guchar aaGamma[256];
SplashCoord minLineWidth;
int modXMin, modYMin, modXMax, modYMax;
SplashClipResult opClipRes;
diff --git a/splash/SplashBitmap.cc b/splash/SplashBitmap.cc
index 9fc7b04..3606a91 100644
--- a/splash/SplashBitmap.cc
+++ b/splash/SplashBitmap.cc
@@ -2,6 +2,8 @@
//
// SplashBitmap.cc
//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
//========================================================================
#include <aconf.h>
diff --git a/splash/SplashBitmap.h b/splash/SplashBitmap.h
index 78f37d6..3dfc939 100644
--- a/splash/SplashBitmap.h
+++ b/splash/SplashBitmap.h
@@ -2,6 +2,8 @@
//
// SplashBitmap.h
//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
//========================================================================
#ifndef SPLASHBITMAP_H
diff --git a/splash/SplashClip.cc b/splash/SplashClip.cc
index da85771..6865c57 100644
--- a/splash/SplashClip.cc
+++ b/splash/SplashClip.cc
@@ -2,6 +2,8 @@
//
// SplashClip.cc
//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
//========================================================================
#include <aconf.h>
@@ -17,52 +19,52 @@
#include "SplashPath.h"
#include "SplashXPath.h"
#include "SplashXPathScanner.h"
-#include "SplashBitmap.h"
#include "SplashClip.h"
//------------------------------------------------------------------------
-// SplashClip.flags
-//------------------------------------------------------------------------
-#define splashClipEO 0x01 // use even-odd rule
+// Compute x * y / 255, where x and y are in [0, 255].
+static inline Guchar mul255(Guchar x, Guchar y) {
+ int z;
+
+ z = (int)x * (int)y;
+ return (Guchar)((z + (z >> 8) + 0x80) >> 8);
+}
//------------------------------------------------------------------------
// 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 = splashCeil(xMax) - 1;
- yMaxI = splashCeil(yMax) - 1;
+SplashClip::SplashClip(int hardXMinA, int hardYMinA,
+ int hardXMaxA, int hardYMaxA) {
+ int w;
+
+ hardXMin = hardXMinA;
+ hardYMin = hardYMinA;
+ hardXMax = hardXMaxA;
+ hardYMax = hardYMaxA;
+ xMin = hardXMin;
+ yMin = hardYMin;
+ xMax = hardXMax;
+ yMax = hardYMax;
+ intBoundsValid = gFalse;
paths = NULL;
- flags = NULL;
+ eo = NULL;
scanners = NULL;
length = size = 0;
+ if ((w = hardXMax + 1) <= 0) {
+ w = 1;
+ }
+ buf = (Guchar *)gmalloc(w);
}
SplashClip::SplashClip(SplashClip *clip) {
- int yMinAA, yMaxAA;
- int i;
+ int w, i;
- antialias = clip->antialias;
+ hardXMin = clip->hardXMin;
+ hardYMin = clip->hardYMin;
+ hardXMax = clip->hardXMax;
+ hardYMax = clip->hardYMax;
xMin = clip->xMin;
yMin = clip->yMin;
xMax = clip->xMax;
@@ -71,25 +73,23 @@ SplashClip::SplashClip(SplashClip *clip) {
yMinI = clip->yMinI;
xMaxI = clip->xMaxI;
yMaxI = clip->yMaxI;
+ intBoundsValid = clip->intBoundsValid;
+ intBoundsStrokeAdjust = clip->intBoundsStrokeAdjust;
length = clip->length;
size = clip->size;
paths = (SplashXPath **)gmallocn(size, sizeof(SplashXPath *));
- flags = (Guchar *)gmallocn(size, sizeof(Guchar));
+ eo = (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];
- if (antialias) {
- yMinAA = yMinI * splashAASize;
- yMaxAA = (yMaxI + 1) * splashAASize - 1;
- } else {
- yMinAA = yMinI;
- yMaxAA = yMaxI;
- }
- scanners[i] = new SplashXPathScanner(paths[i], flags[i] & splashClipEO,
- yMinAA, yMaxAA);
+ eo[i] = clip->eo[i];
+ scanners[i] = new SplashXPathScanner(paths[i], eo[i], yMinI, yMaxI);
+ }
+ if ((w = splashCeil(xMax)) <= 0) {
+ w = 1;
}
+ buf = (Guchar *)gmalloc(w);
}
SplashClip::~SplashClip() {
@@ -100,8 +100,9 @@ SplashClip::~SplashClip() {
delete scanners[i];
}
gfree(paths);
- gfree(flags);
+ gfree(eo);
gfree(scanners);
+ gfree(buf);
}
void SplashClip::grow(int nPaths) {
@@ -113,7 +114,7 @@ void SplashClip::grow(int nPaths) {
size *= 2;
}
paths = (SplashXPath **)greallocn(paths, size, sizeof(SplashXPath *));
- flags = (Guchar *)greallocn(flags, size, sizeof(Guchar));
+ eo = (Guchar *)greallocn(eo, size, sizeof(Guchar));
scanners = (SplashXPathScanner **)
greallocn(scanners, size, sizeof(SplashXPathScanner *));
}
@@ -121,17 +122,18 @@ void SplashClip::grow(int nPaths) {
void SplashClip::resetToRect(SplashCoord x0, SplashCoord y0,
SplashCoord x1, SplashCoord y1) {
- int i;
+ int w, i;
for (i = 0; i < length; ++i) {
delete paths[i];
delete scanners[i];
}
gfree(paths);
- gfree(flags);
+ gfree(eo);
gfree(scanners);
+ gfree(buf);
paths = NULL;
- flags = NULL;
+ eo = NULL;
scanners = NULL;
length = size = 0;
@@ -149,10 +151,11 @@ void SplashClip::resetToRect(SplashCoord x0, SplashCoord y0,
yMin = y1;
yMax = y0;
}
- xMinI = splashFloor(xMin);
- yMinI = splashFloor(yMin);
- xMaxI = splashCeil(xMax) - 1;
- yMaxI = splashCeil(yMax) - 1;
+ intBoundsValid = gFalse;
+ if ((w = splashCeil(xMax)) <= 0) {
+ w = 1;
+ }
+ buf = (Guchar *)gmalloc(w);
}
SplashError SplashClip::clipToRect(SplashCoord x0, SplashCoord y0,
@@ -160,240 +163,358 @@ SplashError SplashClip::clipToRect(SplashCoord x0, SplashCoord y0,
if (x0 < x1) {
if (x0 > xMin) {
xMin = x0;
- xMinI = splashFloor(xMin);
+ intBoundsValid = gFalse;
}
if (x1 < xMax) {
xMax = x1;
- xMaxI = splashCeil(xMax) - 1;
+ intBoundsValid = gFalse;
}
} else {
if (x1 > xMin) {
xMin = x1;
- xMinI = splashFloor(xMin);
+ intBoundsValid = gFalse;
}
if (x0 < xMax) {
xMax = x0;
- xMaxI = splashCeil(xMax) - 1;
+ intBoundsValid = gFalse;
}
}
if (y0 < y1) {
if (y0 > yMin) {
yMin = y0;
- yMinI = splashFloor(yMin);
+ intBoundsValid = gFalse;
}
if (y1 < yMax) {
yMax = y1;
- yMaxI = splashCeil(yMax) - 1;
+ intBoundsValid = gFalse;
}
} else {
if (y1 > yMin) {
yMin = y1;
- yMinI = splashFloor(yMin);
+ intBoundsValid = gFalse;
}
if (y0 < yMax) {
yMax = y0;
- yMaxI = splashCeil(yMax) - 1;
+ intBoundsValid = gFalse;
}
}
return splashOk;
}
SplashError SplashClip::clipToPath(SplashPath *path, SplashCoord *matrix,
- SplashCoord flatness, GBool eo) {
+ SplashCoord flatness, GBool eoA) {
SplashXPath *xPath;
- int yMinAA, yMaxAA;
+ SplashCoord t;
xPath = new SplashXPath(path, matrix, flatness, gTrue);
// check for an empty path
if (xPath->length == 0) {
- xMax = xMin - 1;
- yMax = yMin - 1;
- xMaxI = splashCeil(xMax) - 1;
- yMaxI = splashCeil(yMax) - 1;
+ xMin = yMin = 1;
+ xMax = yMax = 0;
+ intBoundsValid = gFalse;
delete xPath;
+ return splashOk;
+ }
// 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);
+ if (xPath->length == 4 &&
+ xPath->segs[0].y0 == xPath->segs[0].y1 &&
+ xPath->segs[1].x0 == xPath->segs[1].x1 &&
+ xPath->segs[2].x0 == xPath->segs[2].x1 &&
+ xPath->segs[3].y0 == xPath->segs[3].y1) {
+ clipToRect(xPath->segs[1].x0, xPath->segs[0].y0,
+ xPath->segs[2].x0, xPath->segs[3].y0);
+ delete xPath;
+ return splashOk;
+ }
+ if (xPath->length == 4 &&
+ xPath->segs[0].x0 == xPath->segs[0].x1 &&
+ xPath->segs[1].y0 == xPath->segs[1].y1 &&
+ xPath->segs[2].x0 == xPath->segs[2].x1 &&
+ xPath->segs[3].y0 == xPath->segs[3].y1) {
+ clipToRect(xPath->segs[0].x0, xPath->segs[1].y0,
+ xPath->segs[2].x0, xPath->segs[3].y0);
delete xPath;
+ return splashOk;
+ }
+ if (xPath->length == 4 &&
+ xPath->segs[0].x0 == xPath->segs[0].x1 &&
+ xPath->segs[1].x0 == xPath->segs[1].x1 &&
+ xPath->segs[2].y0 == xPath->segs[2].y1 &&
+ xPath->segs[3].y0 == xPath->segs[3].y1) {
+ clipToRect(xPath->segs[0].x0, xPath->segs[2].y0,
+ xPath->segs[1].x0, xPath->segs[3].y0);
+ delete xPath;
+ return splashOk;
+ }
+ grow(1);
+ paths[length] = xPath;
+ eo[length] = (Guchar)eoA;
+ if ((t = xPath->getXMin()) > xMin) {
+ xMin = t;
+ }
+ if ((t = xPath->getYMin()) > yMin) {
+ yMin = t;
+ }
+ if ((t = xPath->getXMax() + 1) < xMax) {
+ xMax = t;
+ }
+ if ((t = xPath->getYMax() + 1) < yMax) {
+ yMax = t;
+ }
+ intBoundsValid = gFalse;
+ scanners[length] = new SplashXPathScanner(xPath, eoA, splashFloor(yMin),
+ splashCeil(yMax) - 1);
+ ++length;
+
+ return splashOk;
+}
+
+SplashClipResult SplashClip::testRect(int rectXMin, int rectYMin,
+ int rectXMax, int rectYMax,
+ GBool strokeAdjust) {
+ // In general, this function tests the rectangle:
+ // x = [rectXMin, rectXMax + 1) (note: coords are ints)
+ // y = [rectYMin, rectYMax + 1)
+ // against the clipping region:
+ // x = [xMin, xMax) (note: coords are fp)
+ // y = [yMin, yMax)
+
+ if (strokeAdjust && length == 0) {
+ // special case for stroke adjustment with a simple clipping
+ // rectangle -- the clipping region is:
+ // x = [xMinI, xMaxI + 1)
+ // y = [yMinI, yMaxI + 1)
+ updateIntBounds(strokeAdjust);
+ if (xMinI > xMaxI || yMinI > yMaxI) {
+ return splashClipAllOutside;
+ }
+ if (rectXMax + 1 <= xMinI ||
+ rectXMin >= xMaxI + 1 ||
+ rectYMax + 1 <= yMinI ||
+ rectYMin >= yMaxI + 1) {
+ return splashClipAllOutside;
+ }
+ if (rectXMin >= xMinI &&
+ rectXMax <= xMaxI &&
+ rectYMin >= yMinI &&
+ rectYMax <= yMaxI) {
+ return splashClipAllInside;
+ }
} else {
- grow(1);
- if (antialias) {
- xPath->aaScale();
+ if (xMin >= xMax || yMin >= yMax) {
+ return splashClipAllOutside;
}
- xPath->sort();
- paths[length] = xPath;
- flags[length] = eo ? splashClipEO : 0;
- if (antialias) {
- yMinAA = yMinI * splashAASize;
- yMaxAA = (yMaxI + 1) * splashAASize - 1;
- } else {
- yMinAA = yMinI;
- yMaxAA = yMaxI;
+ if ((SplashCoord)(rectXMax + 1) <= xMin ||
+ (SplashCoord)rectXMin >= xMax ||
+ (SplashCoord)(rectYMax + 1) <= yMin ||
+ (SplashCoord)rectYMin >= yMax) {
+ return splashClipAllOutside;
+ }
+ if (length == 0 &&
+ (SplashCoord)rectXMin >= xMin &&
+ (SplashCoord)(rectXMax + 1) <= xMax &&
+ (SplashCoord)rectYMin >= yMin &&
+ (SplashCoord)(rectYMax + 1) <= yMax) {
+ return splashClipAllInside;
}
- scanners[length] = new SplashXPathScanner(xPath, eo, yMinAA, yMaxAA);
- ++length;
}
-
- return splashOk;
+ return splashClipPartial;
}
-GBool SplashClip::test(int x, int y) {
- int i;
+void SplashClip::clipSpan(Guchar *line, int y, int x0, int x1,
+ GBool strokeAdjust) {
+ SplashCoord d;
+ int x0a, x1a, x, i;
- // check the rectangle
- if (x < xMinI || x > xMaxI || y < yMinI || y > yMaxI) {
- return gFalse;
+ updateIntBounds(strokeAdjust);
+
+ //--- clip to the integer rectangle
+
+ if (y < yMinI || y > yMaxI ||
+ x1 < xMinI || x0 > xMaxI) {
+ memset(line + x0, 0, x1 - x0 + 1);
+ return;
+ }
+
+ if (x0 > xMinI) {
+ x0a = x0;
+ } else {
+ x0a = xMinI;
+ memset(line + x0, 0, x0a - x0);
+ }
+
+ if (x1 < xMaxI) {
+ x1a = x1;
+ } else {
+ x1a = xMaxI;
+ memset(line + x1a + 1, 0, x1 - x1a);
+ }
+
+ if (x0a > x1a) {
+ return;
}
- // check the paths
- if (antialias) {
- for (i = 0; i < length; ++i) {
- if (!scanners[i]->test(x * splashAASize, y * splashAASize)) {
- return gFalse;
+ //--- clip to the floating point rectangle
+ // (if stroke adjustment is disabled)
+
+ if (!strokeAdjust) {
+
+ // clip left edge (xMin)
+ if (x0a == xMinI) {
+ d = (SplashCoord)(xMinI + 1) - xMin;
+ line[x0a] = (Guchar)(int)((SplashCoord)line[x0a] * d);
+ }
+
+ // clip right edge (xMax)
+ if (x1a == xMaxI) {
+ d = xMax - (SplashCoord)xMaxI;
+ line[x1a] = (Guchar)(int)((SplashCoord)line[x1a] * d);
+ }
+
+ // clip top edge (yMin)
+ if (y == yMinI) {
+ d = (SplashCoord)(yMinI + 1) - yMin;
+ for (x = x0a; x <= x1a; ++x) {
+ line[x] = (Guchar)(int)((SplashCoord)line[x] * d);
}
}
- } else {
- for (i = 0; i < length; ++i) {
- if (!scanners[i]->test(x, y)) {
- return gFalse;
+
+ // clip bottom edge (yMax)
+ if (y == yMaxI) {
+ d = yMax - (SplashCoord)yMaxI;
+ for (x = x0a; x <= x1a; ++x) {
+ line[x] = (Guchar)(int)((SplashCoord)line[x] * d);
}
}
}
- 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 (length == 0) {
+ return;
}
- if ((SplashCoord)rectXMin >= xMin && (SplashCoord)(rectXMax + 1) <= xMax &&
- (SplashCoord)rectYMin >= yMin && (SplashCoord)(rectYMax + 1) <= yMax &&
- length == 0) {
- return splashClipAllInside;
+
+ //--- clip to the paths
+
+ for (i = 0; i < length; ++i) {
+ scanners[i]->getSpan(buf, y, x0a, x1a);
+ for (x = x0a; x <= x1a; ++x) {
+ line[x] = mul255(line[x], buf[x]);
+ }
}
- return splashClipPartial;
}
-SplashClipResult SplashClip::testSpan(int spanXMin, int spanXMax, int spanY) {
- int i;
+GBool SplashClip::clipSpanBinary(Guchar *line, int y, int x0, int x1,
+ GBool strokeAdjust) {
+ int x0a, x1a, x0b, x1b, x, i;
+ Guchar any;
- // 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;
- }
+ updateIntBounds(strokeAdjust);
+
+ if (y < yMinI || y > yMaxI ||
+ x1 < xMinI || x0 > xMaxI) {
+ if (x0 <= x1) {
+ memset(line + x0, 0, x1 - x0 + 1);
}
+ return gFalse;
+ }
+
+ if (x0 > xMinI) {
+ x0a = x0;
+ } else {
+ x0a = xMinI;
+ memset(line + x0, 0, x0a - x0);
+ }
+
+ if (x1 < xMaxI) {
+ x1a = x1;
} else {
- for (i = 0; i < length; ++i) {
- if (!scanners[i]->testSpan(spanXMin, spanXMax, spanY)) {
- return splashClipPartial;
+ x1a = xMaxI;
+ memset(line + x1a + 1, 0, x1 - x1a);
+ }
+
+ if (x0a > x1a) {
+ return gFalse;
+ }
+
+ if (length == 0) {
+ for (x = x0a; x <= x1a; ++x) {
+ if (line[x]) {
+ return gTrue;
}
}
+ return gFalse;
}
- 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);
- }
+ any = 0;
+ for (i = 0; i < length; ++i) {
+ scanners[i]->getSpanBinary(buf, y, x0a, x1a);
+ for (x0b = x0a; x0b <= x1a && !buf[x0b]; ++x0b) ;
+ if (x0a < x0b) {
+ memset(line + x0a, 0, x0b - x0a);
}
- *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;
- }
+ for (x1b = x1a; x1b >= x0b && !buf[x1b]; --x1b) ;
+ if (x1b < x1a) {
+ memset(line + x1b + 1, 0, x1a - x1b);
+ }
+ for (x = x0b; x <= x1b; ++x) {
+ line[x] &= buf[x];
+ any |= line[x];
}
- *x1 = splashFloor(xMax);
}
- // check the paths
- for (i = 0; i < length; ++i) {
- scanners[i]->clipAALine(aaBuf, x0, x1, y);
+ return any != 0;
+}
+
+int SplashClip::getXMinI(GBool strokeAdjust) {
+ updateIntBounds(strokeAdjust);
+ return xMinI;
+}
+
+int SplashClip::getXMaxI(GBool strokeAdjust) {
+ updateIntBounds(strokeAdjust);
+ return xMaxI;
+}
+
+int SplashClip::getYMinI(GBool strokeAdjust) {
+ updateIntBounds(strokeAdjust);
+ return yMinI;
+}
+
+int SplashClip::getYMaxI(GBool strokeAdjust) {
+ updateIntBounds(strokeAdjust);
+ return yMaxI;
+}
+
+void SplashClip::updateIntBounds(GBool strokeAdjust) {
+ if (intBoundsValid && strokeAdjust == intBoundsStrokeAdjust) {
+ return;
+ }
+ if (strokeAdjust && length == 0) {
+ splashStrokeAdjust(xMin, xMax, &xMinI, &xMaxI);
+ splashStrokeAdjust(yMin, yMax, &yMinI, &yMaxI);
+ } else {
+ xMinI = splashFloor(xMin);
+ yMinI = splashFloor(yMin);
+ xMaxI = splashCeil(xMax);
+ yMaxI = splashCeil(yMax);
+ }
+ if (xMinI < hardXMin) {
+ xMinI = hardXMin;
+ }
+ if (yMinI < hardYMin) {
+ yMinI = hardYMin;
+ }
+ if (xMaxI > hardXMax) {
+ xMaxI = hardXMax;
+ }
+ if (yMaxI > hardYMax) {
+ yMaxI = hardYMax;
}
+ // the clipping code uses [xMinI, xMaxI] instead of [xMinI, xMaxI)
+ --xMaxI;
+ --yMaxI;
+ intBoundsValid = gTrue;
+ intBoundsStrokeAdjust = strokeAdjust;
}
diff --git a/splash/SplashClip.h b/splash/SplashClip.h
index 6f580f8..4298622 100644
--- a/splash/SplashClip.h
+++ b/splash/SplashClip.h
@@ -2,6 +2,8 @@
//
// SplashClip.h
//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
//========================================================================
#ifndef SPLASHCLIP_H
@@ -37,9 +39,8 @@ class SplashClip {
public:
// Create a clip, for the given rectangle.
- SplashClip(SplashCoord x0, SplashCoord y0,
- SplashCoord x1, SplashCoord y1,
- GBool antialiasA);
+ SplashClip(int hardXMinA, int hardYMinA,
+ int hardXMaxA, int hardYMaxA);
// Copy a clip.
SplashClip *copy() { return new SplashClip(this); }
@@ -56,10 +57,7 @@ public:
// 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);
+ SplashCoord flatness, GBool eoA);
// Tests a rectangle against the clipping region. Returns one of:
// - splashClipAllInside if the entire rectangle is inside the
@@ -71,15 +69,19 @@ public:
// - splashClipPartial if the rectangle is part inside and part
// outside the clipping region
SplashClipResult testRect(int rectXMin, int rectYMin,
- int rectXMax, int rectYMax);
+ int rectXMax, int rectYMax,
+ GBool strokeAdjust);
- // Similar to testRect, but tests a horizontal span.
- SplashClipResult testSpan(int spanXMin, int spanXMax, int spanY);
+ // Clip a scan line. Modifies line[] by multiplying with clipping
+ // shape values for one scan line: ([x0, x1], y).
+ void clipSpan(Guchar *line, int y, int x0, int x1,
+ GBool strokeAdjust);
- // 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);
+ // Like clipSpan(), but uses the values 0 and 255 only.
+ // Returns true if there are any non-zero values in the result
+ // (i.e., returns false if the entire line is clipped out).
+ GBool clipSpanBinary(Guchar *line, int y, int x0, int x1,
+ GBool strokeAdjust);
// Get the rectangle part of the clip region.
SplashCoord getXMin() { return xMin; }
@@ -88,10 +90,10 @@ public:
SplashCoord getYMax() { return yMax; }
// 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; }
+ int getXMinI(GBool strokeAdjust);
+ int getXMaxI(GBool strokeAdjust);
+ int getYMinI(GBool strokeAdjust);
+ int getYMaxI(GBool strokeAdjust);
// Get the number of arbitrary paths used by the clip region.
int getNumPaths() { return length; }
@@ -100,14 +102,25 @@ private:
SplashClip(SplashClip *clip);
void grow(int nPaths);
+ void updateIntBounds(GBool strokeAdjust);
+
+ int hardXMin, hardYMin, // coordinates cannot fall outside of
+ hardXMax, hardYMax; // [hardXMin, hardXMax), [hardYMin, hardYMax)
+
+ SplashCoord xMin, yMin, // current clip bounding rectangle
+ xMax, yMax; // (these coordinates may be adjusted if
+ // stroke adjustment is enabled)
- GBool antialias;
- SplashCoord xMin, yMin, xMax, yMax;
int xMinI, yMinI, xMaxI, yMaxI;
+ GBool intBoundsValid; // true if xMinI, etc. are valid
+ GBool intBoundsStrokeAdjust; // value of strokeAdjust used to compute
+ // xMinI, etc.
+
SplashXPath **paths;
- Guchar *flags;
+ Guchar *eo;
SplashXPathScanner **scanners;
int length, size;
+ Guchar *buf;
};
#endif
diff --git a/splash/SplashErrorCodes.h b/splash/SplashErrorCodes.h
index 2a70d4b..ebe6091 100644
--- a/splash/SplashErrorCodes.h
+++ b/splash/SplashErrorCodes.h
@@ -2,6 +2,8 @@
//
// SplashErrorCodes.h
//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
//========================================================================
#ifndef SPLASHERRORCODES_H
diff --git a/splash/SplashFTFont.cc b/splash/SplashFTFont.cc
index 218f13e..6fc6b94 100644
--- a/splash/SplashFTFont.cc
+++ b/splash/SplashFTFont.cc
@@ -2,6 +2,8 @@
//
// SplashFTFont.cc
//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
//========================================================================
#include <aconf.h>
@@ -242,23 +244,24 @@ GBool SplashFTFont::makeGlyph(int c, int xFrac, int yFrac,
return gFalse;
}
- flags = 0;
- if (aa) {
- flags |= FT_LOAD_NO_BITMAP;
- }
+ // Set up the load flags:
+ // * disable bitmaps because they look ugly when scaled, rotated,
+ // etc.
+ // * disable autohinting because it can fail badly with font subsets
+ // that use invalid glyph names (the FreeType autohinter depends
+ // on the glyph name to figure out how to autohint the glyph)
+ // * but enable light autohinting for Type 1 fonts because regular
+ // hinting looks pretty bad, and the invalid glyph name issue
+ // seems to be very rare (Type 1 fonts are mostly used for
+ // substitution, in which case the full font is being used, which
+ // means we have the glyph names)
+ flags = FT_LOAD_NO_BITMAP;
if (ff->engine->flags & splashFTNoHinting) {
flags |= FT_LOAD_NO_HINTING;
- } else if (ff->trueType) {
- // 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 (aa) {
- flags |= FT_LOAD_NO_AUTOHINT;
- }
- } else if (ff->type1) {
- // Type 1 fonts seem to look better with 'light' hinting mode
+ } else if (ff->useLightHinting) {
flags |= FT_LOAD_TARGET_LIGHT;
+ } else {
+ flags |= FT_LOAD_NO_AUTOHINT;
}
if (FT_Load_Glyph(ff->face, gid, flags)) {
return gFalse;
diff --git a/splash/SplashFTFont.h b/splash/SplashFTFont.h
index 8e31d14..560b74c 100644
--- a/splash/SplashFTFont.h
+++ b/splash/SplashFTFont.h
@@ -2,6 +2,8 @@
//
// SplashFTFont.h
//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
//========================================================================
#ifndef SPLASHFTFONT_H
diff --git a/splash/SplashFTFontEngine.cc b/splash/SplashFTFontEngine.cc
index 9a4533a..3ba033c 100644
--- a/splash/SplashFTFontEngine.cc
+++ b/splash/SplashFTFontEngine.cc
@@ -2,6 +2,8 @@
//
// SplashFTFontEngine.cc
//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
//========================================================================
#include <aconf.h>
@@ -13,7 +15,7 @@
#endif
#include <stdio.h>
-#ifndef WIN32
+#ifndef _WIN32
# include <unistd.h>
#endif
#include "gmem.h"
@@ -23,6 +25,10 @@
#include "FoFiType1C.h"
#include "SplashFTFontFile.h"
#include "SplashFTFontEngine.h"
+#include FT_MODULE_H
+#ifdef FT_CFF_DRIVER_H
+# include FT_CFF_DRIVER_H
+#endif
#ifdef VMS
#if (__VMS_VER < 70000000)
@@ -36,6 +42,12 @@ static void fileWrite(void *stream, const char *data, int len) {
fwrite(data, 1, len, (FILE *)stream);
}
+#if LOAD_FONTS_FROM_MEM
+static void gstringWrite(void *stream, const char *data, int len) {
+ ((GString *)stream)->append(data, len);
+}
+#endif
+
//------------------------------------------------------------------------
// SplashFTFontEngine
//------------------------------------------------------------------------
@@ -68,29 +80,117 @@ SplashFTFontEngine::~SplashFTFontEngine() {
}
SplashFontFile *SplashFTFontEngine::loadType1Font(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf,
+#else
char *fileName,
GBool deleteFile,
+#endif
const char **enc) {
- return SplashFTFontFile::loadType1Font(this, idA, fileName, deleteFile, enc);
+ return SplashFTFontFile::loadType1Font(this, idA,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
+ fileName, deleteFile,
+#endif
+ enc, gTrue);
}
SplashFontFile *SplashFTFontEngine::loadType1CFont(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf,
+#else
char *fileName,
GBool deleteFile,
+#endif
const char **enc) {
- return SplashFTFontFile::loadType1Font(this, idA, fileName, deleteFile, enc);
+ return SplashFTFontFile::loadType1Font(this, idA,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
+ fileName, deleteFile,
+#endif
+ enc, gFalse);
}
SplashFontFile *SplashFTFontEngine::loadOpenTypeT1CFont(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf,
+#else
char *fileName,
GBool deleteFile,
+#endif
const char **enc) {
- return SplashFTFontFile::loadType1Font(this, idA, fileName, deleteFile, enc);
+ FoFiTrueType *ff;
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf2;
+#else
+ GString *tmpFileName;
+ FILE *tmpFile;
+#endif
+ SplashFontFile *ret;
+
+#if LOAD_FONTS_FROM_MEM
+ if (!(ff = FoFiTrueType::make(fontBuf->getCString(), fontBuf->getLength(),
+ 0, gTrue))) {
+#else
+ if (!(ff = FoFiTrueType::load(fileName, 0, gTrue))) {
+#endif
+ return NULL;
+ }
+ if (ff->isHeadlessCFF()) {
+#if LOAD_FONTS_FROM_MEM
+ fontBuf2 = new GString();
+ ff->convertToType1(NULL, enc, gFalse, &gstringWrite, fontBuf2);
+ delete ff;
+ ret = SplashFTFontFile::loadType1Font(this, idA, fontBuf2, enc,
+ gFalse);
+ if (ret) {
+ delete fontBuf;
+ } else {
+ delete fontBuf2;
+ }
+#else
+ tmpFileName = NULL;
+ if (!openTempFile(&tmpFileName, &tmpFile, "wb", NULL)) {
+ delete ff;
+ return NULL;
+ }
+ ff->convertToType1(NULL, enc, gFalse, &fileWrite, tmpFile);
+ delete ff;
+ fclose(tmpFile);
+ ret = SplashFTFontFile::loadType1Font(this, idA, tmpFileName->getCString(),
+ gTrue, enc, gFalse);
+ if (ret) {
+ if (deleteFile) {
+ unlink(fileName);
+ }
+ } else {
+ unlink(tmpFileName->getCString());
+ }
+ delete tmpFileName;
+#endif
+ } else {
+ delete ff;
+ ret = SplashFTFontFile::loadType1Font(this, idA,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
+ fileName, deleteFile,
+#endif
+ enc, gFalse);
+ }
+ return ret;
}
SplashFontFile *SplashFTFontEngine::loadCIDFont(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf
+#else
char *fileName,
- GBool deleteFile) {
+ GBool deleteFile
+#endif
+ ) {
FoFiType1C *ff;
int *cidToGIDMap;
int nCIDs;
@@ -100,14 +200,24 @@ SplashFontFile *SplashFTFontEngine::loadCIDFont(SplashFontFileID *idA,
if (useCIDs) {
cidToGIDMap = NULL;
nCIDs = 0;
+#if LOAD_FONTS_FROM_MEM
+ } else if ((ff = FoFiType1C::make(fontBuf->getCString(),
+ fontBuf->getLength()))) {
+#else
} else if ((ff = FoFiType1C::load(fileName))) {
+#endif
cidToGIDMap = ff->getCIDToGIDMap(&nCIDs);
delete ff;
} else {
cidToGIDMap = NULL;
nCIDs = 0;
}
- ret = SplashFTFontFile::loadCIDFont(this, idA, fileName, deleteFile,
+ ret = SplashFTFontFile::loadCIDFont(this, idA,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
+ fileName, deleteFile,
+#endif
cidToGIDMap, nCIDs);
if (!ret) {
gfree(cidToGIDMap);
@@ -116,32 +226,90 @@ SplashFontFile *SplashFTFontEngine::loadCIDFont(SplashFontFileID *idA,
}
SplashFontFile *SplashFTFontEngine::loadOpenTypeCFFFont(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf,
+#else
char *fileName,
GBool deleteFile,
+#endif
int *codeToGID,
int codeToGIDLen) {
FoFiTrueType *ff;
- GBool isCID;
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf2;
+#else
+ GString *tmpFileName;
+ FILE *tmpFile;
+#endif
+ char *cffStart;
+ int cffLength;
int *cidToGIDMap;
int nCIDs;
SplashFontFile *ret;
+#if LOAD_FONTS_FROM_MEM
+ if (!(ff = FoFiTrueType::make(fontBuf->getCString(), fontBuf->getLength(),
+ 0, gTrue))) {
+#else
+ if (!(ff = FoFiTrueType::load(fileName, 0, gTrue))) {
+#endif
+ return NULL;
+ }
cidToGIDMap = NULL;
nCIDs = 0;
- isCID = gFalse;
- if (!codeToGID) {
+ if (ff->isHeadlessCFF()) {
+ if (!ff->getCFFBlock(&cffStart, &cffLength)) {
+ return NULL;
+ }
+#if LOAD_FONTS_FROM_MEM
+ fontBuf2 = new GString(cffStart, cffLength);
+ if (!useCIDs) {
+ cidToGIDMap = ff->getCIDToGIDMap(&nCIDs);
+ }
+ ret = SplashFTFontFile::loadCIDFont(this, idA, fontBuf2,
+ cidToGIDMap, nCIDs);
+ if (ret) {
+ delete fontBuf;
+ } else {
+ delete fontBuf2;
+ }
+#else
+ tmpFileName = NULL;
+ if (!openTempFile(&tmpFileName, &tmpFile, "wb", NULL)) {
+ delete ff;
+ return NULL;
+ }
+ fwrite(cffStart, 1, cffLength, tmpFile);
+ fclose(tmpFile);
if (!useCIDs) {
- if ((ff = FoFiTrueType::load(fileName))) {
- if (ff->isOpenTypeCFF()) {
- cidToGIDMap = ff->getCIDToGIDMap(&nCIDs);
- }
- delete ff;
+ cidToGIDMap = ff->getCIDToGIDMap(&nCIDs);
+ }
+ ret = SplashFTFontFile::loadCIDFont(this, idA,
+ tmpFileName->getCString(), gTrue,
+ cidToGIDMap, nCIDs);
+ if (ret) {
+ if (deleteFile) {
+ unlink(fileName);
}
+ } else {
+ unlink(tmpFileName->getCString());
+ }
+ delete tmpFileName;
+#endif
+ } else {
+ if (!codeToGID && !useCIDs && ff->isOpenTypeCFF()) {
+ cidToGIDMap = ff->getCIDToGIDMap(&nCIDs);
}
+ ret = SplashFTFontFile::loadCIDFont(this, idA,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
+ fileName, deleteFile,
+#endif
+ codeToGID ? codeToGID : cidToGIDMap,
+ codeToGID ? codeToGIDLen : nCIDs);
}
- ret = SplashFTFontFile::loadCIDFont(this, idA, fileName, deleteFile,
- codeToGID ? codeToGID : cidToGIDMap,
- codeToGID ? codeToGIDLen : nCIDs);
+ delete ff;
if (!ret) {
gfree(cidToGIDMap);
}
@@ -149,31 +317,59 @@ SplashFontFile *SplashFTFontEngine::loadOpenTypeCFFFont(SplashFontFileID *idA,
}
SplashFontFile *SplashFTFontEngine::loadTrueTypeFont(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf,
+#else
char *fileName,
- int fontNum,
GBool deleteFile,
+#endif
+ int fontNum,
int *codeToGID,
int codeToGIDLen) {
FoFiTrueType *ff;
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf2;
+#else
GString *tmpFileName;
FILE *tmpFile;
+#endif
SplashFontFile *ret;
- //~ this should use fontNum to load the correct font
- if (!(ff = FoFiTrueType::load(fileName))) {
+#if LOAD_FONTS_FROM_MEM
+ if (!(ff = FoFiTrueType::make(fontBuf->getCString(), fontBuf->getLength(),
+ fontNum))) {
+#else
+ if (!(ff = FoFiTrueType::load(fileName, fontNum))) {
+#endif
return NULL;
}
+#if LOAD_FONTS_FROM_MEM
+ fontBuf2 = new GString;
+ ff->writeTTF(&gstringWrite, fontBuf2);
+#else
tmpFileName = NULL;
if (!openTempFile(&tmpFileName, &tmpFile, "wb", NULL)) {
delete ff;
return NULL;
}
ff->writeTTF(&fileWrite, tmpFile);
- delete ff;
fclose(tmpFile);
+#endif
+ delete ff;
ret = SplashFTFontFile::loadTrueTypeFont(this, idA,
- tmpFileName->getCString(), fontNum,
- gTrue, codeToGID, codeToGIDLen);
+#if LOAD_FONTS_FROM_MEM
+ fontBuf2,
+#else
+ tmpFileName->getCString(), gTrue,
+#endif
+ 0, codeToGID, codeToGIDLen);
+#if LOAD_FONTS_FROM_MEM
+ if (ret) {
+ delete fontBuf;
+ } else {
+ delete fontBuf2;
+ }
+#else
if (ret) {
if (deleteFile) {
unlink(fileName);
@@ -182,6 +378,7 @@ SplashFontFile *SplashFTFontEngine::loadTrueTypeFont(SplashFontFileID *idA,
unlink(tmpFileName->getCString());
}
delete tmpFileName;
+#endif
return ret;
}
diff --git a/splash/SplashFTFontEngine.h b/splash/SplashFTFontEngine.h
index 900bff2..37e8820 100644
--- a/splash/SplashFTFontEngine.h
+++ b/splash/SplashFTFontEngine.h
@@ -2,6 +2,8 @@
//
// SplashFTFontEngine.h
//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
//========================================================================
#ifndef SPLASHFTFONTENGINE_H
@@ -18,6 +20,7 @@
#include <ft2build.h>
#include FT_FREETYPE_H
#include "gtypes.h"
+class GString;
class SplashFontFile;
class SplashFontFileID;
@@ -34,19 +37,48 @@ public:
~SplashFTFontEngine();
// Load fonts.
- SplashFontFile *loadType1Font(SplashFontFileID *idA, char *fileName,
- GBool deleteFile, const char **enc);
- SplashFontFile *loadType1CFont(SplashFontFileID *idA, char *fileName,
- GBool deleteFile, const char **enc);
- SplashFontFile *loadOpenTypeT1CFont(SplashFontFileID *idA, char *fileName,
- GBool deleteFile, const char **enc);
- SplashFontFile *loadCIDFont(SplashFontFileID *idA, char *fileName,
- GBool deleteFile);
- SplashFontFile *loadOpenTypeCFFFont(SplashFontFileID *idA, char *fileName,
- GBool deleteFile,
+ SplashFontFile *loadType1Font(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf,
+#else
+ char *fileName, GBool deleteFile,
+#endif
+ const char **enc);
+ SplashFontFile *loadType1CFont(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf,
+#else
+ char *fileName, GBool deleteFile,
+#endif
+ const char **enc);
+ SplashFontFile *loadOpenTypeT1CFont(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf,
+#else
+ char *fileName, GBool deleteFile,
+#endif
+ const char **enc);
+ SplashFontFile *loadCIDFont(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf
+#else
+ char *fileName, GBool deleteFile
+#endif
+ );
+ SplashFontFile *loadOpenTypeCFFFont(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf,
+#else
+ char *fileName, GBool deleteFile,
+#endif
int *codeToGID, int codeToGIDLen);
- SplashFontFile *loadTrueTypeFont(SplashFontFileID *idA, char *fileName,
- int fontNum, GBool deleteFile,
+ SplashFontFile *loadTrueTypeFont(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf,
+#else
+ char *fileName, GBool deleteFile,
+#endif
+ int fontNum,
int *codeToGID, int codeToGIDLen);
private:
diff --git a/splash/SplashFTFontFile.cc b/splash/SplashFTFontFile.cc
index 5761117..5b01fcf 100644
--- a/splash/SplashFTFontFile.cc
+++ b/splash/SplashFTFontFile.cc
@@ -2,6 +2,8 @@
//
// SplashFTFontFile.cc
//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
//========================================================================
#include <aconf.h>
@@ -13,6 +15,7 @@
#endif
#include "gmem.h"
+#include "GString.h"
#include "SplashFTFontEngine.h"
#include "SplashFTFont.h"
#include "SplashFTFontFile.h"
@@ -23,15 +26,25 @@
SplashFontFile *SplashFTFontFile::loadType1Font(SplashFTFontEngine *engineA,
SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBufA,
+#else
char *fileNameA,
GBool deleteFileA,
- const char **encA) {
+#endif
+ const char **encA,
+ GBool useLightHintingA) {
FT_Face faceA;
int *codeToGIDA;
const char *name;
int i;
+#if LOAD_FONTS_FROM_MEM
+ if (FT_New_Memory_Face(engineA->lib, (FT_Byte *)fontBufA->getCString(),
+ fontBufA->getLength(), 0, &faceA)) {
+#else
if (FT_New_Face(engineA->lib, fileNameA, 0, &faceA)) {
+#endif
return NULL;
}
codeToGIDA = (int *)gmallocn(256, sizeof(int));
@@ -42,59 +55,101 @@ SplashFontFile *SplashFTFontFile::loadType1Font(SplashFTFontEngine *engineA,
}
}
- return new SplashFTFontFile(engineA, idA, fileNameA, deleteFileA,
- faceA, codeToGIDA, 256, gFalse, gTrue);
+ return new SplashFTFontFile(engineA, idA,
+#if LOAD_FONTS_FROM_MEM
+ fontBufA,
+#else
+ fileNameA, deleteFileA,
+#endif
+ faceA, codeToGIDA, 256,
+ gFalse, useLightHintingA);
}
SplashFontFile *SplashFTFontFile::loadCIDFont(SplashFTFontEngine *engineA,
SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBufA,
+#else
char *fileNameA,
GBool deleteFileA,
+#endif
int *codeToGIDA,
int codeToGIDLenA) {
FT_Face faceA;
+#if LOAD_FONTS_FROM_MEM
+ if (FT_New_Memory_Face(engineA->lib, (FT_Byte *)fontBufA->getCString(),
+ fontBufA->getLength(), 0, &faceA)) {
+#else
if (FT_New_Face(engineA->lib, fileNameA, 0, &faceA)) {
+#endif
return NULL;
}
- return new SplashFTFontFile(engineA, idA, fileNameA, deleteFileA,
+ return new SplashFTFontFile(engineA, idA,
+#if LOAD_FONTS_FROM_MEM
+ fontBufA,
+#else
+ fileNameA, deleteFileA,
+#endif
faceA, codeToGIDA, codeToGIDLenA,
gFalse, gFalse);
}
SplashFontFile *SplashFTFontFile::loadTrueTypeFont(SplashFTFontEngine *engineA,
SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBufA,
+#else
char *fileNameA,
- int fontNum,
GBool deleteFileA,
+#endif
+ int fontNum,
int *codeToGIDA,
int codeToGIDLenA) {
FT_Face faceA;
+#if LOAD_FONTS_FROM_MEM
+ if (FT_New_Memory_Face(engineA->lib, (FT_Byte *)fontBufA->getCString(),
+ fontBufA->getLength(), fontNum, &faceA)) {
+#else
if (FT_New_Face(engineA->lib, fileNameA, fontNum, &faceA)) {
+#endif
return NULL;
}
- return new SplashFTFontFile(engineA, idA, fileNameA, deleteFileA,
+ return new SplashFTFontFile(engineA, idA,
+#if LOAD_FONTS_FROM_MEM
+ fontBufA,
+#else
+ fileNameA, deleteFileA,
+#endif
faceA, codeToGIDA, codeToGIDLenA,
gTrue, gFalse);
}
SplashFTFontFile::SplashFTFontFile(SplashFTFontEngine *engineA,
SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBufA,
+#else
char *fileNameA, GBool deleteFileA,
+#endif
FT_Face faceA,
int *codeToGIDA, int codeToGIDLenA,
- GBool trueTypeA, GBool type1A):
+ GBool trueTypeA, GBool useLightHintingA):
+#if LOAD_FONTS_FROM_MEM
+ SplashFontFile(idA, fontBufA)
+#else
SplashFontFile(idA, fileNameA, deleteFileA)
+#endif
{
engine = engineA;
face = faceA;
codeToGID = codeToGIDA;
codeToGIDLen = codeToGIDLenA;
trueType = trueTypeA;
- type1 = type1A;
+ useLightHinting = useLightHintingA;
}
SplashFTFontFile::~SplashFTFontFile() {
diff --git a/splash/SplashFTFontFile.h b/splash/SplashFTFontFile.h
index 0f45e4f..21fa517 100644
--- a/splash/SplashFTFontFile.h
+++ b/splash/SplashFTFontFile.h
@@ -2,6 +2,8 @@
//
// SplashFTFontFile.h
//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
//========================================================================
#ifndef SPLASHFTFONTFILE_H
@@ -30,17 +32,31 @@ class SplashFTFontFile: public SplashFontFile {
public:
static SplashFontFile *loadType1Font(SplashFTFontEngine *engineA,
- SplashFontFileID *idA, char *fileNameA,
- GBool deleteFileA, const char **encA);
+ SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBufA,
+#else
+ char *fileNameA, GBool deleteFileA,
+#endif
+ const char **encA,
+ GBool useLightHintingA);
static SplashFontFile *loadCIDFont(SplashFTFontEngine *engineA,
- SplashFontFileID *idA, char *fileNameA,
- GBool deleteFileA,
+ SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBufA,
+#else
+ char *fileNameA, GBool deleteFileA,
+#endif
int *codeToGIDA, int codeToGIDLenA);
static SplashFontFile *loadTrueTypeFont(SplashFTFontEngine *engineA,
SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBufA,
+#else
char *fileNameA,
- int fontNum,
GBool deleteFileA,
+#endif
+ int fontNum,
int *codeToGIDA,
int codeToGIDLenA);
@@ -55,17 +71,21 @@ private:
SplashFTFontFile(SplashFTFontEngine *engineA,
SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBufA,
+#else
char *fileNameA, GBool deleteFileA,
+#endif
FT_Face faceA,
int *codeToGIDA, int codeToGIDLenA,
- GBool trueTypeA, GBool type1A);
+ GBool trueTypeA, GBool useLightHintingA);
SplashFTFontEngine *engine;
FT_Face face;
int *codeToGID;
int codeToGIDLen;
GBool trueType;
- GBool type1;
+ GBool useLightHinting;
friend class SplashFTFont;
};
diff --git a/splash/SplashFont.cc b/splash/SplashFont.cc
index 6ab2ecf..37a3200 100644
--- a/splash/SplashFont.cc
+++ b/splash/SplashFont.cc
@@ -2,6 +2,8 @@
//
// SplashFont.cc
//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
//========================================================================
#include <aconf.h>
diff --git a/splash/SplashFont.h b/splash/SplashFont.h
index 60a2db7..3d15112 100644
--- a/splash/SplashFont.h
+++ b/splash/SplashFont.h
@@ -2,6 +2,8 @@
//
// SplashFont.h
//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
//========================================================================
#ifndef SPLASHFONT_H
diff --git a/splash/SplashFontEngine.cc b/splash/SplashFontEngine.cc
index 5f5ad46..d4673a7 100644
--- a/splash/SplashFontEngine.cc
+++ b/splash/SplashFontEngine.cc
@@ -2,6 +2,8 @@
//
// SplashFontEngine.cc
//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
//========================================================================
#include <aconf.h>
@@ -10,19 +12,14 @@
#pragma implementation
#endif
-#if HAVE_T1LIB_H
-#include <t1lib.h>
-#endif
-
#include <stdlib.h>
#include <stdio.h>
-#ifndef WIN32
+#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"
@@ -40,9 +37,6 @@ extern "C" int unlink(char *filename);
//------------------------------------------------------------------------
SplashFontEngine::SplashFontEngine(
-#if HAVE_T1LIB_H
- GBool enableT1lib,
-#endif
#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
GBool enableFreeType,
Guint freeTypeFlags,
@@ -54,13 +48,6 @@ SplashFontEngine::SplashFontEngine(
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, freeTypeFlags);
@@ -79,11 +66,6 @@ SplashFontEngine::~SplashFontEngine() {
}
}
-#if HAVE_T1LIB_H
- if (t1Engine) {
- delete t1Engine;
- }
-#endif
#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
if (ftEngine) {
delete ftEngine;
@@ -107,24 +89,29 @@ SplashFontFile *SplashFontEngine::getFontFile(SplashFontFileID *id) {
}
SplashFontFile *SplashFontEngine::loadType1Font(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf,
+#else
char *fileName,
GBool deleteFile,
+#endif
const 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);
+ fontFile = ftEngine->loadType1Font(idA,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
+ fileName, deleteFile,
+#endif
+ enc);
}
#endif
-#ifndef WIN32
+#if !LOAD_FONTS_FROM_MEM && !defined(_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
@@ -138,24 +125,29 @@ SplashFontFile *SplashFontEngine::loadType1Font(SplashFontFileID *idA,
}
SplashFontFile *SplashFontEngine::loadType1CFont(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf,
+#else
char *fileName,
GBool deleteFile,
+#endif
const 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);
+ fontFile = ftEngine->loadType1CFont(idA,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
+ fileName, deleteFile,
+#endif
+ enc);
}
#endif
-#ifndef WIN32
+#if !LOAD_FONTS_FROM_MEM && !defined(_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
@@ -169,19 +161,29 @@ SplashFontFile *SplashFontEngine::loadType1CFont(SplashFontFileID *idA,
}
SplashFontFile *SplashFontEngine::loadOpenTypeT1CFont(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf,
+#else
char *fileName,
GBool deleteFile,
+#endif
const char **enc) {
SplashFontFile *fontFile;
fontFile = NULL;
#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
if (!fontFile && ftEngine) {
- fontFile = ftEngine->loadOpenTypeT1CFont(idA, fileName, deleteFile, enc);
+ fontFile = ftEngine->loadOpenTypeT1CFont(idA,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
+ fileName, deleteFile,
+#endif
+ enc);
}
#endif
-#ifndef WIN32
+#if !LOAD_FONTS_FROM_MEM && !defined(_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
@@ -195,18 +197,29 @@ SplashFontFile *SplashFontEngine::loadOpenTypeT1CFont(SplashFontFileID *idA,
}
SplashFontFile *SplashFontEngine::loadCIDFont(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf
+#else
char *fileName,
- GBool deleteFile) {
+ GBool deleteFile
+#endif
+ ) {
SplashFontFile *fontFile;
fontFile = NULL;
#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
if (!fontFile && ftEngine) {
- fontFile = ftEngine->loadCIDFont(idA, fileName, deleteFile);
+ fontFile = ftEngine->loadCIDFont(idA,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf
+#else
+ fileName, deleteFile
+#endif
+ );
}
#endif
-#ifndef WIN32
+#if !LOAD_FONTS_FROM_MEM && !defined(_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
@@ -220,8 +233,12 @@ SplashFontFile *SplashFontEngine::loadCIDFont(SplashFontFileID *idA,
}
SplashFontFile *SplashFontEngine::loadOpenTypeCFFFont(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf,
+#else
char *fileName,
GBool deleteFile,
+#endif
int *codeToGID,
int codeToGIDLen) {
SplashFontFile *fontFile;
@@ -229,12 +246,17 @@ SplashFontFile *SplashFontEngine::loadOpenTypeCFFFont(SplashFontFileID *idA,
fontFile = NULL;
#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
if (!fontFile && ftEngine) {
- fontFile = ftEngine->loadOpenTypeCFFFont(idA, fileName, deleteFile,
+ fontFile = ftEngine->loadOpenTypeCFFFont(idA,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
+ fileName, deleteFile,
+#endif
codeToGID, codeToGIDLen);
}
#endif
-#ifndef WIN32
+#if !LOAD_FONTS_FROM_MEM && !defined(_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
@@ -248,9 +270,13 @@ SplashFontFile *SplashFontEngine::loadOpenTypeCFFFont(SplashFontFileID *idA,
}
SplashFontFile *SplashFontEngine::loadTrueTypeFont(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf,
+#else
char *fileName,
- int fontNum,
GBool deleteFile,
+#endif
+ int fontNum,
int *codeToGID,
int codeToGIDLen,
char *fontName) {
@@ -259,8 +285,13 @@ SplashFontFile *SplashFontEngine::loadTrueTypeFont(SplashFontFileID *idA,
fontFile = NULL;
#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
if (!fontFile && ftEngine) {
- fontFile = ftEngine->loadTrueTypeFont(idA, fileName, fontNum, deleteFile,
- codeToGID, codeToGIDLen);
+ fontFile = ftEngine->loadTrueTypeFont(idA,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
+ fileName, deleteFile,
+#endif
+ fontNum, codeToGID, codeToGIDLen);
}
#endif
@@ -268,7 +299,7 @@ SplashFontFile *SplashFontEngine::loadTrueTypeFont(SplashFontFileID *idA,
gfree(codeToGID);
}
-#ifndef WIN32
+#if !LOAD_FONTS_FROM_MEM && !defined(_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
diff --git a/splash/SplashFontEngine.h b/splash/SplashFontEngine.h
index a6d0e58..43eb6d3 100644
--- a/splash/SplashFontEngine.h
+++ b/splash/SplashFontEngine.h
@@ -2,6 +2,8 @@
//
// SplashFontEngine.h
//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
//========================================================================
#ifndef SPLASHFONTENGINE_H
@@ -14,8 +16,8 @@
#endif
#include "gtypes.h"
+class GString;
-class SplashT1FontEngine;
class SplashFTFontEngine;
class SplashDTFontEngine;
class SplashDT4FontEngine;
@@ -40,9 +42,6 @@ public:
// Create a font engine.
SplashFontEngine(
-#if HAVE_T1LIB_H
- GBool enableT1lib,
-#endif
#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
GBool enableFreeType,
Guint freeTypeFlags,
@@ -56,19 +55,48 @@ public:
SplashFontFile *getFontFile(SplashFontFileID *id);
// Load fonts - these create new SplashFontFile objects.
- SplashFontFile *loadType1Font(SplashFontFileID *idA, char *fileName,
- GBool deleteFile, const char **enc);
- SplashFontFile *loadType1CFont(SplashFontFileID *idA, char *fileName,
- GBool deleteFile, const char **enc);
- SplashFontFile *loadOpenTypeT1CFont(SplashFontFileID *idA, char *fileName,
- GBool deleteFile, const char **enc);
- SplashFontFile *loadCIDFont(SplashFontFileID *idA, char *fileName,
- GBool deleteFile);
- SplashFontFile *loadOpenTypeCFFFont(SplashFontFileID *idA, char *fileName,
- GBool deleteFile,
+ SplashFontFile *loadType1Font(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf,
+#else
+ char *fileName, GBool deleteFile,
+#endif
+ const char **enc);
+ SplashFontFile *loadType1CFont(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf,
+#else
+ char *fileName, GBool deleteFile,
+#endif
+ const char **enc);
+ SplashFontFile *loadOpenTypeT1CFont(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf,
+#else
+ char *fileName, GBool deleteFile,
+#endif
+ const char **enc);
+ SplashFontFile *loadCIDFont(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf
+#else
+ char *fileName, GBool deleteFile
+#endif
+ );
+ SplashFontFile *loadOpenTypeCFFFont(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf,
+#else
+ char *fileName, GBool deleteFile,
+#endif
int *codeToGID, int codeToGIDLen);
- SplashFontFile *loadTrueTypeFont(SplashFontFileID *idA, char *fileName,
- int fontNum, GBool deleteFile,
+ SplashFontFile *loadTrueTypeFont(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf,
+#else
+ char *fileName, GBool deleteFile,
+#endif
+ int fontNum,
int *codeToGID, int codeToGIDLen,
char *fontName);
@@ -87,9 +115,6 @@ private:
SplashFont *fontCache[splashFontCacheSize];
-#if HAVE_T1LIB_H
- SplashT1FontEngine *t1Engine;
-#endif
#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
SplashFTFontEngine *ftEngine;
#endif
diff --git a/splash/SplashFontFile.cc b/splash/SplashFontFile.cc
index acbc12a..d0e36a2 100644
--- a/splash/SplashFontFile.cc
+++ b/splash/SplashFontFile.cc
@@ -2,6 +2,8 @@
//
// SplashFontFile.cc
//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
//========================================================================
#include <aconf.h>
@@ -11,7 +13,7 @@
#endif
#include <stdio.h>
-#ifndef WIN32
+#ifndef _WIN32
# include <unistd.h>
#endif
#include "GString.h"
@@ -28,19 +30,32 @@ extern "C" int unlink(char *filename);
// SplashFontFile
//------------------------------------------------------------------------
-SplashFontFile::SplashFontFile(SplashFontFileID *idA, char *fileNameA,
- GBool deleteFileA) {
+SplashFontFile::SplashFontFile(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBufA
+#else
+ char *fileNameA, GBool deleteFileA
+#endif
+ ) {
id = idA;
+#if LOAD_FONTS_FROM_MEM
+ fontBuf = fontBufA;
+#else
fileName = new GString(fileNameA);
deleteFile = deleteFileA;
+#endif
refCnt = 0;
}
SplashFontFile::~SplashFontFile() {
+#if LOAD_FONTS_FROM_MEM
+ delete fontBuf;
+#else
if (deleteFile) {
unlink(fileName->getCString());
}
delete fileName;
+#endif
delete id;
}
diff --git a/splash/SplashFontFile.h b/splash/SplashFontFile.h
index 9f89312..5f53ece 100644
--- a/splash/SplashFontFile.h
+++ b/splash/SplashFontFile.h
@@ -2,6 +2,8 @@
//
// SplashFontFile.h
//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
//========================================================================
#ifndef SPLASHFONTFILE_H
@@ -46,12 +48,21 @@ public:
protected:
- SplashFontFile(SplashFontFileID *idA, char *fileNameA,
- GBool deleteFileA);
+ SplashFontFile(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBufA
+#else
+ char *fileNameA, GBool deleteFileA
+#endif
+ );
SplashFontFileID *id;
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf;
+#else
GString *fileName;
GBool deleteFile;
+#endif
int refCnt;
friend class SplashFontEngine;
diff --git a/splash/SplashFontFileID.cc b/splash/SplashFontFileID.cc
index af37cb2..3274847 100644
--- a/splash/SplashFontFileID.cc
+++ b/splash/SplashFontFileID.cc
@@ -2,6 +2,8 @@
//
// SplashFontFileID.cc
//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
//========================================================================
#include <aconf.h>
diff --git a/splash/SplashFontFileID.h b/splash/SplashFontFileID.h
index bed11d3..384018a 100644
--- a/splash/SplashFontFileID.h
+++ b/splash/SplashFontFileID.h
@@ -2,6 +2,8 @@
//
// SplashFontFileID.h
//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
//========================================================================
#ifndef SPLASHFONTFILEID_H
diff --git a/splash/SplashGlyphBitmap.h b/splash/SplashGlyphBitmap.h
index 044ba4a..d375b3c 100644
--- a/splash/SplashGlyphBitmap.h
+++ b/splash/SplashGlyphBitmap.h
@@ -2,6 +2,8 @@
//
// SplashGlyphBitmap.h
//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
//========================================================================
#ifndef SPLASHGLYPHBITMAP_H
diff --git a/splash/SplashMath.h b/splash/SplashMath.h
index 3b82e7a..4b7ab1f 100644
--- a/splash/SplashMath.h
+++ b/splash/SplashMath.h
@@ -2,6 +2,8 @@
//
// SplashMath.h
//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
//========================================================================
#ifndef SPLASHMATH_H
@@ -35,19 +37,18 @@ static inline int splashFloor(SplashCoord x) {
Gushort oldCW, newCW, t;
int result;
- __asm__ volatile("fldl %4\n"
- "fnstcw %0\n"
+ __asm__ volatile("fnstcw %0\n"
"movw %0, %3\n"
"andw $0xf3ff, %3\n"
"orw $0x0400, %3\n"
"movw %3, %1\n" // round down
"fldcw %1\n"
- "fistpl %2\n"
+ "fistl %2\n"
"fldcw %0\n"
: "=m" (oldCW), "=m" (newCW), "=m" (result), "=r" (t)
- : "m" (x));
+ : "t" (x));
return result;
-#elif defined(WIN32) && defined(_M_IX86)
+#elif defined(_WIN32) && defined(_M_IX86)
// floor() and (int)() are implemented separately, which results
// in changing the FPCW multiple times - so we optimize it with
// some inline assembly
@@ -81,19 +82,18 @@ static inline int splashCeil(SplashCoord x) {
Gushort oldCW, newCW, t;
int result;
- __asm__ volatile("fldl %4\n"
- "fnstcw %0\n"
+ __asm__ volatile("fnstcw %0\n"
"movw %0, %3\n"
"andw $0xf3ff, %3\n"
"orw $0x0800, %3\n"
"movw %3, %1\n" // round up
"fldcw %1\n"
- "fistpl %2\n"
+ "fistl %2\n"
"fldcw %0\n"
: "=m" (oldCW), "=m" (newCW), "=m" (result), "=r" (t)
- : "m" (x));
+ : "t" (x));
return result;
-#elif defined(WIN32) && defined(_M_IX86)
+#elif defined(_WIN32) && defined(_M_IX86)
// ceil() and (int)() are implemented separately, which results
// in changing the FPCW multiple times - so we optimize it with
// some inline assembly
@@ -128,19 +128,18 @@ static inline int splashRound(SplashCoord x) {
int result;
x += 0.5;
- __asm__ volatile("fldl %4\n"
- "fnstcw %0\n"
+ __asm__ volatile("fnstcw %0\n"
"movw %0, %3\n"
"andw $0xf3ff, %3\n"
"orw $0x0400, %3\n"
"movw %3, %1\n" // round down
"fldcw %1\n"
- "fistpl %2\n"
+ "fistl %2\n"
"fldcw %0\n"
: "=m" (oldCW), "=m" (newCW), "=m" (result), "=r" (t)
- : "m" (x));
+ : "t" (x));
return result;
-#elif defined(WIN32) && defined(_M_IX86)
+#elif defined(_WIN32) && defined(_M_IX86)
// this could use round-to-nearest mode and avoid the "+0.5",
// but that produces slightly different results (because i+0.5
// sometimes rounds up and sometimes down using the even rule)
@@ -223,4 +222,68 @@ static inline GBool splashCheckDet(SplashCoord m11, SplashCoord m12,
#endif
}
+// Perform stroke adjustment on a SplashCoord range [xMin, xMax),
+// resulting in an int range [*xMinI, *xMaxI).
+//
+// There are several options:
+//
+// 1. Round both edge coordinates.
+// Pro: adjacent strokes/fills line up without any gaps or
+// overlaps
+// Con: lines with the same original floating point width can
+// end up with different integer widths, e.g.:
+// xMin = 10.1 xMax = 11.3 (width = 1.2)
+// --> xMinI = 10 xMaxI = 11 (width = 1)
+// but
+// xMin = 10.4 xMax = 11.6 (width = 1.2)
+// --> xMinI = 10 xMaxI = 12 (width = 2)
+//
+// 2. Round the min coordinate; add the ceiling of the width.
+// Pro: lines with the same original floating point width will
+// always end up with the same integer width
+// Con: adjacent strokes/fills can have overlaps (which is
+// problematic with transparency)
+// (This could use floor on the min coordinate, instead of
+// rounding, with similar results.)
+// (If the width is rounded instead of using ceiling, the results
+// Are similar, except that adjacent strokes/fills can have gaps
+// as well as overlaps.)
+//
+// 3. Use floor on the min coordinate and ceiling on the max
+// coordinate.
+// Pro: lines always end up at least as wide as the original
+// floating point width
+// Con: adjacent strokes/fills can have overlaps, and lines with
+// the same original floating point width can end up with
+// different integer widths; the integer width can be more
+// than one pixel wider than the original width, e.g.:
+// xMin = 10.9 xMax = 12.1 (width = 1.2)
+// --> xMinI = 10 xMaxI = 13 (width = 3)
+// but
+// xMin = 10.1 xMax = 11.3 (width = 1.2)
+// --> xMinI = 10 xMaxI = 12 (width = 2)
+static inline void splashStrokeAdjust(SplashCoord xMin, SplashCoord xMax,
+ int *xMinI, int *xMaxI) {
+ int x0, x1;
+
+ // NB: enable exactly one of these.
+#if 1 // 1. Round both edge coordinates.
+ x0 = splashRound(xMin);
+ x1 = splashRound(xMax);
+#endif
+#if 0 // 2. Round the min coordinate; add the ceiling of the width.
+ x0 = splashRound(xMin);
+ x1 = x0 + splashCeil(xMax - xMin);
+#endif
+#if 0 // 3. Use floor on the min coord and ceiling on the max coord.
+ x0 = splashFloor(xMin);
+ x1 = splashCeil(xMax);
+#endif
+ if (x1 == x0) {
+ ++x1;
+ }
+ *xMinI = x0;
+ *xMaxI = x1;
+}
+
#endif
diff --git a/splash/SplashPath.cc b/splash/SplashPath.cc
index b4249ed..4ee8be0 100644
--- a/splash/SplashPath.cc
+++ b/splash/SplashPath.cc
@@ -2,6 +2,8 @@
//
// SplashPath.cc
//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
//========================================================================
#include <aconf.h>
@@ -53,6 +55,7 @@ SplashPath::SplashPath(SplashPath *path) {
memcpy(hints, path->hints, hintsLength * sizeof(SplashPathHint));
} else {
hints = NULL;
+ hintsLength = hintsSize = 0;
}
}
diff --git a/splash/SplashPath.h b/splash/SplashPath.h
index aae51f2..a871530 100644
--- a/splash/SplashPath.h
+++ b/splash/SplashPath.h
@@ -2,6 +2,8 @@
//
// SplashPath.h
//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
//========================================================================
#ifndef SPLASHPATH_H
@@ -94,7 +96,7 @@ public:
// Get the points on the path.
int getLength() { return length; }
- void getPoint(int i, double *x, double *y, Guchar *f)
+ void getPoint(int i, SplashCoord *x, SplashCoord *y, Guchar *f)
{ *x = pts[i].x; *y = pts[i].y; *f = flags[i]; }
// Get the current point.
diff --git a/splash/SplashPattern.cc b/splash/SplashPattern.cc
index b77658e..6ca4fb2 100644
--- a/splash/SplashPattern.cc
+++ b/splash/SplashPattern.cc
@@ -2,6 +2,8 @@
//
// SplashPattern.cc
//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
//========================================================================
#include <aconf.h>
diff --git a/splash/SplashPattern.h b/splash/SplashPattern.h
index 0a02e9c..01207bf 100644
--- a/splash/SplashPattern.h
+++ b/splash/SplashPattern.h
@@ -2,6 +2,8 @@
//
// SplashPattern.h
//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
//========================================================================
#ifndef SPLASHPATTERN_H
diff --git a/splash/SplashScreen.cc b/splash/SplashScreen.cc
index f717173..68a762f 100644
--- a/splash/SplashScreen.cc
+++ b/splash/SplashScreen.cc
@@ -2,6 +2,8 @@
//
// SplashScreen.cc
//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
//========================================================================
#include <aconf.h>
diff --git a/splash/SplashScreen.h b/splash/SplashScreen.h
index 708c37f..4e9767b 100644
--- a/splash/SplashScreen.h
+++ b/splash/SplashScreen.h
@@ -2,6 +2,8 @@
//
// SplashScreen.h
//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
//========================================================================
#ifndef SPLASHSCREEN_H
diff --git a/splash/SplashState.cc b/splash/SplashState.cc
index e6dde61..09c1949 100644
--- a/splash/SplashState.cc
+++ b/splash/SplashState.cc
@@ -2,6 +2,8 @@
//
// SplashState.cc
//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
//========================================================================
#include <aconf.h>
@@ -45,7 +47,7 @@ SplashState::SplashState(int width, int height, GBool vectorAntialias,
blendFunc = NULL;
strokeAlpha = 1;
fillAlpha = 1;
- lineWidth = 0;
+ lineWidth = 1;
lineCap = splashLineCapButt;
lineJoin = splashLineJoinMiter;
miterLimit = 10;
@@ -54,10 +56,12 @@ SplashState::SplashState(int width, int height, GBool vectorAntialias,
lineDashLength = 0;
lineDashPhase = 0;
strokeAdjust = gFalse;
- clip = new SplashClip(0, 0, width, height, vectorAntialias);
+ clip = new SplashClip(0, 0, width, height);
+ clipIsShared = gFalse;
softMask = NULL;
deleteSoftMask = gFalse;
inNonIsolatedGroup = gFalse;
+ inKnockoutGroup = gFalse;
for (i = 0; i < 256; ++i) {
rgbTransferR[i] = (Guchar)i;
rgbTransferG[i] = (Guchar)i;
@@ -87,7 +91,7 @@ SplashState::SplashState(int width, int height, GBool vectorAntialias,
blendFunc = NULL;
strokeAlpha = 1;
fillAlpha = 1;
- lineWidth = 0;
+ lineWidth = 1;
lineCap = splashLineCapButt;
lineJoin = splashLineJoinMiter;
miterLimit = 10;
@@ -96,10 +100,12 @@ SplashState::SplashState(int width, int height, GBool vectorAntialias,
lineDashLength = 0;
lineDashPhase = 0;
strokeAdjust = gFalse;
- clip = new SplashClip(0, 0, width, height, vectorAntialias);
+ clip = new SplashClip(0, 0, width, height);
+ clipIsShared = gFalse;
softMask = NULL;
deleteSoftMask = gFalse;
inNonIsolatedGroup = gFalse;
+ inKnockoutGroup = gFalse;
for (i = 0; i < 256; ++i) {
rgbTransferR[i] = (Guchar)i;
rgbTransferG[i] = (Guchar)i;
@@ -137,10 +143,12 @@ SplashState::SplashState(SplashState *state) {
}
lineDashPhase = state->lineDashPhase;
strokeAdjust = state->strokeAdjust;
- clip = state->clip->copy();
+ clip = state->clip;
+ clipIsShared = gTrue;
softMask = state->softMask;
deleteSoftMask = gFalse;
inNonIsolatedGroup = state->inNonIsolatedGroup;
+ inKnockoutGroup = state->inKnockoutGroup;
memcpy(rgbTransferR, state->rgbTransferR, 256);
memcpy(rgbTransferG, state->rgbTransferG, 256);
memcpy(rgbTransferB, state->rgbTransferB, 256);
@@ -158,7 +166,9 @@ SplashState::~SplashState() {
delete fillPattern;
delete screen;
gfree(lineDash);
- delete clip;
+ if (!clipIsShared) {
+ delete clip;
+ }
if (deleteSoftMask && softMask) {
delete softMask;
}
@@ -192,6 +202,32 @@ void SplashState::setLineDash(SplashCoord *lineDashA, int lineDashLengthA,
lineDashPhase = lineDashPhaseA;
}
+void SplashState::clipResetToRect(SplashCoord x0, SplashCoord y0,
+ SplashCoord x1, SplashCoord y1) {
+ if (clipIsShared) {
+ clip = clip->copy();
+ clipIsShared = gFalse;
+ }
+ clip->resetToRect(x0, y0, x1, y1);
+}
+
+SplashError SplashState::clipToRect(SplashCoord x0, SplashCoord y0,
+ SplashCoord x1, SplashCoord y1) {
+ if (clipIsShared) {
+ clip = clip->copy();
+ clipIsShared = gFalse;
+ }
+ return clip->clipToRect(x0, y0, x1, y1);
+}
+
+SplashError SplashState::clipToPath(SplashPath *path, GBool eo) {
+ if (clipIsShared) {
+ clip = clip->copy();
+ clipIsShared = gFalse;
+ }
+ return clip->clipToPath(path, matrix, flatness, eo);
+}
+
void SplashState::setSoftMask(SplashBitmap *softMaskA) {
if (deleteSoftMask) {
delete softMask;
diff --git a/splash/SplashState.h b/splash/SplashState.h
index 0d1b6c5..fe773a5 100644
--- a/splash/SplashState.h
+++ b/splash/SplashState.h
@@ -2,6 +2,8 @@
//
// SplashState.h
//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
//========================================================================
#ifndef SPLASHSTATE_H
@@ -19,6 +21,7 @@ class SplashPattern;
class SplashScreen;
class SplashClip;
class SplashBitmap;
+class SplashPath;
//------------------------------------------------------------------------
// line cap values
@@ -67,6 +70,12 @@ public:
void setLineDash(SplashCoord *lineDashA, int lineDashLengthA,
SplashCoord lineDashPhaseA);
+ void clipResetToRect(SplashCoord x0, SplashCoord y0,
+ SplashCoord x1, SplashCoord y1);
+ SplashError clipToRect(SplashCoord x0, SplashCoord y0,
+ SplashCoord x1, SplashCoord y1);
+ SplashError clipToPath(SplashPath *path, GBool eo);
+
// Set the soft mask bitmap.
void setSoftMask(SplashBitmap *softMaskA);
@@ -94,9 +103,11 @@ private:
SplashCoord lineDashPhase;
GBool strokeAdjust;
SplashClip *clip;
+ GBool clipIsShared;
SplashBitmap *softMask;
GBool deleteSoftMask;
GBool inNonIsolatedGroup;
+ GBool inKnockoutGroup;
Guchar rgbTransferR[256],
rgbTransferG[256],
rgbTransferB[256];
diff --git a/splash/SplashT1Font.cc b/splash/SplashT1Font.cc
deleted file mode 100644
index 703aacf..0000000
--- a/splash/SplashT1Font.cc
+++ /dev/null
@@ -1,290 +0,0 @@
-//========================================================================
-//
-// 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)splashDist(0, 0, mat[2], 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;
- int x, y, x1, y1;
- GBool needClose;
-
- if (outlineID < 0) {
- outlineID = T1_CopyFont(((SplashT1FontFile *)fontFile)->t1libID);
- outlineSize = (float)splashDist(0, 0, textMat[2], 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))) {
- // NB: t1lib uses integer coordinates here; we keep a running
- // (x,y) total as integers, so that the final point in the path is
- // exactly the same as the first point, thus avoiding weird
- // mitered join glitches
- x = 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;
- y += seg->dest.y;
- path->moveTo(outlineMul * x, -outlineMul * y);
- break;
- case T1_PATHTYPE_LINE:
- x += seg->dest.x;
- y += seg->dest.y;
- path->lineTo(outlineMul * x, -outlineMul * y);
- needClose = gTrue;
- break;
- case T1_PATHTYPE_BEZIER:
- bez = (T1_BEZIERSEGMENT *)seg;
- x1 = x + bez->dest.x;
- y1 = y + bez->dest.y;
- path->curveTo(outlineMul * (x + bez->B.x),
- -outlineMul * (y + bez->B.y),
- outlineMul * (x + bez->C.x),
- -outlineMul * (y + bez->C.y),
- outlineMul * x1,
- -outlineMul * 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
deleted file mode 100644
index 8ea74de..0000000
--- a/splash/SplashT1Font.h
+++ /dev/null
@@ -1,57 +0,0 @@
-//========================================================================
-//
-// 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
deleted file mode 100644
index 277e2fc..0000000
--- a/splash/SplashT1FontEngine.cc
+++ /dev/null
@@ -1,124 +0,0 @@
-//========================================================================
-//
-// 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, const 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,
- const char **enc) {
- return SplashT1FontFile::loadType1Font(this, idA, fileName, deleteFile, enc);
-}
-
-SplashFontFile *SplashT1FontEngine::loadType1CFont(SplashFontFileID *idA,
- char *fileName,
- GBool deleteFile,
- const 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
deleted file mode 100644
index 8942b85..0000000
--- a/splash/SplashT1FontEngine.h
+++ /dev/null
@@ -1,53 +0,0 @@
-//========================================================================
-//
-// 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, const char **enc);
- SplashFontFile *loadType1CFont(SplashFontFileID *idA, char *fileName,
- GBool deleteFile, const 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
deleted file mode 100644
index 8cec742..0000000
--- a/splash/SplashT1FontFile.cc
+++ /dev/null
@@ -1,98 +0,0 @@
-//========================================================================
-//
-// 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,
- const char **encA) {
- int t1libIDA;
- const 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 = (const 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, (char **)encTmp);
-
- return new SplashT1FontFile(engineA, idA, fileNameA, deleteFileA,
- t1libIDA, encTmp, encStrTmp);
-}
-
-SplashT1FontFile::SplashT1FontFile(SplashT1FontEngine *engineA,
- SplashFontFileID *idA,
- char *fileNameA, GBool deleteFileA,
- int t1libIDA, const 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
deleted file mode 100644
index 57de84d..0000000
--- a/splash/SplashT1FontFile.h
+++ /dev/null
@@ -1,58 +0,0 @@
-//========================================================================
-//
-// 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,
- const 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, const char **encA, char *encStrA);
-
- SplashT1FontEngine *engine;
- int t1libID; // t1lib font ID
- const char **enc;
- char *encStr;
-
- friend class SplashT1Font;
-};
-
-#endif // HAVE_T1LIB_H
-
-#endif
diff --git a/splash/SplashTypes.h b/splash/SplashTypes.h
index 6eb2607..240cb6f 100644
--- a/splash/SplashTypes.h
+++ b/splash/SplashTypes.h
@@ -2,6 +2,8 @@
//
// SplashTypes.h
//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
//========================================================================
#ifndef SPLASHTYPES_H
diff --git a/splash/SplashXPath.cc b/splash/SplashXPath.cc
index 6ed2f4b..a159988 100644
--- a/splash/SplashXPath.cc
+++ b/splash/SplashXPath.cc
@@ -2,6 +2,8 @@
//
// SplashXPath.cc
//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
//========================================================================
#include <aconf.h>
@@ -54,12 +56,10 @@ inline void SplashXPath::transform(SplashCoord *matrix,
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;
- int curSubpath, curSubpathX, i, j;
+ SplashCoord xMinFP, xMaxFP, yMinFP, yMaxFP;
+ int curSubpath, i;
// transform the points
pts = (SplashXPathPoint *)gmallocn(path->length, sizeof(SplashXPathPoint));
@@ -67,78 +67,16 @@ SplashXPath::SplashXPath(SplashPath *path, SplashCoord *matrix,
transform(matrix, path->pts[i].x, path->pts[i].y, &pts[i].x, &pts[i].y);
}
- // set up the stroke adjustment hints
+ // do stroke adjustment
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;
- }
- 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;
- // rounding both edge coordinates can result in lines of
- // different widths (e.g., adj=10.1, adj1=11.3 --> x0=10, x1=11;
- // adj0=10.4, adj1=11.6 --> x0=10, x1=12), but it has the
- // benefit of making adjacent strokes/fills line up without any
- // gaps between them
- x0 = splashRound(adj0);
- x1 = splashRound(adj1);
- if (x1 == x0) {
- x1 = x1 + 1;
- }
- adjusts[i].x0 = (SplashCoord)x0;
- adjusts[i].x1 = (SplashCoord)x1 - 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);
+ strokeAdjust(pts, path->hints, path->hintsLength);
}
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) {
@@ -149,7 +87,6 @@ SplashXPath::SplashXPath(SplashPath *path, SplashCoord *matrix,
xsp = x0;
ysp = y0;
curSubpath = i;
- curSubpathX = length;
++i;
} else {
@@ -197,32 +134,130 @@ SplashXPath::SplashXPath(SplashPath *path, SplashCoord *matrix,
}
gfree(pts);
+
+#if HAVE_STD_SORT
+ std::sort(segs, segs + length, SplashXPathSeg::cmpY);
+#else
+ qsort(segs, length, sizeof(SplashXPathSeg), &SplashXPathSeg::cmpY);
+#endif
+
+ if (length == 0) {
+ xMin = yMin = xMax = yMax = 0;
+ } else {
+ if (segs[0].x0 < segs[0].x1) {
+ xMinFP = segs[0].x0;
+ xMaxFP = segs[0].x1;
+ } else {
+ xMinFP = segs[0].x1;
+ xMaxFP = segs[0].x0;
+ }
+ yMinFP = segs[0].y0;
+ yMaxFP = segs[0].y1;
+ for (i = 1; i < length; ++i) {
+ if (segs[i].x0 < xMinFP) {
+ xMinFP = segs[i].x0;
+ } else if (segs[i].x0 > xMaxFP) {
+ xMaxFP = segs[i].x0;
+ }
+ if (segs[i].x1 < xMinFP) {
+ xMinFP = segs[i].x1;
+ } else if (segs[i].x1 > xMaxFP) {
+ xMaxFP = segs[i].x1;
+ }
+ if (segs[i].y1 > yMaxFP) {
+ yMaxFP = segs[i].y1;
+ }
+ }
+ xMin = splashFloor(xMinFP);
+ yMin = splashFloor(yMinFP);
+ xMax = splashFloor(xMaxFP);
+ yMax = splashFloor(yMaxFP);
+ }
}
-// Apply the stroke adjust hints to point <pt>: (*<xp>, *<yp>).
-void SplashXPath::strokeAdjust(SplashXPathAdjust *adjust,
- SplashCoord *xp, SplashCoord *yp) {
- SplashCoord x, y;
+void SplashXPath::strokeAdjust(SplashXPathPoint *pts,
+ SplashPathHint *hints, int nHints) {
+ SplashXPathAdjust *adjusts, *adjust;
+ SplashPathHint *hint;
+ SplashCoord x0, y0, x1, y1, x2, y2, x3, y3;
+ SplashCoord adj0, adj1, d;
+ int xi0, xi1;
+ int i, j;
- 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;
+ // set up the stroke adjustment hints
+ adjusts = (SplashXPathAdjust *)gmallocn(nHints, sizeof(SplashXPathAdjust));
+ for (i = 0; i < nHints; ++i) {
+ hint = &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 {
+ goto done;
}
- } 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;
+ if (adj0 > adj1) {
+ x0 = adj0;
+ adj0 = adj1;
+ adj1 = x0;
+ }
+ d = adj1 - adj0;
+ if (d > 0.04) {
+ d = 0.01;
+ } else {
+ d *= 0.25;
}
+ adjusts[i].x0a = adj0 - d;
+ adjusts[i].x0b = adj0 + d;
+ adjusts[i].xma = (SplashCoord)0.5 * (adj0 + adj1) - d;
+ adjusts[i].xmb = (SplashCoord)0.5 * (adj0 + adj1) + d;
+ adjusts[i].x1a = adj1 - d;
+ adjusts[i].x1b = adj1 + d;
+ splashStrokeAdjust(adj0, adj1, &xi0, &xi1);
+ adjusts[i].x0 = (SplashCoord)xi0;
+ // the "minus epsilon" thing here is needed when vector
+ // antialiasing is turned off -- otherwise stroke adjusted lines
+ // will touch an extra pixel on one edge
+ adjusts[i].x1 = (SplashCoord)xi1 - 0.001;
+ adjusts[i].xm = (SplashCoord)0.5 * (adjusts[i].x0 + adjusts[i].x1);
+ adjusts[i].firstPt = hint->firstPt;
+ adjusts[i].lastPt = hint->lastPt;
}
+
+ // perform stroke adjustment
+ for (i = 0, adjust = adjusts; i < nHints; ++i, ++adjust) {
+ for (j = adjust->firstPt; j <= adjust->lastPt; ++j) {
+ if (adjust->vert) {
+ x0 = pts[j].x;
+ if (x0 > adjust->x0a && x0 < adjust->x0b) {
+ pts[j].x = adjust->x0;
+ } else if (x0 > adjust->xma && x0 < adjust->xmb) {
+ pts[j].x = adjust->xm;
+ } else if (x0 > adjust->x1a && x0 < adjust->x1b) {
+ pts[j].x = adjust->x1;
+ }
+ } else {
+ y0 = pts[j].y;
+ if (y0 > adjust->x0a && y0 < adjust->x0b) {
+ pts[j].y = adjust->x0;
+ } else if (y0 > adjust->xma && y0 < adjust->xmb) {
+ pts[j].y = adjust->xm;
+ } else if (y0 > adjust->x1a && y0 < adjust->x1b) {
+ pts[j].y = adjust->x1;
+ }
+ }
+ }
+ }
+
+ done:
+ gfree(adjusts);
}
SplashXPath::SplashXPath(SplashXPath *xPath) {
@@ -230,6 +265,10 @@ SplashXPath::SplashXPath(SplashXPath *xPath) {
size = xPath->size;
segs = (SplashXPathSeg *)gmallocn(size, sizeof(SplashXPathSeg));
memcpy(segs, xPath->segs, length * sizeof(SplashXPathSeg));
+ xMin = xPath->xMin;
+ yMin = xPath->yMin;
+ xMax = xPath->xMax;
+ yMax = xPath->yMax;
}
SplashXPath::~SplashXPath() {
@@ -341,115 +380,38 @@ void SplashXPath::addCurve(SplashCoord x0, SplashCoord y0,
void SplashXPath::addSegment(SplashCoord x0, SplashCoord y0,
SplashCoord x1, SplashCoord y1) {
grow(1);
- segs[length].x0 = x0;
- segs[length].y0 = y0;
- segs[length].x1 = x1;
- segs[length].y1 = y1;
- segs[length].flags = 0;
- 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;
+ if (y0 <= y1) {
+ segs[length].x0 = x0;
+ segs[length].y0 = y0;
+ segs[length].x1 = x1;
+ segs[length].y1 = y1;
+ segs[length].count = 1;
} else {
+ segs[length].x0 = x1;
+ segs[length].y0 = y1;
+ segs[length].x1 = x0;
+ segs[length].y1 = y0;
+ segs[length].count = -1;
+ }
#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;
- }
- }
+ if (y0 == y1 || x0 == x1 ||
+ !FixedPoint::divCheck(x1 - x0, y1 - y0, &segs[length].dxdy) ||
+ !FixedPoint::divCheck(y1 - y0, x1 - x0, &segs[length].dydx)) {
+ segs[length].dxdy = 0;
+ segs[length].dydx = 0;
+ }
#else
+ if (y0 == y1 || x0 == x1) {
+ segs[length].dxdy = 0;
+ segs[length].dydx = 0;
+ } 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;
-}
-
-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;
- }
-}
-
-#if HAVE_STD_SORT
-
-struct cmpXPathSegsFunctor {
- bool operator()(const SplashXPathSeg &seg0, const SplashXPathSeg &seg1) {
- SplashCoord x0, y0, x1, y1;
-
- if (seg0.flags & splashXPathFlip) {
- x0 = seg0.x1;
- y0 = seg0.y1;
+ if (segs[length].dxdy == 0) {
+ segs[length].dydx = 0;
} else {
- x0 = seg0.x0;
- y0 = seg0.y0;
+ segs[length].dydx = 1 / segs[length].dxdy;
}
- if (seg1.flags & splashXPathFlip) {
- x1 = seg1.x1;
- y1 = seg1.y1;
- } else {
- x1 = seg1.x0;
- y1 = seg1.y0;
- }
- return (y0 != y1) ? (y0 < y1) : (x0 < x1);
- }
-};
-
-#else // HAVE_STD_SORT
-
-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;
-}
-
-#endif // HAVE_STD_SORT
-
-void SplashXPath::sort() {
-#if HAVE_STD_SORT
- std::sort(segs, segs + length, cmpXPathSegsFunctor());
-#else
- qsort(segs, length, sizeof(SplashXPathSeg), &cmpXPathSegs);
#endif
+ ++length;
}
diff --git a/splash/SplashXPath.h b/splash/SplashXPath.h
index 4e06d82..5bcec61 100644
--- a/splash/SplashXPath.h
+++ b/splash/SplashXPath.h
@@ -2,6 +2,8 @@
//
// SplashXPath.h
//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
//========================================================================
#ifndef SPLASHXPATH_H
@@ -16,7 +18,8 @@
#include "SplashTypes.h"
class SplashPath;
-struct SplashXPathAdjust;
+struct SplashXPathPoint;
+struct SplashPathHint;
//------------------------------------------------------------------------
@@ -27,18 +30,44 @@ struct SplashXPathAdjust;
//------------------------------------------------------------------------
struct SplashXPathSeg {
- SplashCoord x0, y0; // first endpoint
+ SplashCoord x0, y0; // first endpoint (y0 <= y1)
SplashCoord x1, y1; // second endpoint
SplashCoord dxdy; // slope: delta-x / delta-y
SplashCoord dydx; // slope: delta-y / delta-x
- Guint flags;
-};
+ int count; // EO/NZWN counter increment
+
+ //----- used by SplashXPathScanner
+ SplashCoord xCur0, xCur1; // current x values
+
+#if HAVE_STD_SORT
+ static bool cmpY(const SplashXPathSeg &seg0,
+ const SplashXPathSeg &seg1) {
+ return seg0.y0 < seg1.y0;
+ }
+#else
+ static int cmpY(const void *seg0, const void *seg1) {
+ SplashCoord cmp;
+
+ cmp = ((SplashXPathSeg *)seg0)->y0
+ - ((SplashXPathSeg *)seg1)->y0;
+ return (cmp > 0) ? 1 : (cmp < 0) ? -1 : 0;
+ }
+#endif
+
+ static int cmpX(SplashXPathSeg *seg0, SplashXPathSeg *seg1) {
+ SplashCoord cmp;
-#define splashXPathHoriz 0x01 // segment is vertical (y0 == y1)
- // (dxdy is undef)
-#define splashXPathVert 0x02 // segment is horizontal (x0 == x1)
- // (dydx is undef)
-#define splashXPathFlip 0x04 // y0 > y1
+ if ((cmp = seg0->xCur0 - seg1->xCur0) == 0) {
+ cmp = seg0->dxdy - seg1->dxdy;
+ }
+ return (cmp > 0) ? 1 : (cmp < 0) ? -1 : 0;
+ }
+
+ static int cmpXi(const void *p0, const void *p1) {
+ return cmpX(*(SplashXPathSeg **)p0, *(SplashXPathSeg **)p1);
+ }
+
+};
//------------------------------------------------------------------------
// SplashXPath
@@ -59,20 +88,18 @@ public:
~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();
+ int getXMin() { return xMin; }
+ int getXMax() { return xMax; }
+ int getYMin() { return yMin; }
+ int getYMax() { return yMax; }
private:
SplashXPath(SplashXPath *xPath);
void transform(SplashCoord *matrix, SplashCoord xi, SplashCoord yi,
SplashCoord *xo, SplashCoord *yo);
- void strokeAdjust(SplashXPathAdjust *adjust,
- SplashCoord *xp, SplashCoord *yp);
+ void strokeAdjust(SplashXPathPoint *pts,
+ SplashPathHint *hints, int nHints);
void grow(int nSegs);
void addCurve(SplashCoord x0, SplashCoord y0,
SplashCoord x1, SplashCoord y1,
@@ -85,6 +112,8 @@ private:
SplashXPathSeg *segs;
int length, size; // length and size of segs array
+ int xMin, xMax;
+ int yMin, yMax;
friend class SplashXPathScanner;
friend class SplashClip;
diff --git a/splash/SplashXPathScanner.cc b/splash/SplashXPathScanner.cc
index abe4593..aee60cc 100644
--- a/splash/SplashXPathScanner.cc
+++ b/splash/SplashXPathScanner.cc
@@ -2,6 +2,8 @@
//
// SplashXPathScanner.cc
//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
//========================================================================
#include <aconf.h>
@@ -16,523 +18,550 @@
#include <algorithm>
#endif
#include "gmem.h"
+#include "GList.h"
#include "SplashMath.h"
#include "SplashXPath.h"
-#include "SplashBitmap.h"
#include "SplashXPathScanner.h"
//------------------------------------------------------------------------
-struct SplashIntersect {
- int y;
- int x0, x1; // intersection of segment with [y, y+1)
- int count; // EO/NZWN counter increment
-};
-
-#if HAVE_STD_SORT
-
-struct cmpIntersectFunctor {
- bool operator()(const SplashIntersect &i0, const SplashIntersect &i1) {
- return (i0.y != i1.y) ? (i0.y < i1.y) : (i0.x0 < i1.x0);
- }
-};
-
-#else // HAVE_STD_SORT
+#define minVertStep 0.05
-static int cmpIntersect(const void *p0, const void *p1) {
- SplashIntersect *i0 = (SplashIntersect *)p0;
- SplashIntersect *i1 = (SplashIntersect *)p1;
- int cmp;
-
- if ((cmp = i0->y - i1->y) == 0) {
- cmp = i0->x0 - i1->x0;
- }
- return cmp;
-}
-
-#endif // HAVE_STD_SORT
-
-//------------------------------------------------------------------------
-// SplashXPathScanner
//------------------------------------------------------------------------
SplashXPathScanner::SplashXPathScanner(SplashXPath *xPathA, GBool eoA,
- int clipYMin, int clipYMax) {
- SplashXPathSeg *seg;
- SplashCoord xMinFP, yMinFP, xMaxFP, yMaxFP;
- int i;
-
+ int yMinA, int yMaxA) {
xPath = xPathA;
eo = eoA;
- partialClip = gFalse;
+ yMin = yMinA;
+ yMax = yMaxA;
- // 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;
+ activeSegs = new GList();
+ nextSeg = 0;
+ yNext = xPath->yMin;
+}
+
+SplashXPathScanner::~SplashXPathScanner() {
+ delete activeSegs;
+}
+
+void SplashXPathScanner::getSpan(Guchar *line, int y, int x0, int x1) {
+ SplashXPathSeg *seg, *seg0;
+ SplashCoord y0, y1, y1p;
+ GBool intersect, last;
+ int eoMask, state0, state1, count, i;
+
+ //--- clear the scan line buffer
+ memset(line + x0, 0, x1 - x0 + 1);
+
+ //--- reset the path
+ if (yNext != y) {
+ delete activeSegs;
+ activeSegs = new GList();
+ nextSeg = 0;
+ while (nextSeg < xPath->length) {
+ seg = &xPath->segs[nextSeg];
+ if (seg->y0 >= y) {
+ break;
}
- if (seg->flags & splashXPathFlip) {
- if (seg->y0 > yMaxFP) {
- yMaxFP = seg->y0;
- }
- } else {
- if (seg->y1 > yMaxFP) {
- yMaxFP = seg->y1;
+ if (seg->y0 != seg->y1 && seg->y1 > y) {
+ if (seg->y0 == y) {
+ seg->xCur0 = seg->x0;
+ } else {
+ seg->xCur0 = seg->x0 + ((SplashCoord)y - seg->y0) * seg->dxdy;
}
+ activeSegs->append(seg);
}
+ ++nextSeg;
}
- xMin = splashFloor(xMinFP);
- xMax = splashFloor(xMaxFP);
- yMin = splashFloor(yMinFP);
- yMax = splashFloor(yMaxFP);
- if (clipYMin > yMin) {
- yMin = clipYMin;
- partialClip = gTrue;
- }
- if (clipYMax < yMax) {
- yMax = clipYMax;
- partialClip = gTrue;
- }
+ activeSegs->sort(&SplashXPathSeg::cmpXi);
}
- allInter = NULL;
- inter = NULL;
- computeIntersections();
- interY = yMin - 1;
-}
+ //--- process the scan line
+ y0 = y;
+ while (y0 < y + 1) {
-SplashXPathScanner::~SplashXPathScanner() {
- gfree(inter);
- gfree(allInter);
-}
+ //--- delete finished segs
+ i = 0;
+ while (i < activeSegs->getLength()) {
+ seg = (SplashXPathSeg *)activeSegs->get(i);
+ if (seg->y1 <= y0) {
+ activeSegs->del(i);
+ } else {
+ ++i;
+ }
+ }
-void SplashXPathScanner::getBBoxAA(int *xMinA, int *yMinA,
- int *xMaxA, int *yMaxA) {
- *xMinA = xMin / splashAASize;
- *yMinA = yMin / splashAASize;
- *xMaxA = xMax / splashAASize;
- *yMaxA = yMax / splashAASize;
-}
+ //--- check for bottom of path
+ if (!activeSegs->getLength() && nextSeg >= xPath->length) {
+ break;
+ }
-void SplashXPathScanner::getSpanBounds(int y, int *spanXMin, int *spanXMax) {
- int interBegin, interEnd, xx, i;
+ //--- sort activeSegs
+ sortActiveSegs();
- if (y < yMin || y > yMax) {
- interBegin = interEnd = 0;
- } else {
- interBegin = inter[y - yMin];
- interEnd = inter[y - yMin + 1];
- }
- if (interBegin < interEnd) {
- *spanXMin = allInter[interBegin].x0;
- xx = allInter[interBegin].x1;
- for (i = interBegin + 1; i < interEnd; ++i) {
- if (allInter[i].x1 > xx) {
- xx = allInter[i].x1;
+ //--- add waiting segs
+ while (nextSeg < xPath->length) {
+ seg = &xPath->segs[nextSeg];
+ if (seg->y0 > y0) {
+ break;
+ }
+ if (seg->y0 != seg->y1) {
+ seg->xCur0 = seg->x0;
+ insertActiveSeg(seg);
}
+ ++nextSeg;
}
- *spanXMax = xx;
- } else {
- *spanXMin = xMax + 1;
- *spanXMax = xMax;
- }
-}
-GBool SplashXPathScanner::test(int x, int y) {
- int interBegin, interEnd, count, i;
-
- if (y < yMin || y > yMax) {
- return gFalse;
- }
- interBegin = inter[y - yMin];
- interEnd = inter[y - yMin + 1];
- count = 0;
- for (i = interBegin; i < interEnd && allInter[i].x0 <= x; ++i) {
- if (x <= allInter[i].x1) {
- return gTrue;
+ //--- get the next "interesting" y value
+ y1 = y + 1;
+ if (nextSeg < xPath->length && xPath->segs[nextSeg].y0 < y1) {
+ y1 = xPath->segs[nextSeg].y0;
+ }
+ for (i = 0; i < activeSegs->getLength(); ++i) {
+ seg = (SplashXPathSeg *)activeSegs->get(i);
+ if (seg->y1 < y1) {
+ y1 = seg->y1;
+ }
}
- count += allInter[i].count;
- }
- return eo ? (count & 1) : (count != 0);
-}
-GBool SplashXPathScanner::testSpan(int x0, int x1, int y) {
- int interBegin, interEnd, count, xx1, i;
+ //--- compute xCur1 values, check for intersections
+ seg0 = NULL;
+ intersect = gFalse;
+ for (i = 0; i < activeSegs->getLength(); ++i) {
+ seg = (SplashXPathSeg *)activeSegs->get(i);
+ if (seg->y1 == y1) {
+ seg->xCur1 = seg->x1;
+ } else {
+ seg->xCur1 = seg->x0 + (y1 - seg->y0) * seg->dxdy;
+ }
+ if (seg0 && seg0->xCur1 > seg->xCur1) {
+ intersect = gTrue;
+ }
+ seg0 = seg;
+ }
- if (y < yMin || y > yMax) {
- return gFalse;
- }
- interBegin = inter[y - yMin];
- interEnd = inter[y - yMin + 1];
- count = 0;
- for (i = interBegin; i < interEnd && allInter[i].x1 < x0; ++i) {
- count += allInter[i].count;
- }
+ //--- draw rectangles
+ if (intersect) {
+ for (; y0 < y1; y0 += minVertStep) {
+ if ((y1p = y0 + minVertStep) >= y1) {
+ y1p = y1;
+ last = gTrue;
+ } else {
+ last = gFalse;
+ }
+ state0 = state1 = count = 0;
+ seg0 = NULL;
+ eoMask = eo ? 1 : 0xffffffff;
+ for (i = 0; i < activeSegs->getLength(); ++i) {
+ seg = (SplashXPathSeg *)activeSegs->get(i);
+ if (last && seg->y1 == y1) {
+ seg->xCur1 = seg->x1;
+ } else {
+ seg->xCur1 = seg->x0 + (y1p - seg->y0) * seg->dxdy;
+ }
+ count += seg->count;
+ state1 = count & eoMask;
+ if (!state0 && state1) {
+ seg0 = seg;
+ } else if (state0 && !state1) {
+ drawRectangle(line, x0, x1, y0, y1p, seg0->xCur0, seg->xCur0);
+ }
+ state0 = state1;
+ }
+ for (i = 0; i < activeSegs->getLength(); ++i) {
+ seg = (SplashXPathSeg *)activeSegs->get(i);
+ seg->xCur0 = seg->xCur1;
+ }
+ sortActiveSegs();
+ }
- // invariant: the subspan [x0,xx1] is inside the path
- xx1 = x0 - 1;
- while (xx1 < x1) {
- if (i >= interEnd) {
- return gFalse;
- }
- if (allInter[i].x0 > xx1 + 1 &&
- !(eo ? (count & 1) : (count != 0))) {
- return gFalse;
- }
- if (allInter[i].x1 > xx1) {
- xx1 = allInter[i].x1;
+ //--- draw trapezoids
+ } else {
+ state0 = state1 = count = 0;
+ seg0 = NULL;
+ eoMask = eo ? 1 : 0xffffffff;
+ for (i = 0; i < activeSegs->getLength(); ++i) {
+ seg = (SplashXPathSeg *)activeSegs->get(i);
+ count += seg->count;
+ state1 = count & eoMask;
+ if (!state0 && state1) {
+ seg0 = seg;
+ } else if (state0 && !state1) {
+ drawTrapezoid(line, x0, x1, y0, y1,
+ seg0->xCur0, seg0->xCur1, seg0->dydx,
+ seg->xCur0, seg->xCur1, seg->dydx);
+ }
+ state0 = state1;
+ }
+ for (i = 0; i < activeSegs->getLength(); ++i) {
+ seg = (SplashXPathSeg *)activeSegs->get(i);
+ seg->xCur0 = seg->xCur1;
+ }
}
- count += allInter[i].count;
- ++i;
+
+ //--- next slice
+ y0 = y1;
}
- return gTrue;
+ yNext = y + 1;
}
-GBool SplashXPathScanner::getNextSpan(int y, int *x0, int *x1) {
- int interEnd, xx0, xx1;
-
- if (y < yMin || y > yMax) {
- return gFalse;
- }
- if (interY != y) {
- interY = y;
- interIdx = inter[y - yMin];
- interCount = 0;
- }
- interEnd = inter[y - yMin + 1];
- if (interIdx >= interEnd) {
- return gFalse;
+void SplashXPathScanner::getSpanBinary(Guchar *line, int y, int x0, int x1) {
+ SplashXPathSeg *seg;
+ int xx0, xx1, xx;
+ int eoMask, state0, state1, count, i;
+
+ //--- clear the scan line buffer
+ memset(line + x0, 0, x1 - x0 + 1);
+
+ //--- reset the path
+ if (yNext != y) {
+ delete activeSegs;
+ activeSegs = new GList();
+ nextSeg = 0;
+ while (nextSeg < xPath->length) {
+ seg = &xPath->segs[nextSeg];
+ if (seg->y0 >= y) {
+ break;
+ }
+ if (seg->y1 > y) {
+ if (seg->y0 == y) {
+ seg->xCur0 = seg->x0;
+ } else {
+ seg->xCur0 = seg->x0 + ((SplashCoord)y - seg->y0) * seg->dxdy;
+ }
+ activeSegs->append(seg);
+ }
+ ++nextSeg;
+ }
+ activeSegs->sort(&SplashXPathSeg::cmpXi);
}
- xx0 = allInter[interIdx].x0;
- xx1 = allInter[interIdx].x1;
- interCount += allInter[interIdx].count;
- ++interIdx;
- while (interIdx < interEnd &&
- (allInter[interIdx].x0 <= xx1 ||
- (eo ? (interCount & 1) : (interCount != 0)))) {
- if (allInter[interIdx].x1 > xx1) {
- xx1 = allInter[interIdx].x1;
+
+ //--- delete finished segs
+ i = 0;
+ while (i < activeSegs->getLength()) {
+ seg = (SplashXPathSeg *)activeSegs->get(i);
+ if (seg->y1 <= y) {
+ activeSegs->del(i);
+ } else {
+ ++i;
}
- interCount += allInter[interIdx].count;
- ++interIdx;
}
- *x0 = xx0;
- *x1 = xx1;
- return gTrue;
-}
-void SplashXPathScanner::computeIntersections() {
- SplashXPathSeg *seg;
- SplashCoord segXMin, segXMax, segYMin, segYMax, xx0, xx1;
- int x, y, y0, y1, i;
+ //--- sort activeSegs
+ sortActiveSegs();
- if (yMin > yMax) {
- return;
+ //--- add waiting segs
+ while (nextSeg < xPath->length) {
+ seg = &xPath->segs[nextSeg];
+ if (seg->y0 >= y + 1) {
+ break;
+ }
+ seg->xCur0 = seg->x0;
+ insertActiveSeg(seg);
+ ++nextSeg;
}
- // build the list of all intersections
- allInterLen = 0;
- allInterSize = 16;
- allInter = (SplashIntersect *)gmallocn(allInterSize,
- sizeof(SplashIntersect));
- for (i = 0; i < xPath->length; ++i) {
- seg = &xPath->segs[i];
- if (seg->flags & splashXPathFlip) {
- segYMin = seg->y1;
- segYMax = seg->y0;
+ //--- compute xCur1 values
+ for (i = 0; i < activeSegs->getLength(); ++i) {
+ seg = (SplashXPathSeg *)activeSegs->get(i);
+ if (seg->y1 <= y + 1) {
+ seg->xCur1 = seg->x1;
} else {
- segYMin = seg->y0;
- segYMax = seg->y1;
+ seg->xCur1 = seg->x0 + ((SplashCoord)(y + 1) - seg->y0) * seg->dxdy;
}
- if (seg->flags & splashXPathHoriz) {
- y = splashFloor(seg->y0);
- if (y >= yMin && y <= yMax) {
- addIntersection(segYMin, segYMax, seg->flags,
- y, splashFloor(seg->x0), splashFloor(seg->x1));
- }
- } else if (seg->flags & splashXPathVert) {
- y0 = splashFloor(segYMin);
- if (y0 < yMin) {
- y0 = yMin;
+ }
+
+ //--- draw spans
+ state0 = state1 = count = 0;
+ eoMask = eo ? 1 : 0xffffffff;
+ xx0 = xx1 = 0; // make gcc happy
+ for (i = 0; i < activeSegs->getLength(); ++i) {
+ seg = (SplashXPathSeg *)activeSegs->get(i);
+ if (seg->y0 <= y && seg->y0 < seg->y1) {
+ count += seg->count;
+ state1 = count & eoMask;
+ }
+ if (state0) {
+ xx = splashCeil(seg->xCur0) - 1;
+ if (xx > xx1) {
+ xx1 = xx;
}
- y1 = splashFloor(segYMax);
- if (y1 > yMax) {
- y1 = yMax;
+ xx = splashFloor(seg->xCur1);
+ if (xx < xx0) {
+ xx0 = xx;
}
- x = splashFloor(seg->x0);
- for (y = y0; y <= y1; ++y) {
- addIntersection(segYMin, segYMax, seg->flags, y, x, x);
+ xx = splashCeil(seg->xCur1) - 1;
+ if (xx > xx1) {
+ xx1 = xx;
}
} else {
- if (seg->x0 < seg->x1) {
- segXMin = seg->x0;
- segXMax = seg->x1;
+ if (seg->xCur0 < seg->xCur1) {
+ xx0 = splashFloor(seg->xCur0);
+ xx1 = splashCeil(seg->xCur1) - 1;
} else {
- segXMin = seg->x1;
- segXMax = seg->x0;
+ xx0 = splashFloor(seg->xCur1);
+ xx1 = splashCeil(seg->xCur0) - 1;
}
- y0 = splashFloor(segYMin);
- if (y0 < yMin) {
- y0 = yMin;
+ }
+ if (!state1) {
+ if (xx0 < x0) {
+ xx0 = x0;
}
- y1 = splashFloor(segYMax);
- if (y1 > yMax) {
- y1 = yMax;
+ if (xx1 > x1) {
+ xx1 = x1;
}
- // this loop could just add seg->dxdy to xx1 on each iteration,
- // but that introduces numerical accuracy problems
- xx1 = seg->x0 + ((SplashCoord)y0 - seg->y0) * seg->dxdy;
- for (y = y0; y <= y1; ++y) {
- xx0 = xx1;
- 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 < segXMin) {
- xx0 = segXMin;
- } else if (xx0 > segXMax) {
- xx0 = segXMax;
- }
- if (xx1 < segXMin) {
- xx1 = segXMin;
- } else if (xx1 > segXMax) {
- xx1 = segXMax;
- }
- addIntersection(segYMin, segYMax, seg->flags, y,
- splashFloor(xx0), splashFloor(xx1));
+ for (xx = xx0; xx <= xx1; ++xx) {
+ line[xx] = 0xff;
}
}
+ state0 = state1;
}
-#if HAVE_STD_SORT
- std::sort(allInter, allInter + allInterLen, cmpIntersectFunctor());
-#else
- qsort(allInter, allInterLen, sizeof(SplashIntersect), cmpIntersect);
-#endif
- // build the list of y pointers
- inter = (int *)gmallocn(yMax - yMin + 2, sizeof(int));
- i = 0;
- for (y = yMin; y <= yMax; ++y) {
- inter[y - yMin] = i;
- while (i < allInterLen && allInter[i].y <= y) {
- ++i;
- }
+ //--- update xCur0 values
+ for (i = 0; i < activeSegs->getLength(); ++i) {
+ seg = (SplashXPathSeg *)activeSegs->get(i);
+ seg->xCur0 = seg->xCur1;
}
- inter[yMax - yMin + 1] = i;
+
+ yNext = y + 1;
}
-void SplashXPathScanner::addIntersection(double segYMin, double segYMax,
- Guint segFlags,
- int y, int x0, int x1) {
- if (allInterLen == allInterSize) {
- allInterSize *= 2;
- allInter = (SplashIntersect *)greallocn(allInter, allInterSize,
- sizeof(SplashIntersect));
+inline void SplashXPathScanner::addArea(Guchar *line, int x, SplashCoord a) {
+ int a2, t;
+
+ a2 = splashRound(a * 255);
+ if (a2 <= 0) {
+ return;
+ }
+ t = line[x] + a2;
+ if (t > 255) {
+ t = 255;
+ }
+ line[x] = t;
+}
+
+// Draw a trapezoid with edges:
+// top: (xa0, y0) - (xb0, y0)
+// left: (xa0, y0) - (xa1, y1)
+// right: (xb0, y0) - (xb1, y1)
+// bottom: (xa1, y1) - (xb1, y1)
+void SplashXPathScanner::drawTrapezoid(Guchar *line, int xMin, int xMax,
+ SplashCoord y0, SplashCoord y1,
+ SplashCoord xa0, SplashCoord xa1,
+ SplashCoord dydxa,
+ SplashCoord xb0, SplashCoord xb1,
+ SplashCoord dydxb) {
+ SplashCoord a, dy;
+ int x0, x1, x2, x3, x;
+
+ // check for a rectangle
+ if (dydxa == 0 && dydxb == 0 && xa0 >= xMin && xb0 <= xMax) {
+ x0 = splashFloor(xa0);
+ x3 = splashFloor(xb0);
+ dy = y1 - y0;
+ if (x0 == x3) {
+ addArea(line, x0, (xb0 - xa0) * dy);
+ } else {
+ addArea(line, x0, ((SplashCoord)1 - (xa0 - x0)) * dy);
+ for (x = x0 + 1; x <= x3 - 1; ++x) {
+ addArea(line, x, y1 - y0);
+ }
+ addArea(line, x3, (xb0 - x3) * (y1 - y0));
+ }
+ return;
}
- allInter[allInterLen].y = y;
- if (x0 < x1) {
- allInter[allInterLen].x0 = x0;
- allInter[allInterLen].x1 = x1;
+
+ if (dydxa > 0) {
+ x0 = splashFloor(xa0);
+ x1 = splashFloor(xa1);
} else {
- allInter[allInterLen].x0 = x1;
- allInter[allInterLen].x1 = x0;
+ x0 = splashFloor(xa1);
+ x1 = splashFloor(xa0);
+ }
+ if (x0 < xMin) {
+ x0 = xMin;
}
- if (segYMin <= y &&
- (SplashCoord)y < segYMax &&
- !(segFlags & splashXPathHoriz)) {
- allInter[allInterLen].count = eo ? 1
- : (segFlags & splashXPathFlip) ? 1 : -1;
+ if (dydxb > 0) {
+ x2 = splashFloor(xb0);
+ x3 = splashFloor(xb1);
} else {
- allInter[allInterLen].count = 0;
+ x2 = splashFloor(xb1);
+ x3 = splashFloor(xb0);
+ }
+ if (x3 > xMax) {
+ x3 = xMax;
+ }
+ for (x = x0; x <= x3; ++x) {
+ a = y1 - y0;
+ if (x <= x1) {
+ a -= areaLeft(x, xa0, y0, xa1, y1, dydxa);
+ }
+ if (x >= x2) {
+ a -= areaRight(x, xb0, y0, xb1, y1, dydxb);
+ }
+ addArea(line, x, a);
}
- ++allInterLen;
}
-void SplashXPathScanner::renderAALine(SplashBitmap *aaBuf,
- int *x0, int *x1, int y) {
- int xx0, xx1, xx, xxMin, xxMax, yy, interEnd;
- Guchar mask;
- SplashColorPtr p;
-
- memset(aaBuf->getDataPtr(), 0, aaBuf->getRowSize() * aaBuf->getHeight());
- xxMin = aaBuf->getWidth();
- xxMax = -1;
- if (yMin <= yMax) {
- if (splashAASize * y < yMin) {
- interIdx = inter[0];
- } else if (splashAASize * y > yMax) {
- interIdx = inter[yMax - yMin + 1];
+// Compute area within a pixel slice ((xp,y0)-(xp+1,y1)) to the left
+// of a trapezoid edge ((x0,y0)-(x1,y1)).
+SplashCoord SplashXPathScanner::areaLeft(int xp,
+ SplashCoord x0, SplashCoord y0,
+ SplashCoord x1, SplashCoord y1,
+ SplashCoord dydx) {
+ SplashCoord a, ya, yb;
+
+ if (dydx >= 0) {
+ if (x0 >= xp) {
+ if (x1 <= xp + 1) {
+ a = ((x0 + x1) * 0.5 - xp) * (y1 - y0);
+ } else {
+ yb = y0 + ((SplashCoord)(xp + 1) - x0) * dydx;
+ a = (y1 - y0) - ((SplashCoord)(xp + 1) - x0) * (yb - y0) * 0.5;
+ }
} else {
- interIdx = inter[splashAASize * y - yMin];
+ if (x1 <= xp + 1) {
+ ya = y0 + ((SplashCoord)xp - x0) * dydx;
+ a = (x1 - xp) * (y1 - ya) * 0.5;
+ } else {
+ // ya = y1 - (x1 - xp - 0.5) * dydx;
+ // a = y1 - ya;
+ a = (x1 - xp - 0.5) * dydx;
+ }
}
- for (yy = 0; yy < splashAASize; ++yy) {
- if (splashAASize * y + yy < yMin) {
- interEnd = inter[0];
- } else if (splashAASize * y + yy > yMax) {
- interEnd = inter[yMax - yMin + 1];
+ } else {
+ if (x0 <= xp + 1) {
+ if (x1 >= xp) {
+ a = ((x0 + x1) * 0.5 - xp) * (y1 - y0);
} else {
- interEnd = inter[splashAASize * y + yy - yMin + 1];
+ ya = y0 + ((SplashCoord)xp - x0) * dydx;
+ a = (x0 - xp) * (ya - y0) * 0.5;
}
- interCount = 0;
- while (interIdx < interEnd) {
- xx0 = allInter[interIdx].x0;
- xx1 = allInter[interIdx].x1;
- interCount += allInter[interIdx].count;
- ++interIdx;
- while (interIdx < interEnd &&
- (allInter[interIdx].x0 <= xx1 ||
- (eo ? (interCount & 1) : (interCount != 0)))) {
- if (allInter[interIdx].x1 > xx1) {
- xx1 = allInter[interIdx].x1;
- }
- interCount += allInter[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;
- }
+ } else {
+ if (x1 >= xp) {
+ yb = y0 + ((SplashCoord)(xp + 1) - x0) * dydx;
+ a = (y1 - y0) - ((SplashCoord)(xp + 1) - x1) * (y1 - yb) * 0.5;
+ } else {
+ // ya = y0 + (xp - x0 + 0.5) * dydx;
+ // a = ya - y0;
+ a = ((SplashCoord)xp - x0 + 0.5) * dydx;
}
}
}
- *x0 = xxMin / splashAASize;
- *x1 = (xxMax - 1) / splashAASize;
+ return a;
}
-void SplashXPathScanner::clipAALine(SplashBitmap *aaBuf,
- int *x0, int *x1, int y) {
- int xx0, xx1, xx, yy, interEnd;
- Guchar mask;
- SplashColorPtr p;
-
- for (yy = 0; yy < splashAASize; ++yy) {
- xx = *x0 * splashAASize;
- if (yMin <= yMax) {
- if (splashAASize * y + yy < yMin) {
- interIdx = interEnd = inter[0];
- } else if (splashAASize * y + yy > yMax) {
- interIdx = interEnd = inter[yMax - yMin + 1];
+// Compute area within a pixel slice ((xp,y0)-(xp+1,y1)) to the left
+// of a trapezoid edge ((x0,y0)-(x1,y1)).
+SplashCoord SplashXPathScanner::areaRight(int xp,
+ SplashCoord x0, SplashCoord y0,
+ SplashCoord x1, SplashCoord y1,
+ SplashCoord dydx) {
+ SplashCoord a, ya, yb;
+
+ if (dydx >= 0) {
+ if (x0 >= xp) {
+ if (x1 <= xp + 1) {
+ a = ((SplashCoord)(xp + 1) - (x0 + x1) * 0.5) * (y1 - y0);
} else {
- interIdx = inter[splashAASize * y + yy - yMin];
- if (splashAASize * y + yy > yMax) {
- interEnd = inter[yMax - yMin + 1];
- } else {
- interEnd = inter[splashAASize * y + yy - yMin + 1];
- }
+ yb = y0 + ((SplashCoord)(xp + 1) - x0) * dydx;
+ a = ((SplashCoord)(xp + 1) - x0) * (yb - y0) * 0.5;
}
- interCount = 0;
- while (interIdx < interEnd && xx < (*x1 + 1) * splashAASize) {
- xx0 = allInter[interIdx].x0;
- xx1 = allInter[interIdx].x1;
- interCount += allInter[interIdx].count;
- ++interIdx;
- while (interIdx < interEnd &&
- (allInter[interIdx].x0 <= xx1 ||
- (eo ? (interCount & 1) : (interCount != 0)))) {
- if (allInter[interIdx].x1 > xx1) {
- xx1 = allInter[interIdx].x1;
- }
- interCount += allInter[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;
- }
+ } else {
+ if (x1 <= xp + 1) {
+ ya = y0 + ((SplashCoord)xp - x0) * dydx;
+ a = (y1 - y0) - (x1 - xp) * (y1 - ya) * 0.5;
+ } else {
+ // ya = y0 + (xp - x0 + 0.5) * dydx;
+ // a = ya - y0;
+ a = ((SplashCoord)xp + 0.5 - x0) * dydx;
}
}
- 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;
+ } else {
+ if (x0 <= xp + 1) {
+ if (x1 >= xp) {
+ a = ((SplashCoord)(xp + 1) - (x0 + x1) * 0.5) * (y1 - y0);
+ } else {
+ ya = y0 + ((SplashCoord)xp - x0) * dydx;
+ a = (y1 - y0) - (x0 - xp) * (ya - y0) * 0.5;
}
- for (; xx + 7 < xx0; xx += 8) {
- *p++ = 0x00;
+ } else {
+ if (x1 >= xp) {
+ yb = y0 + ((SplashCoord)(xp + 1) - x0) * dydx;
+ a = ((SplashCoord)(xp + 1) - x1) * (y1 - yb) * 0.5;
+ } else {
+ // ya = y1 - (x1 - xp - 0.5) * dydx;
+ // a = y1 - ya;
+ a = (x1 - xp - 0.5) * dydx;
}
- if (xx < xx0) {
- *p &= 0xff >> (xx0 & 7);
+ }
+ }
+ return a;
+}
+
+void SplashXPathScanner::drawRectangle(Guchar *line, int xMin, int xMax,
+ SplashCoord y0, SplashCoord y1,
+ SplashCoord x0, SplashCoord x1) {
+ SplashCoord dy, a;
+ int xx0, xx1, x;
+
+ xx0 = splashFloor(x0);
+ if (xx0 < xMin) {
+ xx0 = xMin;
+ }
+ xx1 = splashFloor(x1);
+ if (xx1 > xMax) {
+ xx1 = xMax;
+ }
+ dy = y1 - y0;
+ for (x = xx0; x <= xx1; ++x) {
+ a = dy;
+ if ((SplashCoord)x < x0) {
+ a -= (x0 - x) * dy;
+ }
+ if ((SplashCoord)(x + 1) > x1) {
+ a -= ((SplashCoord)(x + 1) - x1) * dy;
+ }
+ addArea(line, x, a);
+ }
+}
+
+void SplashXPathScanner::sortActiveSegs() {
+ SplashXPathSeg *seg0, *seg1;
+ int i, j, k;
+
+ if (activeSegs->getLength() < 2) {
+ return;
+ }
+ seg0 = (SplashXPathSeg *)activeSegs->get(0);
+ for (i = 1; i < activeSegs->getLength(); ++i) {
+ seg1 = (SplashXPathSeg *)activeSegs->get(i);
+ if (SplashXPathSeg::cmpX(seg0, seg1) > 0) {
+ for (j = i - 1; j > 0; --j) {
+ if (SplashXPathSeg::cmpX((SplashXPathSeg *)activeSegs->get(j - 1),
+ seg1) <= 0) {
+ break;
+ }
}
+ for (k = i; k > j; --k) {
+ activeSegs->put(k, activeSegs->get(k-1));
+ }
+ activeSegs->put(j, seg1);
+ } else {
+ seg0 = seg1;
+ }
+ }
+}
+
+void SplashXPathScanner::insertActiveSeg(SplashXPathSeg *seg) {
+ int i;
+
+ for (i = 0; i < activeSegs->getLength(); ++i) {
+ if (SplashXPathSeg::cmpX(seg, (SplashXPathSeg *)activeSegs->get(i)) < 0) {
+ break;
}
}
+ activeSegs->insert(i, seg);
}
diff --git a/splash/SplashXPathScanner.h b/splash/SplashXPathScanner.h
index 5bb3aaf..519ff4d 100644
--- a/splash/SplashXPathScanner.h
+++ b/splash/SplashXPathScanner.h
@@ -2,6 +2,8 @@
//
// SplashXPathScanner.h
//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
//========================================================================
#ifndef SPLASHXPATHSCANNER_H
@@ -15,9 +17,8 @@
#include "SplashTypes.h"
+class GList;
class SplashXPath;
-class SplashBitmap;
-struct SplashIntersect;
//------------------------------------------------------------------------
// SplashXPathScanner
@@ -28,68 +29,47 @@ public:
// Create a new SplashXPathScanner object. <xPathA> must be sorted.
SplashXPathScanner(SplashXPath *xPathA, GBool eoA,
- int clipYMin, int clipYMax);
+ int yMinA, int yMaxA);
~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);
-
- // Returns true if at least part of the path was outside the
- // clipYMin/clipYMax bounds passed to the constructor.
- GBool hasPartialClip() { return partialClip; }
-
- // 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);
+ // Compute shape values for a scan line. Fills in line[] with shape
+ // values for one scan line: ([x0, x1], y). The values are in [0,
+ // 255].
+ void getSpan(Guchar *line, int y, int x0, int x1);
- // 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);
+ // Like getSpan(), but uses the values 0 and 255 only. Writes 255
+ // for all pixels which include non-zero area inside the path.
+ void getSpanBinary(Guchar *line, int y, int x0, int x1);
private:
- void computeIntersections();
- void addIntersection(double segYMin, double segYMax,
- Guint segFlags,
- int y, int x0, int x1);
+ inline void addArea(Guchar *line, int x, SplashCoord a);
+ void drawTrapezoid(Guchar *line, int xMin, int xMax,
+ SplashCoord y0, SplashCoord y1,
+ SplashCoord xa0, SplashCoord xa1, SplashCoord dydxa,
+ SplashCoord xb0, SplashCoord xb1, SplashCoord dydxb);
+ SplashCoord areaLeft(int xp,
+ SplashCoord x0, SplashCoord y0,
+ SplashCoord x1, SplashCoord y1,
+ SplashCoord dydx);
+ SplashCoord areaRight(int xp,
+ SplashCoord x0, SplashCoord y0,
+ SplashCoord x1, SplashCoord y1,
+ SplashCoord dydx);
+ void drawRectangle(Guchar *line, int xMin, int xMax,
+ SplashCoord y0, SplashCoord y1,
+ SplashCoord x0, SplashCoord x1);
+ void sortActiveSegs();
+ void insertActiveSeg(SplashXPathSeg *seg);
SplashXPath *xPath;
GBool eo;
- int xMin, yMin, xMax, yMax;
- GBool partialClip;
-
- SplashIntersect *allInter; // array of intersections
- int allInterLen; // number of intersections in <allInter>
- int allInterSize; // size of the <allInter> array
- int *inter; // indexes into <allInter> for each y value
- int interY; // current y value - used by getNextSpan
- int interIdx; // current index into <inter> - used by
- // getNextSpan
- int interCount; // current EO/NZWN counter - used by
- // getNextSpan
+ int yMin, yMax;
+
+ GList *activeSegs; // [SplashXPathSeg]
+ int nextSeg;
+ int yNext;
};
#endif
diff --git a/splash/vms_make.com b/splash/vms_make.com
deleted file mode 100644
index e69de29..0000000
--- a/splash/vms_make.com
+++ /dev/null
diff --git a/vms_make.com b/vms_make.com
deleted file mode 100644
index d70caad..0000000
--- a/vms_make.com
+++ /dev/null
@@ -1,736 +0,0 @@
-$!========================================================================
-$!
-$! 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/AcroForm.cc b/xpdf/AcroForm.cc
new file mode 100644
index 0000000..86e7037
--- /dev/null
+++ b/xpdf/AcroForm.cc
@@ -0,0 +1,1897 @@
+//========================================================================
+//
+// AcroForm.cc
+//
+// Copyright 2012 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <math.h>
+#include "gmem.h"
+#include "GString.h"
+#include "GList.h"
+#include "Error.h"
+#include "Object.h"
+#include "PDFDoc.h"
+#include "TextString.h"
+#include "Gfx.h"
+#include "GfxFont.h"
+#include "OptionalContent.h"
+#include "Annot.h"
+#include "Lexer.h"
+#include "AcroForm.h"
+
+//------------------------------------------------------------------------
+
+#define acroFormFlagReadOnly (1 << 0) // all
+#define acroFormFlagRequired (1 << 1) // all
+#define acroFormFlagNoExport (1 << 2) // all
+#define acroFormFlagMultiline (1 << 12) // text
+#define acroFormFlagPassword (1 << 13) // text
+#define acroFormFlagNoToggleToOff (1 << 14) // button
+#define acroFormFlagRadio (1 << 15) // button
+#define acroFormFlagPushbutton (1 << 16) // button
+#define acroFormFlagCombo (1 << 17) // choice
+#define acroFormFlagEdit (1 << 18) // choice
+#define acroFormFlagSort (1 << 19) // choice
+#define acroFormFlagFileSelect (1 << 20) // text
+#define acroFormFlagMultiSelect (1 << 21) // choice
+#define acroFormFlagDoNotSpellCheck (1 << 22) // text, choice
+#define acroFormFlagDoNotScroll (1 << 23) // text
+#define acroFormFlagComb (1 << 24) // text
+#define acroFormFlagRadiosInUnison (1 << 25) // button
+#define acroFormFlagRichText (1 << 25) // text
+#define acroFormFlagCommitOnSelChange (1 << 26) // choice
+
+#define acroFormQuadLeft 0
+#define acroFormQuadCenter 1
+#define acroFormQuadRight 2
+
+#define annotFlagHidden 0x0002
+#define annotFlagPrint 0x0004
+#define annotFlagNoView 0x0020
+
+// distance of Bezier control point from center for circle approximation
+// = (4 * (sqrt(2) - 1) / 3) * r
+#define bezierCircle 0.55228475
+
+//------------------------------------------------------------------------
+
+// map an annotation ref to a page number
+class AcroFormAnnotPage {
+public:
+ AcroFormAnnotPage(int annotNumA, int annotGenA, int pageNumA)
+ { annotNum = annotNumA; annotGen = annotGenA; pageNum = pageNumA; }
+ int annotNum;
+ int annotGen;
+ int pageNum;
+};
+
+//------------------------------------------------------------------------
+// AcroForm
+//------------------------------------------------------------------------
+
+AcroForm *AcroForm::load(PDFDoc *docA, Catalog *catalog, Object *acroFormObjA) {
+ AcroForm *acroForm;
+ Object fieldsObj, obj1, obj2;
+ int i;
+
+ acroForm = new AcroForm(docA, acroFormObjA);
+
+ if (acroFormObjA->dictLookup("NeedAppearances", &obj1)->isBool()) {
+ acroForm->needAppearances = obj1.getBool();
+ }
+ obj1.free();
+
+ acroForm->buildAnnotPageList(catalog);
+
+ if (!acroFormObjA->dictLookup("Fields", &obj1)->isArray()) {
+ if (!obj1.isNull()) {
+ error(errSyntaxError, -1, "AcroForm Fields entry is wrong type");
+ }
+ obj1.free();
+ delete acroForm;
+ return NULL;
+ }
+ for (i = 0; i < obj1.arrayGetLength(); ++i) {
+ obj1.arrayGetNF(i, &obj2);
+ acroForm->scanField(&obj2);
+ obj2.free();
+ }
+ obj1.free();
+
+ return acroForm;
+}
+
+AcroForm::AcroForm(PDFDoc *docA, Object *acroFormObjA): Form(docA) {
+ acroFormObjA->copy(&acroFormObj);
+ needAppearances = gFalse;
+ annotPages = new GList();
+ fields = new GList();
+}
+
+AcroForm::~AcroForm() {
+ acroFormObj.free();
+ deleteGList(annotPages, AcroFormAnnotPage);
+ deleteGList(fields, AcroFormField);
+}
+
+void AcroForm::buildAnnotPageList(Catalog *catalog) {
+ Object annotsObj, annotObj;
+ int pageNum, i;
+
+ for (pageNum = 1; pageNum <= catalog->getNumPages(); ++pageNum) {
+ if (catalog->getPage(pageNum)->getAnnots(&annotsObj)->isArray()) {
+ for (i = 0; i < annotsObj.arrayGetLength(); ++i) {
+ if (annotsObj.arrayGetNF(i, &annotObj)->isRef()) {
+ annotPages->append(new AcroFormAnnotPage(annotObj.getRefNum(),
+ annotObj.getRefGen(),
+ pageNum));
+ }
+ annotObj.free();
+ }
+ }
+ annotsObj.free();
+ }
+ //~ sort the list
+}
+
+int AcroForm::lookupAnnotPage(Object *annotRef) {
+ AcroFormAnnotPage *annotPage;
+ int num, gen, i;
+
+ if (!annotRef->isRef()) {
+ return 0;
+ }
+ num = annotRef->getRefNum();
+ gen = annotRef->getRefGen();
+ //~ use bin search
+ for (i = 0; i < annotPages->getLength(); ++i) {
+ annotPage = (AcroFormAnnotPage *)annotPages->get(i);
+ if (annotPage->annotNum == num && annotPage->annotGen == gen) {
+ return annotPage->pageNum;
+ }
+ }
+ return 0;
+}
+
+void AcroForm::scanField(Object *fieldRef) {
+ AcroFormField *field;
+ Object fieldObj, kidsObj, kidRef, kidObj, subtypeObj;
+ GBool isTerminal;
+ int i;
+
+ fieldRef->fetch(doc->getXRef(), &fieldObj);
+ if (!fieldObj.isDict()) {
+ error(errSyntaxError, -1, "AcroForm field object is wrong type");
+ fieldObj.free();
+ return;
+ }
+
+ // if this field has a Kids array, and all of the kids have a Parent
+ // reference (i.e., they're all form fields, not widget
+ // annotations), then this is a non-terminal field, and we need to
+ // scan the kids
+ isTerminal = gTrue;
+ if (fieldObj.dictLookup("Kids", &kidsObj)->isArray()) {
+ isTerminal = gFalse;
+ for (i = 0; !isTerminal && i < kidsObj.arrayGetLength(); ++i) {
+ kidsObj.arrayGet(i, &kidObj);
+ if (kidObj.isDict()) {
+ if (kidObj.dictLookup("Parent", &subtypeObj)->isNull()) {
+ isTerminal = gTrue;
+ }
+ subtypeObj.free();
+ }
+ kidObj.free();
+ }
+ if (!isTerminal) {
+ for (i = 0; !isTerminal && i < kidsObj.arrayGetLength(); ++i) {
+ kidsObj.arrayGetNF(i, &kidRef);
+ scanField(&kidRef);
+ kidRef.free();
+ }
+ }
+ }
+ kidsObj.free();
+
+ if (isTerminal) {
+ if ((field = AcroFormField::load(this, fieldRef))) {
+ fields->append(field);
+ }
+ }
+
+ fieldObj.free();
+}
+
+void AcroForm::draw(int pageNum, Gfx *gfx, GBool printing) {
+ int i;
+
+ for (i = 0; i < fields->getLength(); ++i) {
+ ((AcroFormField *)fields->get(i))->draw(pageNum, gfx, printing);
+ }
+}
+
+int AcroForm::getNumFields() {
+ return fields->getLength();
+}
+
+FormField *AcroForm::getField(int idx) {
+ return (AcroFormField *)fields->get(idx);
+}
+
+//------------------------------------------------------------------------
+// AcroFormField
+//------------------------------------------------------------------------
+
+AcroFormField *AcroFormField::load(AcroForm *acroFormA, Object *fieldRefA) {
+ GString *typeStr;
+ TextString *nameA;
+ Guint flagsA;
+ GBool haveFlags;
+ Object fieldObjA, parentObj, parentObj2, obj1, obj2;
+ AcroFormFieldType typeA;
+ AcroFormField *field;
+
+ fieldRefA->fetch(acroFormA->doc->getXRef(), &fieldObjA);
+
+ //----- get field info
+
+ if (fieldObjA.dictLookup("T", &obj1)->isString()) {
+ nameA = new TextString(obj1.getString());
+ } else {
+ nameA = new TextString();
+ }
+ obj1.free();
+
+ if (fieldObjA.dictLookup("FT", &obj1)->isName()) {
+ typeStr = new GString(obj1.getName());
+ } else {
+ typeStr = NULL;
+ }
+ obj1.free();
+
+ if (fieldObjA.dictLookup("Ff", &obj1)->isInt()) {
+ flagsA = (Guint)obj1.getInt();
+ haveFlags = gTrue;
+ } else {
+ flagsA = 0;
+ haveFlags = gFalse;
+ }
+ obj1.free();
+
+ //----- get info from parent non-terminal fields
+
+ fieldObjA.dictLookup("Parent", &parentObj);
+ while (parentObj.isDict()) {
+
+ if (parentObj.dictLookup("T", &obj1)->isString()) {
+ if (nameA->getLength()) {
+ nameA->insert(0, (Unicode)'.');
+ }
+ nameA->insert(0, obj1.getString());
+ }
+ obj1.free();
+
+ if (!typeStr) {
+ if (parentObj.dictLookup("FT", &obj1)->isName()) {
+ typeStr = new GString(obj1.getName());
+ }
+ obj1.free();
+ }
+
+ if (!haveFlags) {
+ if (parentObj.dictLookup("Ff", &obj1)->isInt()) {
+ flagsA = (Guint)obj1.getInt();
+ haveFlags = gTrue;
+ }
+ obj1.free();
+ }
+
+ parentObj.dictLookup("Parent", &parentObj2);
+ parentObj.free();
+ parentObj = parentObj2;
+ }
+ parentObj.free();
+
+ if (!typeStr) {
+ error(errSyntaxError, -1, "Missing type in AcroForm field");
+ goto err1;
+ } else if (!typeStr->cmp("Btn")) {
+ if (flagsA & acroFormFlagPushbutton) {
+ typeA = acroFormFieldPushbutton;
+ } else if (flagsA & acroFormFlagRadio) {
+ typeA = acroFormFieldRadioButton;
+ } else {
+ typeA = acroFormFieldCheckbox;
+ }
+ } else if (!typeStr->cmp("Tx")) {
+ if (flagsA & acroFormFlagFileSelect) {
+ typeA = acroFormFieldFileSelect;
+ } else if (flagsA & acroFormFlagMultiline) {
+ typeA = acroFormFieldMultilineText;
+ } else {
+ typeA = acroFormFieldText;
+ }
+ } else if (!typeStr->cmp("Ch")) {
+ if (flagsA & acroFormFlagCombo) {
+ typeA = acroFormFieldComboBox;
+ } else {
+ typeA = acroFormFieldListBox;
+ }
+ } else if (!typeStr->cmp("Sig")) {
+ typeA = acroFormFieldSignature;
+ } else {
+ error(errSyntaxError, -1, "Invalid type in AcroForm field");
+ goto err1;
+ }
+ delete typeStr;
+
+ field = new AcroFormField(acroFormA, fieldRefA, &fieldObjA,
+ typeA, nameA, flagsA);
+ fieldObjA.free();
+ return field;
+
+ err1:
+ delete typeStr;
+ delete nameA;
+ fieldObjA.free();
+ return NULL;
+}
+
+AcroFormField::AcroFormField(AcroForm *acroFormA,
+ Object *fieldRefA, Object *fieldObjA,
+ AcroFormFieldType typeA, TextString *nameA,
+ Guint flagsA) {
+ acroForm = acroFormA;
+ fieldRefA->copy(&fieldRef);
+ fieldObjA->copy(&fieldObj);
+ type = typeA;
+ name = nameA;
+ flags = flagsA;
+}
+
+AcroFormField::~AcroFormField() {
+ fieldRef.free();
+ fieldObj.free();
+ delete name;
+}
+
+const char *AcroFormField::getType() {
+ switch (type) {
+ case acroFormFieldPushbutton: return "PushButton";
+ case acroFormFieldRadioButton: return "RadioButton";
+ case acroFormFieldCheckbox: return "Checkbox";
+ case acroFormFieldFileSelect: return "FileSelect";
+ case acroFormFieldMultilineText: return "MultilineText";
+ case acroFormFieldText: return "Text";
+ case acroFormFieldComboBox: return "ComboBox";
+ case acroFormFieldListBox: return "ListBox";
+ case acroFormFieldSignature: return "Signature";
+ default: return NULL;
+ }
+}
+
+Unicode *AcroFormField::getName(int *length) {
+ Unicode *u, *ret;
+ int n;
+
+ u = name->getUnicode();
+ n = name->getLength();
+ ret = (Unicode *)gmallocn(n, sizeof(Unicode));
+ memcpy(ret, u, n * sizeof(Unicode));
+ *length = n;
+ return ret;
+}
+
+Unicode *AcroFormField::getValue(int *length) {
+ Object obj1;
+ Unicode *u;
+ char *s;
+ TextString *ts;
+ int n, i;
+
+ fieldLookup("V", &obj1);
+ if (obj1.isName()) {
+ s = obj1.getName();
+ n = (int)strlen(s);
+ u = (Unicode *)gmallocn(n, sizeof(Unicode));
+ for (i = 0; i < n; ++i) {
+ u[i] = s[i] & 0xff;
+ }
+ *length = n;
+ return u;
+ } else if (obj1.isString()) {
+ ts = new TextString(obj1.getString());
+ n = ts->getLength();
+ u = (Unicode *)gmallocn(n, sizeof(Unicode));
+ memcpy(u, ts->getUnicode(), n * sizeof(Unicode));
+ *length = n;
+ delete ts;
+ return u;
+ } else {
+ return NULL;
+ }
+}
+
+void AcroFormField::draw(int pageNum, Gfx *gfx, GBool printing) {
+ Object kidsObj, annotRef, annotObj;
+ int i;
+
+ // find the annotation object(s)
+ if (fieldObj.dictLookup("Kids", &kidsObj)->isArray()) {
+ for (i = 0; i < kidsObj.arrayGetLength(); ++i) {
+ kidsObj.arrayGetNF(i, &annotRef);
+ annotRef.fetch(acroForm->doc->getXRef(), &annotObj);
+ drawAnnot(pageNum, gfx, printing, &annotRef, &annotObj);
+ annotObj.free();
+ annotRef.free();
+ }
+ } else {
+ drawAnnot(pageNum, gfx, printing, &fieldRef, &fieldObj);
+ }
+ kidsObj.free();
+}
+
+void AcroFormField::drawAnnot(int pageNum, Gfx *gfx, GBool printing,
+ Object *annotRef, Object *annotObj) {
+ Object obj1, obj2;
+ double xMin, yMin, xMax, yMax, t;
+ int annotFlags;
+ GBool oc;
+
+ if (!annotObj->isDict()) {
+ return;
+ }
+
+ //----- get the page number
+
+ // the "P" (page) field in annotations is optional, so we can't
+ // depend on it here
+ if (acroForm->lookupAnnotPage(annotRef) != pageNum) {
+ return;
+ }
+
+ //----- check annotation flags
+
+ if (annotObj->dictLookup("F", &obj1)->isInt()) {
+ annotFlags = obj1.getInt();
+ } else {
+ annotFlags = 0;
+ }
+ obj1.free();
+ if ((annotFlags & annotFlagHidden) ||
+ (printing && !(annotFlags & annotFlagPrint)) ||
+ (!printing && (annotFlags & annotFlagNoView))) {
+ return;
+ }
+
+ //----- check the optional content entry
+
+ annotObj->dictLookupNF("OC", &obj1);
+ if (acroForm->doc->getOptionalContent()->evalOCObject(&obj1, &oc) && !oc) {
+ obj1.free();
+ return;
+ }
+ obj1.free();
+
+ //----- get the bounding box
+
+ if (annotObj->dictLookup("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(errSyntaxError, -1, "Bad bounding box for annotation");
+ obj1.free();
+ return;
+ }
+ obj1.free();
+
+ //----- draw it
+
+ if (acroForm->needAppearances) {
+ drawNewAppearance(gfx, annotObj->getDict(),
+ xMin, yMin, xMax, yMax);
+ } else {
+ drawExistingAppearance(gfx, annotObj->getDict(),
+ xMin, yMin, xMax, yMax);
+ }
+}
+
+// Draw the existing appearance stream for a single annotation
+// attached to this field.
+void AcroFormField::drawExistingAppearance(Gfx *gfx, Dict *annot,
+ double xMin, double yMin,
+ double xMax, double yMax) {
+ Object apObj, asObj, appearance, obj1;
+
+ //----- get the appearance stream
+
+ if (annot->lookup("AP", &apObj)->isDict()) {
+ apObj.dictLookup("N", &obj1);
+ if (obj1.isDict()) {
+ if (annot->lookup("AS", &asObj)->isName()) {
+ obj1.dictLookupNF(asObj.getName(), &appearance);
+ } else if (obj1.dictGetLength() == 1) {
+ obj1.dictGetValNF(0, &appearance);
+ } else {
+ obj1.dictLookupNF("Off", &appearance);
+ }
+ asObj.free();
+ } else {
+ apObj.dictLookupNF("N", &appearance);
+ }
+ obj1.free();
+ }
+ apObj.free();
+
+ //----- draw it
+
+ if (!appearance.isNone()) {
+ gfx->drawAnnot(&appearance, NULL, xMin, yMin, xMax, yMax);
+ appearance.free();
+ }
+}
+
+// Regenerate the appearnce for this field, and draw it.
+void AcroFormField::drawNewAppearance(Gfx *gfx, Dict *annot,
+ double xMin, double yMin,
+ double xMax, double yMax) {
+ Object appearance, mkObj, ftObj, appearDict, drObj, apObj, asObj;
+ Object obj1, obj2, obj3;
+ Dict *mkDict;
+ MemStream *appearStream;
+ GfxFontDict *fontDict;
+ GBool hasCaption;
+ double dx, dy, r;
+ GString *caption, *da;
+ GString **text;
+ GBool *selection;
+ AnnotBorderType borderType;
+ double borderWidth;
+ double *borderDash;
+ GString *appearanceState;
+ int borderDashLength, rot, quadding, comb, nOptions, topIdx, i, j;
+
+ 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:.4f} {1:.4f} re f\n",
+ xMax - xMin, yMax - yMin);
+ }
+ obj1.free();
+ }
+
+ // get the field type
+ fieldLookup("FT", &ftObj);
+
+ // draw the border
+ borderType = annotBorderSolid;
+ borderWidth = 1;
+ borderDash = NULL;
+ borderDashLength = 0;
+ if (annot->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 (annot->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 (mkDict) {
+ if (borderWidth > 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") && (flags & acroFormFlagRadio) && !hasCaption) {
+ r = 0.5 * (dx < dy ? dx : dy);
+ switch (borderType) {
+ case annotBorderDashed:
+ appearBuf->append("[");
+ for (i = 0; i < borderDashLength; ++i) {
+ appearBuf->appendf(" {0:.4f}", borderDash[i]);
+ }
+ appearBuf->append("] 0 d\n");
+ // fall through to the solid case
+ case annotBorderSolid:
+ case annotBorderUnderlined:
+ appearBuf->appendf("{0:.4f} w\n", borderWidth);
+ setColor(obj1.getArray(), gFalse, 0);
+ drawCircle(0.5 * dx, 0.5 * dy, r - 0.5 * borderWidth, "s");
+ break;
+ case annotBorderBeveled:
+ case annotBorderInset:
+ appearBuf->appendf("{0:.4f} w\n", 0.5 * borderWidth);
+ setColor(obj1.getArray(), gFalse, 0);
+ drawCircle(0.5 * dx, 0.5 * dy, r - 0.25 * borderWidth, "s");
+ setColor(obj1.getArray(), gFalse,
+ borderType == annotBorderBeveled ? 1 : -1);
+ drawCircleTopLeft(0.5 * dx, 0.5 * dy, r - 0.75 * borderWidth);
+ setColor(obj1.getArray(), gFalse,
+ borderType == annotBorderBeveled ? -1 : 1);
+ drawCircleBottomRight(0.5 * dx, 0.5 * dy, r - 0.75 * borderWidth);
+ break;
+ }
+
+ } else {
+ switch (borderType) {
+ case annotBorderDashed:
+ appearBuf->append("[");
+ for (i = 0; i < borderDashLength; ++i) {
+ appearBuf->appendf(" {0:.4f}", borderDash[i]);
+ }
+ appearBuf->append("] 0 d\n");
+ // fall through to the solid case
+ case annotBorderSolid:
+ appearBuf->appendf("{0:.4f} w\n", borderWidth);
+ setColor(obj1.getArray(), gFalse, 0);
+ appearBuf->appendf("{0:.4f} {0:.4f} {1:.4f} {2:.4f} re s\n",
+ 0.5 * borderWidth,
+ dx - borderWidth, dy - borderWidth);
+ break;
+ case annotBorderBeveled:
+ case annotBorderInset:
+ setColor(obj1.getArray(), gTrue,
+ borderType == annotBorderBeveled ? 1 : -1);
+ appearBuf->append("0 0 m\n");
+ appearBuf->appendf("0 {0:.4f} l\n", dy);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n", dx, dy);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ dx - borderWidth, dy - borderWidth);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ borderWidth, dy - borderWidth);
+ appearBuf->appendf("{0:.4f} {0:.4f} l\n", borderWidth);
+ appearBuf->append("f\n");
+ setColor(obj1.getArray(), gTrue,
+ borderType == annotBorderBeveled ? -1 : 1);
+ appearBuf->append("0 0 m\n");
+ appearBuf->appendf("{0:.4f} 0 l\n", dx);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n", dx, dy);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ dx - borderWidth, dy - borderWidth);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ dx - borderWidth, borderWidth);
+ appearBuf->appendf("{0:.4f} {0:.4f} l\n", borderWidth);
+ appearBuf->append("f\n");
+ break;
+ case annotBorderUnderlined:
+ appearBuf->appendf("{0:.4f} w\n", borderWidth);
+ setColor(obj1.getArray(), gFalse, 0);
+ appearBuf->appendf("0 0 m {0:.4f} 0 l s\n", dx);
+ break;
+ }
+
+ // clip to the inside of the border
+ appearBuf->appendf("{0:.4f} {0:.4f} {1:.4f} {2:.4f} re W n\n",
+ borderWidth,
+ dx - 2 * borderWidth, dy - 2 * borderWidth);
+ }
+ }
+ obj1.free();
+ }
+ }
+ gfree(borderDash);
+
+ // get the resource dictionary
+ fieldLookup("DR", &drObj);
+
+ // build the font dictionary
+ if (drObj.isDict() && drObj.dictLookup("Font", &obj1)->isDict()) {
+ fontDict = new GfxFontDict(acroForm->doc->getXRef(), NULL, obj1.getDict());
+ } else {
+ fontDict = NULL;
+ }
+ obj1.free();
+
+ // get the default appearance string
+ if (fieldLookup("DA", &obj1)->isString()) {
+ da = obj1.getString()->copy();
+ } else {
+ da = NULL;
+ }
+ obj1.free();
+
+ // get the rotation value
+ rot = 0;
+ if (mkDict) {
+ if (mkDict->lookup("R", &obj1)->isInt()) {
+ rot = obj1.getInt();
+ }
+ obj1.free();
+ }
+
+ // get the appearance state
+ annot->lookup("AP", &apObj);
+ annot->lookup("AS", &asObj);
+ appearanceState = NULL;
+ if (asObj.isName()) {
+ appearanceState = new GString(asObj.getName());
+ } else if (apObj.isDict()) {
+ apObj.dictLookup("N", &obj1);
+ if (obj1.isDict() && obj1.dictGetLength() == 1) {
+ appearanceState = new GString(obj1.dictGetKey(0));
+ }
+ obj1.free();
+ }
+ if (!appearanceState) {
+ appearanceState = new GString("Off");
+ }
+ asObj.free();
+ apObj.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 (flags & acroFormFlagRadio) {
+ //~ Acrobat doesn't draw a caption if there is no AP dict (?)
+ if (fieldLookup("V", &obj1)
+ ->isName(appearanceState->getCString())) {
+ if (caption) {
+ drawText(caption, da, fontDict, gFalse, 0, acroFormQuadCenter,
+ gFalse, gTrue, rot, xMin, yMin, xMax, yMax, borderWidth);
+ } else {
+ if (mkDict) {
+ if (mkDict->lookup("BC", &obj2)->isArray() &&
+ obj2.arrayGetLength() > 0) {
+ dx = xMax - xMin;
+ dy = yMax - yMin;
+ setColor(obj2.getArray(), gTrue, 0);
+ drawCircle(0.5 * dx, 0.5 * dy, 0.2 * (dx < dy ? dx : dy), "f");
+ }
+ obj2.free();
+ }
+ }
+ }
+ obj1.free();
+ // pushbutton
+ } else if (flags & acroFormFlagPushbutton) {
+ if (caption) {
+ drawText(caption, da, fontDict, gFalse, 0, acroFormQuadCenter,
+ gFalse, gFalse, rot, xMin, yMin, xMax, yMax, borderWidth);
+ }
+ // checkbox
+ } else {
+ fieldLookup("V", &obj1);
+ if (obj1.isName() && !obj1.isName("Off")) {
+ if (!caption) {
+ caption = new GString("3"); // ZapfDingbats checkmark
+ }
+ drawText(caption, da, fontDict, gFalse, 0, acroFormQuadCenter,
+ gFalse, gTrue, rot, xMin, yMin, xMax, yMax, borderWidth);
+ }
+ obj1.free();
+ }
+ if (caption) {
+ delete caption;
+ }
+ } else if (ftObj.isName("Tx")) {
+ //~ value strings can be Unicode
+ if (!fieldLookup("V", &obj1)->isString()) {
+ obj1.free();
+ fieldLookup("DV", &obj1);
+ }
+ if (obj1.isString()) {
+ if (fieldLookup("Q", &obj2)->isInt()) {
+ quadding = obj2.getInt();
+ } else {
+ quadding = acroFormQuadLeft;
+ }
+ obj2.free();
+ comb = 0;
+ if (flags & acroFormFlagComb) {
+ if (fieldLookup("MaxLen", &obj2)->isInt()) {
+ comb = obj2.getInt();
+ }
+ obj2.free();
+ }
+ drawText(obj1.getString(), da, fontDict,
+ flags & acroFormFlagMultiline, comb, quadding,
+ gTrue, gFalse, rot, xMin, yMin, xMax, yMax, borderWidth);
+ }
+ obj1.free();
+ } else if (ftObj.isName("Ch")) {
+ //~ value/option strings can be Unicode
+ if (fieldLookup("Q", &obj1)->isInt()) {
+ quadding = obj1.getInt();
+ } else {
+ quadding = acroFormQuadLeft;
+ }
+ obj1.free();
+ // combo box
+ if (flags & acroFormFlagCombo) {
+ if (fieldLookup("V", &obj1)->isString()) {
+ drawText(obj1.getString(), da, fontDict,
+ gFalse, 0, quadding, gTrue, gFalse, rot,
+ xMin, yMin, xMax, yMax, borderWidth);
+ //~ Acrobat draws a popup icon on the right side
+ }
+ obj1.free();
+ // list box
+ } else {
+ if (fieldObj.dictLookup("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("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 (fieldObj.dictLookup("TI", &obj2)->isInt()) {
+ topIdx = obj2.getInt();
+ } else {
+ topIdx = 0;
+ }
+ obj2.free();
+ // draw the text
+ drawListBox(text, selection, nOptions, topIdx, da, fontDict, quadding,
+ xMin, yMin, xMax, yMax, borderWidth);
+ for (i = 0; i < nOptions; ++i) {
+ delete text[i];
+ }
+ gfree(text);
+ gfree(selection);
+ }
+ obj1.free();
+ }
+ } else if (ftObj.isName("Sig")) {
+ //~unimp
+ } else {
+ error(errSyntaxError, -1, "Unknown field type");
+ }
+
+ delete appearanceState;
+ if (da) {
+ delete da;
+ }
+
+ // build the appearance stream dictionary
+ appearDict.initDict(acroForm->doc->getXRef());
+ appearDict.dictAdd(copyString("Length"),
+ obj1.initInt(appearBuf->getLength()));
+ appearDict.dictAdd(copyString("Subtype"), obj1.initName("Form"));
+ obj1.initArray(acroForm->doc->getXRef());
+ 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.initStream(appearStream);
+
+ // draw it
+ gfx->drawAnnot(&appearance, NULL, xMin, yMin, xMax, yMax);
+
+ appearance.free();
+ delete appearBuf;
+ appearBuf = NULL;
+ 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 AcroFormField::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 AcroFormField::drawText(GString *text, GString *da, GfxFontDict *fontDict,
+ GBool multiline, int comb, int quadding,
+ GBool txField, GBool forceZapfDingbats, int rot,
+ double xMin, double yMin, double xMax, double yMax,
+ double border) {
+ GString *text2;
+ GList *daToks;
+ GString *tok;
+ GfxFont *font;
+ double dx, dy;
+ double fontSize, fontSize2, 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)
+
+ // check for a Unicode string
+ //~ this currently drops all non-Latin1 characters
+ if (text->getLength() >= 2 &&
+ text->getChar(0) == '\xfe' && text->getChar(1) == '\xff') {
+ text2 = new GString();
+ for (i = 2; i+1 < text->getLength(); i += 2) {
+ c = ((text->getChar(i) & 0xff) << 8) + (text->getChar(i+1) & 0xff);
+ if (c <= 0xff) {
+ text2->append((char)c);
+ } else {
+ text2->append('?');
+ }
+ }
+ } else {
+ text2 = text;
+ }
+
+ // 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(errSyntaxError, -1, "Unknown font in field's DA string");
+ }
+ } else {
+ error(errSyntaxError, -1,
+ "Invalid font name in 'Tf' operator in field's DA string");
+ }
+ tok = (GString *)daToks->get(tfPos + 1);
+ fontSize = atof(tok->getCString());
+ } else {
+ error(errSyntaxError, -1, "Missing 'Tf' operator in field's DA string");
+ }
+
+ // setup
+ if (txField) {
+ appearBuf->append("/Tx BMC\n");
+ }
+ appearBuf->append("q\n");
+ if (rot == 90) {
+ appearBuf->appendf("0 1 -1 0 {0:.4f} 0 cm\n", xMax - xMin);
+ dx = yMax - yMin;
+ dy = xMax - xMin;
+ } else if (rot == 180) {
+ appearBuf->appendf("-1 0 0 -1 {0:.4f} {1:.4f} cm\n",
+ xMax - xMin, yMax - yMin);
+ dx = xMax - yMax;
+ dy = yMax - yMin;
+ } else if (rot == 270) {
+ appearBuf->appendf("0 -1 1 0 0 {0:.4f} cm\n", yMax - yMin);
+ dx = yMax - yMin;
+ dy = xMax - xMin;
+ } else { // assume rot == 0
+ dx = xMax - xMin;
+ dy = yMax - yMin;
+ }
+ appearBuf->append("BT\n");
+
+ // multi-line text
+ if (multiline) {
+ // note: the comb flag is ignored in multiline mode
+
+ wMax = dx - 2 * border - 4;
+
+ // compute font autosize
+ if (fontSize == 0) {
+ for (fontSize = 20; fontSize > 1; --fontSize) {
+ y = dy - 3;
+ w2 = 0;
+ i = 0;
+ while (i < text2->getLength()) {
+ getNextLine(text2, 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 = dy - 3;
+
+ // 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:.4f}", 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:.4f} Tm\n", y);
+ }
+
+ // write a series of lines of text
+ i = 0;
+ xPrev = 0;
+ while (i < text2->getLength()) {
+
+ getNextLine(text2, i, font, fontSize, wMax, &j, &w, &k);
+
+ // compute text start position
+ switch (quadding) {
+ case acroFormQuadLeft:
+ default:
+ x = border + 2;
+ break;
+ case acroFormQuadCenter:
+ x = (dx - w) / 2;
+ break;
+ case acroFormQuadRight:
+ x = dx - border - 2 - w;
+ break;
+ }
+
+ // draw the line
+ appearBuf->appendf("{0:.4f} {1:.4f} Td\n", x - xPrev, -fontSize);
+ appearBuf->append('(');
+ for (; i < j; ++i) {
+ c = text2->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 = (dx - 2 * border) / comb;
+
+ // compute font autosize
+ if (fontSize == 0) {
+ fontSize = dy - 2 * border;
+ if (w < fontSize) {
+ fontSize = w;
+ }
+ fontSize = floor(fontSize);
+ if (tfPos >= 0) {
+ tok = (GString *)daToks->get(tfPos + 1);
+ tok->clear();
+ tok->appendf("{0:.4f}", fontSize);
+ }
+ }
+
+ // compute text start position
+ switch (quadding) {
+ case acroFormQuadLeft:
+ default:
+ x = border + 2;
+ break;
+ case acroFormQuadCenter:
+ x = border + 2 + 0.5 * (comb - text2->getLength()) * w;
+ break;
+ case acroFormQuadRight:
+ x = border + 2 + (comb - text2->getLength()) * w;
+ break;
+ }
+ y = 0.5 * dy - 0.4 * fontSize;
+
+ // set the font matrix
+ if (tmPos >= 0) {
+ tok = (GString *)daToks->get(tmPos + 4);
+ tok->clear();
+ tok->appendf("{0:.4f}", x);
+ tok = (GString *)daToks->get(tmPos + 5);
+ tok->clear();
+ tok->appendf("{0:.4f}", 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:.4f} {1:.4f} 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 < text2->getLength(); ++i) {
+ if (i > 0) {
+ appearBuf->appendf("{0:.4f} 0 Td\n", w);
+ }
+ appearBuf->append('(');
+ c = text2->getChar(i) & 0xff;
+ if (c == '(' || c == ')' || c == '\\') {
+ appearBuf->append('\\');
+ appearBuf->append(c);
+ } else if (c < 0x20 || c >= 0x80) {
+ appearBuf->appendf("{0:.4f} 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 < text2->getLength(); ++i) {
+ w += ((Gfx8BitFont *)font)->getWidth(text2->getChar(i));
+ }
+ } else {
+ // otherwise, make a crude estimate
+ w = text2->getLength() * 0.5;
+ }
+
+ // compute font autosize
+ if (fontSize == 0) {
+ fontSize = dy - 2 * border;
+ fontSize2 = (dx - 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:.4f}", fontSize);
+ }
+ }
+
+ // compute text start position
+ w *= fontSize;
+ switch (quadding) {
+ case acroFormQuadLeft:
+ default:
+ x = border + 2;
+ break;
+ case acroFormQuadCenter:
+ x = (dx - w) / 2;
+ break;
+ case acroFormQuadRight:
+ x = dx - border - 2 - w;
+ break;
+ }
+ y = 0.5 * dy - 0.4 * fontSize;
+
+ // set the font matrix
+ if (tmPos >= 0) {
+ tok = (GString *)daToks->get(tmPos + 4);
+ tok->clear();
+ tok->appendf("{0:.4f}", x);
+ tok = (GString *)daToks->get(tmPos + 5);
+ tok->clear();
+ tok->appendf("{0:.4f}", 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:.4f} {1:.4f} Tm\n", x, y);
+ }
+
+ // write the text string
+ appearBuf->append('(');
+ for (i = 0; i < text2->getLength(); ++i) {
+ c = text2->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);
+ }
+ if (text2 != text) {
+ delete text2;
+ }
+}
+
+// Draw the variable text or caption for a field.
+void AcroFormField::drawListBox(GString **text, GBool *selection,
+ int nOptions, int topIdx,
+ GString *da, GfxFontDict *fontDict,
+ GBool quadding, double xMin, double yMin,
+ double xMax, double yMax, double border) {
+ GList *daToks;
+ GString *tok;
+ GfxFont *font;
+ double fontSize, fontSize2, 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(errSyntaxError, -1, "Unknown font in field's DA string");
+ }
+ } else {
+ error(errSyntaxError, -1,
+ "Invalid font name in 'Tf' operator in field's DA string");
+ }
+ tok = (GString *)daToks->get(tfPos + 1);
+ fontSize = atof(tok->getCString());
+ } else {
+ error(errSyntaxError, -1, "Missing 'Tf' operator in field's DA string");
+ }
+
+ // 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:.4f}", 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:.4f} {1:.4f} {2:.4f} {3:.4f} 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 acroFormQuadLeft:
+ default:
+ x = border + 2;
+ break;
+ case acroFormQuadCenter:
+ x = (xMax - xMin - w) / 2;
+ break;
+ case acroFormQuadRight:
+ 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:.4f}", x);
+ tok = (GString *)daToks->get(tmPos + 5);
+ tok->clear();
+ tok->appendf("{0:.4f}", 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:.4f} {1:.4f} 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 AcroFormField::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>).
+// <cmd> is used to draw the circle ("f", "s", or "b").
+void AcroFormField::drawCircle(double cx, double cy, double r,
+ const char *cmd) {
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n",
+ cx + r, cy);
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
+ cx + r, cy + bezierCircle * r,
+ cx + bezierCircle * r, cy + r,
+ cx, cy + r);
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
+ cx - bezierCircle * r, cy + r,
+ cx - r, cy + bezierCircle * r,
+ cx - r, cy);
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
+ cx - r, cy - bezierCircle * r,
+ cx - bezierCircle * r, cy - r,
+ cx, cy - r);
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
+ cx + bezierCircle * r, cy - r,
+ cx + r, cy - bezierCircle * r,
+ cx + r, cy);
+ appearBuf->appendf("{0:s}\n", cmd);
+}
+
+// Draw the top-left half of an (approximate) circle of radius <r>
+// centered at (<cx>, <cy>).
+void AcroFormField::drawCircleTopLeft(double cx, double cy, double r) {
+ double r2;
+
+ r2 = r / sqrt(2.0);
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n",
+ cx + r2, cy + r2);
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} 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:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} 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 AcroFormField::drawCircleBottomRight(double cx, double cy, double r) {
+ double r2;
+
+ r2 = r / sqrt(2.0);
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n",
+ cx - r2, cy - r2);
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} 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:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} 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");
+}
+
+Object *AcroFormField::getResources(Object *res) {
+ Object kidsObj, annotObj, obj1;
+ int i;
+
+ if (acroForm->needAppearances) {
+ fieldLookup("DR", res);
+ } else {
+ res->initArray(acroForm->doc->getXRef());
+ // find the annotation object(s)
+ if (fieldObj.dictLookup("Kids", &kidsObj)->isArray()) {
+ for (i = 0; i < kidsObj.arrayGetLength(); ++i) {
+ kidsObj.arrayGet(i, &annotObj);
+ if (annotObj.isDict()) {
+ if (getAnnotResources(annotObj.getDict(), &obj1)->isDict()) {
+ res->arrayAdd(&obj1);
+ } else {
+ obj1.free();
+ }
+ }
+ annotObj.free();
+ }
+ } else {
+ if (getAnnotResources(fieldObj.getDict(), &obj1)->isDict()) {
+ res->arrayAdd(&obj1);
+ } else {
+ obj1.free();
+ }
+ }
+ kidsObj.free();
+ }
+
+ return res;
+}
+
+Object *AcroFormField::getAnnotResources(Dict *annot, Object *res) {
+ Object apObj, asObj, appearance, obj1;
+
+ // get the appearance stream
+ if (annot->lookup("AP", &apObj)->isDict()) {
+ apObj.dictLookup("N", &obj1);
+ if (obj1.isDict()) {
+ if (annot->lookup("AS", &asObj)->isName()) {
+ obj1.dictLookup(asObj.getName(), &appearance);
+ } else if (obj1.dictGetLength() == 1) {
+ obj1.dictGetVal(0, &appearance);
+ } else {
+ obj1.dictLookup("Off", &appearance);
+ }
+ asObj.free();
+ } else {
+ obj1.copy(&appearance);
+ }
+ obj1.free();
+ }
+ apObj.free();
+
+ if (appearance.isStream()) {
+ appearance.streamGetDict()->lookup("Resources", res);
+ } else {
+ res->initNull();
+ }
+ appearance.free();
+
+ return res;
+}
+
+// Look up an inheritable field dictionary entry.
+Object *AcroFormField::fieldLookup(const char *key, Object *obj) {
+ return fieldLookup(fieldObj.getDict(), key, obj);
+}
+
+Object *AcroFormField::fieldLookup(Dict *dict, const char *key, Object *obj) {
+ Object parent;
+
+ if (!dict->lookup(key, obj)->isNull()) {
+ return obj;
+ }
+ obj->free();
+ if (dict->lookup("Parent", &parent)->isDict()) {
+ fieldLookup(parent.getDict(), key, obj);
+ } else {
+ // some fields don't specify a parent, so we check the AcroForm
+ // dictionary just in case
+ acroForm->acroFormObj.dictLookup(key, obj);
+ }
+ parent.free();
+ return obj;
+}
diff --git a/xpdf/AcroForm.h b/xpdf/AcroForm.h
new file mode 100644
index 0000000..8e0075a
--- /dev/null
+++ b/xpdf/AcroForm.h
@@ -0,0 +1,128 @@
+//========================================================================
+//
+// AcroForm.h
+//
+// Copyright 2012 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef ACROFORM_H
+#define ACROFORM_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "Form.h"
+
+class TextString;
+class GfxFont;
+class GfxFontDict;
+
+//------------------------------------------------------------------------
+
+class AcroForm: public Form {
+public:
+
+ static AcroForm *load(PDFDoc *docA, Catalog *catalog, Object *acroFormObjA);
+
+ virtual ~AcroForm();
+
+ virtual const char *getType() { return "AcroForm"; }
+
+ virtual void draw(int pageNum, Gfx *gfx, GBool printing);
+
+ virtual int getNumFields();
+ virtual FormField *getField(int idx);
+
+private:
+
+ AcroForm(PDFDoc *docA, Object *acroFormObjA);
+ void buildAnnotPageList(Catalog *catalog);
+ int lookupAnnotPage(Object *annotRef);
+ void scanField(Object *fieldRef);
+
+ Object acroFormObj;
+ GBool needAppearances;
+ GList *annotPages; // [AcroFormAnnotPage]
+ GList *fields; // [AcroFormField]
+
+ friend class AcroFormField;
+};
+
+//------------------------------------------------------------------------
+
+enum AcroFormFieldType {
+ acroFormFieldPushbutton,
+ acroFormFieldRadioButton,
+ acroFormFieldCheckbox,
+ acroFormFieldFileSelect,
+ acroFormFieldMultilineText,
+ acroFormFieldText,
+ acroFormFieldComboBox,
+ acroFormFieldListBox,
+ acroFormFieldSignature
+};
+
+class AcroFormField: public FormField {
+public:
+
+ static AcroFormField *load(AcroForm *acroFormA, Object *fieldRefA);
+
+ virtual ~AcroFormField();
+
+ virtual const char *getType();
+ virtual Unicode *getName(int *length);
+ virtual Unicode *getValue(int *length);
+
+ virtual Object *getResources(Object *res);
+
+private:
+
+ AcroFormField(AcroForm *acroFormA, Object *fieldRefA, Object *fieldObjA,
+ AcroFormFieldType typeA, TextString *nameA,
+ Guint flagsA);
+ void draw(int pageNum, Gfx *gfx, GBool printing);
+ void drawAnnot(int pageNum, Gfx *gfx, GBool printing,
+ Object *annotRef, Object *annotObj);
+ void drawExistingAppearance(Gfx *gfx, Dict *annot,
+ double xMin, double yMin,
+ double xMax, double yMax);
+ void drawNewAppearance(Gfx *gfx, Dict *annot,
+ double xMin, double yMin,
+ double xMax, double yMax);
+ 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, int rot,
+ double xMin, double yMin, double xMax, double yMax,
+ double border);
+ void drawListBox(GString **text, GBool *selection,
+ int nOptions, int topIdx,
+ GString *da, GfxFontDict *fontDict,
+ GBool quadding, double xMin, double yMin,
+ double xMax, double yMax, double border);
+ 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, const char *cmd);
+ void drawCircleTopLeft(double cx, double cy, double r);
+ void drawCircleBottomRight(double cx, double cy, double r);
+ Object *getAnnotResources(Dict *annot, Object *res);
+ Object *fieldLookup(const char *key, Object *obj);
+ Object *fieldLookup(Dict *dict, const char *key, Object *obj);
+
+ AcroForm *acroForm;
+ Object fieldRef;
+ Object fieldObj;
+ AcroFormFieldType type;
+ TextString *name;
+ Guint flags;
+ GString *appearBuf;
+
+ friend class AcroForm;
+};
+
+#endif
diff --git a/xpdf/Annot.cc b/xpdf/Annot.cc
index 887157f..ff1e376 100644
--- a/xpdf/Annot.cc
+++ b/xpdf/Annot.cc
@@ -24,56 +24,44 @@
#include "Lexer.h"
#include "PDFDoc.h"
#include "OptionalContent.h"
+#include "Form.h"
#include "Annot.h"
+// the MSVC math.h doesn't define this
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
//------------------------------------------------------------------------
#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
+#define lineEndSize1 6
+#define lineEndSize2 10
+#define lineArrowAngle (M_PI / 6)
+
//------------------------------------------------------------------------
// AnnotBorderStyle
//------------------------------------------------------------------------
AnnotBorderStyle::AnnotBorderStyle(AnnotBorderType typeA, double widthA,
double *dashA, int dashLengthA,
- double rA, double gA, double bA) {
+ double *colorA, int nColorCompsA) {
type = typeA;
width = widthA;
dash = dashA;
dashLength = dashLengthA;
- r = rA;
- g = gA;
- b = bA;
+ color[0] = colorA[0];
+ color[1] = colorA[1];
+ color[2] = colorA[2];
+ color[3] = colorA[3];
+ nColorComps = nColorCompsA;
}
AnnotBorderStyle::~AnnotBorderStyle() {
@@ -92,7 +80,8 @@ Annot::Annot(PDFDoc *docA, Dict *dict, Ref *refA) {
double borderWidth;
double *borderDash;
int borderDashLength;
- double borderR, borderG, borderB;
+ double borderColor[4];
+ int nBorderColorComps;
double t;
int i;
@@ -160,9 +149,11 @@ Annot::Annot(PDFDoc *docA, Dict *dict, Ref *refA) {
borderWidth = 1;
borderDash = NULL;
borderDashLength = 0;
- borderR = 0;
- borderG = 0;
- borderB = 1;
+ nBorderColorComps = 3;
+ borderColor[0] = 0;
+ borderColor[1] = 0;
+ borderColor[2] = 1;
+ borderColor[3] = 0;
if (dict->lookup("BS", &obj1)->isDict()) {
if (obj1.dictLookup("S", &obj2)->isName()) {
if (obj2.isName("S")) {
@@ -223,28 +214,31 @@ Annot::Annot(PDFDoc *docA, Dict *dict, Ref *refA) {
}
obj2.free();
}
+ } else {
+ // an empty Border array also means "no border"
+ borderWidth = 0;
}
}
}
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();
+ if (dict->lookup("C", &obj1)->isArray() &&
+ (obj1.arrayGetLength() == 1 ||
+ obj1.arrayGetLength() == 3 ||
+ obj1.arrayGetLength() == 4)) {
+ nBorderColorComps = obj1.arrayGetLength();
+ for (i = 0; i < nBorderColorComps; ++i) {
+ if (obj1.arrayGet(i, &obj2)->isNum()) {
+ borderColor[i] = obj2.getNum();
+ } else {
+ borderColor[i] = 0;
+ }
+ obj2.free();
}
- obj1.free();
}
obj1.free();
borderStyle = new AnnotBorderStyle(borderType, borderWidth,
borderDash, borderDashLength,
- borderR, borderG, borderB);
+ borderColor, nBorderColorComps);
//----- get the appearance state
@@ -304,350 +298,188 @@ Annot::~Annot() {
ocObj.free();
}
-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 rot, dashLength, ff, quadding, comb, nOptions, topIdx, i, j;
+void Annot::generateAnnotAppearance() {
+ Object obj;
+
+ appearance.fetch(doc->getXRef(), &obj);
+ if (!obj.isStream()) {
+ if (type) {
+ if (!type->cmp("Line")) {
+ generateLineAppearance();
+ } else if (!type->cmp("PolyLine")) {
+ generatePolyLineAppearance();
+ } else if (!type->cmp("Polygon")) {
+ generatePolygonAppearance();
+ }
+ }
+ }
+ obj.free();
+}
- // must be a Widget annotation
- if (type && type->cmp("Widget")) {
+//~ this doesn't draw the caption
+void Annot::generateLineAppearance() {
+ Object annotObj, gfxStateDict, appearDict, obj1, obj2;
+ MemStream *appearStream;
+ double x1, y1, x2, y2, dx, dy, len, w;
+ double lx1, ly1, lx2, ly2;
+ double tx1, ty1, tx2, ty2;
+ double ax1, ay1, ax2, ay2;
+ double bx1, by1, bx2, by2;
+ double leaderLen, leaderExtLen, leaderOffLen;
+ AnnotLineEndType lineEnd1, lineEnd2;
+ GBool fill;
+
+ if (!getObject(&annotObj)->isDict()) {
+ annotObj.free();
return;
}
appearBuf = new GString();
- // get the appearance characteristics (MK) dictionary
- if (annot->lookup("MK", &mkObj)->isDict()) {
- mkDict = mkObj.getDict();
- } else {
- mkDict = NULL;
+ //----- check for transparency
+ if (annotObj.dictLookup("CA", &obj1)->isNum()) {
+ gfxStateDict.initDict(doc->getXRef());
+ gfxStateDict.dictAdd(copyString("ca"), obj1.copy(&obj2));
+ appearBuf->append("/GS1 gs\n");
}
+ obj1.free();
- // 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);
+ //----- set line style, colors
+ setLineStyle(borderStyle, &w);
+ setStrokeColor(borderStyle->getColor(), borderStyle->getNumColorComps());
+ fill = gFalse;
+ if (annotObj.dictLookup("IC", &obj1)->isArray()) {
+ if (setFillColor(&obj1)) {
+ fill = gTrue;
}
- obj1.free();
- }
-
- // get the field type
- fieldLookup(field, acroForm, "FT", &ftObj);
-
- // get the field flags (Ff) value
- if (fieldLookup(field, acroForm, "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);
- }
- }
+ //----- get line properties
+ if (annotObj.dictLookup("L", &obj1)->isArray() &&
+ obj1.arrayGetLength() == 4) {
+ if (obj1.arrayGet(0, &obj2)->isNum()) {
+ x1 = obj2.getNum();
+ } else {
+ obj2.free();
obj1.free();
+ goto err1;
}
+ obj2.free();
+ if (obj1.arrayGet(1, &obj2)->isNum()) {
+ y1 = obj2.getNum();
+ } else {
+ obj2.free();
+ obj1.free();
+ goto err1;
+ }
+ obj2.free();
+ if (obj1.arrayGet(2, &obj2)->isNum()) {
+ x2 = obj2.getNum();
+ } else {
+ obj2.free();
+ obj1.free();
+ goto err1;
+ }
+ obj2.free();
+ if (obj1.arrayGet(3, &obj2)->isNum()) {
+ y2 = obj2.getNum();
+ } else {
+ obj2.free();
+ obj1.free();
+ goto err1;
+ }
+ obj2.free();
+ } else {
+ obj1.free();
+ goto err1;
}
-
- // get the resource dictionary
- fieldLookup(field, acroForm, "DR", &drObj);
-
- // build the font dictionary
- if (drObj.isDict() && drObj.dictLookup("Font", &obj1)->isDict()) {
- fontDict = new GfxFontDict(doc->getXRef(), NULL, obj1.getDict());
+ obj1.free();
+ lineEnd1 = lineEnd2 = annotLineEndNone;
+ if (annotObj.dictLookup("LE", &obj1)->isArray() &&
+ obj1.arrayGetLength() == 2) {
+ lineEnd1 = parseLineEndType(obj1.arrayGet(0, &obj2));
+ obj2.free();
+ lineEnd2 = parseLineEndType(obj1.arrayGet(1, &obj2));
+ obj2.free();
+ }
+ obj1.free();
+ if (annotObj.dictLookup("LL", &obj1)->isNum()) {
+ leaderLen = obj1.getNum();
} else {
- fontDict = NULL;
+ leaderLen = 0;
}
obj1.free();
-
- // get the default appearance string
- if (fieldLookup(field, acroForm, "DA", &obj1)->isNull()) {
- obj1.free();
- acroForm->lookup("DA", &obj1);
+ if (annotObj.dictLookup("LLE", &obj1)->isNum()) {
+ leaderExtLen = obj1.getNum();
+ } else {
+ leaderExtLen = 0;
}
- if (obj1.isString()) {
- da = obj1.getString()->copy();
+ obj1.free();
+ if (annotObj.dictLookup("LLO", &obj1)->isNum()) {
+ leaderOffLen = obj1.getNum();
} else {
- da = NULL;
+ leaderOffLen = 0;
}
obj1.free();
- // get the rotation value
- rot = 0;
- if (mkDict) {
- if (mkDict->lookup("R", &obj1)->isInt()) {
- rot = obj1.getInt();
- }
- obj1.free();
+ //----- compute positions
+ x1 -= xMin;
+ y1 -= yMin;
+ x2 -= xMin;
+ y2 -= yMin;
+ dx = x2 - x1;
+ dy = y2 - y1;
+ len = sqrt(dx*dx + dy*dy);
+ if (len > 0) {
+ dx /= len;
+ dy /= len;
}
-
- // 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, acroForm, "V", &obj1)
- ->isName(appearanceState->getCString())) {
- if (caption) {
- drawText(caption, da, fontDict, gFalse, 0, fieldQuadCenter,
- gFalse, gTrue, rot);
- } else {
- if (mkDict) {
- if (mkDict->lookup("BC", &obj2)->isArray() &&
- obj2.arrayGetLength() > 0) {
- dx = xMax - xMin;
- dy = yMax - yMin;
- setColor(obj2.getArray(), gTrue, 0);
- drawCircle(0.5 * dx, 0.5 * dy, 0.2 * (dx < dy ? dx : dy),
- gTrue);
- }
- obj2.free();
- }
- }
- }
- obj1.free();
- // pushbutton
- } else if (ff & fieldFlagPushbutton) {
- if (caption) {
- drawText(caption, da, fontDict, gFalse, 0, fieldQuadCenter,
- gFalse, gFalse, rot);
- }
- // checkbox
- } else {
- fieldLookup(field, acroForm, "V", &obj1);
- if (obj1.isName() && !obj1.isName("Off")) {
- if (!caption) {
- caption = new GString("3"); // ZapfDingbats checkmark
- }
- drawText(caption, da, fontDict, gFalse, 0, fieldQuadCenter,
- gFalse, gTrue, rot);
- }
- obj1.free();
- }
- if (caption) {
- delete caption;
- }
- } else if (ftObj.isName("Tx")) {
- //~ value strings can be Unicode
- if (!fieldLookup(field, acroForm, "V", &obj1)->isString()) {
- obj1.free();
- fieldLookup(field, acroForm, "DV", &obj1);
- }
- if (obj1.isString()) {
- if (fieldLookup(field, acroForm, "Q", &obj2)->isInt()) {
- quadding = obj2.getInt();
- } else {
- quadding = fieldQuadLeft;
- }
- obj2.free();
- comb = 0;
- if (ff & fieldFlagComb) {
- if (fieldLookup(field, acroForm, "MaxLen", &obj2)->isInt()) {
- comb = obj2.getInt();
- }
- obj2.free();
- }
- drawText(obj1.getString(), da, fontDict,
- ff & fieldFlagMultiline, comb, quadding, gTrue, gFalse, rot);
- }
- obj1.free();
- } else if (ftObj.isName("Ch")) {
- //~ value/option strings can be Unicode
- if (fieldLookup(field, acroForm, "Q", &obj1)->isInt()) {
- quadding = obj1.getInt();
- } else {
- quadding = fieldQuadLeft;
- }
- obj1.free();
- // combo box
- if (ff & fieldFlagCombo) {
- if (fieldLookup(field, acroForm, "V", &obj1)->isString()) {
- drawText(obj1.getString(), da, fontDict,
- gFalse, 0, quadding, gTrue, gFalse, rot);
- //~ 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, acroForm, "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
+ if (leaderLen != 0) {
+ ax1 = x1 + leaderOffLen * dy;
+ ay1 = y1 - leaderOffLen * dx;
+ lx1 = ax1 + leaderLen * dy;
+ ly1 = ay1 - leaderLen * dx;
+ bx1 = lx1 + leaderExtLen * dy;
+ by1 = ly1 - leaderExtLen * dx;
+ ax2 = x2 + leaderOffLen * dy;
+ ay2 = y2 - leaderOffLen * dx;
+ lx2 = ax2 + leaderLen * dy;
+ ly2 = ay2 - leaderLen * dx;
+ bx2 = lx2 + leaderExtLen * dy;
+ by2 = ly2 - leaderExtLen * dx;
} else {
- error(errSyntaxError, -1, "Unknown field type");
+ lx1 = x1;
+ ly1 = y1;
+ lx2 = x2;
+ ly2 = y2;
+ ax1 = ay1 = ax2 = ay2 = 0; // make gcc happy
+ bx1 = by1 = bx2 = by2 = 0;
+ }
+ adjustLineEndpoint(lineEnd1, lx1, ly1, dx, dy, w, &tx1, &ty1);
+ adjustLineEndpoint(lineEnd2, lx2, ly2, -dx, -dy, w, &tx2, &ty2);
+
+ //----- draw leaders
+ if (leaderLen != 0) {
+ appearBuf->appendf("{0:.4f} {1:.4f} m {2:.4f} {3:.4f} l\n",
+ ax1, ay1, bx1, by1);
+ appearBuf->appendf("{0:.4f} {1:.4f} m {2:.4f} {3:.4f} l\n",
+ ax2, ay2 , bx2, by2);
}
- if (da) {
- delete da;
+ //----- draw the line
+ appearBuf->appendf("{0:.4f} {1:.4f} m {2:.4f} {3:.4f} l\n",
+ tx1, ty1, tx2, ty2);
+ appearBuf->append("S\n");
+
+ //----- draw the arrows
+ if (borderStyle->getType() == annotBorderDashed) {
+ appearBuf->append("[] 0 d\n");
}
+ drawLineArrow(lineEnd1, lx1, ly1, dx, dy, w, fill);
+ drawLineArrow(lineEnd2, lx2, ly2, -dx, -dy, w, fill);
- // build the appearance stream dictionary
+ //----- build the appearance stream dictionary
appearDict.initDict(doc->getXRef());
appearDict.dictAdd(copyString("Length"),
obj1.initInt(appearBuf->getLength()));
@@ -658,757 +490,482 @@ void Annot::generateFieldAppearance(Dict *field, Dict *annot, Dict *acroForm) {
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));
+ if (gfxStateDict.isDict()) {
+ obj1.initDict(doc->getXRef());
+ obj2.initDict(doc->getXRef());
+ obj2.dictAdd(copyString("GS1"), &gfxStateDict);
+ obj1.dictAdd(copyString("ExtGState"), &obj2);
+ appearDict.dictAdd(copyString("Resources"), &obj1);
}
- drObj.free();
- // build the appearance stream
+ //----- 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();
+ err1:
+ annotObj.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;
+//~ this doesn't handle line ends (arrows)
+void Annot::generatePolyLineAppearance() {
+ Object annotObj, gfxStateDict, appearDict, obj1, obj2;
+ MemStream *appearStream;
+ double x1, y1, w;
+ int 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');
+ if (!getObject(&annotObj)->isDict()) {
+ annotObj.free();
+ return;
}
-}
-// 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, int rot) {
- GString *text2;
- GList *daToks;
- GString *tok;
- GfxFont *font;
- double dx, dy;
- 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)
-
- // check for a Unicode string
- //~ this currently drops all non-Latin1 characters
- if (text->getLength() >= 2 &&
- text->getChar(0) == '\xfe' && text->getChar(1) == '\xff') {
- text2 = new GString();
- for (i = 2; i+1 < text->getLength(); i += 2) {
- c = ((text->getChar(i) & 0xff) << 8) + (text->getChar(i+1) & 0xff);
- if (c <= 0xff) {
- text2->append((char)c);
- } else {
- text2->append('?');
- }
- }
- } else {
- text2 = text;
- }
+ appearBuf = new GString();
- // 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;
+ //----- check for transparency
+ if (annotObj.dictLookup("CA", &obj1)->isNum()) {
+ gfxStateDict.initDict(doc->getXRef());
+ gfxStateDict.dictAdd(copyString("ca"), obj1.copy(&obj2));
+ appearBuf->append("/GS1 gs\n");
}
+ obj1.free();
- // 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");
- }
- }
+ //----- set line style, colors
+ setLineStyle(borderStyle, &w);
+ setStrokeColor(borderStyle->getColor(), borderStyle->getNumColorComps());
+ // fill = gFalse;
+ // if (annotObj.dictLookup("IC", &obj1)->isArray()) {
+ // if (setFillColor(&obj1)) {
+ // fill = gTrue;
+ // }
+ // }
+ // obj1.free();
+
+ //----- draw line
+ if (!annotObj.dictLookup("Vertices", &obj1)->isArray()) {
+ obj1.free();
+ goto err1;
}
-
- // 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(errSyntaxError, -1, "Unknown font in field's DA string");
- }
+ for (i = 0; i+1 < obj1.arrayGetLength(); i += 2) {
+ if (!obj1.arrayGet(i, &obj2)->isNum()) {
+ obj2.free();
+ obj1.free();
+ goto err1;
+ }
+ x1 = obj2.getNum();
+ obj2.free();
+ if (!obj1.arrayGet(i+1, &obj2)->isNum()) {
+ obj2.free();
+ obj1.free();
+ goto err1;
+ }
+ y1 = obj2.getNum();
+ obj2.free();
+ x1 -= xMin;
+ y1 -= yMin;
+ if (i == 0) {
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n", x1, y1);
} else {
- error(errSyntaxError, -1,
- "Invalid font name in 'Tf' operator in field's DA string");
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n", x1, y1);
}
- tok = (GString *)daToks->get(tfPos + 1);
- fontSize = atof(tok->getCString());
- } else {
- error(errSyntaxError, -1, "Missing 'Tf' operator in field's DA string");
}
+ appearBuf->append("S\n");
+ obj1.free();
- // get the border width
- border = borderStyle->getWidth();
-
- // setup
- if (txField) {
- appearBuf->append("/Tx BMC\n");
- }
- appearBuf->append("q\n");
- if (rot == 90) {
- appearBuf->appendf("0 1 -1 0 {0:.2f} 0 cm\n", xMax - xMin);
- dx = yMax - yMin;
- dy = xMax - xMin;
- } else if (rot == 180) {
- appearBuf->appendf("-1 0 0 -1 {0:.2f} {1:.2f} cm\n",
- xMax - xMin, yMax - yMin);
- dx = xMax - yMax;
- dy = yMax - yMin;
- } else if (rot == 270) {
- appearBuf->appendf("0 -1 1 0 0 {0:.2f} cm\n", yMax - yMin);
- dx = yMax - yMin;
- dy = xMax - xMin;
- } else { // assume rot == 0
- dx = xMax - xMin;
- dy = yMax - yMin;
+ //----- build the appearance stream dictionary
+ appearDict.initDict(doc->getXRef());
+ appearDict.dictAdd(copyString("Length"),
+ obj1.initInt(appearBuf->getLength()));
+ appearDict.dictAdd(copyString("Subtype"), obj1.initName("Form"));
+ obj1.initArray(doc->getXRef());
+ 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);
+ if (gfxStateDict.isDict()) {
+ obj1.initDict(doc->getXRef());
+ obj2.initDict(doc->getXRef());
+ obj2.dictAdd(copyString("GS1"), &gfxStateDict);
+ obj1.dictAdd(copyString("ExtGState"), &obj2);
+ appearDict.dictAdd(copyString("Resources"), &obj1);
}
- appearBuf->append("BT\n");
-
- // multi-line text
- if (multiline) {
- // note: the comb flag is ignored in multiline mode
-
- wMax = dx - 2 * border - 4;
-
- // compute font autosize
- if (fontSize == 0) {
- for (fontSize = 20; fontSize > 1; --fontSize) {
- y = dy - 3;
- w2 = 0;
- i = 0;
- while (i < text2->getLength()) {
- getNextLine(text2, 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 = dy - 3;
-
- // 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 < text2->getLength()) {
-
- getNextLine(text2, i, font, fontSize, wMax, &j, &w, &k);
-
- // compute text start position
- switch (quadding) {
- case fieldQuadLeft:
- default:
- x = border + 2;
- break;
- case fieldQuadCenter:
- x = (dx - w) / 2;
- break;
- case fieldQuadRight:
- x = dx - 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 = text2->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 = (dx - 2 * border) / comb;
-
- // compute font autosize
- if (fontSize == 0) {
- fontSize = dy - 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 - text2->getLength()) * w;
- break;
- case fieldQuadRight:
- x = border + 2 + (comb - text2->getLength()) * w;
- break;
- }
- y = 0.5 * dy - 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 < text2->getLength(); ++i) {
- if (i > 0) {
- appearBuf->appendf("{0:.2f} 0 Td\n", w);
- }
- appearBuf->append('(');
- c = text2->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 < text2->getLength(); ++i) {
- w += ((Gfx8BitFont *)font)->getWidth(text2->getChar(i));
- }
- } else {
- // otherwise, make a crude estimate
- w = text2->getLength() * 0.5;
- }
+ //----- build the appearance stream
+ appearStream = new MemStream(appearBuf->getCString(), 0,
+ appearBuf->getLength(), &appearDict);
+ appearance.free();
+ appearance.initStream(appearStream);
- // compute font autosize
- if (fontSize == 0) {
- fontSize = dy - 2 * border;
- fontSize2 = (dx - 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);
- }
- }
+ err1:
+ annotObj.free();
+}
- // compute text start position
- w *= fontSize;
- switch (quadding) {
- case fieldQuadLeft:
- default:
- x = border + 2;
- break;
- case fieldQuadCenter:
- x = (dx - w) / 2;
- break;
- case fieldQuadRight:
- x = dx - border - 2 - w;
- break;
- }
- y = 0.5 * dy - 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);
- }
+void Annot::generatePolygonAppearance() {
+ Object annotObj, gfxStateDict, appearDict, obj1, obj2;
+ MemStream *appearStream;
+ double x1, y1;
+ int i;
- // write the DA string
- if (daToks) {
- for (i = 0; i < daToks->getLength(); ++i) {
- appearBuf->append((GString *)daToks->get(i))->append(' ');
- }
- }
+ if (!getObject(&annotObj)->isDict()) {
+ annotObj.free();
+ return;
+ }
- // 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);
- }
+ appearBuf = new GString();
- // write the text string
- appearBuf->append('(');
- for (i = 0; i < text2->getLength(); ++i) {
- c = text2->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");
- }
+ //----- check for transparency
+ if (annotObj.dictLookup("CA", &obj1)->isNum()) {
+ gfxStateDict.initDict(doc->getXRef());
+ gfxStateDict.dictAdd(copyString("ca"), obj1.copy(&obj2));
+ appearBuf->append("/GS1 gs\n");
}
+ obj1.free();
- // cleanup
- appearBuf->append("ET\n");
- appearBuf->append("Q\n");
- if (txField) {
- appearBuf->append("EMC\n");
+ //----- set fill color
+ if (!annotObj.dictLookup("IC", &obj1)->isArray() ||
+ !setFillColor(&obj1)) {
+ obj1.free();
+ goto err1;
}
+ obj1.free();
- if (daToks) {
- deleteGList(daToks, GString);
- }
- if (text2 != text) {
- delete text2;
+ //----- fill polygon
+ if (!annotObj.dictLookup("Vertices", &obj1)->isArray()) {
+ obj1.free();
+ goto err1;
}
-}
-
-// 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 = 0; i+1 < obj1.arrayGetLength(); i += 2) {
+ if (!obj1.arrayGet(i, &obj2)->isNum()) {
+ obj2.free();
+ obj1.free();
+ goto err1;
}
- 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;
- }
+ x1 = obj2.getNum();
+ obj2.free();
+ if (!obj1.arrayGet(i+1, &obj2)->isNum()) {
+ obj2.free();
+ obj1.free();
+ goto err1;
}
- } 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(errSyntaxError, -1, "Unknown font in field's DA string");
- }
+ y1 = obj2.getNum();
+ obj2.free();
+ x1 -= xMin;
+ y1 -= yMin;
+ if (i == 0) {
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n", x1, y1);
} else {
- error(errSyntaxError, -1,
- "Invalid font name in 'Tf' operator in field's DA string");
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n", x1, y1);
}
- tok = (GString *)daToks->get(tfPos + 1);
- fontSize = atof(tok->getCString());
- } else {
- error(errSyntaxError, -1, "Missing 'Tf' operator in field's DA string");
}
+ appearBuf->append("f\n");
+ obj1.free();
- // 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);
- }
+ //----- build the appearance stream dictionary
+ appearDict.initDict(doc->getXRef());
+ appearDict.dictAdd(copyString("Length"),
+ obj1.initInt(appearBuf->getLength()));
+ appearDict.dictAdd(copyString("Subtype"), obj1.initName("Form"));
+ obj1.initArray(doc->getXRef());
+ 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);
+ if (gfxStateDict.isDict()) {
+ obj1.initDict(doc->getXRef());
+ obj2.initDict(doc->getXRef());
+ obj2.dictAdd(copyString("GS1"), &gfxStateDict);
+ obj1.dictAdd(copyString("ExtGState"), &obj2);
+ appearDict.dictAdd(copyString("Resources"), &obj1);
}
- // 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(' ');
- }
- }
+ //----- build the appearance stream
+ appearStream = new MemStream(appearBuf->getCString(), 0,
+ appearBuf->getLength(), &appearDict);
+ appearance.free();
+ appearance.initStream(appearStream);
- // 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);
- }
+ err1:
+ annotObj.free();
+}
- // change the text color if selected
- if (selection[i]) {
- appearBuf->append("1 g\n");
- }
+void Annot::setLineStyle(AnnotBorderStyle *bs, double *lineWidth) {
+ double *dash;
+ double w;
+ int dashLength, i;
- // 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);
- }
+ if ((w = borderStyle->getWidth()) <= 0) {
+ w = 0.1;
+ }
+ *lineWidth = w;
+ appearBuf->appendf("{0:.4f} w\n", w);
+ // this treats beveled/inset/underline as solid
+ if (borderStyle->getType() == annotBorderDashed) {
+ borderStyle->getDash(&dash, &dashLength);
+ appearBuf->append("[");
+ for (i = 0; i < dashLength; ++i) {
+ appearBuf->appendf(" {0:.4f}", dash[i]);
}
- appearBuf->append(") Tj\n");
-
- // cleanup
- appearBuf->append("ET\n");
- appearBuf->append("Q\n");
-
- // next line
- y -= 1.1 * fontSize;
+ appearBuf->append("] 0 d\n");
}
+ appearBuf->append("0 j\n0 J\n");
+}
- if (daToks) {
- deleteGList(daToks, GString);
+void Annot::setStrokeColor(double *color, int nComps) {
+ switch (nComps) {
+ case 0:
+ appearBuf->append("0 G\n");
+ break;
+ case 1:
+ appearBuf->appendf("{0:.2f} G\n", color[0]);
+ break;
+ case 3:
+ appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} RG\n",
+ color[0], color[1], color[2]);
+ break;
+ case 4:
+ appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} K\n",
+ color[0], color[1], color[2], color[3]);
+ break;
}
}
-// 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;
+GBool Annot::setFillColor(Object *colorObj) {
+ Object obj;
+ double color[4];
+ int i;
+
+ if (!colorObj->isArray()) {
+ return gFalse;
+ }
+ for (i = 0; i < colorObj->arrayGetLength(); ++i) {
+ if (colorObj->arrayGet(i, &obj)->isNum()) {
+ color[i] = obj.getNum();
} else {
- // otherwise, make a crude estimate
- dw = 0.5 * fontSize;
+ color[i] = 0;
}
- w += dw;
+ obj.free();
}
- 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;
- }
+ switch (colorObj->arrayGetLength()) {
+ case 1:
+ appearBuf->appendf("{0:.2f} g\n", color[0]);
+ return gTrue;
+ case 3:
+ appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} rg\n",
+ color[0], color[1], color[2]);
+ return gTrue;
+ case 4:
+ appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.3f} k\n",
+ color[0], color[1],
+ color[2], color[3]);
+ return gTrue;
}
- *end = j;
+ return gFalse;
+}
- // 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;
+AnnotLineEndType Annot::parseLineEndType(Object *obj) {
+ if (obj->isName("None")) {
+ return annotLineEndNone;
+ } else if (obj->isName("Square")) {
+ return annotLineEndSquare;
+ } else if (obj->isName("Circle")) {
+ return annotLineEndCircle;
+ } else if (obj->isName("Diamond")) {
+ return annotLineEndDiamond;
+ } else if (obj->isName("OpenArrow")) {
+ return annotLineEndOpenArrow;
+ } else if (obj->isName("ClosedArrow")) {
+ return annotLineEndClosedArrow;
+ } else if (obj->isName("Butt")) {
+ return annotLineEndButt;
+ } else if (obj->isName("ROpenArrow")) {
+ return annotLineEndROpenArrow;
+ } else if (obj->isName("RClosedArrow")) {
+ return annotLineEndRClosedArrow;
+ } else if (obj->isName("Slash")) {
+ return annotLineEndSlash;
+ } else {
+ return annotLineEndNone;
}
- *width = w;
+}
- // next line
- while (j < text->getLength() && text->getChar(j) == ' ') {
- ++j;
- }
- if (j < text->getLength() && text->getChar(j) == 0x0d) {
- ++j;
+void Annot::adjustLineEndpoint(AnnotLineEndType lineEnd,
+ double x, double y, double dx, double dy,
+ double w, double *tx, double *ty) {
+ switch (lineEnd) {
+ case annotLineEndNone:
+ w = 0;
+ break;
+ case annotLineEndSquare:
+ w *= lineEndSize1;
+ break;
+ case annotLineEndCircle:
+ w *= lineEndSize1;
+ break;
+ case annotLineEndDiamond:
+ w *= lineEndSize1;
+ break;
+ case annotLineEndOpenArrow:
+ w = 0;
+ break;
+ case annotLineEndClosedArrow:
+ w *= lineEndSize2 * cos(lineArrowAngle);
+ break;
+ case annotLineEndButt:
+ w = 0;
+ break;
+ case annotLineEndROpenArrow:
+ w *= lineEndSize2 * cos(lineArrowAngle);
+ break;
+ case annotLineEndRClosedArrow:
+ w *= lineEndSize2 * cos(lineArrowAngle);
+ break;
+ case annotLineEndSlash:
+ w = 0;
+ break;
}
- if (j < text->getLength() && text->getChar(j) == 0x0a) {
- ++j;
+ *tx = x + w * dx;
+ *ty = y + w * dy;
+}
+
+void Annot::drawLineArrow(AnnotLineEndType lineEnd,
+ double x, double y, double dx, double dy,
+ double w, GBool fill) {
+ switch (lineEnd) {
+ case annotLineEndNone:
+ break;
+ case annotLineEndSquare:
+ w *= lineEndSize1;
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n",
+ x + w*dx + 0.5*w*dy,
+ y + w*dy - 0.5*w*dx);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ x + 0.5*w*dy,
+ y - 0.5*w*dx);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ x - 0.5*w*dy,
+ y + 0.5*w*dx);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ x + w*dx - 0.5*w*dy,
+ y + w*dy + 0.5*w*dx);
+ appearBuf->append(fill ? "b\n" : "s\n");
+ break;
+ case annotLineEndCircle:
+ w *= lineEndSize1;
+ drawCircle(x + 0.5*w*dx, y + 0.5*w*dy, 0.5*w, fill ? "b" : "s");
+ break;
+ case annotLineEndDiamond:
+ w *= lineEndSize1;
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n", x, y);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ x + 0.5*w*dx - 0.5*w*dy,
+ y + 0.5*w*dy + 0.5*w*dx);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ x + w*dx,
+ y + w*dy);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ x + 0.5*w*dx + 0.5*w*dy,
+ y + 0.5*w*dy - 0.5*w*dx);
+ appearBuf->append(fill ? "b\n" : "s\n");
+ break;
+ case annotLineEndOpenArrow:
+ w *= lineEndSize2;
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n",
+ x + w*cos(lineArrowAngle)*dx + w*sin(lineArrowAngle)*dy,
+ y + w*cos(lineArrowAngle)*dy - w*sin(lineArrowAngle)*dx);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n", x, y);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ x + w*cos(lineArrowAngle)*dx - w*sin(lineArrowAngle)*dy,
+ y + w*cos(lineArrowAngle)*dy + w*sin(lineArrowAngle)*dx);
+ appearBuf->append("S\n");
+ break;
+ case annotLineEndClosedArrow:
+ w *= lineEndSize2;
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n",
+ x + w*cos(lineArrowAngle)*dx + w*sin(lineArrowAngle)*dy,
+ y + w*cos(lineArrowAngle)*dy - w*sin(lineArrowAngle)*dx);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n", x, y);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ x + w*cos(lineArrowAngle)*dx - w*sin(lineArrowAngle)*dy,
+ y + w*cos(lineArrowAngle)*dy + w*sin(lineArrowAngle)*dx);
+ appearBuf->append(fill ? "b\n" : "s\n");
+ break;
+ case annotLineEndButt:
+ w *= lineEndSize1;
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n",
+ x + 0.5*w*dy,
+ y - 0.5*w*dx);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ x - 0.5*w*dy,
+ y + 0.5*w*dx);
+ appearBuf->append("S\n");
+ break;
+ case annotLineEndROpenArrow:
+ w *= lineEndSize2;
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n",
+ x + w*sin(lineArrowAngle)*dy,
+ y - w*sin(lineArrowAngle)*dx);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ x + w*cos(lineArrowAngle)*dx,
+ y + w*cos(lineArrowAngle)*dy);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ x - w*sin(lineArrowAngle)*dy,
+ y + w*sin(lineArrowAngle)*dx);
+ appearBuf->append("S\n");
+ break;
+ case annotLineEndRClosedArrow:
+ w *= lineEndSize2;
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n",
+ x + w*sin(lineArrowAngle)*dy,
+ y - w*sin(lineArrowAngle)*dx);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ x + w*cos(lineArrowAngle)*dx,
+ y + w*cos(lineArrowAngle)*dy);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ x - w*sin(lineArrowAngle)*dy,
+ y + w*sin(lineArrowAngle)*dx);
+ appearBuf->append(fill ? "b\n" : "s\n");
+ break;
+ case annotLineEndSlash:
+ w *= lineEndSize1;
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n",
+ x + 0.5*w*cos(lineArrowAngle)*dy
+ - 0.5*w*sin(lineArrowAngle)*dx,
+ y - 0.5*w*cos(lineArrowAngle)*dx
+ - 0.5*w*sin(lineArrowAngle)*dy);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ x - 0.5*w*cos(lineArrowAngle)*dy
+ + 0.5*w*sin(lineArrowAngle)*dx,
+ y + 0.5*w*cos(lineArrowAngle)*dx
+ + 0.5*w*sin(lineArrowAngle)*dy);
+ appearBuf->append("S\n");
+ break;
}
- *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",
+// <cmd> is used to draw the circle ("f", "s", or "b").
+void Annot::drawCircle(double cx, double cy, double r, const char *cmd) {
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n",
cx + r, cy);
- appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} 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",
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} 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",
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} 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",
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
cx + bezierCircle * r, cy - r,
cx + r, cy - bezierCircle * r,
cx + r, cy);
- appearBuf->append(fill ? "f\n" : "s\n");
+ appearBuf->appendf("{0:s}\n", cmd);
}
// Draw the top-left half of an (approximate) circle of radius <r>
@@ -1417,16 +974,16 @@ void Annot::drawCircleTopLeft(double cx, double cy, double r) {
double r2;
r2 = r / sqrt(2.0);
- appearBuf->appendf("{0:.2f} {1:.2f} m\n",
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n",
cx + r2, cy + r2);
- appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} 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",
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
cx - (1 + bezierCircle) * r2,
cy + (1 - bezierCircle) * r2,
cx - (1 + bezierCircle) * r2,
@@ -1442,16 +999,16 @@ void Annot::drawCircleBottomRight(double cx, double cy, double r) {
double r2;
r2 = r / sqrt(2.0);
- appearBuf->appendf("{0:.2f} {1:.2f} m\n",
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n",
cx - r2, cy - r2);
- appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} 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",
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
cx + (1 + bezierCircle) * r2,
cy - (1 - bezierCircle) * r2,
cx + (1 + bezierCircle) * r2,
@@ -1461,32 +1018,7 @@ void Annot::drawCircleBottomRight(double cx, double cy, double r) {
appearBuf->append("S\n");
}
-// Look up an inheritable field dictionary entry.
-Object *Annot::fieldLookup(Dict *field, Dict *acroForm,
- const 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(), acroForm, key, obj);
- } else if (acroForm) {
- // some fields don't specify a parent, so we check the AcroForm
- // dictionary just in case
- fieldLookup(acroForm, NULL, key, obj);
- } else {
- obj->initNull();
- }
- parent.free();
- return obj;
-}
-
void Annot::draw(Gfx *gfx, GBool printing) {
- Object obj;
GBool oc, isLink;
// check the flags
@@ -1503,10 +1035,8 @@ void Annot::draw(Gfx *gfx, GBool printing) {
// draw the appearance stream
isLink = type && !type->cmp("Link");
- appearance.fetch(doc->getXRef(), &obj);
- gfx->drawAnnot(&obj, isLink ? borderStyle : (AnnotBorderStyle *)NULL,
+ gfx->drawAnnot(&appearance, isLink ? borderStyle : (AnnotBorderStyle *)NULL,
xMin, yMin, xMax, yMax);
- obj.free();
}
Object *Annot::getObject(Object *obj) {
@@ -1524,8 +1054,9 @@ Object *Annot::getObject(Object *obj) {
Annots::Annots(PDFDoc *docA, Object *annotsObj) {
Annot *annot;
- Object obj1;
+ Object obj1, obj2;
Ref ref;
+ GBool drawWidgetAnnots;
int size;
int i;
@@ -1535,6 +1066,13 @@ Annots::Annots(PDFDoc *docA, Object *annotsObj) {
nAnnots = 0;
if (annotsObj->isArray()) {
+ // Kludge: some PDF files define an empty AcroForm, but still
+ // include Widget-type annotations -- in that case, we want to
+ // draw the widgets (since the form code won't). This really
+ // ought to look for Widget-type annotations that are not included
+ // in any form field.
+ drawWidgetAnnots = !doc->getCatalog()->getForm() ||
+ doc->getCatalog()->getForm()->getNumFields() == 0;
for (i = 0; i < annotsObj->arrayGetLength(); ++i) {
if (annotsObj->arrayGetNF(i, &obj1)->isRef()) {
ref = obj1.getRef();
@@ -1544,16 +1082,20 @@ Annots::Annots(PDFDoc *docA, Object *annotsObj) {
ref.num = ref.gen = -1;
}
if (obj1.isDict()) {
- annot = new Annot(doc, obj1.getDict(), &ref);
- if (annot->isOk()) {
- if (nAnnots >= size) {
- size += 16;
- annots = (Annot **)greallocn(annots, size, sizeof(Annot *));
+ if (drawWidgetAnnots ||
+ !obj1.dictLookup("Subtype", &obj2)->isName("Widget")) {
+ annot = new Annot(doc, obj1.getDict(), &ref);
+ if (annot->isOk()) {
+ if (nAnnots >= size) {
+ size += 16;
+ annots = (Annot **)greallocn(annots, size, sizeof(Annot *));
+ }
+ annots[nAnnots++] = annot;
+ } else {
+ delete annot;
}
- annots[nAnnots++] = annot;
- } else {
- delete annot;
}
+ obj2.free();
}
obj1.free();
}
@@ -1569,69 +1111,11 @@ Annots::~Annots() {
gfree(annots);
}
-void Annots::generateAppearances() {
- Dict *acroForm;
- Object obj1, obj2;
- Ref ref;
+void Annots::generateAnnotAppearances() {
int i;
- acroForm = doc->getCatalog()->getAcroForm()->isDict() ?
- doc->getCatalog()->getAcroForm()->getDict() : NULL;
- 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();
+ for (i = 0; i < nAnnots; ++i) {
+ annots[i]->generateAnnotAppearance();
}
}
diff --git a/xpdf/Annot.h b/xpdf/Annot.h
index 987e80e..6f52ac4 100644
--- a/xpdf/Annot.h
+++ b/xpdf/Annot.h
@@ -38,15 +38,15 @@ public:
AnnotBorderStyle(AnnotBorderType typeA, double widthA,
double *dashA, int dashLengthA,
- double rA, double gA, double bA);
+ double *colorA, int nColorCompsA);
~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; }
+ int getNumColorComps() { return nColorComps; }
+ double *getColor() { return color; }
private:
@@ -54,7 +54,23 @@ private:
double width;
double *dash;
int dashLength;
- double r, g, b;
+ double color[4];
+ int nColorComps;
+};
+
+//------------------------------------------------------------------------
+
+enum AnnotLineEndType {
+ annotLineEndNone,
+ annotLineEndSquare,
+ annotLineEndCircle,
+ annotLineEndDiamond,
+ annotLineEndOpenArrow,
+ annotLineEndClosedArrow,
+ annotLineEndButt,
+ annotLineEndROpenArrow,
+ annotLineEndRClosedArrow,
+ annotLineEndSlash
};
//------------------------------------------------------------------------
@@ -85,25 +101,26 @@ public:
GBool match(Ref *refA)
{ return ref.num == refA->num && ref.gen == refA->gen; }
- void generateFieldAppearance(Dict *field, Dict *annot, Dict *acroForm);
+ void generateAnnotAppearance();
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, int rot);
- 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 generateLineAppearance();
+ void generatePolyLineAppearance();
+ void generatePolygonAppearance();
+ void setLineStyle(AnnotBorderStyle *bs, double *lineWidth);
+ void setStrokeColor(double *color, int nComps);
+ GBool setFillColor(Object *colorObj);
+ AnnotLineEndType parseLineEndType(Object *obj);
+ void adjustLineEndpoint(AnnotLineEndType lineEnd,
+ double x, double y, double dx, double dy,
+ double w, double *tx, double *ty);
+ void drawLineArrow(AnnotLineEndType lineEnd,
+ double x, double y, double dx, double dy,
+ double w, GBool fill);
+ void drawCircle(double cx, double cy, double r, const char *cmd);
void drawCircleTopLeft(double cx, double cy, double r);
void drawCircleBottomRight(double cx, double cy, double r);
- Object *fieldLookup(Dict *field, Dict *acroForm,
- const char *key, Object *obj);
PDFDoc *doc;
XRef *xref; // the xref table for this PDF file
@@ -137,9 +154,9 @@ public:
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();
+ // Generate an appearance stream for any non-form-field annotation
+ // that is missing it.
+ void generateAnnotAppearances();
private:
diff --git a/xpdf/CMap.cc b/xpdf/CMap.cc
index bb80bdd..affdf93 100644
--- a/xpdf/CMap.cc
+++ b/xpdf/CMap.cc
@@ -283,34 +283,36 @@ void CMap::copyVector(CMapVectorEntry *dest, CMapVectorEntry *src) {
void CMap::addCIDs(Guint start, Guint end, Guint nBytes, CID firstCID) {
CMapVectorEntry *vec;
- CID cid;
- int byte;
- Guint i, j;
-
- vec = vector;
- for (i = nBytes - 1; i >= 1; --i) {
- byte = (start >> (8 * i)) & 0xff;
- if (!vec[byte].isVector) {
- vec[byte].isVector = gTrue;
- vec[byte].vector =
- (CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry));
- for (j = 0; j < 256; ++j) {
- vec[byte].vector[j].isVector = gFalse;
- vec[byte].vector[j].cid = 0;
+ int byte, byte0, byte1;
+ Guint start1, end1, i, j, k;
+
+ start1 = start & 0xffffff00;
+ end1 = end & 0xffffff00;
+ for (i = start1; i <= end1; i += 0x100) {
+ vec = vector;
+ for (j = nBytes - 1; j >= 1; --j) {
+ byte = (i >> (8 * j)) & 0xff;
+ if (!vec[byte].isVector) {
+ vec[byte].isVector = gTrue;
+ vec[byte].vector =
+ (CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry));
+ for (k = 0; k < 256; ++k) {
+ vec[byte].vector[k].isVector = gFalse;
+ vec[byte].vector[k].cid = 0;
+ }
}
+ vec = vec[byte].vector;
}
- vec = vec[byte].vector;
- }
- cid = firstCID;
- for (byte = (int)(start & 0xff); byte <= (int)(end & 0xff); ++byte) {
- if (vec[byte].isVector) {
- error(errSyntaxError, -1,
- "Invalid CID ({0:x} - {1:x} [{2:d} bytes]) in CMap",
- start, end, nBytes);
- } else {
- vec[byte].cid = cid;
+ byte0 = (i < start) ? (start & 0xff) : 0;
+ byte1 = (i + 0xff > end) ? (end & 0xff) : 0xff;
+ for (byte = byte0; byte <= byte1; ++byte) {
+ if (vec[byte].isVector) {
+ error(errSyntaxError, -1, "Invalid CID ({0:x} [{1:d} bytes]) in CMap",
+ i, nBytes);
+ } else {
+ vec[byte].cid = firstCID + ((i + byte) - start);
+ }
}
- ++cid;
}
}
diff --git a/xpdf/Catalog.cc b/xpdf/Catalog.cc
index 6c8a703..e2e2a87 100644
--- a/xpdf/Catalog.cc
+++ b/xpdf/Catalog.cc
@@ -2,7 +2,7 @@
//
// Catalog.cc
//
-// Copyright 1996-2007 Glyph & Cog, LLC
+// Copyright 1996-2013 Glyph & Cog, LLC
//
//========================================================================
@@ -27,7 +27,8 @@
#include "Page.h"
#include "Error.h"
#include "Link.h"
-#include "PDFDocEncoding.h"
+#include "Form.h"
+#include "TextString.h"
#include "Catalog.h"
//------------------------------------------------------------------------
@@ -69,23 +70,20 @@ PageTreeNode::~PageTreeNode() {
class EmbeddedFile {
public:
- EmbeddedFile(Unicode *nameA, int nameLenA, Object *streamRefA);
+ EmbeddedFile(TextString *nameA, Object *streamRefA);
~EmbeddedFile();
- Unicode *name;
- int nameLen;
+ TextString *name;
Object streamRef;
};
-EmbeddedFile::EmbeddedFile(Unicode *nameA, int nameLenA,
- Object *streamRefA) {
+EmbeddedFile::EmbeddedFile(TextString *nameA, Object *streamRefA) {
name = nameA;
- nameLen = nameLenA;
streamRefA->copy(&streamRef);
}
EmbeddedFile::~EmbeddedFile() {
- gfree(name);
+ delete name;
streamRef.free();
}
@@ -105,6 +103,7 @@ Catalog::Catalog(PDFDoc *docA) {
pageRefs = NULL;
numPages = 0;
baseURI = NULL;
+ form = NULL;
embeddedFiles = NULL;
xref->getCatalog(&catDict);
@@ -165,6 +164,10 @@ Catalog::Catalog(PDFDoc *docA) {
// get the AcroForm dictionary
catDict.dictLookup("AcroForm", &acroForm);
+ if (!acroForm.isNull()) {
+ form = Form::load(doc, this, &acroForm);
+ }
+
// get the OCProperties dictionary
catDict.dictLookup("OCProperties", &ocProperties);
@@ -205,6 +208,9 @@ Catalog::~Catalog() {
structTreeRoot.free();
outline.free();
acroForm.free();
+ if (form) {
+ delete form;
+ }
ocProperties.free();
if (embeddedFiles) {
deleteGList(embeddedFiles, EmbeddedFile);
@@ -236,7 +242,8 @@ GString *Catalog::readMetadata() {
GString *s;
Dict *dict;
Object obj;
- int c;
+ char buf[4096];
+ int n;
if (!metadata.isStream()) {
return NULL;
@@ -249,8 +256,8 @@ GString *Catalog::readMetadata() {
obj.free();
s = new GString();
metadata.streamReset();
- while ((c = metadata.streamGetChar()) != EOF) {
- s->append(c);
+ while ((n = metadata.streamGetBlock(buf, sizeof(buf))) > 0) {
+ s->append(buf, n);
}
metadata.streamClose();
return s;
@@ -615,6 +622,12 @@ void Catalog::readFileAttachmentAnnots(Object *pageNodeRef,
Object pageNode, kids, kid, annots, annot, subtype, fileSpec, contents;
int i;
+ // check for an invalid object reference (e.g., in a damaged PDF file)
+ if (pageNodeRef->getRefNum() < 0 ||
+ pageNodeRef->getRefNum() >= xref->getNumObjects()) {
+ return;
+ }
+
// check for a page tree loop
if (pageNodeRef->isRef()) {
if (touchedObjs[pageNodeRef->getRefNum()]) {
@@ -661,42 +674,22 @@ void Catalog::readFileAttachmentAnnots(Object *pageNodeRef,
void Catalog::readEmbeddedFile(Object *fileSpec, Object *name1) {
Object name2, efObj, streamObj;
GString *s;
- Unicode *name;
- int nameLen, i;
+ TextString *name;
if (fileSpec->isDict()) {
if (fileSpec->dictLookup("UF", &name2)->isString()) {
- s = name2.getString();
+ name = new TextString(name2.getString());
} else {
name2.free();
if (fileSpec->dictLookup("F", &name2)->isString()) {
- s = name2.getString();
+ name = new TextString(name2.getString());
} else if (name1 && name1->isString()) {
- s = name1->getString();
- } else {
- s = NULL;
- }
- }
- if (s) {
- if ((s->getChar(0) & 0xff) == 0xfe &&
- (s->getChar(1) & 0xff) == 0xff) {
- nameLen = (s->getLength() - 2) / 2;
- name = (Unicode *)gmallocn(nameLen, sizeof(Unicode));
- for (i = 0; i < nameLen; ++i) {
- name[i] = ((s->getChar(2 + 2*i) & 0xff) << 8) |
- (s->getChar(3 + 2*i) & 0xff);
- }
+ name = new TextString(name1->getString());
} else {
- nameLen = s->getLength();
- name = (Unicode *)gmallocn(nameLen, sizeof(Unicode));
- for (i = 0; i < nameLen; ++i) {
- name[i] = pdfDocEncoding[s->getChar(i) & 0xff];
- }
+ s = new GString("?");
+ name = new TextString(s);
+ delete s;
}
- } else {
- nameLen = 1;
- name = (Unicode *)gmallocn(nameLen, sizeof(Unicode));
- name[0] = '?';
}
name2.free();
if (fileSpec->dictLookup("EF", &efObj)->isDict()) {
@@ -704,13 +697,13 @@ void Catalog::readEmbeddedFile(Object *fileSpec, Object *name1) {
if (!embeddedFiles) {
embeddedFiles = new GList();
}
- embeddedFiles->append(new EmbeddedFile(name, nameLen, &streamObj));
+ embeddedFiles->append(new EmbeddedFile(name, &streamObj));
} else {
- gfree(name);
+ delete name;
}
streamObj.free();
} else {
- gfree(name);
+ delete name;
}
efObj.free();
}
@@ -721,11 +714,15 @@ int Catalog::getNumEmbeddedFiles() {
}
Unicode *Catalog::getEmbeddedFileName(int idx) {
- return ((EmbeddedFile *)embeddedFiles->get(idx))->name;
+ return ((EmbeddedFile *)embeddedFiles->get(idx))->name->getUnicode();
}
int Catalog::getEmbeddedFileNameLength(int idx) {
- return ((EmbeddedFile *)embeddedFiles->get(idx))->nameLen;
+ return ((EmbeddedFile *)embeddedFiles->get(idx))->name->getLength();
+}
+
+Object *Catalog::getEmbeddedFileStreamRef(int idx) {
+ return &((EmbeddedFile *)embeddedFiles->get(idx))->streamRef;
}
Object *Catalog::getEmbeddedFileStreamObj(int idx, Object *strObj) {
diff --git a/xpdf/Catalog.h b/xpdf/Catalog.h
index efbbeda..7f064aa 100644
--- a/xpdf/Catalog.h
+++ b/xpdf/Catalog.h
@@ -26,6 +26,7 @@ class PageAttrs;
struct Ref;
class LinkDest;
class PageTreeNode;
+class Form;
//------------------------------------------------------------------------
// Catalog
@@ -82,12 +83,15 @@ public:
Object *getAcroForm() { return &acroForm; }
+ Form *getForm() { return form; }
+
Object *getOCProperties() { return &ocProperties; }
// Get the list of embedded files.
int getNumEmbeddedFiles();
Unicode *getEmbeddedFileName(int idx);
int getEmbeddedFileNameLength(int idx);
+ Object *getEmbeddedFileStreamRef(int idx);
Object *getEmbeddedFileStreamObj(int idx, Object *strObj);
private:
@@ -106,6 +110,7 @@ private:
Object structTreeRoot; // structure tree root dictionary
Object outline; // outline dictionary
Object acroForm; // AcroForm dictionary
+ Form *form; // parsed form
Object ocProperties; // OCProperties dictionary
GList *embeddedFiles; // embedded file list [EmbeddedFile]
GBool ok; // true if catalog is valid
diff --git a/xpdf/CharCodeToUnicode.cc b/xpdf/CharCodeToUnicode.cc
index ff40595..674b992 100644
--- a/xpdf/CharCodeToUnicode.cc
+++ b/xpdf/CharCodeToUnicode.cc
@@ -167,7 +167,7 @@ CharCodeToUnicode *CharCodeToUnicode::parseUnicodeToUnicode(
while (getLine(buf, sizeof(buf), f)) {
++line;
if (!(tok = strtok(buf, " \t\r\n")) ||
- !parseHex(tok, strlen(tok), &u0)) {
+ !parseHex(tok, (int)strlen(tok), &u0)) {
error(errSyntaxWarning, -1,
"Bad line ({0:d}) in unicodeToUnicode file '{1:t}'",
line, fileName);
@@ -178,7 +178,7 @@ CharCodeToUnicode *CharCodeToUnicode::parseUnicodeToUnicode(
if (!(tok = strtok(NULL, " \t\r\n"))) {
break;
}
- if (!parseHex(tok, strlen(tok), &uBuf[n])) {
+ if (!parseHex(tok, (int)strlen(tok), &uBuf[n])) {
error(errSyntaxWarning, -1,
"Bad line ({0:d}) in unicodeToUnicode file '{1:t}'",
line, fileName);
@@ -336,23 +336,21 @@ void CharCodeToUnicode::parseCMap1(int (*getCharFunc)(void *), void *data,
if (code1 > maxCode || code2 > maxCode) {
error(errSyntaxWarning, -1,
"Invalid entry in bfrange block in ToUnicode CMap");
- if (code1 > maxCode) {
- code1 = maxCode;
- }
if (code2 > maxCode) {
code2 = maxCode;
}
}
if (!strcmp(tok3, "[")) {
i = 0;
- while (pst->getToken(tok1, sizeof(tok1), &n1) &&
- code1 + i <= code2) {
+ while (pst->getToken(tok1, sizeof(tok1), &n1)) {
if (!strcmp(tok1, "]")) {
break;
}
if (tok1[0] == '<' && tok1[n1 - 1] == '>') {
- tok1[n1 - 1] = '\0';
- addMapping(code1 + i, tok1 + 1, n1 - 2, 0);
+ if (code1 + i <= code2) {
+ tok1[n1 - 1] = '\0';
+ addMapping(code1 + i, tok1 + 1, n1 - 2, 0);
+ }
} else {
error(errSyntaxWarning, -1,
"Illegal entry in bfrange block in ToUnicode CMap");
diff --git a/xpdf/CharCodeToUnicode.h b/xpdf/CharCodeToUnicode.h
index b4ccd04..1b870ef 100644
--- a/xpdf/CharCodeToUnicode.h
+++ b/xpdf/CharCodeToUnicode.h
@@ -74,6 +74,8 @@ public:
// code supported by the mapping.
CharCode getLength() { return mapLen; }
+ GBool isIdentity() { return !map; }
+
private:
void parseCMap1(int (*getCharFunc)(void *), void *data, int nBits);
diff --git a/xpdf/Decrypt.cc b/xpdf/Decrypt.cc
index afde3c3..bf0bfb7 100644
--- a/xpdf/Decrypt.cc
+++ b/xpdf/Decrypt.cc
@@ -16,13 +16,12 @@
#include "gmem.h"
#include "Decrypt.h"
-static void aesKeyExpansion(DecryptAESState *s,
- Guchar *objKey, int objKeyLen);
-static void aesDecryptBlock(DecryptAESState *s, Guchar *in, GBool last);
static void aes256KeyExpansion(DecryptAES256State *s,
Guchar *objKey, int objKeyLen);
static void aes256DecryptBlock(DecryptAES256State *s, Guchar *in, GBool last);
static void sha256(Guchar *msg, int msgLen, Guchar *hash);
+static void sha384(Guchar *msg, int msgLen, Guchar *hash);
+static void sha512(Guchar *msg, int msgLen, Guchar *hash);
static Guchar passwordPad[32] = {
0x28, 0xbf, 0x4e, 0x5e, 0x4e, 0x75, 0x8a, 0x41,
@@ -45,6 +44,7 @@ GBool Decrypt::makeFileKey(int encVersion, int encRevision, int keyLength,
DecryptAES256State state;
Guchar test[127 + 56], test2[32];
GString *userPassword2;
+ const char *userPW;
Guchar fState[256];
Guchar tmpKey[16];
Guchar fx, fy;
@@ -52,7 +52,7 @@ GBool Decrypt::makeFileKey(int encVersion, int encRevision, int keyLength,
*ownerPasswordOk = gFalse;
- if (encRevision == 5) {
+ if (encRevision == 5 || encRevision == 6) {
// check the owner password
if (ownerPassword) {
@@ -65,6 +65,10 @@ GBool Decrypt::makeFileKey(int encVersion, int encRevision, int keyLength,
memcpy(test + len, ownerKey->getCString() + 32, 8);
memcpy(test + len + 8, userKey->getCString(), 48);
sha256(test, len + 56, test);
+ if (encRevision == 6) {
+ r6Hash(test, 32, ownerPassword->getCString(), len,
+ userKey->getCString());
+ }
if (!memcmp(test, ownerKey->getCString(), 32)) {
// compute the file key from the owner password
@@ -72,6 +76,10 @@ GBool Decrypt::makeFileKey(int encVersion, int encRevision, int keyLength,
memcpy(test + len, ownerKey->getCString() + 40, 8);
memcpy(test + len + 8, userKey->getCString(), 48);
sha256(test, len + 56, test);
+ if (encRevision == 6) {
+ r6Hash(test, 32, ownerPassword->getCString(), len,
+ userKey->getCString());
+ }
aes256KeyExpansion(&state, test, 32);
for (i = 0; i < 16; ++i) {
state.cbc[i] = 0;
@@ -90,34 +98,45 @@ GBool Decrypt::makeFileKey(int encVersion, int encRevision, int keyLength,
// check the user password
if (userPassword) {
//~ this is supposed to convert the password to UTF-8 using "SASLprep"
+ userPW = userPassword->getCString();
len = userPassword->getLength();
if (len > 127) {
len = 127;
}
- memcpy(test, userPassword->getCString(), len);
- memcpy(test + len, userKey->getCString() + 32, 8);
- sha256(test, len + 8, test);
- if (!memcmp(test, userKey->getCString(), 32)) {
-
- // compute the file key from the user password
- memcpy(test, userPassword->getCString(), len);
- memcpy(test + len, userKey->getCString() + 40, 8);
- sha256(test, len + 8, test);
- aes256KeyExpansion(&state, test, 32);
- for (i = 0; i < 16; ++i) {
- state.cbc[i] = 0;
- }
- aes256DecryptBlock(&state, (Guchar *)userEnc->getCString(), gFalse);
- memcpy(fileKey, state.buf, 16);
- aes256DecryptBlock(&state, (Guchar *)userEnc->getCString() + 16,
- gFalse);
- memcpy(fileKey + 16, state.buf, 16);
+ } else {
+ userPW = "";
+ len = 0;
+ }
+ memcpy(test, userPW, len);
+ memcpy(test + len, userKey->getCString() + 32, 8);
+ sha256(test, len + 8, test);
+ if (encRevision == 6) {
+ r6Hash(test, 32, userPW, len, NULL);
+ }
+ if (!memcmp(test, userKey->getCString(), 32)) {
- return gTrue;
+ // compute the file key from the user password
+ memcpy(test, userPW, len);
+ memcpy(test + len, userKey->getCString() + 40, 8);
+ sha256(test, len + 8, test);
+ if (encRevision == 6) {
+ r6Hash(test, 32, userPW, len, NULL);
}
+ aes256KeyExpansion(&state, test, 32);
+ for (i = 0; i < 16; ++i) {
+ state.cbc[i] = 0;
+ }
+ aes256DecryptBlock(&state, (Guchar *)userEnc->getCString(), gFalse);
+ memcpy(fileKey, state.buf, 16);
+ aes256DecryptBlock(&state, (Guchar *)userEnc->getCString() + 16,
+ gFalse);
+ memcpy(fileKey + 16, state.buf, 16);
+
+ return gTrue;
}
return gFalse;
+
} else {
// try using the supplied owner password to generate the user password
@@ -172,6 +191,61 @@ GBool Decrypt::makeFileKey(int encVersion, int encRevision, int keyLength,
}
}
+void Decrypt::r6Hash(Guchar *key, int keyLen, const char *pwd, int pwdLen,
+ char *userKey) {
+ Guchar key1[64*(127+64+48)];
+ DecryptAESState state128;
+ int n, i, j, k;
+
+ i = 0;
+ while (1) {
+ memcpy(key1, pwd, pwdLen);
+ memcpy(key1 + pwdLen, key, keyLen);
+ n = pwdLen + keyLen;
+ if (userKey) {
+ memcpy(key1 + pwdLen + keyLen, userKey, 48);
+ n += 48;
+ }
+ for (j = 1; j < 64; ++j) {
+ memcpy(key1 + j * n, key1, n);
+ }
+ n *= 64;
+ aesKeyExpansion(&state128, key, 16, gFalse);
+ for (j = 0; j < 16; ++j) {
+ state128.cbc[j] = key[16+j];
+ }
+ for (j = 0; j < n; j += 16) {
+ aesEncryptBlock(&state128, key1 + j);
+ memcpy(key1 + j, state128.buf, 16);
+ }
+ k = 0;
+ for (j = 0; j < 16; ++j) {
+ k += key1[j] % 3;
+ }
+ k %= 3;
+ switch (k) {
+ case 0:
+ sha256(key1, n, key);
+ keyLen = 32;
+ break;
+ case 1:
+ sha384(key1, n, key);
+ keyLen = 48;
+ break;
+ case 2:
+ sha512(key1, n, key);
+ keyLen = 64;
+ break;
+ }
+ // from the spec, it appears that i should be incremented after
+ // the test, but that doesn't match what Adobe does
+ ++i;
+ if (i >= 64 && key1[n - 1] <= i - 32) {
+ break;
+ }
+ }
+}
+
GBool Decrypt::makeFileKey2(int encVersion, int encRevision, int keyLength,
GString *ownerKey, GString *userKey,
int permissions, GString *fileID,
@@ -305,8 +379,6 @@ DecryptStream::~DecryptStream() {
}
void DecryptStream::reset() {
- int i;
-
str->reset();
switch (algo) {
case cryptRC4:
@@ -315,17 +387,13 @@ void DecryptStream::reset() {
state.rc4.buf = EOF;
break;
case cryptAES:
- aesKeyExpansion(&state.aes, objKey, objKeyLength);
- for (i = 0; i < 16; ++i) {
- state.aes.cbc[i] = str->getChar();
- }
+ aesKeyExpansion(&state.aes, objKey, objKeyLength, gTrue);
+ str->getBlock((char *)state.aes.cbc, 16);
state.aes.bufIdx = 16;
break;
case cryptAES256:
aes256KeyExpansion(&state.aes256, objKey, objKeyLength);
- for (i = 0; i < 16; ++i) {
- state.aes256.cbc[i] = str->getChar();
- }
+ str->getBlock((char *)state.aes256.cbc, 16);
state.aes256.bufIdx = 16;
break;
}
@@ -333,7 +401,7 @@ void DecryptStream::reset() {
int DecryptStream::getChar() {
Guchar in[16];
- int c, i;
+ int c;
c = EOF; // make gcc happy
switch (algo) {
@@ -350,11 +418,8 @@ int DecryptStream::getChar() {
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;
+ if (str->getBlock((char *)in, 16) != 16) {
+ return EOF;
}
aesDecryptBlock(&state.aes, in, str->lookChar() == EOF);
}
@@ -366,11 +431,8 @@ int DecryptStream::getChar() {
break;
case cryptAES256:
if (state.aes256.bufIdx == 16) {
- for (i = 0; i < 16; ++i) {
- if ((c = str->getChar()) == EOF) {
- return EOF;
- }
- in[i] = (Guchar)c;
+ if (str->getBlock((char *)in, 16) != 16) {
+ return EOF;
}
aes256DecryptBlock(&state.aes256, in, str->lookChar() == EOF);
}
@@ -386,7 +448,7 @@ int DecryptStream::getChar() {
int DecryptStream::lookChar() {
Guchar in[16];
- int c, i;
+ int c;
c = EOF; // make gcc happy
switch (algo) {
@@ -402,11 +464,8 @@ int DecryptStream::lookChar() {
break;
case cryptAES:
if (state.aes.bufIdx == 16) {
- for (i = 0; i < 16; ++i) {
- if ((c = str->getChar()) == EOF) {
- return EOF;
- }
- in[i] = c;
+ if (str->getBlock((char *)in, 16) != 16) {
+ return EOF;
}
aesDecryptBlock(&state.aes, in, str->lookChar() == EOF);
}
@@ -418,11 +477,8 @@ int DecryptStream::lookChar() {
break;
case cryptAES256:
if (state.aes256.bufIdx == 16) {
- for (i = 0; i < 16; ++i) {
- if ((c = str->getChar()) == EOF) {
- return EOF;
- }
- in[i] = c;
+ if (str->getBlock((char *)in, 16) != 16) {
+ return EOF;
}
aes256DecryptBlock(&state.aes256, in, str->lookChar() == EOF);
}
@@ -540,6 +596,14 @@ static inline Guint rotWord(Guint x) {
return ((x << 8) & 0xffffffff) | (x >> 24);
}
+static inline void subBytes(Guchar *state) {
+ int i;
+
+ for (i = 0; i < 16; ++i) {
+ state[i] = sbox[state[i]];
+ }
+}
+
static inline void invSubBytes(Guchar *state) {
int i;
@@ -548,6 +612,29 @@ static inline void invSubBytes(Guchar *state) {
}
}
+static inline void shiftRows(Guchar *state) {
+ Guchar t;
+
+ t = state[4];
+ state[4] = state[5];
+ state[5] = state[6];
+ state[6] = state[7];
+ state[7] = t;
+
+ t = state[8];
+ state[8] = state[10];
+ state[10] = t;
+ t = state[9];
+ state[9] = state[11];
+ state[11] = t;
+
+ t = state[15];
+ state[15] = state[14];
+ state[14] = state[13];
+ state[13] = state[12];
+ state[12] = t;
+}
+
static inline void invShiftRows(Guchar *state) {
Guchar t;
@@ -571,6 +658,22 @@ static inline void invShiftRows(Guchar *state) {
state[15] = t;
}
+// {02} \cdot s
+static inline Guchar mul02(Guchar s) {
+ Guchar s2;
+
+ s2 = (s & 0x80) ? ((s << 1) ^ 0x1b) : (s << 1);
+ return s2;
+}
+
+// {03} \cdot s
+static inline Guchar mul03(Guchar s) {
+ Guchar s2;
+
+ s2 = (s & 0x80) ? ((s << 1) ^ 0x1b) : (s << 1);
+ return s ^ s2;
+}
+
// {09} \cdot s
static inline Guchar mul09(Guchar s) {
Guchar s2, s4, s8;
@@ -611,6 +714,22 @@ static inline Guchar mul0e(Guchar s) {
return s2 ^ s4 ^ s8;
}
+static inline void mixColumns(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] = mul02(s0) ^ mul03(s1) ^ s2 ^ s3;
+ state[4+c] = s0 ^ mul02(s1) ^ mul03(s2) ^ s3;
+ state[8+c] = s0 ^ s1 ^ mul02(s2) ^ mul03(s3);
+ state[12+c] = mul03(s0) ^ s1 ^ s2 ^ mul02(s3);
+ }
+}
+
static inline void invMixColumns(Guchar *state) {
int c;
Guchar s0, s1, s2, s3;
@@ -654,8 +773,9 @@ static inline void addRoundKey(Guchar *state, Guint *w) {
}
}
-static void aesKeyExpansion(DecryptAESState *s,
- Guchar *objKey, int objKeyLen) {
+void aesKeyExpansion(DecryptAESState *s,
+ Guchar *objKey, int objKeyLen,
+ GBool decrypt) {
Guint temp;
int i, round;
@@ -672,12 +792,50 @@ static void aesKeyExpansion(DecryptAESState *s,
}
s->w[i] = s->w[i-4] ^ temp;
}
+ if (decrypt) {
+ for (round = 1; round <= 9; ++round) {
+ invMixColumnsW(&s->w[round * 4]);
+ }
+ }
+}
+
+void aesEncryptBlock(DecryptAESState *s, Guchar *in) {
+ int c, round;
+
+ // initial state + CBC
+ for (c = 0; c < 4; ++c) {
+ s->state[c] = in[4*c] ^ s->cbc[4*c];
+ s->state[4+c] = in[4*c+1] ^ s->cbc[4*c+1];
+ s->state[8+c] = in[4*c+2] ^ s->cbc[4*c+2];
+ s->state[12+c] = in[4*c+3] ^ s->cbc[4*c+3];
+ }
+
+ // round 0
+ addRoundKey(s->state, &s->w[0]);
+
+ // rounds 1 .. 9
for (round = 1; round <= 9; ++round) {
- invMixColumnsW(&s->w[round * 4]);
+ subBytes(s->state);
+ shiftRows(s->state);
+ mixColumns(s->state);
+ addRoundKey(s->state, &s->w[round * 4]);
+ }
+
+ // round 10
+ subBytes(s->state);
+ shiftRows(s->state);
+ addRoundKey(s->state, &s->w[10 * 4]);
+
+ // output + save for next CBC
+ for (c = 0; c < 4; ++c) {
+ s->buf[4*c] = s->cbc[4*c] = s->state[c];
+ s->buf[4*c+1] = s->cbc[4*c+1] = s->state[4+c];
+ s->buf[4*c+2] = s->cbc[4*c+2] = s->state[8+c];
+ s->buf[4*c+3] = s->cbc[4*c+3] = s->state[12+c];
}
}
-static void aesDecryptBlock(DecryptAESState *s, Guchar *in, GBool last) {
+void aesDecryptBlock(DecryptAESState *s, Guchar *in, GBool last) {
int c, round, n, i;
// initial state
@@ -844,151 +1002,187 @@ static inline Gulong md5Round4(Gulong a, Gulong b, Gulong c, Gulong d,
return b + rotateLeft((a + (c ^ (b | ~d)) + Xk + Ti), s);
}
-void md5(Guchar *msg, int msgLen, Guchar *digest) {
+void md5Start(MD5State *state) {
+ state->a = 0x67452301;
+ state->b = 0xefcdab89;
+ state->c = 0x98badcfe;
+ state->d = 0x10325476;
+ state->bufLen = 0;
+ state->msgLen = 0;
+}
+
+static void md5ProcessBlock(MD5State *state) {
Gulong x[16];
- Gulong a, b, c, d, aa, bb, cc, dd;
- int n64;
- int i, j, k;
+ Gulong a, b, c, d;
+ int i;
- // sanity check
- if (msgLen < 0) {
- return;
+ for (i = 0; i < 16; ++i) {
+ x[i] = state->buf[4*i] | (state->buf[4*i+1] << 8) |
+ (state->buf[4*i+2] << 16) | (state->buf[4*i+3] << 24);
}
- // 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;
- }
+ a = state->a;
+ b = state->b;
+ c = state->c;
+ d = state->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
+ state->a += a;
+ state->b += b;
+ state->c += c;
+ state->d += d;
+
+ state->bufLen = 0;
+}
- // 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;
+void md5Append(MD5State *state, Guchar *data, int dataLen) {
+ Guchar *p;
+ int remain, k;
+
+ p = data;
+ remain = dataLen;
+ while (state->bufLen + remain >= 64) {
+ k = 64 - state->bufLen;
+ memcpy(state->buf + state->bufLen, p, k);
+ state->bufLen = 64;
+ md5ProcessBlock(state);
+ p += k;
+ remain -= k;
}
+ if (remain > 0) {
+ memcpy(state->buf + state->bufLen, p, remain);
+ state->bufLen += remain;
+ }
+ state->msgLen += dataLen;
+}
+
+void md5Finish(MD5State *state) {
+ // padding and length
+ state->buf[state->bufLen++] = 0x80;
+ if (state->bufLen > 56) {
+ while (state->bufLen < 64) {
+ state->buf[state->bufLen++] = 0x00;
+ }
+ md5ProcessBlock(state);
+ }
+ while (state->bufLen < 56) {
+ state->buf[state->bufLen++] = 0x00;
+ }
+ state->buf[56] = (Guchar)(state->msgLen << 3);
+ state->buf[57] = (Guchar)(state->msgLen >> 5);
+ state->buf[58] = (Guchar)(state->msgLen >> 13);
+ state->buf[59] = (Guchar)(state->msgLen >> 21);
+ state->buf[60] = (Guchar)(state->msgLen >> 29);
+ state->buf[61] = (Guchar)0;
+ state->buf[62] = (Guchar)0;
+ state->buf[63] = (Guchar)0;
+ state->bufLen = 64;
+ md5ProcessBlock(state);
// 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);
+ state->digest[0] = (Guchar)state->a;
+ state->digest[1] = (Guchar)(state->a >> 8);
+ state->digest[2] = (Guchar)(state->a >> 16);
+ state->digest[3] = (Guchar)(state->a >> 24);
+ state->digest[4] = (Guchar)state->b;
+ state->digest[5] = (Guchar)(state->b >> 8);
+ state->digest[6] = (Guchar)(state->b >> 16);
+ state->digest[7] = (Guchar)(state->b >> 24);
+ state->digest[8] = (Guchar)state->c;
+ state->digest[9] = (Guchar)(state->c >> 8);
+ state->digest[10] = (Guchar)(state->c >> 16);
+ state->digest[11] = (Guchar)(state->c >> 24);
+ state->digest[12] = (Guchar)state->d;
+ state->digest[13] = (Guchar)(state->d >> 8);
+ state->digest[14] = (Guchar)(state->d >> 16);
+ state->digest[15] = (Guchar)(state->d >> 24);
+}
+
+void md5(Guchar *msg, int msgLen, Guchar *digest) {
+ MD5State state;
+ int i;
+
+ if (msgLen < 0) {
+ return;
+ }
+ md5Start(&state);
+ md5Append(&state, msg, msgLen);
+ md5Finish(&state);
+ for (i = 0; i < 16; ++i) {
+ digest[i] = state.digest[i];
+ }
}
//------------------------------------------------------------------------
@@ -1042,7 +1236,7 @@ static inline Guint sha256sigma1(Guint x) {
return rotr(x, 17) ^ rotr(x, 19) ^ (x >> 10);
}
-void sha256HashBlock(Guchar *blk, Guint *H) {
+static void sha256HashBlock(Guchar *blk, Guint *H) {
Guint W[64];
Guint a, b, c, d, e, f, g, h;
Guint T1, T2;
@@ -1147,3 +1341,270 @@ static void sha256(Guchar *msg, int msgLen, Guchar *hash) {
hash[i*4 + 3] = (Guchar)H[i];
}
}
+
+//------------------------------------------------------------------------
+// SHA-384 and SHA-512 hashes
+//------------------------------------------------------------------------
+
+typedef unsigned long long SHA512Uint64;
+
+static SHA512Uint64 sha512K[80] = {
+ 0x428a2f98d728ae22LL, 0x7137449123ef65cdLL,
+ 0xb5c0fbcfec4d3b2fLL, 0xe9b5dba58189dbbcLL,
+ 0x3956c25bf348b538LL, 0x59f111f1b605d019LL,
+ 0x923f82a4af194f9bLL, 0xab1c5ed5da6d8118LL,
+ 0xd807aa98a3030242LL, 0x12835b0145706fbeLL,
+ 0x243185be4ee4b28cLL, 0x550c7dc3d5ffb4e2LL,
+ 0x72be5d74f27b896fLL, 0x80deb1fe3b1696b1LL,
+ 0x9bdc06a725c71235LL, 0xc19bf174cf692694LL,
+ 0xe49b69c19ef14ad2LL, 0xefbe4786384f25e3LL,
+ 0x0fc19dc68b8cd5b5LL, 0x240ca1cc77ac9c65LL,
+ 0x2de92c6f592b0275LL, 0x4a7484aa6ea6e483LL,
+ 0x5cb0a9dcbd41fbd4LL, 0x76f988da831153b5LL,
+ 0x983e5152ee66dfabLL, 0xa831c66d2db43210LL,
+ 0xb00327c898fb213fLL, 0xbf597fc7beef0ee4LL,
+ 0xc6e00bf33da88fc2LL, 0xd5a79147930aa725LL,
+ 0x06ca6351e003826fLL, 0x142929670a0e6e70LL,
+ 0x27b70a8546d22ffcLL, 0x2e1b21385c26c926LL,
+ 0x4d2c6dfc5ac42aedLL, 0x53380d139d95b3dfLL,
+ 0x650a73548baf63deLL, 0x766a0abb3c77b2a8LL,
+ 0x81c2c92e47edaee6LL, 0x92722c851482353bLL,
+ 0xa2bfe8a14cf10364LL, 0xa81a664bbc423001LL,
+ 0xc24b8b70d0f89791LL, 0xc76c51a30654be30LL,
+ 0xd192e819d6ef5218LL, 0xd69906245565a910LL,
+ 0xf40e35855771202aLL, 0x106aa07032bbd1b8LL,
+ 0x19a4c116b8d2d0c8LL, 0x1e376c085141ab53LL,
+ 0x2748774cdf8eeb99LL, 0x34b0bcb5e19b48a8LL,
+ 0x391c0cb3c5c95a63LL, 0x4ed8aa4ae3418acbLL,
+ 0x5b9cca4f7763e373LL, 0x682e6ff3d6b2b8a3LL,
+ 0x748f82ee5defb2fcLL, 0x78a5636f43172f60LL,
+ 0x84c87814a1f0ab72LL, 0x8cc702081a6439ecLL,
+ 0x90befffa23631e28LL, 0xa4506cebde82bde9LL,
+ 0xbef9a3f7b2c67915LL, 0xc67178f2e372532bLL,
+ 0xca273eceea26619cLL, 0xd186b8c721c0c207LL,
+ 0xeada7dd6cde0eb1eLL, 0xf57d4f7fee6ed178LL,
+ 0x06f067aa72176fbaLL, 0x0a637dc5a2c898a6LL,
+ 0x113f9804bef90daeLL, 0x1b710b35131c471bLL,
+ 0x28db77f523047d84LL, 0x32caab7b40c72493LL,
+ 0x3c9ebe0a15c9bebcLL, 0x431d67c49c100d4cLL,
+ 0x4cc5d4becb3e42b6LL, 0x597f299cfc657e2aLL,
+ 0x5fcb6fab3ad6faecLL, 0x6c44198c4a475817LL
+};
+
+static inline SHA512Uint64 rotr64(SHA512Uint64 x, Guint n) {
+ return (x >> n) | (x << (64 - n));
+}
+
+static inline SHA512Uint64 sha512Ch(SHA512Uint64 x, SHA512Uint64 y,
+ SHA512Uint64 z) {
+ return (x & y) ^ (~x & z);
+}
+
+static inline SHA512Uint64 sha512Maj(SHA512Uint64 x, SHA512Uint64 y,
+ SHA512Uint64 z) {
+ return (x & y) ^ (x & z) ^ (y & z);
+}
+
+static inline SHA512Uint64 sha512Sigma0(SHA512Uint64 x) {
+ return rotr64(x, 28) ^ rotr64(x, 34) ^ rotr64(x, 39);
+}
+
+static inline SHA512Uint64 sha512Sigma1(SHA512Uint64 x) {
+ return rotr64(x, 14) ^ rotr64(x, 18) ^ rotr64(x, 41);
+}
+
+static inline SHA512Uint64 sha512sigma0(SHA512Uint64 x) {
+ return rotr64(x, 1) ^ rotr64(x, 8) ^ (x >> 7);
+}
+
+static inline SHA512Uint64 sha512sigma1(SHA512Uint64 x) {
+ return rotr64(x, 19) ^ rotr64(x, 61) ^ (x >> 6);
+}
+
+static void sha512HashBlock(Guchar *blk, SHA512Uint64 *H) {
+ SHA512Uint64 W[80];
+ SHA512Uint64 a, b, c, d, e, f, g, h;
+ SHA512Uint64 T1, T2;
+ Guint t;
+
+ // 1. prepare the message schedule
+ for (t = 0; t < 16; ++t) {
+ W[t] = ((SHA512Uint64)blk[t*8] << 56) |
+ ((SHA512Uint64)blk[t*8 + 1] << 48) |
+ ((SHA512Uint64)blk[t*8 + 2] << 40) |
+ ((SHA512Uint64)blk[t*8 + 3] << 32) |
+ ((SHA512Uint64)blk[t*8 + 4] << 24) |
+ ((SHA512Uint64)blk[t*8 + 5] << 16) |
+ ((SHA512Uint64)blk[t*8 + 6] << 8) |
+ (SHA512Uint64)blk[t*8 + 7];
+ }
+ for (t = 16; t < 80; ++t) {
+ W[t] = sha512sigma1(W[t-2]) + W[t-7] + sha512sigma0(W[t-15]) + W[t-16];
+ }
+
+ // 2. initialize the eight working variables
+ a = H[0];
+ b = H[1];
+ c = H[2];
+ d = H[3];
+ e = H[4];
+ f = H[5];
+ g = H[6];
+ h = H[7];
+
+ // 3.
+ for (t = 0; t < 80; ++t) {
+ T1 = h + sha512Sigma1(e) + sha512Ch(e,f,g) + sha512K[t] + W[t];
+ T2 = sha512Sigma0(a) + sha512Maj(a,b,c);
+ h = g;
+ g = f;
+ f = e;
+ e = d + T1;
+ d = c;
+ c = b;
+ b = a;
+ a = T1 + T2;
+ }
+
+ // 4. compute the intermediate hash value
+ H[0] += a;
+ H[1] += b;
+ H[2] += c;
+ H[3] += d;
+ H[4] += e;
+ H[5] += f;
+ H[6] += g;
+ H[7] += h;
+}
+
+static void sha512(Guchar *msg, int msgLen, Guchar *hash) {
+ Guchar blk[128];
+ SHA512Uint64 H[8];
+ int blkLen, i;
+
+ H[0] = 0x6a09e667f3bcc908LL;
+ H[1] = 0xbb67ae8584caa73bLL;
+ H[2] = 0x3c6ef372fe94f82bLL;
+ H[3] = 0xa54ff53a5f1d36f1LL;
+ H[4] = 0x510e527fade682d1LL;
+ H[5] = 0x9b05688c2b3e6c1fLL;
+ H[6] = 0x1f83d9abfb41bd6bLL;
+ H[7] = 0x5be0cd19137e2179LL;
+
+ blkLen = 0;
+ for (i = 0; i + 128 <= msgLen; i += 128) {
+ sha512HashBlock(msg + i, H);
+ }
+ blkLen = msgLen - i;
+ if (blkLen > 0) {
+ memcpy(blk, msg + i, blkLen);
+ }
+
+ // pad the message
+ blk[blkLen++] = 0x80;
+ if (blkLen > 112) {
+ while (blkLen < 128) {
+ blk[blkLen++] = 0;
+ }
+ sha512HashBlock(blk, H);
+ blkLen = 0;
+ }
+ while (blkLen < 112) {
+ blk[blkLen++] = 0;
+ }
+ blk[112] = 0;
+ blk[113] = 0;
+ blk[114] = 0;
+ blk[115] = 0;
+ blk[116] = 0;
+ blk[117] = 0;
+ blk[118] = 0;
+ blk[119] = 0;
+ blk[120] = 0;
+ blk[121] = 0;
+ blk[122] = 0;
+ blk[123] = 0;
+ blk[124] = (Guchar)(msgLen >> 21);
+ blk[125] = (Guchar)(msgLen >> 13);
+ blk[126] = (Guchar)(msgLen >> 5);
+ blk[127] = (Guchar)(msgLen << 3);
+ sha512HashBlock(blk, H);
+
+ // copy the output into the buffer (convert words to bytes)
+ for (i = 0; i < 8; ++i) {
+ hash[i*8] = (Guchar)(H[i] >> 56);
+ hash[i*8 + 1] = (Guchar)(H[i] >> 48);
+ hash[i*8 + 2] = (Guchar)(H[i] >> 40);
+ hash[i*8 + 3] = (Guchar)(H[i] >> 32);
+ hash[i*8 + 4] = (Guchar)(H[i] >> 24);
+ hash[i*8 + 5] = (Guchar)(H[i] >> 16);
+ hash[i*8 + 6] = (Guchar)(H[i] >> 8);
+ hash[i*8 + 7] = (Guchar)H[i];
+ }
+}
+
+static void sha384(Guchar *msg, int msgLen, Guchar *hash) {
+ Guchar blk[128];
+ SHA512Uint64 H[8];
+ int blkLen, i;
+
+ H[0] = 0xcbbb9d5dc1059ed8LL;
+ H[1] = 0x629a292a367cd507LL;
+ H[2] = 0x9159015a3070dd17LL;
+ H[3] = 0x152fecd8f70e5939LL;
+ H[4] = 0x67332667ffc00b31LL;
+ H[5] = 0x8eb44a8768581511LL;
+ H[6] = 0xdb0c2e0d64f98fa7LL;
+ H[7] = 0x47b5481dbefa4fa4LL;
+
+ blkLen = 0;
+ for (i = 0; i + 128 <= msgLen; i += 128) {
+ sha512HashBlock(msg + i, H);
+ }
+ blkLen = msgLen - i;
+ if (blkLen > 0) {
+ memcpy(blk, msg + i, blkLen);
+ }
+
+ // pad the message
+ blk[blkLen++] = 0x80;
+ if (blkLen > 112) {
+ while (blkLen < 128) {
+ blk[blkLen++] = 0;
+ }
+ sha512HashBlock(blk, H);
+ blkLen = 0;
+ }
+ while (blkLen < 112) {
+ blk[blkLen++] = 0;
+ }
+ blk[112] = 0;
+ blk[113] = 0;
+ blk[114] = 0;
+ blk[115] = 0;
+ blk[116] = 0;
+ blk[117] = 0;
+ blk[118] = 0;
+ blk[119] = 0;
+ blk[120] = 0;
+ blk[121] = 0;
+ blk[122] = 0;
+ blk[123] = 0;
+ blk[124] = (Guchar)(msgLen >> 21);
+ blk[125] = (Guchar)(msgLen >> 13);
+ blk[126] = (Guchar)(msgLen >> 5);
+ blk[127] = (Guchar)(msgLen << 3);
+ sha512HashBlock(blk, H);
+
+ // copy the output into the buffer (convert words to bytes)
+ for (i = 0; i < 6; ++i) {
+ hash[i*8] = (Guchar)(H[i] >> 56);
+ hash[i*8 + 1] = (Guchar)(H[i] >> 48);
+ hash[i*8 + 2] = (Guchar)(H[i] >> 40);
+ hash[i*8 + 3] = (Guchar)(H[i] >> 32);
+ hash[i*8 + 4] = (Guchar)(H[i] >> 24);
+ hash[i*8 + 5] = (Guchar)(H[i] >> 16);
+ hash[i*8 + 6] = (Guchar)(H[i] >> 8);
+ hash[i*8 + 7] = (Guchar)H[i];
+ }
+}
diff --git a/xpdf/Decrypt.h b/xpdf/Decrypt.h
index 156acec..5ddcfc8 100644
--- a/xpdf/Decrypt.h
+++ b/xpdf/Decrypt.h
@@ -42,6 +42,8 @@ public:
private:
+ static void r6Hash(Guchar *key, int keyLen, const char *pwd, int pwdLen,
+ char *userKey);
static GBool makeFileKey2(int encVersion, int encRevision, int keyLength,
GString *ownerKey, GString *userKey,
int permissions, GString *fileID,
@@ -104,8 +106,24 @@ private:
//------------------------------------------------------------------------
+struct MD5State {
+ Gulong a, b, c, d;
+ Guchar buf[64];
+ int bufLen;
+ int msgLen;
+ Guchar digest[16];
+};
+
extern void rc4InitKey(Guchar *key, int keyLen, Guchar *state);
extern Guchar rc4DecryptByte(Guchar *state, Guchar *x, Guchar *y, Guchar c);
+void md5Start(MD5State *state);
+void md5Append(MD5State *state, Guchar *data, int dataLen);
+void md5Finish(MD5State *state);
extern void md5(Guchar *msg, int msgLen, Guchar *digest);
+extern void aesKeyExpansion(DecryptAESState *s,
+ Guchar *objKey, int objKeyLen,
+ GBool decrypt);
+extern void aesEncryptBlock(DecryptAESState *s, Guchar *in);
+extern void aesDecryptBlock(DecryptAESState *s, Guchar *in, GBool last);
#endif
diff --git a/xpdf/Dict.cc b/xpdf/Dict.cc
index 3ef804b..7e33fff 100644
--- a/xpdf/Dict.cc
+++ b/xpdf/Dict.cc
@@ -20,13 +20,24 @@
#include "Dict.h"
//------------------------------------------------------------------------
+
+struct DictEntry {
+ char *key;
+ Object val;
+ DictEntry *next;
+};
+
+//------------------------------------------------------------------------
// Dict
//------------------------------------------------------------------------
Dict::Dict(XRef *xrefA) {
xref = xrefA;
- entries = NULL;
- size = length = 0;
+ size = 8;
+ length = 0;
+ entries = (DictEntry *)gmallocn(size, sizeof(DictEntry));
+ hashTab = (DictEntry **)gmallocn(2 * size - 1, sizeof(DictEntry *));
+ memset(hashTab, 0, (2 * size - 1) * sizeof(DictEntry *));
ref = 1;
}
@@ -38,32 +49,69 @@ Dict::~Dict() {
entries[i].val.free();
}
gfree(entries);
+ gfree(hashTab);
}
void Dict::add(char *key, Object *val) {
- if (length == size) {
- if (length == 0) {
- size = 8;
- } else {
- size *= 2;
+ DictEntry *e;
+ int h;
+
+ if ((e = find(key))) {
+ e->val.free();
+ e->val = *val;
+ gfree(key);
+ } else {
+ if (length == size) {
+ expand();
}
- entries = (DictEntry *)greallocn(entries, size, sizeof(DictEntry));
+ h = hash(key);
+ entries[length].key = key;
+ entries[length].val = *val;
+ entries[length].next = hashTab[h];
+ hashTab[h] = &entries[length];
+ ++length;
}
- entries[length].key = key;
- entries[length].val = *val;
- ++length;
}
-inline DictEntry *Dict::find(const char *key) {
- int i;
+void Dict::expand() {
+ int h, i;
+ size *= 2;
+ entries = (DictEntry *)greallocn(entries, size, sizeof(DictEntry));
+ hashTab = (DictEntry **)greallocn(hashTab, 2 * size - 1,
+ sizeof(DictEntry *));
+ memset(hashTab, 0, (2 * size - 1) * sizeof(DictEntry *));
for (i = 0; i < length; ++i) {
- if (!strcmp(key, entries[i].key))
- return &entries[i];
+ h = hash(entries[i].key);
+ entries[i].next = hashTab[h];
+ hashTab[h] = &entries[i];
+ }
+}
+
+inline DictEntry *Dict::find(const char *key) {
+ DictEntry *e;
+ int h;
+
+ h = hash(key);
+ for (e = hashTab[h]; e; e = e->next) {
+ if (!strcmp(key, e->key)) {
+ return e;
+ }
}
return NULL;
}
+int Dict::hash(const char *key) {
+ const char *p;
+ unsigned int h;
+
+ h = 0;
+ for (p = key; *p; ++p) {
+ h = 17 * h + (int)(*p & 0xff);
+ }
+ return (int)(h % (2 * size - 1));
+}
+
GBool Dict::is(const char *type) {
DictEntry *e;
diff --git a/xpdf/Dict.h b/xpdf/Dict.h
index 1f3f8b5..301d0b8 100644
--- a/xpdf/Dict.h
+++ b/xpdf/Dict.h
@@ -17,15 +17,12 @@
#include "Object.h"
+struct DictEntry;
+
//------------------------------------------------------------------------
// Dict
//------------------------------------------------------------------------
-struct DictEntry {
- char *key;
- Object val;
-};
-
class Dict {
public:
@@ -67,11 +64,14 @@ private:
XRef *xref; // the xref table for this PDF file
DictEntry *entries; // array of entries
+ DictEntry **hashTab; // hash table pointers
int size; // size of <entries> array
int length; // number of entries in dictionary
int ref; // reference count
DictEntry *find(const char *key);
+ void expand();
+ int hash(const char *key);
};
#endif
diff --git a/xpdf/Error.cc b/xpdf/Error.cc
index cb4f27c..600442e 100644
--- a/xpdf/Error.cc
+++ b/xpdf/Error.cc
@@ -2,7 +2,7 @@
//
// Error.cc
//
-// Copyright 1996-2003 Glyph & Cog, LLC
+// Copyright 1996-2013 Glyph & Cog, LLC
//
//========================================================================
@@ -41,9 +41,12 @@ void setErrorCallback(void (*cbk)(void *data, ErrorCategory category,
errorCbkData = data;
}
-void CDECL error(ErrorCategory category, int pos, const char *msg, ...) {
+void CDECL error(ErrorCategory category, GFileOffset pos,
+ const char *msg, ...) {
va_list args;
- GString *s;
+ GString *s, *sanitized;
+ char c;
+ int i;
// NB: this can be called before the globalParams object is created
if (!errorCbk && globalParams && globalParams->getErrQuiet()) {
@@ -52,17 +55,32 @@ void CDECL error(ErrorCategory category, int pos, const char *msg, ...) {
va_start(args, msg);
s = GString::formatv(msg, args);
va_end(args);
+
+ // remove non-printable characters, just in case they might cause
+ // problems for the terminal program
+ sanitized = new GString();
+ for (i = 0; i < s->getLength(); ++i) {
+ c = s->getChar(i);
+ if (c >= 0x20 && c <= 0x7e) {
+ sanitized->append(c);
+ } else {
+ sanitized->appendf("<{0:02x}>", c & 0xff);
+ }
+ }
+
if (errorCbk) {
- (*errorCbk)(errorCbkData, category, pos, s->getCString());
+ (*errorCbk)(errorCbkData, category, (int)pos, sanitized->getCString());
} else {
if (pos >= 0) {
fprintf(stderr, "%s (%d): %s\n",
- errorCategoryNames[category], pos, s->getCString());
+ errorCategoryNames[category], (int)pos, sanitized->getCString());
} else {
fprintf(stderr, "%s: %s\n",
- errorCategoryNames[category], s->getCString());
+ errorCategoryNames[category], sanitized->getCString());
}
fflush(stderr);
}
+
delete s;
+ delete sanitized;
}
diff --git a/xpdf/Error.h b/xpdf/Error.h
index a4ae5c9..6c6afe3 100644
--- a/xpdf/Error.h
+++ b/xpdf/Error.h
@@ -17,11 +17,12 @@
#include <stdio.h>
#include "config.h"
+#include "gfile.h"
enum ErrorCategory {
errSyntaxWarning, // PDF syntax error which can be worked around;
// output will probably be correct
- errSyntaxError, // PDF syntax error which can be worked around;
+ errSyntaxError, // PDF syntax error which cannot be worked around;
// output will probably be incorrect
errConfig, // error in Xpdf config info (xpdfrc file, etc.)
errCommandLine, // error in user-supplied parameters, action not
@@ -37,6 +38,7 @@ extern void setErrorCallback(void (*cbk)(void *data, ErrorCategory category,
int pos, char *msg),
void *data);
-extern void CDECL error(ErrorCategory category, int pos, const char *msg, ...);
+extern void CDECL error(ErrorCategory category, GFileOffset pos,
+ const char *msg, ...);
#endif
diff --git a/xpdf/Form.cc b/xpdf/Form.cc
new file mode 100644
index 0000000..f65a1e2
--- /dev/null
+++ b/xpdf/Form.cc
@@ -0,0 +1,67 @@
+//========================================================================
+//
+// Form.cc
+//
+// Copyright 2012 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include "GlobalParams.h"
+#include "Error.h"
+#include "Object.h"
+#include "PDFDoc.h"
+#include "AcroForm.h"
+#include "XFAForm.h"
+#include "Form.h"
+
+//------------------------------------------------------------------------
+// Form
+//------------------------------------------------------------------------
+
+Form *Form::load(PDFDoc *docA, Catalog *catalog, Object *acroFormObj) {
+ Form *form;
+ Object xfaObj, catDict, needsRenderingObj;
+
+ if (!acroFormObj->isDict()) {
+ error(errSyntaxError, -1, "AcroForm object is wrong type");
+ return NULL;
+ }
+ //~ temporary: create an XFAForm only for XFAF, not for dynamic XFA
+ acroFormObj->dictLookup("XFA", &xfaObj);
+ docA->getXRef()->getCatalog(&catDict);
+ catDict.dictLookup("NeedsRendering", &needsRenderingObj);
+ catDict.free();
+ if (globalParams->getEnableXFA() &&
+ !xfaObj.isNull() &&
+ !(needsRenderingObj.isBool() && needsRenderingObj.getBool())) {
+ form = XFAForm::load(docA, acroFormObj, &xfaObj);
+ } else {
+ form = AcroForm::load(docA, catalog, acroFormObj);
+ }
+ xfaObj.free();
+ needsRenderingObj.free();
+ return form;
+}
+
+Form::Form(PDFDoc *docA) {
+ doc = docA;
+}
+
+Form::~Form() {
+}
+
+//------------------------------------------------------------------------
+// FormField
+//------------------------------------------------------------------------
+
+FormField::FormField() {
+}
+
+FormField::~FormField() {
+}
diff --git a/xpdf/Form.h b/xpdf/Form.h
new file mode 100644
index 0000000..50c0f47
--- /dev/null
+++ b/xpdf/Form.h
@@ -0,0 +1,64 @@
+//========================================================================
+//
+// Form.h
+//
+// Copyright 2012 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef FORM_H
+#define FORM_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+
+class Gfx;
+class FormField;
+
+//------------------------------------------------------------------------
+
+class Form {
+public:
+
+ static Form *load(PDFDoc *docA, Catalog *catalog, Object *acroFormObj);
+
+ virtual ~Form();
+
+ virtual const char *getType() = 0;
+
+ virtual void draw(int pageNum, Gfx *gfx, GBool printing) = 0;
+
+ virtual int getNumFields() = 0;
+ virtual FormField *getField(int idx) = 0;
+
+protected:
+
+ Form(PDFDoc *docA);
+
+ PDFDoc *doc;
+};
+
+//------------------------------------------------------------------------
+
+class FormField {
+public:
+
+ FormField();
+ virtual ~FormField();
+
+ virtual const char *getType() = 0;
+ virtual Unicode *getName(int *length) = 0;
+ virtual Unicode *getValue(int *length) = 0;
+
+ // Return the resource dictionaries used to draw this field. The
+ // returned object must be either a dictionary or an array of
+ // dictonaries.
+ virtual Object *getResources(Object *res) = 0;
+};
+
+#endif
diff --git a/xpdf/Function.cc b/xpdf/Function.cc
index 2659af1..a06d10d 100644
--- a/xpdf/Function.cc
+++ b/xpdf/Function.cc
@@ -17,6 +17,7 @@
#include <ctype.h>
#include <math.h>
#include "gmem.h"
+#include "GList.h"
#include "Object.h"
#include "Dict.h"
#include "Stream.h"
@@ -350,7 +351,7 @@ SampledFunction::SampledFunction(Object *funcObj, Dict *dict) {
samples = (double *)gmallocn(nSamples, sizeof(double));
buf = 0;
bits = 0;
- bitMask = (1 << sampleBits) - 1;
+ bitMask = (sampleBits < 32) ? ((1 << sampleBits) - 1) : 0xffffffffU;
str->reset();
for (i = 0; i < nSamples; ++i) {
if (sampleBits == 8) {
@@ -778,55 +779,57 @@ void StitchingFunction::transform(double *in, double *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
-};
+// This is not an enum, because we can't foreward-declare the enum
+// type in Function.h
+#define psOpAbs 0
+#define psOpAdd 1
+#define psOpAnd 2
+#define psOpAtan 3
+#define psOpBitshift 4
+#define psOpCeiling 5
+#define psOpCopy 6
+#define psOpCos 7
+#define psOpCvi 8
+#define psOpCvr 9
+#define psOpDiv 10
+#define psOpDup 11
+#define psOpEq 12
+#define psOpExch 13
+#define psOpExp 14
+#define psOpFalse 15
+#define psOpFloor 16
+#define psOpGe 17
+#define psOpGt 18
+#define psOpIdiv 19
+#define psOpIndex 20
+#define psOpLe 21
+#define psOpLn 22
+#define psOpLog 23
+#define psOpLt 24
+#define psOpMod 25
+#define psOpMul 26
+#define psOpNe 27
+#define psOpNeg 28
+#define psOpNot 29
+#define psOpOr 30
+#define psOpPop 31
+#define psOpRoll 32
+#define psOpRound 33
+#define psOpSin 34
+#define psOpSqrt 35
+#define psOpSub 36
+#define psOpTrue 37
+#define psOpTruncate 38
+#define psOpXor 39
+#define psOpPush 40
+#define psOpJ 41
+#define psOpJz 42
+
+#define nPSOps 43
// 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.
+// The index in this table is equivalent to the psOpXXX defines.
static const char *psOpNames[] = {
"abs",
"add",
@@ -870,218 +873,22 @@ static const char *psOpNames[] = {
"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;
+struct PSCode {
+ int op;
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)
- };
+ double d;
+ int i;
+ } val;
};
#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(errSyntaxError, -1, "Stack overflow in PostScript function");
- return gFalse;
- }
- return gTrue;
-}
-
-GBool PSStack::checkUnderflow() {
- if (sp == psStackSize) {
- error(errSyntaxError, -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(errSyntaxError, -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(errSyntaxError, -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 || n > psStackSize || sp + n > psStackSize) {
- 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;
+ GList *tokens;
GString *tok;
double in[funcMaxInputs];
- int i;
+ int tokPtr, codePtr, i;
codeString = NULL;
code = NULL;
@@ -1104,22 +911,27 @@ PostScriptFunction::PostScriptFunction(Object *funcObj, Dict *dict) {
}
str = funcObj->getStream();
- //----- parse the function
+ //----- tokenize the function
codeString = new GString();
+ tokens = new GList();
str->reset();
- if (!(tok = getToken(str)) || tok->cmp("{")) {
+ while ((tok = getToken(str))) {
+ tokens->append(tok);
+ }
+ str->close();
+
+ //----- parse the function
+ if (tokens->getLength() < 1 ||
+ ((GString *)tokens->get(0))->cmp("{")) {
error(errSyntaxError, -1, "Expected '{' at start of PostScript function");
- if (tok) {
- delete tok;
- }
- goto err1;
+ goto err2;
}
- delete tok;
+ tokPtr = 1;
codePtr = 0;
- if (!parseCode(str, &codePtr)) {
+ if (!parseCode(tokens, &tokPtr, &codePtr)) {
goto err2;
}
- str->close();
+ codeLen = codePtr;
//----- set up the cache
for (i = 0; i < m; ++i) {
@@ -1131,16 +943,16 @@ PostScriptFunction::PostScriptFunction(Object *funcObj, Dict *dict) {
ok = gTrue;
err2:
- str->close();
+ deleteGList(tokens, GString);
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();
+ code = (PSCode *)gmallocn(codeSize, sizeof(PSCode));
+ memcpy(code, func->code, codeSize * sizeof(PSCode));
}
PostScriptFunction::~PostScriptFunction() {
@@ -1151,8 +963,9 @@ PostScriptFunction::~PostScriptFunction() {
}
void PostScriptFunction::transform(double *in, double *out) {
- PSStack *stack;
- int i;
+ double stack[psStackSize];
+ double x;
+ int sp, i;
// check the cache
for (i = 0; i < m; ++i) {
@@ -1167,25 +980,28 @@ void PostScriptFunction::transform(double *in, double *out) {
return;
}
- stack = new PSStack();
for (i = 0; i < m; ++i) {
- //~ may need to check for integers here
- stack->pushReal(in[i]);
+ stack[psStackSize - 1 - i] = in[i];
}
- exec(stack, 0);
- for (i = n - 1; i >= 0; --i) {
- out[i] = stack->popNum();
- if (out[i] < range[i][0]) {
+ sp = exec(stack, psStackSize - m);
+ // if (sp < psStackSize - n) {
+ // error(errSyntaxWarning, -1,
+ // "Extra values on stack at end of PostScript function");
+ // }
+ if (sp > psStackSize - n) {
+ error(errSyntaxError, -1, "Stack underflow in PostScript function");
+ sp = psStackSize - n;
+ }
+ for (i = 0; i < n; ++i) {
+ x = stack[sp + n - 1 - i];
+ if (x < range[i][0]) {
out[i] = range[i][0];
- } else if (out[i] > range[i][1]) {
+ } else if (x > range[i][1]) {
out[i] = range[i][1];
+ } else {
+ out[i] = x;
}
}
- // if (!stack->empty()) {
- // error(errSyntaxWarning, -1,
- // "Extra values on stack at end of PostScript function");
- // }
- delete stack;
// save current result in the cache
for (i = 0; i < m; ++i) {
@@ -1196,101 +1012,71 @@ void PostScriptFunction::transform(double *in, double *out) {
}
}
-GBool PostScriptFunction::parseCode(Stream *str, int *codePtr) {
+GBool PostScriptFunction::parseCode(GList *tokens, int *tokPtr, int *codePtr) {
GString *tok;
char *p;
- GBool isReal;
- int opPtr, elsePtr;
int a, b, mid, cmp;
+ int codePtr0, codePtr1;
while (1) {
- if (!(tok = getToken(str))) {
+ if (*tokPtr >= tokens->getLength()) {
error(errSyntaxError, -1,
"Unexpected end of PostScript function stream");
return gFalse;
}
+ tok = (GString *)tokens->get((*tokPtr)++);
p = tok->getCString();
if (isdigit(*p) || *p == '.' || *p == '-') {
- isReal = gFalse;
- for (; *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;
+ addCodeD(codePtr, psOpPush, atof(tok->getCString()));
} else if (!tok->cmp("{")) {
- delete tok;
- opPtr = *codePtr;
- *codePtr += 3;
- resizeCode(opPtr + 2);
- if (!parseCode(str, codePtr)) {
+ codePtr0 = *codePtr;
+ addCodeI(codePtr, psOpJz, 0);
+ if (!parseCode(tokens, tokPtr, codePtr)) {
return gFalse;
}
- if (!(tok = getToken(str))) {
+ if (*tokPtr >= tokens->getLength()) {
error(errSyntaxError, -1,
"Unexpected end of PostScript function stream");
return gFalse;
}
- if (!tok->cmp("{")) {
- elsePtr = *codePtr;
- if (!parseCode(str, codePtr)) {
+ tok = (GString *)tokens->get((*tokPtr)++);
+ if (!tok->cmp("if")) {
+ code[codePtr0].val.i = *codePtr;
+ } else if (!tok->cmp("{")) {
+ codePtr1 = *codePtr;
+ addCodeI(codePtr, psOpJ, 0);
+ code[codePtr0].val.i = *codePtr;
+ if (!parseCode(tokens, tokPtr, codePtr)) {
return gFalse;
}
- delete tok;
- if (!(tok = getToken(str))) {
+ if (*tokPtr >= tokens->getLength()) {
error(errSyntaxError, -1,
"Unexpected end of PostScript function stream");
return gFalse;
}
- } else {
- elsePtr = -1;
- }
- if (!tok->cmp("if")) {
- if (elsePtr >= 0) {
- error(errSyntaxError, -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) {
+ tok = (GString *)tokens->get((*tokPtr)++);
+ if (!tok->cmp("ifelse")) {
+ code[codePtr1].val.i = *codePtr;
+ } else {
error(errSyntaxError, -1,
- "Got 'ifelse' operator with one block in PostScript function");
+ "Expected 'ifelse' in PostScript function stream");
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(errSyntaxError, -1,
- "Expected if/ifelse operator in PostScript function");
- delete tok;
+ "Expected 'if' in PostScript function stream");
return gFalse;
}
- delete tok;
} else if (!tok->cmp("}")) {
- delete tok;
- resizeCode(*codePtr);
- code[*codePtr].type = psOperator;
- code[*codePtr].op = psOpReturn;
- ++*codePtr;
break;
+ } else if (!tok->cmp("if")) {
+ error(errSyntaxError, -1,
+ "Unexpected 'if' in PostScript function stream");
+ return gFalse;
+ } else if (!tok->cmp("ifelse")) {
+ error(errSyntaxError, -1,
+ "Unexpected 'ifelse' in PostScript function stream");
+ return gFalse;
} else {
a = -1;
b = nPSOps;
@@ -1311,19 +1097,55 @@ GBool PostScriptFunction::parseCode(Stream *str, int *codePtr) {
error(errSyntaxError, -1,
"Unknown operator '{0:t}' in PostScript function",
tok);
- delete tok;
return gFalse;
}
- delete tok;
- resizeCode(*codePtr);
- code[*codePtr].type = psOperator;
- code[*codePtr].op = (PSOp)a;
- ++*codePtr;
+ addCode(codePtr, a);
}
}
return gTrue;
}
+void PostScriptFunction::addCode(int *codePtr, int op) {
+ if (*codePtr >= codeSize) {
+ if (codeSize) {
+ codeSize *= 2;
+ } else {
+ codeSize = 16;
+ }
+ code = (PSCode *)greallocn(code, codeSize, sizeof(PSCode));
+ }
+ code[*codePtr].op = op;
+ ++(*codePtr);
+}
+
+void PostScriptFunction::addCodeI(int *codePtr, int op, int x) {
+ if (*codePtr >= codeSize) {
+ if (codeSize) {
+ codeSize *= 2;
+ } else {
+ codeSize = 16;
+ }
+ code = (PSCode *)greallocn(code, codeSize, sizeof(PSCode));
+ }
+ code[*codePtr].op = op;
+ code[*codePtr].val.i = x;
+ ++(*codePtr);
+}
+
+void PostScriptFunction::addCodeD(int *codePtr, int op, double x) {
+ if (*codePtr >= codeSize) {
+ if (codeSize) {
+ codeSize *= 2;
+ } else {
+ codeSize = 16;
+ }
+ code = (PSCode *)greallocn(code, codeSize, sizeof(PSCode));
+ }
+ code[*codePtr].op = op;
+ code[*codePtr].val.d = x;
+ ++(*codePtr);
+}
+
GString *PostScriptFunction::getToken(Stream *str) {
GString *s;
int c;
@@ -1333,7 +1155,8 @@ GString *PostScriptFunction::getToken(Stream *str) {
comment = gFalse;
while (1) {
if ((c = str->getChar()) == EOF) {
- break;
+ delete s;
+ return NULL;
}
codeString->append(c);
if (comment) {
@@ -1372,323 +1195,362 @@ GString *PostScriptFunction::getToken(Stream *str) {
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);
+int PostScriptFunction::exec(double *stack, int sp0) {
+ PSCode *c;
+ double tmp[psStackSize];
+ double t;
+ int sp, ip, nn, k, i;
+
+ sp = sp0;
+ ip = 0;
+ while (ip < codeLen) {
+ c = &code[ip++];
+ switch(c->op) {
+ case psOpAbs:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = fabs(stack[sp]);
break;
- case psReal:
- stack->pushReal(code[codePtr++].real);
+ case psOpAdd:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = stack[sp + 1] + stack[sp];
+ ++sp;
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);
+ case psOpAnd:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = (int)stack[sp + 1] & (int)stack[sp];
+ ++sp;
+ break;
+ case psOpAtan:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = atan2(stack[sp + 1], stack[sp]);
+ ++sp;
+ break;
+ case psOpBitshift:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ k = (int)stack[sp + 1];
+ nn = (int)stack[sp];
+ if (nn > 0) {
+ stack[sp + 1] = k << nn;
+ } else if (nn < 0) {
+ stack[sp + 1] = k >> -nn;
+ } else {
+ stack[sp + 1] = k;
+ }
+ ++sp;
+ break;
+ case psOpCeiling:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = ceil(stack[sp]);
+ break;
+ case psOpCopy:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ nn = (int)stack[sp++];
+ if (nn < 0) {
+ goto invalidArg;
+ }
+ if (sp + nn > psStackSize) {
+ goto underflow;
+ }
+ if (sp - nn < 0) {
+ goto overflow;
+ }
+ for (i = 0; i < nn; ++i) {
+ stack[sp - nn + i] = stack[sp + i];
+ }
+ sp -= nn;
+ break;
+ case psOpCos:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = cos(stack[sp]);
+ break;
+ case psOpCvi:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = (int)stack[sp];
+ break;
+ case psOpCvr:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ break;
+ case psOpDiv:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = stack[sp + 1] / stack[sp];
+ ++sp;
+ break;
+ case psOpDup:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ if (sp < 1) {
+ goto overflow;
+ }
+ stack[sp - 1] = stack[sp];
+ --sp;
+ break;
+ case psOpEq:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = stack[sp + 1] == stack[sp] ? 1 : 0;
+ ++sp;
+ break;
+ case psOpExch:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ t = stack[sp];
+ stack[sp] = stack[sp + 1];
+ stack[sp + 1] = t;
+ break;
+ case psOpExp:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = pow(stack[sp + 1], stack[sp]);
+ ++sp;
+ break;
+ case psOpFalse:
+ if (sp < 1) {
+ goto overflow;
+ }
+ stack[sp - 1] = 0;
+ --sp;
+ break;
+ case psOpFloor:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = floor(stack[sp]);
+ break;
+ case psOpGe:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = stack[sp + 1] >= stack[sp] ? 1 : 0;
+ ++sp;
+ break;
+ case psOpGt:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = stack[sp + 1] > stack[sp] ? 1 : 0;
+ ++sp;
+ break;
+ case psOpIdiv:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = (int)stack[sp + 1] / (int)stack[sp];
+ ++sp;
+ break;
+ case psOpIndex:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ k = (int)stack[sp];
+ if (k < 0) {
+ goto invalidArg;
+ }
+ if (sp + 1 + k >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = stack[sp + 1 + k];
+ break;
+ case psOpLe:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = stack[sp + 1] <= stack[sp] ? 1 : 0;
+ ++sp;
+ break;
+ case psOpLn:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = log(stack[sp]);
+ break;
+ case psOpLog:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = log10(stack[sp]);
+ break;
+ case psOpLt:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = stack[sp + 1] < stack[sp] ? 1 : 0;
+ ++sp;
+ break;
+ case psOpMod:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = (int)stack[sp + 1] % (int)stack[sp];
+ ++sp;
+ break;
+ case psOpMul:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = stack[sp + 1] * stack[sp];
+ ++sp;
+ break;
+ case psOpNe:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = stack[sp + 1] != stack[sp] ? 1 : 0;
+ ++sp;
+ break;
+ case psOpNeg:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = -stack[sp];
+ break;
+ case psOpNot:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = stack[sp] == 0 ? 1 : 0;
+ break;
+ case psOpOr:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = (int)stack[sp + 1] | (int)stack[sp];
+ ++sp;
+ break;
+ case psOpPop:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ ++sp;
+ break;
+ case psOpRoll:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ k = (int)stack[sp++];
+ nn = (int)stack[sp++];
+ if (nn < 0) {
+ goto invalidArg;
+ }
+ if (sp + nn > psStackSize) {
+ goto underflow;
+ }
+ if (k >= 0) {
+ k %= nn;
+ } else {
+ k = -k % nn;
+ if (k) {
+ k = nn - k;
}
- codePtr = code[codePtr + 1].blk;
- break;
- case psOpReturn:
- return;
+ }
+ for (i = 0; i < nn; ++i) {
+ tmp[i] = stack[sp + i];
+ }
+ for (i = 0; i < nn; ++i) {
+ stack[sp + i] = tmp[(i + k) % nn];
}
break;
- default:
- error(errSyntaxError, -1,
- "Internal: bad object in PostScript function code");
+ case psOpRound:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ t = stack[sp];
+ stack[sp] = (t >= 0) ? floor(t + 0.5) : ceil(t - 0.5);
+ break;
+ case psOpSin:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = sin(stack[sp]);
+ break;
+ case psOpSqrt:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = sqrt(stack[sp]);
+ break;
+ case psOpSub:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = stack[sp + 1] - stack[sp];
+ ++sp;
+ break;
+ case psOpTrue:
+ if (sp < 1) {
+ goto overflow;
+ }
+ stack[sp - 1] = 1;
+ --sp;
+ break;
+ case psOpTruncate:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ t = stack[sp];
+ stack[sp] = (t >= 0) ? floor(t) : ceil(t);
+ break;
+ case psOpXor:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = (int)stack[sp + 1] ^ (int)stack[sp];
+ ++sp;
+ break;
+ case psOpPush:
+ if (sp < 1) {
+ goto overflow;
+ }
+ stack[--sp] = c->val.d;
+ break;
+ case psOpJ:
+ ip = c->val.i;
+ break;
+ case psOpJz:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ k = (int)stack[sp++];
+ if (k == 0) {
+ ip = c->val.i;
+ }
break;
}
}
+ return sp;
+
+ underflow:
+ error(errSyntaxError, -1, "Stack underflow in PostScript function");
+ return sp;
+ overflow:
+ error(errSyntaxError, -1, "Stack overflow in PostScript function");
+ return sp;
+ invalidArg:
+ error(errSyntaxError, -1, "Invalid arg in PostScript function");
+ return sp;
}
diff --git a/xpdf/Function.h b/xpdf/Function.h
index 8852dca..5cf4516 100644
--- a/xpdf/Function.h
+++ b/xpdf/Function.h
@@ -18,10 +18,10 @@
#include "gtypes.h"
#include "Object.h"
+class GList;
class Dict;
class Stream;
-struct PSObject;
-class PSStack;
+struct PSCode;
//------------------------------------------------------------------------
// Function
@@ -217,13 +217,16 @@ public:
private:
PostScriptFunction(PostScriptFunction *func);
- GBool parseCode(Stream *str, int *codePtr);
+ GBool parseCode(GList *tokens, int *tokPtr, int *codePtr);
+ void addCode(int *codePtr, int op);
+ void addCodeI(int *codePtr, int op, int x);
+ void addCodeD(int *codePtr, int op, double x);
GString *getToken(Stream *str);
- void resizeCode(int newSize);
- void exec(PSStack *stack, int codePtr);
+ int exec(double *stack, int sp0);
GString *codeString;
- PSObject *code;
+ PSCode *code;
+ int codeLen;
int codeSize;
double cacheIn[funcMaxInputs];
double cacheOut[funcMaxOutputs];
diff --git a/xpdf/Gfx.cc b/xpdf/Gfx.cc
index 1979f84..0fa48f8 100644
--- a/xpdf/Gfx.cc
+++ b/xpdf/Gfx.cc
@@ -2,7 +2,7 @@
//
// Gfx.cc
//
-// Copyright 1996-2003 Glyph & Cog, LLC
+// Copyright 1996-2013 Glyph & Cog, LLC
//
//========================================================================
@@ -36,7 +36,7 @@
#include "Annot.h"
#include "OptionalContent.h"
#include "Error.h"
-#include "PDFDocEncoding.h"
+#include "TextString.h"
#include "Gfx.h"
// the MSVC math.h doesn't define this
@@ -80,11 +80,15 @@
// fill.
#define patchColorDelta (dblToCol(1 / 256.0))
+// Max errors (undefined operator, wrong number of args) allowed before
+// giving up on a content stream.
+#define contentStreamErrorLimit 500
+
//------------------------------------------------------------------------
// Operator table
//------------------------------------------------------------------------
-#ifdef WIN32 // this works around a bug in the VC7 compiler
+#ifdef _WIN32 // this works around a bug in the VC7 compiler
# pragma optimize("",off)
#endif
@@ -257,7 +261,7 @@ Operator Gfx::opTab[] = {
&Gfx::opCurveTo2},
};
-#ifdef WIN32 // this works around a bug in the VC7 compiler
+#ifdef _WIN32 // this works around a bug in the VC7 compiler
# pragma optimize("",on)
#endif
@@ -337,15 +341,31 @@ GfxFont *GfxResources::lookupFont(char *name) {
for (resPtr = this; resPtr; resPtr = resPtr->next) {
if (resPtr->fonts) {
- if ((font = resPtr->fonts->lookup(name)))
+ if ((font = resPtr->fonts->lookup(name))) {
return font;
+ }
}
}
error(errSyntaxError, -1, "Unknown font tag '{0:s}'", name);
return NULL;
}
-GBool GfxResources::lookupXObject(char *name, Object *obj) {
+GfxFont *GfxResources::lookupFontByRef(Ref ref) {
+ GfxFont *font;
+ GfxResources *resPtr;
+
+ for (resPtr = this; resPtr; resPtr = resPtr->next) {
+ if (resPtr->fonts) {
+ if ((font = resPtr->fonts->lookupByRef(ref))) {
+ return font;
+ }
+ }
+ }
+ error(errSyntaxError, -1, "Unknown font ref {0:d}.{1:d}", ref.num, ref.gen);
+ return NULL;
+}
+
+GBool GfxResources::lookupXObject(const char *name, Object *obj) {
GfxResources *resPtr;
for (resPtr = this; resPtr; resPtr = resPtr->next) {
@@ -359,7 +379,7 @@ GBool GfxResources::lookupXObject(char *name, Object *obj) {
return gFalse;
}
-GBool GfxResources::lookupXObjectNF(char *name, Object *obj) {
+GBool GfxResources::lookupXObjectNF(const char *name, Object *obj) {
GfxResources *resPtr;
for (resPtr = this; resPtr; resPtr = resPtr->next) {
@@ -373,9 +393,16 @@ GBool GfxResources::lookupXObjectNF(char *name, Object *obj) {
return gFalse;
}
-void GfxResources::lookupColorSpace(char *name, Object *obj) {
+void GfxResources::lookupColorSpace(const char *name, Object *obj) {
GfxResources *resPtr;
+ //~ should also test for G, RGB, and CMYK - but only in inline images (?)
+ if (!strcmp(name, "DeviceGray") ||
+ !strcmp(name, "DeviceRGB") ||
+ !strcmp(name, "DeviceCMYK")) {
+ obj->initNull();
+ return;
+ }
for (resPtr = this; resPtr; resPtr = resPtr->next) {
if (resPtr->colorSpaceDict.isDict()) {
if (!resPtr->colorSpaceDict.dictLookup(name, obj)->isNull()) {
@@ -387,15 +414,19 @@ void GfxResources::lookupColorSpace(char *name, Object *obj) {
obj->initNull();
}
-GfxPattern *GfxResources::lookupPattern(char *name) {
+GfxPattern *GfxResources::lookupPattern(const char *name
+ ) {
GfxResources *resPtr;
GfxPattern *pattern;
- Object obj;
+ Object objRef, obj;
for (resPtr = this; resPtr; resPtr = resPtr->next) {
if (resPtr->patternDict.isDict()) {
if (!resPtr->patternDict.dictLookup(name, &obj)->isNull()) {
- pattern = GfxPattern::parse(&obj);
+ resPtr->patternDict.dictLookupNF(name, &objRef);
+ pattern = GfxPattern::parse(&objRef, &obj
+ );
+ objRef.free();
obj.free();
return pattern;
}
@@ -406,7 +437,8 @@ GfxPattern *GfxResources::lookupPattern(char *name) {
return NULL;
}
-GfxShading *GfxResources::lookupShading(char *name) {
+GfxShading *GfxResources::lookupShading(const char *name
+ ) {
GfxResources *resPtr;
GfxShading *shading;
Object obj;
@@ -414,7 +446,8 @@ GfxShading *GfxResources::lookupShading(char *name) {
for (resPtr = this; resPtr; resPtr = resPtr->next) {
if (resPtr->shadingDict.isDict()) {
if (!resPtr->shadingDict.dictLookup(name, &obj)->isNull()) {
- shading = GfxShading::parse(&obj);
+ shading = GfxShading::parse(&obj
+ );
obj.free();
return shading;
}
@@ -425,7 +458,7 @@ GfxShading *GfxResources::lookupShading(char *name) {
return NULL;
}
-GBool GfxResources::lookupGState(char *name, Object *obj) {
+GBool GfxResources::lookupGState(const char *name, Object *obj) {
GfxResources *resPtr;
for (resPtr = this; resPtr; resPtr = resPtr->next) {
@@ -440,7 +473,7 @@ GBool GfxResources::lookupGState(char *name, Object *obj) {
return gFalse;
}
-GBool GfxResources::lookupPropertiesNF(char *name, Object *obj) {
+GBool GfxResources::lookupPropertiesNF(const char *name, Object *obj) {
GfxResources *resPtr;
for (resPtr = this; resPtr; resPtr = resPtr->next) {
@@ -491,6 +524,7 @@ Gfx::Gfx(PDFDoc *docA, OutputDev *outA, int pageNum, Dict *resDict,
markedContentStack = new GList();
ocState = gTrue;
parser = NULL;
+ contentStreamStack = new GList();
abortCheckCbk = abortCheckCbkA;
abortCheckCbkData = abortCheckCbkDataA;
@@ -535,6 +569,7 @@ Gfx::Gfx(PDFDoc *docA, OutputDev *outA, Dict *resDict,
markedContentStack = new GList();
ocState = gTrue;
parser = NULL;
+ contentStreamStack = new GList();
abortCheckCbk = abortCheckCbkA;
abortCheckCbkData = abortCheckCbkDataA;
@@ -563,41 +598,99 @@ Gfx::~Gfx() {
popResources();
}
deleteGList(markedContentStack, GfxMarkedContent);
+ delete contentStreamStack;
}
-void Gfx::display(Object *obj, GBool topLevel) {
- Object obj2;
+void Gfx::display(Object *objRef, GBool topLevel) {
+ Object obj1, obj2;
int i;
- if (obj->isArray()) {
- for (i = 0; i < obj->arrayGetLength(); ++i) {
- obj->arrayGet(i, &obj2);
+ objRef->fetch(xref, &obj1);
+ if (obj1.isArray()) {
+ for (i = 0; i < obj1.arrayGetLength(); ++i) {
+ obj1.arrayGetNF(i, &obj2);
+ if (checkForContentStreamLoop(&obj2)) {
+ obj2.free();
+ obj1.free();
+ return;
+ }
+ obj2.free();
+ }
+ for (i = 0; i < obj1.arrayGetLength(); ++i) {
+ obj1.arrayGet(i, &obj2);
if (!obj2.isStream()) {
- error(errSyntaxError, -1, "Weird page contents");
+ error(errSyntaxError, -1, "Invalid object type for content stream");
obj2.free();
+ obj1.free();
return;
}
obj2.free();
}
- } else if (!obj->isStream()) {
- error(errSyntaxError, -1, "Weird page contents");
+ contentStreamStack->append(&obj1);
+ } else if (obj1.isStream()) {
+ if (checkForContentStreamLoop(objRef)) {
+ obj1.free();
+ return;
+ }
+ contentStreamStack->append(objRef);
+ } else {
+ error(errSyntaxError, -1, "Invalid object type for content stream");
+ obj1.free();
return;
}
- parser = new Parser(xref, new Lexer(xref, obj), gFalse);
+ parser = new Parser(xref, new Lexer(xref, &obj1), gFalse);
go(topLevel);
delete parser;
parser = NULL;
+ contentStreamStack->del(contentStreamStack->getLength() - 1);
+ obj1.free();
+}
+
+// If <ref> is already on contentStreamStack, i.e., if there is a loop
+// in the content streams, report an error, and return true.
+GBool Gfx::checkForContentStreamLoop(Object *ref) {
+ Object *objPtr;
+ Object obj1;
+ int i, j;
+
+ if (ref->isRef()) {
+ for (i = 0; i < contentStreamStack->getLength(); ++i) {
+ objPtr = (Object *)contentStreamStack->get(i);
+ if (objPtr->isRef()) {
+ if (ref->getRefNum() == objPtr->getRefNum() &&
+ ref->getRefGen() == objPtr->getRefGen()) {
+ error(errSyntaxError, -1, "Loop in content streams");
+ return gTrue;
+ }
+ } else if (objPtr->isArray()) {
+ for (j = 0; j < objPtr->arrayGetLength(); ++j) {
+ objPtr->arrayGetNF(j, &obj1);
+ if (obj1.isRef()) {
+ if (ref->getRefNum() == obj1.getRefNum() &&
+ ref->getRefGen() == obj1.getRefGen()) {
+ error(errSyntaxError, -1, "Loop in content streams");
+ obj1.free();
+ return gTrue;
+ }
+ }
+ obj1.free();
+ }
+ }
+ }
+ }
+ return gFalse;
}
void Gfx::go(GBool topLevel) {
Object obj;
Object args[maxArgs];
int numArgs, i;
- int lastAbortCheck;
+ int lastAbortCheck, errCount;
// scan a sequence of objects
updateLevel = 1; // make sure even empty pages trigger a call to dump()
lastAbortCheck = 0;
+ errCount = 0;
numArgs = 0;
parser->getObj(&obj);
while (!obj.isEOF()) {
@@ -613,7 +706,9 @@ void Gfx::go(GBool topLevel) {
printf("\n");
fflush(stdout);
}
- execOp(&obj, args, numArgs);
+ if (!execOp(&obj, args, numArgs)) {
+ ++errCount;
+ }
obj.free();
for (i = 0; i < numArgs; ++i)
args[i].free();
@@ -635,6 +730,13 @@ void Gfx::go(GBool topLevel) {
}
}
+ // check for too many errors
+ if (errCount > contentStreamErrorLimit) {
+ error(errSyntaxError, -1,
+ "Too many errors - giving up on this content stream");
+ break;
+ }
+
// got an argument - save it
} else if (numArgs < maxArgs) {
args[numArgs++] = obj;
@@ -678,7 +780,8 @@ void Gfx::go(GBool topLevel) {
}
}
-void Gfx::execOp(Object *cmd, Object args[], int numArgs) {
+// Returns true if successful, false on error.
+GBool Gfx::execOp(Object *cmd, Object args[], int numArgs) {
Operator *op;
char *name;
Object *argPtr;
@@ -687,9 +790,11 @@ void Gfx::execOp(Object *cmd, Object args[], int numArgs) {
// find operator
name = cmd->getCmd();
if (!(op = findOp(name))) {
- if (ignoreUndef == 0)
- error(errSyntaxError, getPos(), "Unknown operator '{0:s}'", name);
- return;
+ if (ignoreUndef > 0) {
+ return gTrue;
+ }
+ error(errSyntaxError, getPos(), "Unknown operator '{0:s}'", name);
+ return gFalse;
}
// type check args
@@ -698,7 +803,7 @@ void Gfx::execOp(Object *cmd, Object args[], int numArgs) {
if (numArgs < op->numArgs) {
error(errSyntaxError, getPos(),
"Too few ({0:d}) args to '{1:s}' operator", numArgs, name);
- return;
+ return gFalse;
}
if (numArgs > op->numArgs) {
#if 0
@@ -713,7 +818,7 @@ void Gfx::execOp(Object *cmd, Object args[], int numArgs) {
error(errSyntaxError, getPos(),
"Too many ({0:d}) args to '{1:s}' operator",
numArgs, name);
- return;
+ return gFalse;
}
}
for (i = 0; i < numArgs; ++i) {
@@ -721,12 +826,14 @@ void Gfx::execOp(Object *cmd, Object args[], int numArgs) {
error(errSyntaxError, getPos(),
"Arg #{0:d} to '{1:s}' operator is wrong type ({2:s})",
i, name, argPtr[i].getTypeName());
- return;
+ return gFalse;
}
}
// do it
(this->*op->func)(argPtr, numArgs);
+
+ return gTrue;
}
Operator *Gfx::findOp(char *name) {
@@ -766,7 +873,7 @@ GBool Gfx::checkArg(Object *arg, TchkType type) {
return gFalse;
}
-int Gfx::getPos() {
+GFileOffset Gfx::getPos() {
return parser ? parser->getPos() : -1;
}
@@ -840,7 +947,7 @@ void Gfx::opSetLineWidth(Object args[], int numArgs) {
}
void Gfx::opSetExtGState(Object args[], int numArgs) {
- Object obj1, obj2, obj3, obj4, obj5;
+ Object obj1, obj2, obj3, objRef3, obj4, obj5;
Object args2[2];
GfxBlendMode mode;
GBool haveFillOP;
@@ -895,22 +1002,21 @@ void Gfx::opSetExtGState(Object args[], int numArgs) {
args2[1].free();
}
obj2.free();
-#if 0 //~ need to add a new version of GfxResources::lookupFont() that
- //~ takes an indirect ref instead of a name
+ if (obj1.dictLookup("FL", &obj2)->isNum()) {
+ opSetFlat(&obj2, 1);
+ }
+ obj2.free();
+
+ // font
if (obj1.dictLookup("Font", &obj2)->isArray() &&
obj2.arrayGetLength() == 2) {
- obj2.arrayGet(0, &args2[0]);
- obj2.arrayGet(1, &args2[1]);
- if (args2[0].isDict() && args2[1].isNum()) {
- opSetFont(args2, 2);
+ obj2.arrayGetNF(0, &obj3);
+ obj2.arrayGetNF(1, &obj4);
+ if (obj3.isRef() && obj4.isNum()) {
+ doSetFont(res->lookupFontByRef(obj3.getRef()), obj4.getNum());
}
- args2[0].free();
- args2[1].free();
- }
- obj2.free();
-#endif
- if (obj1.dictLookup("FL", &obj2)->isNum()) {
- opSetFlat(&obj2, 1);
+ obj3.free();
+ obj4.free();
}
obj2.free();
@@ -1045,7 +1151,8 @@ void Gfx::opSetExtGState(Object args[], int numArgs) {
blendingColorSpace = NULL;
isolated = knockout = gFalse;
if (!obj4.dictLookup("CS", &obj5)->isNull()) {
- blendingColorSpace = GfxColorSpace::parse(&obj5);
+ blendingColorSpace = GfxColorSpace::parse(&obj5
+ );
}
obj5.free();
if (obj4.dictLookup("I", &obj5)->isBool()) {
@@ -1066,8 +1173,10 @@ void Gfx::opSetExtGState(Object args[], int numArgs) {
}
}
}
- doSoftMask(&obj3, alpha, blendingColorSpace,
+ obj2.dictLookupNF("G", &objRef3);
+ doSoftMask(&obj3, &objRef3, alpha, blendingColorSpace,
isolated, knockout, funcs[0], &backdropColor);
+ objRef3.free();
if (funcs[0]) {
delete funcs[0];
}
@@ -1090,7 +1199,7 @@ void Gfx::opSetExtGState(Object args[], int numArgs) {
obj1.free();
}
-void Gfx::doSoftMask(Object *str, GBool alpha,
+void Gfx::doSoftMask(Object *str, Object *strRef, GBool alpha,
GfxColorSpace *blendingColorSpace,
GBool isolated, GBool knockout,
Function *transferFunc, GfxColor *backdropColor) {
@@ -1149,7 +1258,7 @@ void Gfx::doSoftMask(Object *str, GBool alpha,
// draw it
++formDepth;
- drawForm(str, resDict, m, bbox, gTrue, gTrue,
+ drawForm(strRef, resDict, m, bbox, gTrue, gTrue,
blendingColorSpace, isolated, knockout,
alpha, transferFunc, backdropColor);
--formDepth;
@@ -1171,7 +1280,7 @@ void Gfx::opSetFillGray(Object args[], int numArgs) {
GfxColor color;
state->setFillPattern(NULL);
- state->setFillColorSpace(new GfxDeviceGrayColorSpace());
+ state->setFillColorSpace(GfxColorSpace::create(csDeviceGray));
out->updateFillColorSpace(state);
color.c[0] = dblToCol(args[0].getNum());
state->setFillColor(&color);
@@ -1182,7 +1291,7 @@ void Gfx::opSetStrokeGray(Object args[], int numArgs) {
GfxColor color;
state->setStrokePattern(NULL);
- state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
+ state->setStrokeColorSpace(GfxColorSpace::create(csDeviceGray));
out->updateStrokeColorSpace(state);
color.c[0] = dblToCol(args[0].getNum());
state->setStrokeColor(&color);
@@ -1194,7 +1303,7 @@ void Gfx::opSetFillCMYKColor(Object args[], int numArgs) {
int i;
state->setFillPattern(NULL);
- state->setFillColorSpace(new GfxDeviceCMYKColorSpace());
+ state->setFillColorSpace(GfxColorSpace::create(csDeviceCMYK));
out->updateFillColorSpace(state);
for (i = 0; i < 4; ++i) {
color.c[i] = dblToCol(args[i].getNum());
@@ -1208,7 +1317,7 @@ void Gfx::opSetStrokeCMYKColor(Object args[], int numArgs) {
int i;
state->setStrokePattern(NULL);
- state->setStrokeColorSpace(new GfxDeviceCMYKColorSpace());
+ state->setStrokeColorSpace(GfxColorSpace::create(csDeviceCMYK));
out->updateStrokeColorSpace(state);
for (i = 0; i < 4; ++i) {
color.c[i] = dblToCol(args[i].getNum());
@@ -1222,7 +1331,7 @@ void Gfx::opSetFillRGBColor(Object args[], int numArgs) {
int i;
state->setFillPattern(NULL);
- state->setFillColorSpace(new GfxDeviceRGBColorSpace());
+ state->setFillColorSpace(GfxColorSpace::create(csDeviceRGB));
out->updateFillColorSpace(state);
for (i = 0; i < 3; ++i) {
color.c[i] = dblToCol(args[i].getNum());
@@ -1236,7 +1345,7 @@ void Gfx::opSetStrokeRGBColor(Object args[], int numArgs) {
int i;
state->setStrokePattern(NULL);
- state->setStrokeColorSpace(new GfxDeviceRGBColorSpace());
+ state->setStrokeColorSpace(GfxColorSpace::create(csDeviceRGB));
out->updateStrokeColorSpace(state);
for (i = 0; i < 3; ++i) {
color.c[i] = dblToCol(args[i].getNum());
@@ -1253,9 +1362,11 @@ void Gfx::opSetFillColorSpace(Object args[], int numArgs) {
state->setFillPattern(NULL);
res->lookupColorSpace(args[0].getName(), &obj);
if (obj.isNull()) {
- colorSpace = GfxColorSpace::parse(&args[0]);
+ colorSpace = GfxColorSpace::parse(&args[0]
+ );
} else {
- colorSpace = GfxColorSpace::parse(&obj);
+ colorSpace = GfxColorSpace::parse(&obj
+ );
}
obj.free();
if (colorSpace) {
@@ -1277,9 +1388,11 @@ void Gfx::opSetStrokeColorSpace(Object args[], int numArgs) {
state->setStrokePattern(NULL);
res->lookupColorSpace(args[0].getName(), &obj);
if (obj.isNull()) {
- colorSpace = GfxColorSpace::parse(&args[0]);
+ colorSpace = GfxColorSpace::parse(&args[0]
+ );
} else {
- colorSpace = GfxColorSpace::parse(&obj);
+ colorSpace = GfxColorSpace::parse(&obj
+ );
}
obj.free();
if (colorSpace) {
@@ -1350,7 +1463,8 @@ void Gfx::opSetFillColorN(Object args[], int numArgs) {
out->updateFillColor(state);
}
if (args[numArgs-1].isName() &&
- (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
+ (pattern = res->lookupPattern(args[numArgs-1].getName()
+ ))) {
state->setFillPattern(pattern);
}
@@ -1395,7 +1509,8 @@ void Gfx::opSetStrokeColorN(Object args[], int numArgs) {
out->updateStrokeColor(state);
}
if (args[numArgs-1].isName() &&
- (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
+ (pattern = res->lookupPattern(args[numArgs-1].getName()
+ ))) {
state->setStrokePattern(pattern);
}
@@ -1751,11 +1866,11 @@ void Gfx::doPatternText() {
}
void Gfx::doPatternImageMask(Object *ref, Stream *str, int width, int height,
- GBool invert, GBool inlineImg) {
+ GBool invert, GBool inlineImg, GBool interpolate) {
saveState();
out->setSoftMaskFromImageMask(state, ref, str,
- width, height, invert, inlineImg);
+ width, height, invert, inlineImg, interpolate);
state->clearPath();
state->moveTo(0, 0);
@@ -1773,11 +1888,11 @@ void Gfx::doTilingPatternFill(GfxTilingPattern *tPat,
GfxPatternColorSpace *patCS;
GfxColorSpace *cs;
GfxState *savedState;
- double xMin, yMin, xMax, yMax, x, y, x1, y1;
+ double xMin, yMin, xMax, yMax, x, y, x1, y1, t;
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 bbox[4], m[6], ictm[6], m1[6], imb[6];
double det;
double xstep, ystep;
int i;
@@ -1791,7 +1906,12 @@ void Gfx::doTilingPatternFill(GfxTilingPattern *tPat,
btm = baseMatrix;
ptm = tPat->getMatrix();
// iCTM = invert CTM
- det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
+ det = ctm[0] * ctm[3] - ctm[1] * ctm[2];
+ if (fabs(det) < 0.000001) {
+ error(errSyntaxError, getPos(), "Singular matrix in tiling pattern fill");
+ return;
+ }
+ det = 1 / det;
ictm[0] = ctm[3] * det;
ictm[1] = -ctm[1] * det;
ictm[2] = -ctm[2] * det;
@@ -1814,7 +1934,12 @@ void Gfx::doTilingPatternFill(GfxTilingPattern *tPat,
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]);
+ det = m1[0] * m1[3] - m1[1] * m1[2];
+ if (fabs(det) < 0.000001) {
+ error(errSyntaxError, getPos(), "Singular matrix in tiling pattern fill");
+ return;
+ }
+ det = 1 / det;
imb[0] = m1[3] * det;
imb[1] = -m1[1] * det;
imb[2] = -m1[2] * det;
@@ -1839,9 +1964,9 @@ void Gfx::doTilingPatternFill(GfxTilingPattern *tPat,
out->updateFillColor(state);
out->updateStrokeColor(state);
} else {
- state->setFillColorSpace(new GfxDeviceGrayColorSpace());
+ state->setFillColorSpace(GfxColorSpace::create(csDeviceGray));
out->updateFillColorSpace(state);
- state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
+ state->setStrokeColorSpace(GfxColorSpace::create(csDeviceGray));
out->updateStrokeColorSpace(state);
}
if (!stroke) {
@@ -1912,21 +2037,31 @@ void Gfx::doTilingPatternFill(GfxTilingPattern *tPat,
// draw the pattern
//~ this should treat negative steps differently -- start at right/top
//~ edge instead of left/bottom (?)
+ bbox[0] = tPat->getBBox()[0];
+ bbox[1] = tPat->getBBox()[1];
+ bbox[2] = tPat->getBBox()[2];
+ bbox[3] = tPat->getBBox()[3];
+ if (bbox[0] > bbox[2]) {
+ t = bbox[0]; bbox[0] = bbox[2]; bbox[2] = t;
+ }
+ if (bbox[1] > bbox[3]) {
+ t = bbox[1]; bbox[1] = bbox[3]; bbox[3] = t;
+ }
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;
+ xi0 = (int)ceil((xMin - bbox[2]) / xstep);
+ xi1 = (int)floor((xMax - bbox[0]) / xstep) + 1;
+ yi0 = (int)ceil((yMin - bbox[3]) / ystep);
+ yi1 = (int)floor((yMax - bbox[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, this, tPat->getContentStream(),
+ out->tilingPatternFill(state, this, tPat->getContentStreamRef(),
tPat->getPaintType(), tPat->getResDict(),
- m1, tPat->getBBox(),
+ m1, bbox,
xi0, yi0, xi1, yi1, xstep, ystep);
} else {
for (yi = yi0; yi < yi1; ++yi) {
@@ -1935,8 +2070,8 @@ void Gfx::doTilingPatternFill(GfxTilingPattern *tPat,
y = yi * ystep;
m1[4] = x * m[0] + y * m[2] + m[4];
m1[5] = x * m[1] + y * m[3] + m[5];
- drawForm(tPat->getContentStream(), tPat->getResDict(),
- m1, tPat->getBBox());
+ drawForm(tPat->getContentStreamRef(), tPat->getResDict(),
+ m1, bbox);
}
}
}
@@ -1979,7 +2114,12 @@ void Gfx::doShadingPatternFill(GfxShadingPattern *sPat,
btm = baseMatrix;
ptm = sPat->getMatrix();
// iCTM = invert CTM
- det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
+ det = ctm[0] * ctm[3] - ctm[1] * ctm[2];
+ if (fabs(det) < 0.000001) {
+ error(errSyntaxError, getPos(), "Singular matrix in shading pattern fill");
+ return;
+ }
+ det = 1 / det;
ictm[0] = ctm[3] * det;
ictm[1] = -ctm[1] * det;
ictm[2] = -ctm[2] * det;
@@ -2074,11 +2214,16 @@ void Gfx::opShFill(Object args[], int numArgs) {
GfxState *savedState;
double xMin, yMin, xMax, yMax;
+ if (!out->needNonText()) {
+ return;
+ }
+
if (!ocState) {
return;
}
- if (!(shading = res->lookupShading(args[0].getName()))) {
+ if (!(shading = res->lookupShading(args[0].getName()
+ ))) {
return;
}
@@ -2487,7 +2632,7 @@ void Gfx::doRadialShFill(GfxRadialShading *shading) {
GfxColor colorA, colorB;
double xa, ya, xb, yb, ra, rb;
double ta, tb, sa, sb;
- double sz, sMin, sMax, h;
+ double sMin, sMax, h;
double sLeft, sRight, sTop, sBottom, sZero, sDiag;
GBool haveSLeft, haveSRight, haveSTop, haveSBottom, haveSZero;
GBool haveSMin, haveSMax;
@@ -2513,18 +2658,14 @@ void Gfx::doRadialShFill(GfxRadialShading *shading) {
if (h == 0) {
enclosed = gTrue;
theta = 0; // make gcc happy
- sz = 0; // make gcc happy
} else if (r1 - r0 == 0) {
enclosed = gFalse;
theta = 0;
- sz = 0; // make gcc happy
- } else if (fabs(r1 - r0) >= h) {
+ } else if (fabs(r1 - r0) >= h - 0.0001) {
enclosed = gTrue;
theta = 0; // make gcc happy
- sz = 0; // make gcc happy
} else {
enclosed = gFalse;
- sz = -r0 / (r1 - r0);
theta = asin((r1 - r0) / h);
}
if (enclosed) {
@@ -2598,7 +2739,7 @@ void Gfx::doRadialShFill(GfxRadialShading *shading) {
haveSMin = gTrue;
}
}
- if (haveSZero && sZero < 0) {
+ if (haveSZero && sZero <= 0) {
if (!haveSMin || sZero > sMin) {
sMin = sZero;
}
@@ -2865,34 +3006,56 @@ void Gfx::doRadialShFill(GfxRadialShading *shading) {
void Gfx::doGouraudTriangleShFill(GfxGouraudTriangleShading *shading) {
double x0, y0, x1, y1, x2, y2;
- GfxColor color0, color1, color2;
+ double color0[gfxColorMaxComps];
+ double color1[gfxColorMaxComps];
+ double color2[gfxColorMaxComps];
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);
+ shading->getTriangle(i, &x0, &y0, color0,
+ &x1, &y1, color1,
+ &x2, &y2, color2);
+ gouraudFillTriangle(x0, y0, color0, x1, y1, color1, x2, y2, color2,
+ shading, 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) {
+void Gfx::gouraudFillTriangle(double x0, double y0, double *color0,
+ double x1, double y1, double *color1,
+ double x2, double y2, double *color2,
+ GfxGouraudTriangleShading *shading, int depth) {
+ double dx0, dy0, dx1, dy1, dx2, dy2;
double x01, y01, x12, y12, x20, y20;
- GfxColor color01, color12, color20;
- int i;
-
+ double color01[gfxColorMaxComps];
+ double color12[gfxColorMaxComps];
+ double color20[gfxColorMaxComps];
+ GfxColor c0, c1, c2;
+ int nComps, i;
+
+ // recursion ends when:
+ // (1) color difference is smaller than gouraudColorDelta; or
+ // (2) triangles are smaller than 0.5 pixel (note that "device
+ // space" is 72dpi when generating PostScript); or
+ // (3) max recursion depth (gouraudMaxDepth) is hit.
+ nComps = shading->getColorSpace()->getNComps();
+ shading->getColor(color0, &c0);
+ shading->getColor(color1, &c1);
+ shading->getColor(color2, &c2);
for (i = 0; i < nComps; ++i) {
- if (abs(color0->c[i] - color1->c[i]) > gouraudColorDelta ||
- abs(color1->c[i] - color2->c[i]) > gouraudColorDelta) {
+ if (abs(c0.c[i] - c1.c[i]) > gouraudColorDelta ||
+ abs(c1.c[i] - c2.c[i]) > gouraudColorDelta) {
break;
}
}
- if (i == nComps || depth == gouraudMaxDepth) {
- state->setFillColor(color0);
+ state->transformDelta(x1 - x0, y1 - y0, &dx0, &dy0);
+ state->transformDelta(x2 - x1, y2 - y1, &dx1, &dy1);
+ state->transformDelta(x0 - x2, y0 - y2, &dx2, &dy2);
+ if (i == nComps ||
+ depth == gouraudMaxDepth ||
+ (fabs(dx0) < 0.5 && fabs(dy0) < 0.5 &&
+ fabs(dx1) < 0.5 && fabs(dy1) < 0.5 &&
+ fabs(dx2) < 0.5 && fabs(dy2) < 0.5)) {
+ state->setFillColor(&c0);
out->updateFillColor(state);
state->moveTo(x0, y0);
state->lineTo(x1, y1);
@@ -2907,21 +3070,19 @@ void Gfx::gouraudFillTriangle(double x0, double y0, GfxColor *color0,
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);
+ for (i = 0; i < shading->getNComps(); ++i) {
+ color01[i] = 0.5 * (color0[i] + color1[i]);
+ color12[i] = 0.5 * (color1[i] + color2[i]);
+ color20[i] = 0.5 * (color2[i] + color0[i]);
+ }
+ gouraudFillTriangle(x0, y0, color0, x01, y01, color01,
+ x20, y20, color20, shading, depth + 1);
+ gouraudFillTriangle(x01, y01, color01, x1, y1, color1,
+ x12, y12, color12, shading, depth + 1);
+ gouraudFillTriangle(x01, y01, color01, x12, y12, color12,
+ x20, y20, color20, shading, depth + 1);
+ gouraudFillTriangle(x20, y20, color20, x12, y12, color12,
+ x2, y2, color2, shading, depth + 1);
}
}
@@ -2938,31 +3099,32 @@ void Gfx::doPatchMeshShFill(GfxPatchMeshShading *shading) {
start = 0;
}
for (i = 0; i < shading->getNPatches(); ++i) {
- fillPatch(shading->getPatch(i), shading->getColorSpace()->getNComps(),
- start);
+ fillPatch(shading->getPatch(i), shading, start);
}
}
-void Gfx::fillPatch(GfxPatch *patch, int nComps, int depth) {
+void Gfx::fillPatch(GfxPatch *patch, GfxPatchMeshShading *shading, int depth) {
GfxPatch patch00, patch01, patch10, patch11;
+ GfxColor c00, c01, c10, c11;
double xx[4][8], yy[4][8];
double xxm, yym;
- int i;
+ int nComps, i;
+ nComps = shading->getColorSpace()->getNComps();
+ shading->getColor(patch->color[0][0], &c00);
+ shading->getColor(patch->color[0][1], &c01);
+ shading->getColor(patch->color[1][0], &c10);
+ shading->getColor(patch->color[1][1], &c11);
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) {
+ if (abs(c00.c[i] - c01.c[i]) > patchColorDelta ||
+ abs(c01.c[i] - c11.c[i]) > patchColorDelta ||
+ abs(c11.c[i] - c10.c[i]) > patchColorDelta ||
+ abs(c10.c[i] - c00.c[i]) > patchColorDelta) {
break;
}
}
if (i == nComps || depth == patchMaxDepth) {
- state->setFillColor(&patch->color[0][0]);
+ state->setFillColor(&c00);
out->updateFillColor(state);
state->moveTo(patch->x[0][0], patch->y[0][0]);
state->curveTo(patch->x[0][1], patch->y[0][1],
@@ -3039,35 +3201,33 @@ void Gfx::fillPatch(GfxPatch *patch, int nComps, int depth) {
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);
+ for (i = 0; i < shading->getNComps(); ++i) {
+ patch00.color[0][0][i] = patch->color[0][0][i];
+ patch00.color[0][1][i] = 0.5 * (patch->color[0][0][i] +
+ patch->color[0][1][i]);
+ patch01.color[0][0][i] = patch00.color[0][1][i];
+ patch01.color[0][1][i] = patch->color[0][1][i];
+ patch01.color[1][1][i] = 0.5 * (patch->color[0][1][i] +
+ patch->color[1][1][i]);
+ patch11.color[0][1][i] = patch01.color[1][1][i];
+ patch11.color[1][1][i] = patch->color[1][1][i];
+ patch11.color[1][0][i] = 0.5 * (patch->color[1][1][i] +
+ patch->color[1][0][i]);
+ patch10.color[1][1][i] = patch11.color[1][0][i];
+ patch10.color[1][0][i] = patch->color[1][0][i];
+ patch10.color[0][0][i] = 0.5 * (patch->color[1][0][i] +
+ patch->color[0][0][i]);
+ patch00.color[1][0][i] = patch10.color[0][0][i];
+ patch00.color[1][1][i] = 0.5 * (patch00.color[1][0][i] +
+ patch01.color[1][1][i]);
+ patch01.color[1][0][i] = patch00.color[1][1][i];
+ patch11.color[0][0][i] = patch00.color[1][1][i];
+ patch10.color[0][1][i] = patch00.color[1][1][i];
+ }
+ fillPatch(&patch00, shading, depth + 1);
+ fillPatch(&patch10, shading, depth + 1);
+ fillPatch(&patch01, shading, depth + 1);
+ fillPatch(&patch11, shading, depth + 1);
}
}
@@ -3123,19 +3283,22 @@ void Gfx::opSetCharSpacing(Object args[], int numArgs) {
}
void Gfx::opSetFont(Object args[], int numArgs) {
- GfxFont *font;
+ doSetFont(res->lookupFont(args[0].getName()), args[1].getNum());
+}
- if (!(font = res->lookupFont(args[0].getName()))) {
+void Gfx::doSetFont(GfxFont *font, double size) {
+ if (!font) {
+ state->setFont(NULL, 0);
return;
}
if (printCommands) {
printf(" font: tag=%s name='%s' %g\n",
font->getTag()->getCString(),
font->getName() ? font->getName()->getCString() : "???",
- args[1].getNum());
+ size);
fflush(stdout);
}
- state->setFont(font, args[1].getNum());
+ state->setFont(font, size);
fontChanged = gTrue;
}
@@ -3343,7 +3506,7 @@ void Gfx::doShowText(GString *s) {
double x0, y0, x1, y1;
double oldCTM[6], newCTM[6];
double *mat;
- Object charProc;
+ Object charProcRef, charProc;
Dict *resDict;
Parser *oldParser;
GfxState *savedState;
@@ -3427,12 +3590,13 @@ void Gfx::doShowText(GString *s) {
state->transformDelta(dx, dy, &ddx, &ddy);
if (!out->beginType3Char(state, curX + riseX, curY + riseY, ddx, ddy,
code, u, uLen)) {
- ((Gfx8BitFont *)font)->getCharProc(code, &charProc);
+ ((Gfx8BitFont *)font)->getCharProcNF(code, &charProcRef);
+ charProcRef.fetch(xref, &charProc);
if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
pushResources(resDict);
}
if (charProc.isStream()) {
- display(&charProc, gFalse);
+ display(&charProcRef, gFalse);
} else {
error(errSyntaxError, getPos(),
"Missing or bad Type3 CharProc entry");
@@ -3442,6 +3606,7 @@ void Gfx::doShowText(GString *s) {
popResources();
}
charProc.free();
+ charProcRef.free();
}
restoreStateStack(savedState);
curX += tdx;
@@ -3592,44 +3757,53 @@ void Gfx::opXObject(Object args[], int numArgs) {
obj1.free();
return;
}
+#if USE_EXCEPTIONS
+ try {
+#endif
#if OPI_SUPPORT
- obj1.streamGetDict()->lookup("OPI", &opiDict);
- if (opiDict.isDict()) {
- out->opiBegin(state, opiDict.getDict());
- }
+ 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()) {
+ 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);
- doImage(&refObj, obj1.getStream(), gFalse);
+ if (out->useDrawForm() && refObj.isRef()) {
+ out->drawForm(refObj.getRef());
+ } else {
+ doForm(&refObj, &obj1);
+ }
refObj.free();
- }
- } else if (obj2.isName("Form")) {
- res->lookupXObjectNF(name, &refObj);
- if (out->useDrawForm() && refObj.isRef()) {
- out->drawForm(refObj.getRef());
+ } 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(errSyntaxError, getPos(),
+ "Unknown XObject subtype '{0:s}'", obj2.getName());
} 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(errSyntaxError, getPos(),
- "Unknown XObject subtype '{0:s}'", obj2.getName());
- } else {
- error(errSyntaxError, getPos(),
- "XObject subtype is missing or wrong type");
- }
- obj2.free();
+ error(errSyntaxError, getPos(),
+ "XObject subtype is missing or wrong type");
+ }
+ obj2.free();
#if OPI_SUPPORT
- if (opiDict.isDict()) {
- out->opiEnd(state, opiDict.getDict());
+ if (opiDict.isDict()) {
+ out->opiEnd(state, opiDict.getDict());
+ }
+ opiDict.free();
+#endif
+#if USE_EXCEPTIONS
+ } catch (GMemException e) {
+ obj1.free();
+ throw;
}
- opiDict.free();
#endif
obj1.free();
}
@@ -3649,6 +3823,7 @@ void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
int maskWidth, maskHeight;
GBool maskInvert;
Stream *maskStr;
+ GBool interpolate;
Object obj1, obj2;
int i, n;
@@ -3710,6 +3885,9 @@ void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
}
if (obj1.isInt()) {
bits = obj1.getInt();
+ if (bits < 1 || bits > 16) {
+ goto err2;
+ }
} else if (mask) {
bits = 1;
} else {
@@ -3718,6 +3896,15 @@ void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
obj1.free();
}
+ // interpolate flag
+ dict->lookup("Interpolate", &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ dict->lookup("I", &obj1);
+ }
+ interpolate = obj1.isBool() && obj1.getBool();
+ obj1.free();
+
// display a mask
if (mask) {
@@ -3751,9 +3938,11 @@ void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
// draw it
} else {
if (state->getFillColorSpace()->getMode() == csPattern) {
- doPatternImageMask(ref, str, width, height, invert, inlineImg);
+ doPatternImageMask(ref, str, width, height, invert, inlineImg,
+ interpolate);
} else {
- out->drawImageMask(state, ref, str, width, height, invert, inlineImg);
+ out->drawImageMask(state, ref, str, width, height, invert, inlineImg,
+ interpolate);
}
}
@@ -3775,13 +3964,14 @@ void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
}
}
if (!obj1.isNull()) {
- colorSpace = GfxColorSpace::parse(&obj1);
+ colorSpace = GfxColorSpace::parse(&obj1
+ );
} else if (csMode == streamCSDeviceGray) {
- colorSpace = new GfxDeviceGrayColorSpace();
+ colorSpace = GfxColorSpace::create(csDeviceGray);
} else if (csMode == streamCSDeviceRGB) {
- colorSpace = new GfxDeviceRGBColorSpace();
+ colorSpace = GfxColorSpace::create(csDeviceRGB);
} else if (csMode == streamCSDeviceCMYK) {
- colorSpace = new GfxDeviceCMYKColorSpace();
+ colorSpace = GfxColorSpace::create(csDeviceCMYK);
} else {
colorSpace = NULL;
}
@@ -3860,7 +4050,8 @@ void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
obj2.free();
}
}
- maskColorSpace = GfxColorSpace::parse(&obj1);
+ maskColorSpace = GfxColorSpace::parse(&obj1
+ );
obj1.free();
if (!maskColorSpace || maskColorSpace->getMode() != csDeviceGray) {
goto err1;
@@ -3977,14 +4168,17 @@ void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
} else {
if (haveSoftMask) {
out->drawSoftMaskedImage(state, ref, str, width, height, colorMap,
- maskStr, maskWidth, maskHeight, maskColorMap);
+ maskStr, maskWidth, maskHeight, maskColorMap,
+ interpolate);
delete maskColorMap;
} else if (haveExplicitMask) {
out->drawMaskedImage(state, ref, str, width, height, colorMap,
- maskStr, maskWidth, maskHeight, maskInvert);
+ maskStr, maskWidth, maskHeight, maskInvert,
+ interpolate);
} else {
out->drawImage(state, ref, str, width, height, colorMap,
- haveColorKeyMask ? maskColors : (int *)NULL, inlineImg);
+ haveColorKeyMask ? maskColors : (int *)NULL, inlineImg,
+ interpolate);
}
}
@@ -4006,7 +4200,7 @@ void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
error(errSyntaxError, getPos(), "Bad image parameters");
}
-void Gfx::doForm(Object *str) {
+void Gfx::doForm(Object *strRef, Object *str) {
Dict *dict;
GBool transpGroup, isolated, knockout;
GfxColorSpace *blendingColorSpace;
@@ -4087,7 +4281,8 @@ void Gfx::doForm(Object *str) {
if (obj1.dictLookup("S", &obj2)->isName("Transparency")) {
transpGroup = gTrue;
if (!obj1.dictLookup("CS", &obj3)->isNull()) {
- blendingColorSpace = GfxColorSpace::parse(&obj3);
+ blendingColorSpace = GfxColorSpace::parse(&obj3
+ );
}
obj3.free();
if (obj1.dictLookup("I", &obj3)->isBool()) {
@@ -4105,7 +4300,7 @@ void Gfx::doForm(Object *str) {
// draw it
++formDepth;
- drawForm(str, resDict, m, bbox,
+ drawForm(strRef, resDict, m, bbox,
transpGroup, gFalse, blendingColorSpace, isolated, knockout);
--formDepth;
@@ -4117,7 +4312,8 @@ void Gfx::doForm(Object *str) {
ocState = ocSaved;
}
-void Gfx::drawForm(Object *str, Dict *resDict, double *matrix, double *bbox,
+void Gfx::drawForm(Object *strRef, Dict *resDict,
+ double *matrix, double *bbox,
GBool transpGroup, GBool softMask,
GfxColorSpace *blendingColorSpace,
GBool isolated, GBool knockout,
@@ -4181,7 +4377,7 @@ void Gfx::drawForm(Object *str, Dict *resDict, double *matrix, double *bbox,
}
// draw the form
- display(str, gFalse);
+ display(strRef, gFalse);
if (softMask || transpGroup) {
out->endTransparencyGroup(state);
@@ -4210,13 +4406,17 @@ void Gfx::drawForm(Object *str, Dict *resDict, double *matrix, double *bbox,
return;
}
+void Gfx::takeContentStreamStack(Gfx *oldGfx) {
+ contentStreamStack->append(oldGfx->contentStreamStack);
+}
+
//------------------------------------------------------------------------
// in-line image operators
//------------------------------------------------------------------------
void Gfx::opBeginImage(Object args[], int numArgs) {
Stream *str;
- int c1, c2;
+ int c1, c2, c3;
// NB: this function is run even if ocState is false -- doImage() is
// responsible for skipping over the inline image data
@@ -4231,9 +4431,11 @@ void Gfx::opBeginImage(Object args[], int numArgs) {
// skip 'EI' tag
c1 = str->getUndecodedStream()->getChar();
c2 = str->getUndecodedStream()->getChar();
- while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) {
+ c3 = str->getUndecodedStream()->lookChar();
+ while (!(c1 == 'E' && c2 == 'I' && Lexer::isSpace(c3)) && c3 != EOF) {
c1 = c2;
c2 = str->getUndecodedStream()->getChar();
+ c3 = str->getUndecodedStream()->lookChar();
}
delete str;
}
@@ -4328,9 +4530,7 @@ void Gfx::opBeginMarkedContent(Object args[], int numArgs) {
GfxMarkedContent *mc;
Object obj;
GBool ocStateNew;
- GString *s;
- Unicode *u;
- int uLen, i;
+ TextString *s;
GfxMarkedContentKind mcKind;
if (printCommands) {
@@ -4351,24 +4551,9 @@ void Gfx::opBeginMarkedContent(Object args[], int numArgs) {
mcKind = gfxMCOptionalContent;
} else if (args[0].isName("Span") && numArgs == 2 && args[1].isDict()) {
if (args[1].dictLookup("ActualText", &obj)->isString()) {
- s = obj.getString();
- if ((s->getChar(0) & 0xff) == 0xfe &&
- (s->getChar(1) & 0xff) == 0xff) {
- uLen = (s->getLength() - 2) / 2;
- u = (Unicode *)gmallocn(uLen, sizeof(Unicode));
- for (i = 0; i < uLen; ++i) {
- u[i] = ((s->getChar(2 + 2*i) & 0xff) << 8) |
- (s->getChar(3 + 2*i) & 0xff);
- }
- } else {
- uLen = s->getLength();
- u = (Unicode *)gmallocn(uLen, sizeof(Unicode));
- for (i = 0; i < uLen; ++i) {
- u[i] = pdfDocEncoding[s->getChar(i) & 0xff];
- }
- }
- out->beginActualText(state, u, uLen);
- gfree(u);
+ s = new TextString(obj.getString());
+ out->beginActualText(state, s->getUnicode(), s->getLength());
+ delete s;
mcKind = gfxMCActualText;
}
obj.free();
@@ -4416,14 +4601,14 @@ void Gfx::opMarkPoint(Object args[], int numArgs) {
// misc
//------------------------------------------------------------------------
-void Gfx::drawAnnot(Object *str, AnnotBorderStyle *borderStyle,
+void Gfx::drawAnnot(Object *strRef, AnnotBorderStyle *borderStyle,
double xMin, double yMin, double xMax, double yMax) {
Dict *dict, *resDict;
- Object matrixObj, bboxObj, resObj, obj1;
+ Object str, matrixObj, bboxObj, resObj, obj1;
double formXMin, formYMin, formXMax, formYMax;
double x, y, sx, sy, tx, ty;
double m[6], bbox[4];
- double r, g, b;
+ double *borderColor;
GfxColor color;
double *dash, *dash2;
int dashLength;
@@ -4439,16 +4624,18 @@ void Gfx::drawAnnot(Object *str, AnnotBorderStyle *borderStyle,
}
// draw the appearance stream (if there is one)
- if (str->isStream()) {
+ strRef->fetch(xref, &str);
+ if (str.isStream()) {
// get stream dict
- dict = str->streamGetDict();
+ dict = str.streamGetDict();
// get the form bounding box
dict->lookup("BBox", &bboxObj);
if (!bboxObj.isArray()) {
- bboxObj.free();
error(errSyntaxError, getPos(), "Bad form bounding box");
+ bboxObj.free();
+ str.free();
return;
}
for (i = 0; i < 4; ++i) {
@@ -4548,22 +4735,43 @@ void Gfx::drawAnnot(Object *str, AnnotBorderStyle *borderStyle,
resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
// draw it
- drawForm(str, resDict, m, bbox);
+ drawForm(strRef, resDict, m, bbox);
resObj.free();
}
+ str.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);
+ if (borderStyle && borderStyle->getWidth() > 0 &&
+ borderStyle->getNumColorComps() > 0) {
+ borderColor = borderStyle->getColor();
+ switch (borderStyle->getNumColorComps()) {
+ case 1:
+ if (state->getStrokeColorSpace()->getMode() != csDeviceGray) {
+ state->setStrokePattern(NULL);
+ state->setStrokeColorSpace(GfxColorSpace::create(csDeviceGray));
+ out->updateStrokeColorSpace(state);
+ }
+ break;
+ case 3:
+ if (state->getStrokeColorSpace()->getMode() != csDeviceRGB) {
+ state->setStrokePattern(NULL);
+ state->setStrokeColorSpace(GfxColorSpace::create(csDeviceRGB));
+ out->updateStrokeColorSpace(state);
+ }
+ break;
+ case 4:
+ if (state->getStrokeColorSpace()->getMode() != csDeviceCMYK) {
+ state->setStrokePattern(NULL);
+ state->setStrokeColorSpace(GfxColorSpace::create(csDeviceCMYK));
+ out->updateStrokeColorSpace(state);
+ }
+ break;
+ }
+ color.c[0] = dblToCol(borderColor[0]);
+ color.c[1] = dblToCol(borderColor[1]);
+ color.c[2] = dblToCol(borderColor[2]);
+ color.c[3] = dblToCol(borderColor[3]);
state->setStrokeColor(&color);
out->updateStrokeColor(state);
state->setLineWidth(borderStyle->getWidth());
diff --git a/xpdf/Gfx.h b/xpdf/Gfx.h
index 2753766..b734777 100644
--- a/xpdf/Gfx.h
+++ b/xpdf/Gfx.h
@@ -16,6 +16,7 @@
#endif
#include "gtypes.h"
+#include "gfile.h"
class GString;
class GList;
@@ -84,13 +85,16 @@ public:
~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);
- GBool lookupPropertiesNF(char *name, Object *obj);
+ GfxFont *lookupFontByRef(Ref ref);
+ GBool lookupXObject(const char *name, Object *obj);
+ GBool lookupXObjectNF(const char *name, Object *obj);
+ void lookupColorSpace(const char *name, Object *obj);
+ GfxPattern *lookupPattern(const char *name
+ );
+ GfxShading *lookupShading(const char *name
+ );
+ GBool lookupGState(const char *name, Object *obj);
+ GBool lookupPropertiesNF(const char *name, Object *obj);
GfxResources *getNext() { return next; }
@@ -152,12 +156,13 @@ public:
~Gfx();
- // Interpret a stream or array of streams.
- void display(Object *obj, GBool topLevel = gTrue);
+ // Interpret a stream or array of streams. <objRef> should be a
+ // reference wherever possible (for loop-checking).
+ void display(Object *objRef, 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,
+ void drawAnnot(Object *strRef, AnnotBorderStyle *borderStyle,
double xMin, double yMin, double xMax, double yMax);
// Save graphics state.
@@ -169,13 +174,21 @@ public:
// Get the current graphics state object.
GfxState *getState() { return state; }
- void drawForm(Object *str, Dict *resDict, double *matrix, double *bbox,
+ void drawForm(Object *strRef, 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);
+ // Take all of the content stream stack entries from <oldGfx>. This
+ // is useful when creating a new Gfx object to handle a pattern,
+ // etc., where it's useful to check for loops that span both Gfx
+ // objects. This function should be called immediately after the
+ // Gfx constructor, i.e., before processing any content streams with
+ // the new Gfx object.
+ void takeContentStreamStack(Gfx *oldGfx);
+
private:
PDFDoc *doc;
@@ -201,6 +214,8 @@ private:
GList *markedContentStack; // BMC/BDC/EMC stack [GfxMarkedContent]
Parser *parser; // parser for page content stream(s)
+ GList *contentStreamStack; // stack of open content streams, used
+ // for loop-checking
GBool // callback to check for an abort
(*abortCheckCbk)(void *data);
@@ -208,11 +223,12 @@ private:
static Operator opTab[]; // table of operators
+ GBool checkForContentStreamLoop(Object *ref);
void go(GBool topLevel);
- void execOp(Object *cmd, Object args[], int numArgs);
+ GBool execOp(Object *cmd, Object args[], int numArgs);
Operator *findOp(char *name);
GBool checkArg(Object *arg, TchkType type);
- int getPos();
+ GFileOffset getPos();
// graphics state operators
void opSave(Object args[], int numArgs);
@@ -225,7 +241,7 @@ private:
void opSetMiterLimit(Object args[], int numArgs);
void opSetLineWidth(Object args[], int numArgs);
void opSetExtGState(Object args[], int numArgs);
- void doSoftMask(Object *str, GBool alpha,
+ void doSoftMask(Object *str, Object *strRef, GBool alpha,
GfxColorSpace *blendingColorSpace,
GBool isolated, GBool knockout,
Function *transferFunc, GfxColor *backdropColor);
@@ -268,7 +284,7 @@ private:
void doPatternStroke();
void doPatternText();
void doPatternImageMask(Object *ref, Stream *str, int width, int height,
- GBool invert, GBool inlineImg);
+ GBool invert, GBool inlineImg, GBool interpolate);
void doTilingPatternFill(GfxTilingPattern *tPat,
GBool stroke, GBool eoFill, GBool text);
void doShadingPatternFill(GfxShadingPattern *sPat,
@@ -282,12 +298,12 @@ private:
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 gouraudFillTriangle(double x0, double y0, double *color0,
+ double x1, double y1, double *color1,
+ double x2, double y2, double *color2,
+ GfxGouraudTriangleShading *shading, int depth);
void doPatchMeshShFill(GfxPatchMeshShading *shading);
- void fillPatch(GfxPatch *patch, int nComps, int depth);
+ void fillPatch(GfxPatch *patch, GfxPatchMeshShading *shading, int depth);
void doEndPath();
// path clipping operators
@@ -301,6 +317,7 @@ private:
// text state operators
void opSetCharSpacing(Object args[], int numArgs);
void opSetFont(Object args[], int numArgs);
+ void doSetFont(GfxFont *font, double size);
void opSetTextLeading(Object args[], int numArgs);
void opSetTextRender(Object args[], int numArgs);
void opSetTextRise(Object args[], int numArgs);
@@ -324,7 +341,7 @@ private:
// XObject operators
void opXObject(Object args[], int numArgs);
void doImage(Object *ref, Stream *str, GBool inlineImg);
- void doForm(Object *str);
+ void doForm(Object *strRef, Object *str);
// in-line image operators
void opBeginImage(Object args[], int numArgs);
diff --git a/xpdf/GfxFont.cc b/xpdf/GfxFont.cc
index aa88e78..0ebb5b9 100644
--- a/xpdf/GfxFont.cc
+++ b/xpdf/GfxFont.cc
@@ -146,6 +146,7 @@ static int readFromStream(void *data) {
GfxFontLoc::GfxFontLoc() {
path = NULL;
fontNum = 0;
+ oblique = 0;
encoding = NULL;
substIdx = -1;
}
@@ -175,6 +176,8 @@ GfxFont *GfxFont::makeFont(XRef *xref, char *tagA, Ref idA, Dict *fontDict) {
fontDict->lookup("BaseFont", &obj1);
if (obj1.isName()) {
nameA = new GString(obj1.getName());
+ } else if (obj1.isString()) {
+ nameA = obj1.getString()->copy();
}
obj1.free();
@@ -451,7 +454,7 @@ void GfxFont::readFontDescriptor(XRef *xref, Dict *fontDict) {
}
// some broken font descriptors set ascent and descent to 0;
// others set it to ridiculous values (e.g., 32768)
- if (t != 0 && t < 3) {
+ if (t != 0 && t < 1.9) {
ascent = t;
}
}
@@ -464,7 +467,7 @@ void GfxFont::readFontDescriptor(XRef *xref, Dict *fontDict) {
t = -t;
}
// some broken font descriptors set ascent and descent to 0
- if (t != 0 && t > -3) {
+ if (t != 0 && t > -1.9) {
descent = t;
}
}
@@ -489,7 +492,8 @@ CharCodeToUnicode *GfxFont::readToUnicodeCMap(Dict *fontDict, int nBits,
CharCodeToUnicode *ctu) {
GString *buf;
Object obj1;
- int c;
+ char buf2[4096];
+ int n;
if (!fontDict->lookup("ToUnicode", &obj1)->isStream()) {
obj1.free();
@@ -497,8 +501,8 @@ CharCodeToUnicode *GfxFont::readToUnicodeCMap(Dict *fontDict, int nBits,
}
buf = new GString();
obj1.streamReset();
- while ((c = obj1.streamGetChar()) != EOF) {
- buf->append(c);
+ while ((n = obj1.streamGetBlock(buf2, sizeof(buf2))) > 0) {
+ buf->append(buf2, n);
}
obj1.streamClose();
obj1.free();
@@ -518,6 +522,7 @@ GfxFontLoc *GfxFont::locateFont(XRef *xref, GBool ps) {
PSFontParam16 *psFont16;
Object refObj, embFontObj;
int substIdx, fontNum;
+ double oblique;
GBool embed;
if (type == fontType3) {
@@ -570,7 +575,7 @@ GfxFontLoc *GfxFont::locateFont(XRef *xref, GBool ps) {
}
//----- PS passthrough
- if (ps && !isCIDFont() && globalParams->getPSFontPassthrough()) {
+ if (ps && name && !isCIDFont() && globalParams->getPSFontPassthrough()) {
fontLoc = new GfxFontLoc();
fontLoc->locType = gfxFontLocResident;
fontLoc->fontType = fontType1;
@@ -578,6 +583,13 @@ GfxFontLoc *GfxFont::locateFont(XRef *xref, GBool ps) {
return fontLoc;
}
+ //----- external font file (fontFile, fontDir)
+ if (name && (path = globalParams->findFontFile(name))) {
+ if ((fontLoc = getExternalFont(path, 0, 0, isCIDFont()))) {
+ return fontLoc;
+ }
+ }
+
//----- PS resident Base-14 font
if (ps && !isCIDFont() && ((Gfx8BitFont *)this)->base14) {
fontLoc = new GfxFontLoc();
@@ -587,28 +599,19 @@ GfxFontLoc *GfxFont::locateFont(XRef *xref, GBool ps) {
return fontLoc;
}
- //----- external font file (fontFile, fontDir)
- if ((path = globalParams->findFontFile(name))) {
- if ((fontLoc = getExternalFont(path, isCIDFont()))) {
- return fontLoc;
- }
- }
-
//----- external font file for Base-14 font
if (!ps && !isCIDFont() && ((Gfx8BitFont *)this)->base14) {
base14Name = new GString(((Gfx8BitFont *)this)->base14->base14Name);
- if ((path = globalParams->findFontFile(base14Name))) {
- if ((fontLoc = getExternalFont(path, gFalse))) {
- delete base14Name;
- return fontLoc;
- }
- }
+ path = globalParams->findBase14FontFile(base14Name, &fontNum, &oblique);
delete base14Name;
+ if (path && (fontLoc = getExternalFont(path, fontNum, oblique, gFalse))) {
+ return fontLoc;
+ }
}
//----- system font
- if ((path = globalParams->findSystemFontFile(name, &sysFontType,
- &fontNum))) {
+ if (name && (path = globalParams->findSystemFontFile(name, &sysFontType,
+ &fontNum))) {
if (isCIDFont()) {
if (sysFontType == sysFontTTF || sysFontType == sysFontTTC) {
fontLoc = new GfxFontLoc();
@@ -624,13 +627,13 @@ GfxFontLoc *GfxFont::locateFont(XRef *xref, GBool ps) {
fontLoc->locType = gfxFontLocExternal;
fontLoc->fontType = fontTrueType;
fontLoc->path = path;
+ fontLoc->fontNum = fontNum;
return fontLoc;
} else if (sysFontType == sysFontPFA || sysFontType == sysFontPFB) {
fontLoc = new GfxFontLoc();
fontLoc->locType = gfxFontLocExternal;
fontLoc->fontType = fontType1;
fontLoc->path = path;
- fontLoc->fontNum = fontNum;
return fontLoc;
}
}
@@ -641,7 +644,7 @@ GfxFontLoc *GfxFont::locateFont(XRef *xref, GBool ps) {
//----- 8-bit PS resident font
if (ps) {
- if ((path = globalParams->getPSResidentFont(name))) {
+ if (name && (path = globalParams->getPSResidentFont(name))) {
fontLoc = new GfxFontLoc();
fontLoc->locType = gfxFontLocResident;
fontLoc->fontType = fontType1;
@@ -675,10 +678,10 @@ GfxFontLoc *GfxFont::locateFont(XRef *xref, GBool ps) {
fontLoc->substIdx = substIdx;
return fontLoc;
} else {
- path = globalParams->findFontFile(substName);
+ path = globalParams->findBase14FontFile(substName, &fontNum, &oblique);
delete substName;
if (path) {
- if ((fontLoc = getExternalFont(path, gFalse))) {
+ if ((fontLoc = getExternalFont(path, fontNum, oblique, gFalse))) {
error(errSyntaxWarning, -1, "Substituting font '{0:s}' for '{1:t}'",
base14SubstFonts[substIdx], name);
fontLoc->substIdx = substIdx;
@@ -692,7 +695,7 @@ GfxFontLoc *GfxFont::locateFont(XRef *xref, GBool ps) {
}
//----- 16-bit PS resident font
- if (ps && ((psFont16 = globalParams->getPSResidentFont16(
+ if (ps && name && ((psFont16 = globalParams->getPSResidentFont16(
name,
((GfxCIDFont *)this)->getWMode())))) {
fontLoc = new GfxFontLoc();
@@ -720,7 +723,7 @@ GfxFontLoc *GfxFont::locateFont(XRef *xref, GBool ps) {
//----- CID font substitution
if ((path = globalParams->findCCFontFile(
((GfxCIDFont *)this)->getCollection()))) {
- if ((fontLoc = getExternalFont(path, gTrue))) {
+ if ((fontLoc = getExternalFont(path, 0, 0, gTrue))) {
error(errSyntaxWarning, -1, "Substituting font '{0:t}' for '{1:t}'",
fontLoc->path, name);
return fontLoc;
@@ -733,15 +736,18 @@ GfxFontLoc *GfxFont::locateFont(XRef *xref, GBool ps) {
GfxFontLoc *GfxFont::locateBase14Font(GString *base14Name) {
GString *path;
+ int fontNum;
+ double oblique;
- path = globalParams->findFontFile(base14Name);
+ path = globalParams->findBase14FontFile(base14Name, &fontNum, &oblique);
if (!path) {
return NULL;
}
- return getExternalFont(path, gFalse);
+ return getExternalFont(path, fontNum, oblique, gFalse);
}
-GfxFontLoc *GfxFont::getExternalFont(GString *path, GBool cid) {
+GfxFontLoc *GfxFont::getExternalFont(GString *path, int fontNum,
+ double oblique, GBool cid) {
FoFiIdentifierType fft;
GfxFontType fontType;
GfxFontLoc *fontLoc;
@@ -768,6 +774,9 @@ GfxFontLoc *GfxFont::getExternalFont(GString *path, GBool cid) {
case fofiIdOpenTypeCFFCID:
fontType = fontCIDType0COT;
break;
+ case fofiIdDfont:
+ fontType = cid ? fontCIDType2 : fontTrueType;
+ break;
case fofiIdUnknown:
case fofiIdError:
default:
@@ -784,6 +793,8 @@ GfxFontLoc *GfxFont::getExternalFont(GString *path, GBool cid) {
fontLoc->locType = gfxFontLocExternal;
fontLoc->fontType = fontType;
fontLoc->path = path;
+ fontLoc->fontNum = fontNum;
+ fontLoc->oblique = oblique;
return fontLoc;
}
@@ -791,8 +802,7 @@ char *GfxFont::readEmbFontFile(XRef *xref, int *len) {
char *buf;
Object obj1, obj2;
Stream *str;
- int c;
- int size, i;
+ int size, n;
obj1.initRef(embFontID.num, embFontID.gen);
obj1.fetch(xref, &obj2);
@@ -805,21 +815,19 @@ char *GfxFont::readEmbFontFile(XRef *xref, int *len) {
}
str = obj2.getStream();
+ size = 0;
buf = NULL;
- i = size = 0;
str->reset();
- while ((c = str->getChar()) != EOF) {
- if (i == size) {
- if (size > INT_MAX - 4096) {
- error(errSyntaxError, -1, "Embedded font file is too large");
- break;
- }
- size += 4096;
- buf = (char *)grealloc(buf, size);
- }
- buf[i++] = c;
- }
- *len = i;
+ do {
+ if (size > INT_MAX - 4096) {
+ error(errSyntaxError, -1, "Embedded font file is too large");
+ break;
+ }
+ buf = (char *)grealloc(buf, size + 4096);
+ n = str->getBlock(buf + size, 4096);
+ size += n;
+ } while (n == 4096);
+ *len = size;
str->close();
obj2.free();
@@ -908,8 +916,8 @@ Gfx8BitFont::Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
fontBBox[2] = 0.001 * builtinFont->bbox[2];
fontBBox[3] = 0.001 * builtinFont->bbox[3];
} else {
- ascent = 0.95;
- descent = -0.35;
+ ascent = 0.75;
+ descent = -0.25;
fontBBox[0] = fontBBox[1] = fontBBox[2] = fontBBox[3] = 0;
}
@@ -1491,6 +1499,15 @@ Object *Gfx8BitFont::getCharProc(int code, Object *proc) {
return proc;
}
+Object *Gfx8BitFont::getCharProcNF(int code, Object *proc) {
+ if (enc[code] && charProcs.isDict()) {
+ charProcs.dictLookupNF(enc[code], proc);
+ } else {
+ proc->initNull();
+ }
+ return proc;
+}
+
Dict *Gfx8BitFont::getResources() {
return resources.isDict() ? resources.getDict() : (Dict *)NULL;
}
@@ -1565,7 +1582,6 @@ GfxCIDFont::GfxCIDFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
error(errSyntaxError, -1,
"Missing or empty DescendantFonts entry in Type 0 font");
obj1.free();
-
goto err1;
}
if (!obj1.arrayGet(0, &desFontDictObj)->isDict()) {
@@ -1661,6 +1677,7 @@ GfxCIDFont::GfxCIDFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
}
cidToGID[cidToGIDLen++] = (c1 << 8) + c2;
}
+ obj1.streamClose();
} else if (!obj1.isName("Identity") && !obj1.isNull()) {
error(errSyntaxError, -1, "Invalid CIDToGIDMap entry in CID font");
}
@@ -1836,7 +1853,8 @@ GfxCIDFont::GfxCIDFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
err2:
obj1.free();
desFontDictObj.free();
- err1:;
+ err1:
+ error(errSyntaxError, -1, "Failed to parse font object for '{0:t}'", name);
}
GfxCIDFont::~GfxCIDFont() {
@@ -2016,3 +2034,16 @@ GfxFont *GfxFontDict::lookup(char *tag) {
}
return NULL;
}
+
+GfxFont *GfxFontDict::lookupByRef(Ref ref) {
+ int i;
+
+ for (i = 0; i < numFonts; ++i) {
+ if (fonts[i] &&
+ fonts[i]->getID()->num == ref.num &&
+ fonts[i]->getID()->gen == ref.gen) {
+ return fonts[i];
+ }
+ }
+ return NULL;
+}
diff --git a/xpdf/GfxFont.h b/xpdf/GfxFont.h
index db45ef0..1ec46ec 100644
--- a/xpdf/GfxFont.h
+++ b/xpdf/GfxFont.h
@@ -100,8 +100,11 @@ public:
// (if locType == gfxFontLocExternal)
// PS font name
// (if locType == gfxFontLocResident)
- int fontNum; // for TrueType collections
+ int fontNum; // for TrueType collections and Mac dfonts
// (if locType == gfxFontLocExternal)
+ double oblique; // sheer factor to oblique this font
+ // (used when substituting a plain
+ // font for an oblique font)
GString *encoding; // PS font encoding, only for 16-bit fonts
// (if locType == gfxFontLocResident)
int wMode; // writing mode, only for 16-bit fonts
@@ -207,7 +210,8 @@ protected:
void readFontDescriptor(XRef *xref, Dict *fontDict);
CharCodeToUnicode *readToUnicodeCMap(Dict *fontDict, int nBits,
CharCodeToUnicode *ctu);
- static GfxFontLoc *getExternalFont(GString *path, GBool cid);
+ static GfxFontLoc *getExternalFont(GString *path, int fontNum,
+ double oblique, GBool cid);
GString *tag; // PDF font tag
Ref id; // reference (used as unique ID)
@@ -267,6 +271,7 @@ public:
// Return the Type 3 CharProc for the character associated with <code>.
Object *getCharProc(int code, Object *proc);
+ Object *getCharProcNF(int code, Object *proc);
// Return the Type 3 Resources dictionary, or NULL if none.
Dict *getResources();
@@ -347,6 +352,7 @@ public:
// Get the specified font.
GfxFont *lookup(char *tag);
+ GfxFont *lookupByRef(Ref ref);
// Iterative access.
int getNumFonts() { return numFonts; }
diff --git a/xpdf/GfxState.cc b/xpdf/GfxState.cc
index cf5e7c2..bdaa5e6 100644
--- a/xpdf/GfxState.cc
+++ b/xpdf/GfxState.cc
@@ -20,6 +20,7 @@
#include "Object.h"
#include "Array.h"
#include "Page.h"
+#include "XRef.h"
#include "GfxState.h"
//------------------------------------------------------------------------
@@ -28,7 +29,6 @@
// loops in the color space object structure.
#define colorSpaceRecursionLimit 8
-
//------------------------------------------------------------------------
static inline GfxColorComp clip01(GfxColorComp x) {
@@ -101,7 +101,8 @@ GfxColorSpace::GfxColorSpace() {
GfxColorSpace::~GfxColorSpace() {
}
-GfxColorSpace *GfxColorSpace::parse(Object *csObj, int recursion) {
+GfxColorSpace *GfxColorSpace::parse(Object *csObj,
+ int recursion) {
GfxColorSpace *cs;
Object obj1;
@@ -112,11 +113,11 @@ GfxColorSpace *GfxColorSpace::parse(Object *csObj, int recursion) {
cs = NULL;
if (csObj->isName()) {
if (csObj->isName("DeviceGray") || csObj->isName("G")) {
- cs = new GfxDeviceGrayColorSpace();
+ cs = GfxColorSpace::create(csDeviceGray);
} else if (csObj->isName("DeviceRGB") || csObj->isName("RGB")) {
- cs = new GfxDeviceRGBColorSpace();
+ cs = GfxColorSpace::create(csDeviceRGB);
} else if (csObj->isName("DeviceCMYK") || csObj->isName("CMYK")) {
- cs = new GfxDeviceCMYKColorSpace();
+ cs = GfxColorSpace::create(csDeviceCMYK);
} else if (csObj->isName("Pattern")) {
cs = new GfxPatternColorSpace(NULL);
} else {
@@ -125,11 +126,11 @@ GfxColorSpace *GfxColorSpace::parse(Object *csObj, int recursion) {
} else if (csObj->isArray() && csObj->arrayGetLength() > 0) {
csObj->arrayGet(0, &obj1);
if (obj1.isName("DeviceGray") || obj1.isName("G")) {
- cs = new GfxDeviceGrayColorSpace();
+ cs = GfxColorSpace::create(csDeviceGray);
} else if (obj1.isName("DeviceRGB") || obj1.isName("RGB")) {
- cs = new GfxDeviceRGBColorSpace();
+ cs = GfxColorSpace::create(csDeviceRGB);
} else if (obj1.isName("DeviceCMYK") || obj1.isName("CMYK")) {
- cs = new GfxDeviceCMYKColorSpace();
+ cs = GfxColorSpace::create(csDeviceCMYK);
} else if (obj1.isName("CalGray")) {
cs = GfxCalGrayColorSpace::parse(csObj->getArray(), recursion);
} else if (obj1.isName("CalRGB")) {
@@ -137,15 +138,20 @@ GfxColorSpace *GfxColorSpace::parse(Object *csObj, int recursion) {
} else if (obj1.isName("Lab")) {
cs = GfxLabColorSpace::parse(csObj->getArray(), recursion);
} else if (obj1.isName("ICCBased")) {
- cs = GfxICCBasedColorSpace::parse(csObj->getArray(), recursion);
+ cs = GfxICCBasedColorSpace::parse(csObj->getArray(),
+ recursion);
} else if (obj1.isName("Indexed") || obj1.isName("I")) {
- cs = GfxIndexedColorSpace::parse(csObj->getArray(), recursion);
+ cs = GfxIndexedColorSpace::parse(csObj->getArray(),
+ recursion);
} else if (obj1.isName("Separation")) {
- cs = GfxSeparationColorSpace::parse(csObj->getArray(), recursion);
+ cs = GfxSeparationColorSpace::parse(csObj->getArray(),
+ recursion);
} else if (obj1.isName("DeviceN")) {
- cs = GfxDeviceNColorSpace::parse(csObj->getArray(), recursion);
+ cs = GfxDeviceNColorSpace::parse(csObj->getArray(),
+ recursion);
} else if (obj1.isName("Pattern")) {
- cs = GfxPatternColorSpace::parse(csObj->getArray(), recursion);
+ cs = GfxPatternColorSpace::parse(csObj->getArray(),
+ recursion);
} else {
error(errSyntaxError, -1, "Bad color space");
}
@@ -156,6 +162,20 @@ GfxColorSpace *GfxColorSpace::parse(Object *csObj, int recursion) {
return cs;
}
+GfxColorSpace *GfxColorSpace::create(GfxColorSpaceMode mode) {
+ GfxColorSpace *cs;
+
+ cs = NULL;
+ if (mode == csDeviceGray) {
+ cs = new GfxDeviceGrayColorSpace();
+ } else if (mode == csDeviceRGB) {
+ cs = new GfxDeviceRGBColorSpace();
+ } else if (mode == csDeviceCMYK) {
+ cs = new GfxDeviceCMYKColorSpace();
+ }
+ return cs;
+}
+
void GfxColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange,
int maxImgPixel) {
int i;
@@ -185,9 +205,13 @@ GfxDeviceGrayColorSpace::~GfxDeviceGrayColorSpace() {
}
GfxColorSpace *GfxDeviceGrayColorSpace::copy() {
- return new GfxDeviceGrayColorSpace();
+ GfxDeviceGrayColorSpace *cs;
+
+ cs = new GfxDeviceGrayColorSpace();
+ return cs;
}
+
void GfxDeviceGrayColorSpace::getGray(GfxColor *color, GfxGray *gray) {
*gray = clip01(color->c[0]);
}
@@ -233,6 +257,7 @@ GfxColorSpace *GfxCalGrayColorSpace::copy() {
return cs;
}
+
GfxColorSpace *GfxCalGrayColorSpace::parse(Array *arr, int recursion) {
GfxCalGrayColorSpace *cs;
Object obj1, obj2, obj3;
@@ -311,9 +336,13 @@ GfxDeviceRGBColorSpace::~GfxDeviceRGBColorSpace() {
}
GfxColorSpace *GfxDeviceRGBColorSpace::copy() {
- return new GfxDeviceRGBColorSpace();
+ GfxDeviceRGBColorSpace *cs;
+
+ cs = new GfxDeviceRGBColorSpace();
+ return cs;
}
+
void GfxDeviceRGBColorSpace::getGray(GfxColor *color, GfxGray *gray) {
*gray = clip01((GfxColorComp)(0.3 * color->c[0] +
0.59 * color->c[1] +
@@ -456,6 +485,7 @@ GfxColorSpace *GfxCalRGBColorSpace::parse(Array *arr, int recursion) {
return cs;
}
+
void GfxCalRGBColorSpace::getGray(GfxColor *color, GfxGray *gray) {
*gray = clip01((GfxColorComp)(0.299 * color->c[0] +
0.587 * color->c[1] +
@@ -505,9 +535,13 @@ GfxDeviceCMYKColorSpace::~GfxDeviceCMYKColorSpace() {
}
GfxColorSpace *GfxDeviceCMYKColorSpace::copy() {
- return new GfxDeviceCMYKColorSpace();
+ GfxDeviceCMYKColorSpace *cs;
+
+ cs = new GfxDeviceCMYKColorSpace();
+ return cs;
}
+
void GfxDeviceCMYKColorSpace::getGray(GfxColor *color, GfxGray *gray) {
*gray = clip01((GfxColorComp)(gfxColorComp1 - color->c[3]
- 0.3 * color->c[0]
@@ -706,6 +740,7 @@ GfxColorSpace *GfxLabColorSpace::parse(Array *arr, int recursion) {
return cs;
}
+
void GfxLabColorSpace::getGray(GfxColor *color, GfxGray *gray) {
GfxRGB rgb;
@@ -720,6 +755,7 @@ void GfxLabColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
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;
@@ -756,6 +792,7 @@ 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);
@@ -831,7 +868,8 @@ GfxColorSpace *GfxICCBasedColorSpace::copy() {
return cs;
}
-GfxColorSpace *GfxICCBasedColorSpace::parse(Array *arr, int recursion) {
+GfxColorSpace *GfxICCBasedColorSpace::parse(Array *arr,
+ int recursion) {
GfxICCBasedColorSpace *cs;
Ref iccProfileStreamA;
int nCompsA;
@@ -874,16 +912,17 @@ GfxColorSpace *GfxICCBasedColorSpace::parse(Array *arr, int recursion) {
nCompsA = 4;
}
if (dict->lookup("Alternate", &obj2)->isNull() ||
- !(altA = GfxColorSpace::parse(&obj2, recursion + 1))) {
+ !(altA = GfxColorSpace::parse(&obj2,
+ recursion + 1))) {
switch (nCompsA) {
case 1:
- altA = new GfxDeviceGrayColorSpace();
+ altA = GfxColorSpace::create(csDeviceGray);
break;
case 3:
- altA = new GfxDeviceRGBColorSpace();
+ altA = GfxColorSpace::create(csDeviceRGB);
break;
case 4:
- altA = new GfxDeviceCMYKColorSpace();
+ altA = GfxColorSpace::create(csDeviceCMYK);
break;
default:
error(errSyntaxError, -1, "Bad ICCBased color space - invalid N");
@@ -910,6 +949,7 @@ GfxColorSpace *GfxICCBasedColorSpace::parse(Array *arr, int recursion) {
return cs;
}
+
void GfxICCBasedColorSpace::getGray(GfxColor *color, GfxGray *gray) {
alt->getGray(color, gray);
}
@@ -981,7 +1021,8 @@ GfxColorSpace *GfxIndexedColorSpace::copy() {
return cs;
}
-GfxColorSpace *GfxIndexedColorSpace::parse(Array *arr, int recursion) {
+GfxColorSpace *GfxIndexedColorSpace::parse(Array *arr,
+ int recursion) {
GfxIndexedColorSpace *cs;
GfxColorSpace *baseA;
int indexHighA;
@@ -995,7 +1036,8 @@ GfxColorSpace *GfxIndexedColorSpace::parse(Array *arr, int recursion) {
goto err1;
}
arr->get(1, &obj1);
- if (!(baseA = GfxColorSpace::parse(&obj1, recursion + 1))) {
+ if (!(baseA = GfxColorSpace::parse(&obj1,
+ recursion + 1))) {
error(errSyntaxError, -1, "Bad Indexed color space (base color space)");
goto err2;
}
@@ -1060,6 +1102,7 @@ GfxColorSpace *GfxIndexedColorSpace::parse(Array *arr, int recursion) {
return NULL;
}
+
GfxColor *GfxIndexedColorSpace::mapColorToBase(GfxColor *color,
GfxColor *baseColor) {
Guchar *p;
@@ -1146,12 +1189,16 @@ GfxSeparationColorSpace::~GfxSeparationColorSpace() {
}
GfxColorSpace *GfxSeparationColorSpace::copy() {
- return new GfxSeparationColorSpace(name->copy(), alt->copy(), func->copy(),
- nonMarking, overprintMask);
+ GfxSeparationColorSpace *cs;
+
+ cs = new GfxSeparationColorSpace(name->copy(), alt->copy(), func->copy(),
+ nonMarking, overprintMask);
+ return cs;
}
//~ handle the 'All' and 'None' colorants
-GfxColorSpace *GfxSeparationColorSpace::parse(Array *arr, int recursion) {
+GfxColorSpace *GfxSeparationColorSpace::parse(Array *arr,
+ int recursion) {
GfxSeparationColorSpace *cs;
GString *nameA;
GfxColorSpace *altA;
@@ -1169,7 +1216,8 @@ GfxColorSpace *GfxSeparationColorSpace::parse(Array *arr, int recursion) {
nameA = new GString(obj1.getName());
obj1.free();
arr->get(2, &obj1);
- if (!(altA = GfxColorSpace::parse(&obj1, recursion + 1))) {
+ if (!(altA = GfxColorSpace::parse(&obj1,
+ recursion + 1))) {
error(errSyntaxError, -1,
"Bad Separation color space (alternate color space)");
goto err3;
@@ -1193,6 +1241,7 @@ GfxColorSpace *GfxSeparationColorSpace::parse(Array *arr, int recursion) {
return NULL;
}
+
void GfxSeparationColorSpace::getGray(GfxColor *color, GfxGray *gray) {
double x;
double c[gfxColorMaxComps];
@@ -1303,12 +1352,16 @@ GfxDeviceNColorSpace::~GfxDeviceNColorSpace() {
}
GfxColorSpace *GfxDeviceNColorSpace::copy() {
- return new GfxDeviceNColorSpace(nComps, names, alt->copy(), func->copy(),
- nonMarking, overprintMask);
+ GfxDeviceNColorSpace *cs;
+
+ cs = new GfxDeviceNColorSpace(nComps, names, alt->copy(), func->copy(),
+ nonMarking, overprintMask);
+ return cs;
}
//~ handle the 'None' colorant
-GfxColorSpace *GfxDeviceNColorSpace::parse(Array *arr, int recursion) {
+GfxColorSpace *GfxDeviceNColorSpace::parse(Array *arr,
+ int recursion) {
GfxDeviceNColorSpace *cs;
int nCompsA;
GString *namesA[gfxColorMaxComps];
@@ -1343,7 +1396,8 @@ GfxColorSpace *GfxDeviceNColorSpace::parse(Array *arr, int recursion) {
}
obj1.free();
arr->get(2, &obj1);
- if (!(altA = GfxColorSpace::parse(&obj1, recursion + 1))) {
+ if (!(altA = GfxColorSpace::parse(&obj1,
+ recursion + 1))) {
error(errSyntaxError, -1,
"Bad DeviceN color space (alternate color space)");
goto err3;
@@ -1369,6 +1423,7 @@ GfxColorSpace *GfxDeviceNColorSpace::parse(Array *arr, int recursion) {
return NULL;
}
+
void GfxDeviceNColorSpace::getGray(GfxColor *color, GfxGray *gray) {
double x[gfxColorMaxComps], c[gfxColorMaxComps];
GfxColor color2;
@@ -1438,11 +1493,15 @@ GfxPatternColorSpace::~GfxPatternColorSpace() {
}
GfxColorSpace *GfxPatternColorSpace::copy() {
- return new GfxPatternColorSpace(under ? under->copy() :
- (GfxColorSpace *)NULL);
+ GfxPatternColorSpace *cs;
+
+ cs = new GfxPatternColorSpace(under ? under->copy() :
+ (GfxColorSpace *)NULL);
+ return cs;
}
-GfxColorSpace *GfxPatternColorSpace::parse(Array *arr, int recursion) {
+GfxColorSpace *GfxPatternColorSpace::parse(Array *arr,
+ int recursion) {
GfxPatternColorSpace *cs;
GfxColorSpace *underA;
Object obj1;
@@ -1454,7 +1513,8 @@ GfxColorSpace *GfxPatternColorSpace::parse(Array *arr, int recursion) {
underA = NULL;
if (arr->getLength() == 2) {
arr->get(1, &obj1);
- if (!(underA = GfxColorSpace::parse(&obj1, recursion + 1))) {
+ if (!(underA = GfxColorSpace::parse(&obj1,
+ recursion + 1))) {
error(errSyntaxError, -1,
"Bad Pattern color space (underlying color space)");
obj1.free();
@@ -1466,6 +1526,7 @@ GfxColorSpace *GfxPatternColorSpace::parse(Array *arr, int recursion) {
return cs;
}
+
void GfxPatternColorSpace::getGray(GfxColor *color, GfxGray *gray) {
*gray = 0;
}
@@ -1495,24 +1556,26 @@ GfxPattern::GfxPattern(int typeA) {
GfxPattern::~GfxPattern() {
}
-GfxPattern *GfxPattern::parse(Object *obj) {
+GfxPattern *GfxPattern::parse(Object *objRef, Object *obj
+ ) {
GfxPattern *pattern;
- Object obj1;
+ Object typeObj;
if (obj->isDict()) {
- obj->dictLookup("PatternType", &obj1);
+ obj->dictLookup("PatternType", &typeObj);
} else if (obj->isStream()) {
- obj->streamGetDict()->lookup("PatternType", &obj1);
+ obj->streamGetDict()->lookup("PatternType", &typeObj);
} 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);
+ if (typeObj.isInt() && typeObj.getInt() == 1) {
+ pattern = GfxTilingPattern::parse(objRef, obj);
+ } else if (typeObj.isInt() && typeObj.getInt() == 2) {
+ pattern = GfxShadingPattern::parse(obj
+ );
}
- obj1.free();
+ typeObj.free();
return pattern;
}
@@ -1520,7 +1583,7 @@ GfxPattern *GfxPattern::parse(Object *obj) {
// GfxTilingPattern
//------------------------------------------------------------------------
-GfxTilingPattern *GfxTilingPattern::parse(Object *patObj) {
+GfxTilingPattern *GfxTilingPattern::parse(Object *patObjRef, Object *patObj) {
GfxTilingPattern *pat;
Dict *dict;
int paintTypeA, tilingTypeA;
@@ -1597,7 +1660,7 @@ GfxTilingPattern *GfxTilingPattern::parse(Object *patObj) {
obj1.free();
pat = new GfxTilingPattern(paintTypeA, tilingTypeA, bboxA, xStepA, yStepA,
- &resDictA, matrixA, patObj);
+ &resDictA, matrixA, patObjRef);
resDictA.free();
return pat;
}
@@ -1605,7 +1668,7 @@ GfxTilingPattern *GfxTilingPattern::parse(Object *patObj) {
GfxTilingPattern::GfxTilingPattern(int paintTypeA, int tilingTypeA,
double *bboxA, double xStepA, double yStepA,
Object *resDictA, double *matrixA,
- Object *contentStreamA):
+ Object *contentStreamRefA):
GfxPattern(1)
{
int i;
@@ -1621,24 +1684,25 @@ GfxTilingPattern::GfxTilingPattern(int paintTypeA, int tilingTypeA,
for (i = 0; i < 6; ++i) {
matrix[i] = matrixA[i];
}
- contentStreamA->copy(&contentStream);
+ contentStreamRefA->copy(&contentStreamRef);
}
GfxTilingPattern::~GfxTilingPattern() {
resDict.free();
- contentStream.free();
+ contentStreamRef.free();
}
GfxPattern *GfxTilingPattern::copy() {
return new GfxTilingPattern(paintType, tilingType, bbox, xStep, yStep,
- &resDict, matrix, &contentStream);
+ &resDict, matrix, &contentStreamRef);
}
//------------------------------------------------------------------------
// GfxShadingPattern
//------------------------------------------------------------------------
-GfxShadingPattern *GfxShadingPattern::parse(Object *patObj) {
+GfxShadingPattern *GfxShadingPattern::parse(Object *patObj
+ ) {
Dict *dict;
GfxShading *shadingA;
double matrixA[6];
@@ -1651,7 +1715,8 @@ GfxShadingPattern *GfxShadingPattern::parse(Object *patObj) {
dict = patObj->getDict();
dict->lookup("Shading", &obj1);
- shadingA = GfxShading::parse(&obj1);
+ shadingA = GfxShading::parse(&obj1
+ );
obj1.free();
if (!shadingA) {
return NULL;
@@ -1724,7 +1789,8 @@ GfxShading::~GfxShading() {
}
}
-GfxShading *GfxShading::parse(Object *obj) {
+GfxShading *GfxShading::parse(Object *obj
+ ) {
GfxShading *shading;
Dict *dict;
int typeA;
@@ -1748,17 +1814,21 @@ GfxShading *GfxShading::parse(Object *obj) {
switch (typeA) {
case 1:
- shading = GfxFunctionShading::parse(dict);
+ shading = GfxFunctionShading::parse(dict
+ );
break;
case 2:
- shading = GfxAxialShading::parse(dict);
+ shading = GfxAxialShading::parse(dict
+ );
break;
case 3:
- shading = GfxRadialShading::parse(dict);
+ shading = GfxRadialShading::parse(dict
+ );
break;
case 4:
if (obj->isStream()) {
- shading = GfxGouraudTriangleShading::parse(4, dict, obj->getStream());
+ shading = GfxGouraudTriangleShading::parse(4, dict, obj->getStream()
+ );
} else {
error(errSyntaxError, -1, "Invalid Type 4 shading object");
goto err1;
@@ -1766,7 +1836,8 @@ GfxShading *GfxShading::parse(Object *obj) {
break;
case 5:
if (obj->isStream()) {
- shading = GfxGouraudTriangleShading::parse(5, dict, obj->getStream());
+ shading = GfxGouraudTriangleShading::parse(5, dict, obj->getStream()
+ );
} else {
error(errSyntaxError, -1, "Invalid Type 5 shading object");
goto err1;
@@ -1774,7 +1845,8 @@ GfxShading *GfxShading::parse(Object *obj) {
break;
case 6:
if (obj->isStream()) {
- shading = GfxPatchMeshShading::parse(6, dict, obj->getStream());
+ shading = GfxPatchMeshShading::parse(6, dict, obj->getStream()
+ );
} else {
error(errSyntaxError, -1, "Invalid Type 6 shading object");
goto err1;
@@ -1782,7 +1854,8 @@ GfxShading *GfxShading::parse(Object *obj) {
break;
case 7:
if (obj->isStream()) {
- shading = GfxPatchMeshShading::parse(7, dict, obj->getStream());
+ shading = GfxPatchMeshShading::parse(7, dict, obj->getStream()
+ );
} else {
error(errSyntaxError, -1, "Invalid Type 7 shading object");
goto err1;
@@ -1799,12 +1872,14 @@ GfxShading *GfxShading::parse(Object *obj) {
return NULL;
}
-GBool GfxShading::init(Dict *dict) {
+GBool GfxShading::init(Dict *dict
+ ) {
Object obj1, obj2;
int i;
dict->lookup("ColorSpace", &obj1);
- if (!(colorSpace = GfxColorSpace::parse(&obj1))) {
+ if (!(colorSpace = GfxColorSpace::parse(&obj1
+ ))) {
error(errSyntaxError, -1, "Bad color space in shading dictionary");
obj1.free();
return gFalse;
@@ -1901,7 +1976,8 @@ GfxFunctionShading::~GfxFunctionShading() {
}
}
-GfxFunctionShading *GfxFunctionShading::parse(Dict *dict) {
+GfxFunctionShading *GfxFunctionShading::parse(Dict *dict
+ ) {
GfxFunctionShading *shading;
double x0A, y0A, x1A, y1A;
double matrixA[6];
@@ -1916,9 +1992,9 @@ GfxFunctionShading *GfxFunctionShading::parse(Dict *dict) {
obj1.arrayGetLength() == 4) {
x0A = obj1.arrayGet(0, &obj2)->getNum();
obj2.free();
- y0A = obj1.arrayGet(1, &obj2)->getNum();
+ x1A = obj1.arrayGet(1, &obj2)->getNum();
obj2.free();
- x1A = obj1.arrayGet(2, &obj2)->getNum();
+ y0A = obj1.arrayGet(2, &obj2)->getNum();
obj2.free();
y1A = obj1.arrayGet(3, &obj2)->getNum();
obj2.free();
@@ -1970,7 +2046,8 @@ GfxFunctionShading *GfxFunctionShading::parse(Dict *dict) {
shading = new GfxFunctionShading(x0A, y0A, x1A, y1A, matrixA,
funcsA, nFuncsA);
- if (!shading->init(dict)) {
+ if (!shading->init(dict
+ )) {
delete shading;
return NULL;
}
@@ -2060,7 +2137,8 @@ GfxAxialShading::~GfxAxialShading() {
}
}
-GfxAxialShading *GfxAxialShading::parse(Dict *dict) {
+GfxAxialShading *GfxAxialShading::parse(Dict *dict
+ ) {
GfxAxialShading *shading;
double x0A, y0A, x1A, y1A;
double t0A, t1A;
@@ -2137,7 +2215,8 @@ GfxAxialShading *GfxAxialShading::parse(Dict *dict) {
shading = new GfxAxialShading(x0A, y0A, x1A, y1A, t0A, t1A,
funcsA, nFuncsA, extend0A, extend1A);
- if (!shading->init(dict)) {
+ if (!shading->init(dict
+ )) {
delete shading;
return NULL;
}
@@ -2226,7 +2305,8 @@ GfxRadialShading::~GfxRadialShading() {
}
}
-GfxRadialShading *GfxRadialShading::parse(Dict *dict) {
+GfxRadialShading *GfxRadialShading::parse(Dict *dict
+ ) {
GfxRadialShading *shading;
double x0A, y0A, r0A, x1A, y1A, r1A;
double t0A, t1A;
@@ -2307,7 +2387,8 @@ GfxRadialShading *GfxRadialShading::parse(Dict *dict) {
shading = new GfxRadialShading(x0A, y0A, r0A, x1A, y1A, r1A, t0A, t1A,
funcsA, nFuncsA, extend0A, extend1A);
- if (!shading->init(dict)) {
+ if (!shading->init(dict
+ )) {
delete shading;
return NULL;
}
@@ -2413,7 +2494,7 @@ GfxGouraudTriangleShading::GfxGouraudTriangleShading(
int typeA,
GfxGouraudVertex *verticesA, int nVerticesA,
int (*trianglesA)[3], int nTrianglesA,
- Function **funcsA, int nFuncsA):
+ int nCompsA, Function **funcsA, int nFuncsA):
GfxShading(typeA)
{
int i;
@@ -2422,6 +2503,7 @@ GfxGouraudTriangleShading::GfxGouraudTriangleShading(
nVertices = nVerticesA;
triangles = trianglesA;
nTriangles = nTrianglesA;
+ nComps = nCompsA;
nFuncs = nFuncsA;
for (i = 0; i < nFuncs; ++i) {
funcs[i] = funcsA[i];
@@ -2440,6 +2522,7 @@ GfxGouraudTriangleShading::GfxGouraudTriangleShading(
nTriangles = shading->nTriangles;
triangles = (int (*)[3])gmallocn(nTriangles * 3, sizeof(int));
memcpy(triangles, shading->triangles, nTriangles * 3 * sizeof(int));
+ nComps = shading->nComps;
nFuncs = shading->nFuncs;
for (i = 0; i < nFuncs; ++i) {
funcs[i] = shading->funcs[i]->copy();
@@ -2456,9 +2539,9 @@ GfxGouraudTriangleShading::~GfxGouraudTriangleShading() {
}
}
-GfxGouraudTriangleShading *GfxGouraudTriangleShading::parse(int typeA,
- Dict *dict,
- Stream *str) {
+GfxGouraudTriangleShading *GfxGouraudTriangleShading::parse(
+ int typeA, Dict *dict, Stream *str
+ ) {
GfxGouraudTriangleShading *shading;
Function *funcsA[gfxColorMaxComps];
int nFuncsA;
@@ -2469,7 +2552,7 @@ GfxGouraudTriangleShading *GfxGouraudTriangleShading::parse(int typeA,
double cMul[gfxColorMaxComps];
GfxGouraudVertex *verticesA;
int (*trianglesA)[3];
- int nComps, nVerticesA, nTrianglesA, vertSize, triSize;
+ int nCompsA, nVerticesA, nTrianglesA, vertSize, triSize;
Guint x, y, flag;
Guint c[gfxColorMaxComps];
GfxShadingBitBuf *bitBuf;
@@ -2531,7 +2614,7 @@ GfxGouraudTriangleShading *GfxGouraudTriangleShading::parse(int typeA,
obj2.free();
cMul[i] = (cMax[i] - cMin[i]) / (double)((1 << compBits) - 1);
}
- nComps = i;
+ nCompsA = i;
} else {
error(errSyntaxError, -1,
"Missing or invalid Decode array in shading dictionary");
@@ -2585,12 +2668,12 @@ GfxGouraudTriangleShading *GfxGouraudTriangleShading::parse(int typeA,
!bitBuf->getBits(coordBits, &y)) {
break;
}
- for (i = 0; i < nComps; ++i) {
+ for (i = 0; i < nCompsA; ++i) {
if (!bitBuf->getBits(compBits, &c[i])) {
break;
}
}
- if (i < nComps) {
+ if (i < nCompsA) {
break;
}
if (nVerticesA == vertSize) {
@@ -2600,9 +2683,8 @@ GfxGouraudTriangleShading *GfxGouraudTriangleShading::parse(int typeA,
}
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]);
+ for (i = 0; i < nCompsA; ++i) {
+ verticesA[nVerticesA].color[i] = cMin[i] + cMul[i] * (double)c[i];
}
++nVerticesA;
bitBuf->flushBits();
@@ -2657,8 +2739,9 @@ GfxGouraudTriangleShading *GfxGouraudTriangleShading::parse(int typeA,
shading = new GfxGouraudTriangleShading(typeA, verticesA, nVerticesA,
trianglesA, nTrianglesA,
- funcsA, nFuncsA);
- if (!shading->init(dict)) {
+ nCompsA, funcsA, nFuncsA);
+ if (!shading->init(dict
+ )) {
delete shading;
return NULL;
}
@@ -2676,54 +2759,46 @@ GfxShading *GfxGouraudTriangleShading::copy() {
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];
+ double *x0, double *y0, double *color0,
+ double *x1, double *y1, double *color1,
+ double *x2, double *y2, double *color2) {
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;
+ for (j = 0; j < nComps; ++j) {
+ color0[j] = vertices[v].color[j];
}
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;
+ for (j = 0; j < nComps; ++j) {
+ color1[j] = vertices[v].color[j];
}
v = triangles[i][2];
*x2 = vertices[v].x;
*y2 = vertices[v].y;
+ for (j = 0; j < nComps; ++j) {
+ color2[j] = vertices[v].color[j];
+ }
+}
+
+void GfxGouraudTriangleShading::getColor(double *in, GfxColor *out) {
+ double c[gfxColorMaxComps];
+ int i;
+
if (nFuncs > 0) {
- in = colToDbl(vertices[v].color.c[0]);
- for (j = 0; j < nFuncs; ++j) {
- funcs[j]->transform(&in, &out[j]);
+ for (i = 0; i < nFuncs; ++i) {
+ funcs[i]->transform(in, &c[i]);
}
- for (j = 0; j < gfxColorMaxComps; ++j) {
- color2->c[j] = dblToCol(out[j]);
+ for (i = 0; i < colorSpace->getNComps(); ++i) {
+ out->c[i] = dblToCol(c[i]);
}
} else {
- *color2 = vertices[v].color;
+ for (i = 0; i < nComps; ++i) {
+ out->c[i] = dblToCol(in[i]);
+ }
}
}
@@ -2733,6 +2808,7 @@ void GfxGouraudTriangleShading::getTriangle(
GfxPatchMeshShading::GfxPatchMeshShading(int typeA,
GfxPatch *patchesA, int nPatchesA,
+ int nCompsA,
Function **funcsA, int nFuncsA):
GfxShading(typeA)
{
@@ -2740,6 +2816,7 @@ GfxPatchMeshShading::GfxPatchMeshShading(int typeA,
patches = patchesA;
nPatches = nPatchesA;
+ nComps = nCompsA;
nFuncs = nFuncsA;
for (i = 0; i < nFuncs; ++i) {
funcs[i] = funcsA[i];
@@ -2754,6 +2831,7 @@ GfxPatchMeshShading::GfxPatchMeshShading(GfxPatchMeshShading *shading):
nPatches = shading->nPatches;
patches = (GfxPatch *)gmallocn(nPatches, sizeof(GfxPatch));
memcpy(patches, shading->patches, nPatches * sizeof(GfxPatch));
+ nComps = shading->nComps;
nFuncs = shading->nFuncs;
for (i = 0; i < nFuncs; ++i) {
funcs[i] = shading->funcs[i]->copy();
@@ -2770,7 +2848,8 @@ GfxPatchMeshShading::~GfxPatchMeshShading() {
}
GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
- Stream *str) {
+ Stream *str
+ ) {
GfxPatchMeshShading *shading;
Function *funcsA[gfxColorMaxComps];
int nFuncsA;
@@ -2780,11 +2859,11 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
double xMul, yMul;
double cMul[gfxColorMaxComps];
GfxPatch *patchesA, *p;
- int nComps, nPatchesA, patchesSize, nPts, nColors;
+ int nCompsA, nPatchesA, patchesSize, nPts, nColors;
Guint flag;
double x[16], y[16];
Guint xi, yi;
- GfxColorComp c[4][gfxColorMaxComps];
+ double c[4][gfxColorMaxComps];
Guint ci;
GfxShadingBitBuf *bitBuf;
Object obj1, obj2;
@@ -2833,7 +2912,7 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
obj2.free();
cMul[i] = (cMax[i] - cMin[i]) / (double)((1 << compBits) - 1);
}
- nComps = i;
+ nCompsA = i;
} else {
error(errSyntaxError, -1,
"Missing or invalid Decode array in shading dictionary");
@@ -2907,13 +2986,13 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
break;
}
for (i = 0; i < nColors; ++i) {
- for (j = 0; j < nComps; ++j) {
+ for (j = 0; j < nCompsA; ++j) {
if (!bitBuf->getBits(compBits, &ci)) {
break;
}
- c[i][j] = dblToCol(cMin[j] + cMul[j] * (double)ci);
+ c[i][j] = cMin[j] + cMul[j] * (double)ci;
}
- if (j < nComps) {
+ if (j < nCompsA) {
break;
}
}
@@ -2953,11 +3032,11 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
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];
+ for (j = 0; j < nCompsA; ++j) {
+ p->color[0][0][j] = c[0][j];
+ p->color[0][1][j] = c[1][j];
+ p->color[1][1][j] = c[2][j];
+ p->color[1][0][j] = c[3][j];
}
break;
case 1:
@@ -2985,11 +3064,11 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
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];
+ for (j = 0; j < nCompsA; ++j) {
+ p->color[0][0][j] = patchesA[nPatchesA-1].color[0][1][j];
+ p->color[0][1][j] = patchesA[nPatchesA-1].color[1][1][j];
+ p->color[1][1][j] = c[0][j];
+ p->color[1][0][j] = c[1][j];
}
break;
case 2:
@@ -3017,11 +3096,11 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
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];
+ for (j = 0; j < nCompsA; ++j) {
+ p->color[0][0][j] = patchesA[nPatchesA-1].color[1][1][j];
+ p->color[0][1][j] = patchesA[nPatchesA-1].color[1][0][j];
+ p->color[1][1][j] = c[0][j];
+ p->color[1][0][j] = c[1][j];
}
break;
case 3:
@@ -3049,11 +3128,11 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
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];
+ for (j = 0; j < nCompsA; ++j) {
+ p->color[0][1][j] = patchesA[nPatchesA-1].color[1][0][j];
+ p->color[0][1][j] = patchesA[nPatchesA-1].color[0][0][j];
+ p->color[1][1][j] = c[0][j];
+ p->color[1][0][j] = c[1][j];
}
break;
}
@@ -3092,11 +3171,11 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
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];
+ for (j = 0; j < nCompsA; ++j) {
+ p->color[0][0][j] = c[0][j];
+ p->color[0][1][j] = c[1][j];
+ p->color[1][1][j] = c[2][j];
+ p->color[1][0][j] = c[3][j];
}
break;
case 1:
@@ -3132,11 +3211,11 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
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];
+ for (j = 0; j < nCompsA; ++j) {
+ p->color[0][0][j] = patchesA[nPatchesA-1].color[0][1][j];
+ p->color[0][1][j] = patchesA[nPatchesA-1].color[1][1][j];
+ p->color[1][1][j] = c[0][j];
+ p->color[1][0][j] = c[1][j];
}
break;
case 2:
@@ -3172,11 +3251,11 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
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];
+ for (j = 0; j < nCompsA; ++j) {
+ p->color[0][0][j] = patchesA[nPatchesA-1].color[1][1][j];
+ p->color[0][1][j] = patchesA[nPatchesA-1].color[1][0][j];
+ p->color[1][1][j] = c[0][j];
+ p->color[1][0][j] = c[1][j];
}
break;
case 3:
@@ -3212,11 +3291,11 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
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];
+ for (j = 0; j < nCompsA; ++j) {
+ p->color[0][0][j] = patchesA[nPatchesA-1].color[1][0][j];
+ p->color[0][1][j] = patchesA[nPatchesA-1].color[0][0][j];
+ p->color[1][1][j] = c[0][j];
+ p->color[1][0][j] = c[1][j];
}
break;
}
@@ -3273,8 +3352,9 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
}
shading = new GfxPatchMeshShading(typeA, patchesA, nPatchesA,
- funcsA, nFuncsA);
- if (!shading->init(dict)) {
+ nCompsA, funcsA, nFuncsA);
+ if (!shading->init(dict
+ )) {
delete shading;
return NULL;
}
@@ -3290,6 +3370,24 @@ GfxShading *GfxPatchMeshShading::copy() {
return new GfxPatchMeshShading(this);
}
+void GfxPatchMeshShading::getColor(double *in, GfxColor *out) {
+ double c[gfxColorMaxComps];
+ int i;
+
+ if (nFuncs > 0) {
+ for (i = 0; i < nFuncs; ++i) {
+ funcs[i]->transform(in, &c[i]);
+ }
+ for (i = 0; i < colorSpace->getNComps(); ++i) {
+ out->c[i] = dblToCol(c[i]);
+ }
+ } else {
+ for (i = 0; i < nComps; ++i) {
+ out->c[i] = dblToCol(in[i]);
+ }
+ }
+}
+
//------------------------------------------------------------------------
// GfxImageColorMap
//------------------------------------------------------------------------
@@ -3310,7 +3408,11 @@ GfxImageColorMap::GfxImageColorMap(int bitsA, Object *decode,
// bits per component and color space
bits = bitsA;
- maxPixel = (1 << bits) - 1;
+ if (bits <= 8) {
+ maxPixel = (1 << bits) - 1;
+ } else {
+ maxPixel = 0xff;
+ }
colorSpace = colorSpaceA;
// initialize
@@ -3431,7 +3533,11 @@ GfxImageColorMap::GfxImageColorMap(GfxImageColorMap *colorMap) {
lookup[k] = NULL;
lookup2[k] = NULL;
}
- n = 1 << bits;
+ if (bits <= 8) {
+ n = 1 << bits;
+ } else {
+ n = 256;
+ }
for (k = 0; k < nComps; ++k) {
lookup[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp));
memcpy(lookup[k], colorMap->lookup[k], n * sizeof(GfxColorComp));
@@ -3521,7 +3627,11 @@ void GfxImageColorMap::getCMYK(Guchar *x, GfxCMYK *cmyk) {
void GfxImageColorMap::getColor(Guchar *x, GfxColor *color) {
int maxPixel, i;
- maxPixel = (1 << bits) - 1;
+ if (bits <= 8) {
+ maxPixel = (1 << bits) - 1;
+ } else {
+ maxPixel = 0xff;
+ }
for (i = 0; i < nComps; ++i) {
color->c[i] = dblToCol(decodeLow[i] + (x[i] * decodeRange[i]) / maxPixel);
}
@@ -3811,7 +3921,8 @@ void GfxPath::offset(double dx, double dy) {
//------------------------------------------------------------------------
GfxState::GfxState(double hDPIA, double vDPIA, PDFRectangle *pageBox,
- int rotateA, GBool upsideDown) {
+ int rotateA, GBool upsideDown
+ ) {
double kx, ky;
hDPI = hDPIA;
@@ -3861,8 +3972,8 @@ GfxState::GfxState(double hDPIA, double vDPIA, PDFRectangle *pageBox,
pageHeight = ky * (py2 - py1);
}
- fillColorSpace = new GfxDeviceGrayColorSpace();
- strokeColorSpace = new GfxDeviceGrayColorSpace();
+ fillColorSpace = GfxColorSpace::create(csDeviceGray);
+ strokeColorSpace = GfxColorSpace::create(csDeviceGray);
fillColor.c[0] = 0;
strokeColor.c[0] = 0;
fillPattern = NULL;
diff --git a/xpdf/GfxState.h b/xpdf/GfxState.h
index 5d57de9..74ab5eb 100644
--- a/xpdf/GfxState.h
+++ b/xpdf/GfxState.h
@@ -140,7 +140,13 @@ public:
virtual GfxColorSpaceMode getMode() = 0;
// Construct a color space. Returns NULL if unsuccessful.
- static GfxColorSpace *parse(Object *csObj, int recursion = 0);
+ static GfxColorSpace *parse(Object *csObj,
+ int recursion = 0);
+
+ // Construct a simple color space. The <mode> argument can be
+ // csDeviceGray, csDeviceRGB, or csDeviceCMYK.
+ static GfxColorSpace *create(GfxColorSpaceMode mode);
+
// Convert to gray, RGB, or CMYK.
virtual void getGray(GfxColor *color, GfxGray *gray) = 0;
@@ -381,7 +387,8 @@ public:
virtual GfxColorSpaceMode getMode() { return csICCBased; }
// Construct an ICCBased color space. Returns NULL if unsuccessful.
- static GfxColorSpace *parse(Array *arr, int recursion);
+ static GfxColorSpace *parse(Array *arr,
+ int recursion);
virtual void getGray(GfxColor *color, GfxGray *gray);
virtual void getRGB(GfxColor *color, GfxRGB *rgb);
@@ -418,7 +425,8 @@ public:
virtual GfxColorSpaceMode getMode() { return csIndexed; }
// Construct an Indexed color space. Returns NULL if unsuccessful.
- static GfxColorSpace *parse(Array *arr, int recursion);
+ static GfxColorSpace *parse(Array *arr,
+ int recursion);
virtual void getGray(GfxColor *color, GfxGray *gray);
virtual void getRGB(GfxColor *color, GfxRGB *rgb);
@@ -457,7 +465,8 @@ public:
virtual GfxColorSpaceMode getMode() { return csSeparation; }
// Construct a Separation color space. Returns NULL if unsuccessful.
- static GfxColorSpace *parse(Array *arr, int recursion);
+ static GfxColorSpace *parse(Array *arr,
+ int recursion);
virtual void getGray(GfxColor *color, GfxGray *gray);
virtual void getRGB(GfxColor *color, GfxRGB *rgb);
@@ -499,7 +508,8 @@ public:
virtual GfxColorSpaceMode getMode() { return csDeviceN; }
// Construct a DeviceN color space. Returns NULL if unsuccessful.
- static GfxColorSpace *parse(Array *arr, int recursion);
+ static GfxColorSpace *parse(Array *arr,
+ int recursion);
virtual void getGray(GfxColor *color, GfxGray *gray);
virtual void getRGB(GfxColor *color, GfxRGB *rgb);
@@ -542,7 +552,8 @@ public:
virtual GfxColorSpaceMode getMode() { return csPattern; }
// Construct a Pattern color space. Returns NULL if unsuccessful.
- static GfxColorSpace *parse(Array *arr, int recursion);
+ static GfxColorSpace *parse(Array *arr,
+ int recursion);
virtual void getGray(GfxColor *color, GfxGray *gray);
virtual void getRGB(GfxColor *color, GfxRGB *rgb);
@@ -570,7 +581,8 @@ public:
GfxPattern(int typeA);
virtual ~GfxPattern();
- static GfxPattern *parse(Object *obj);
+ static GfxPattern *parse(Object *objRef, Object *obj
+ );
virtual GfxPattern *copy() = 0;
@@ -588,7 +600,7 @@ private:
class GfxTilingPattern: public GfxPattern {
public:
- static GfxTilingPattern *parse(Object *patObj);
+ static GfxTilingPattern *parse(Object *patObjRef, Object *patObj);
virtual ~GfxTilingPattern();
virtual GfxPattern *copy();
@@ -601,7 +613,7 @@ public:
Dict *getResDict()
{ return resDict.isDict() ? resDict.getDict() : (Dict *)NULL; }
double *getMatrix() { return matrix; }
- Object *getContentStream() { return &contentStream; }
+ Object *getContentStreamRef() { return &contentStreamRef; }
private:
@@ -616,7 +628,7 @@ private:
double xStep, yStep;
Object resDict;
double matrix[6];
- Object contentStream;
+ Object contentStreamRef;
};
//------------------------------------------------------------------------
@@ -626,7 +638,8 @@ private:
class GfxShadingPattern: public GfxPattern {
public:
- static GfxShadingPattern *parse(Object *patObj);
+ static GfxShadingPattern *parse(Object *patObj
+ );
virtual ~GfxShadingPattern();
virtual GfxPattern *copy();
@@ -653,7 +666,8 @@ public:
GfxShading(GfxShading *shading);
virtual ~GfxShading();
- static GfxShading *parse(Object *obj);
+ static GfxShading *parse(Object *obj
+ );
virtual GfxShading *copy() = 0;
@@ -667,7 +681,8 @@ public:
protected:
- GBool init(Dict *dict);
+ GBool init(Dict *dict
+ );
int type;
GfxColorSpace *colorSpace;
@@ -691,7 +706,8 @@ public:
GfxFunctionShading(GfxFunctionShading *shading);
virtual ~GfxFunctionShading();
- static GfxFunctionShading *parse(Dict *dict);
+ static GfxFunctionShading *parse(Dict *dict
+ );
virtual GfxShading *copy();
@@ -725,7 +741,8 @@ public:
GfxAxialShading(GfxAxialShading *shading);
virtual ~GfxAxialShading();
- static GfxAxialShading *parse(Dict *dict);
+ static GfxAxialShading *parse(Dict *dict
+ );
virtual GfxShading *copy();
@@ -763,7 +780,8 @@ public:
GfxRadialShading(GfxRadialShading *shading);
virtual ~GfxRadialShading();
- static GfxRadialShading *parse(Dict *dict);
+ static GfxRadialShading *parse(Dict *dict
+ );
virtual GfxShading *copy();
@@ -793,7 +811,7 @@ private:
struct GfxGouraudVertex {
double x, y;
- GfxColor color;
+ double color[gfxColorMaxComps];
};
class GfxGouraudTriangleShading: public GfxShading {
@@ -802,18 +820,21 @@ public:
GfxGouraudTriangleShading(int typeA,
GfxGouraudVertex *verticesA, int nVerticesA,
int (*trianglesA)[3], int nTrianglesA,
- Function **funcsA, int nFuncsA);
+ int nCompsA, Function **funcsA, int nFuncsA);
GfxGouraudTriangleShading(GfxGouraudTriangleShading *shading);
virtual ~GfxGouraudTriangleShading();
- static GfxGouraudTriangleShading *parse(int typeA, Dict *dict, Stream *str);
+ static GfxGouraudTriangleShading *parse(int typeA, Dict *dict, Stream *str
+ );
virtual GfxShading *copy();
+ int getNComps() { return nComps; }
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);
+ void getTriangle(int i, double *x0, double *y0, double *color0,
+ double *x1, double *y1, double *color1,
+ double *x2, double *y2, double *color2);
+ void getColor(double *in, GfxColor *out);
private:
@@ -822,6 +843,7 @@ private:
int (*triangles)[3];
int nTriangles;
Function *funcs[gfxColorMaxComps];
+ int nComps; // number of color components (1 if nFuncs > 0)
int nFuncs;
};
@@ -832,29 +854,33 @@ private:
struct GfxPatch {
double x[4][4];
double y[4][4];
- GfxColor color[2][2];
+ double color[2][2][gfxColorMaxComps];
};
class GfxPatchMeshShading: public GfxShading {
public:
GfxPatchMeshShading(int typeA, GfxPatch *patchesA, int nPatchesA,
- Function **funcsA, int nFuncsA);
+ int nCompsA, Function **funcsA, int nFuncsA);
GfxPatchMeshShading(GfxPatchMeshShading *shading);
virtual ~GfxPatchMeshShading();
- static GfxPatchMeshShading *parse(int typeA, Dict *dict, Stream *str);
+ static GfxPatchMeshShading *parse(int typeA, Dict *dict, Stream *str
+ );
virtual GfxShading *copy();
+ int getNComps() { return nComps; }
int getNPatches() { return nPatches; }
GfxPatch *getPatch(int i) { return &patches[i]; }
+ void getColor(double *in, GfxColor *out);
private:
GfxPatch *patches;
int nPatches;
Function *funcs[gfxColorMaxComps];
+ int nComps; // number of color components (1 if nFuncs > 0)
int nFuncs;
};
@@ -1040,7 +1066,8 @@ public:
// 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);
+ int rotateA, GBool upsideDown
+ );
// Destructor.
~GfxState();
diff --git a/xpdf/GlobalParams.cc b/xpdf/GlobalParams.cc
index 63e932b..ea7e9fd 100644
--- a/xpdf/GlobalParams.cc
+++ b/xpdf/GlobalParams.cc
@@ -12,15 +12,19 @@
#pragma implementation
#endif
+#ifdef _WIN32
+# define _WIN32_WINNT 0x0500 // for GetSystemWindowsDirectory
+# include <windows.h>
+#endif
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#ifdef ENABLE_PLUGINS
-# ifndef WIN32
+# ifndef _WIN32
# include <dlfcn.h>
# endif
#endif
-#ifdef WIN32
+#ifdef _WIN32
# include <shlobj.h>
#endif
#if HAVE_PAPER_H
@@ -31,6 +35,7 @@
#include "GList.h"
#include "GHash.h"
#include "gfile.h"
+#include "FoFiIdentifier.h"
#include "Error.h"
#include "NameToCharCode.h"
#include "CharCodeToUnicode.h"
@@ -43,8 +48,9 @@
#endif
#include "GlobalParams.h"
-#ifdef WIN32
+#ifdef _WIN32
# define strcasecmp stricmp
+# define strncasecmp strnicmp
#endif
#if MULTITHREADED
@@ -68,7 +74,7 @@
#include "UTF8.h"
#ifdef ENABLE_PLUGINS
-# ifdef WIN32
+# ifdef _WIN32
extern XpdfPluginVecTable xpdfPluginVecTable;
# endif
#endif
@@ -84,25 +90,29 @@ static struct {
const char *name;
const char *t1FileName;
const char *ttFileName;
+ const char *macFileName; // may be .dfont, .ttf, or .ttc
+ const char *macFontName; // font name inside .dfont or .ttc
+ const char *obliqueFont; // name of font to oblique
+ double obliqueFactor; // oblique sheer factor
} 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},
+ {"Courier", "n022003l.pfb", "cour.ttf", "Courier", "Courier", NULL, 0},
+ {"Courier-Bold", "n022004l.pfb", "courbd.ttf", "Courier", "Courier Bold", NULL, 0},
+ {"Courier-BoldOblique", "n022024l.pfb", "courbi.ttf", "Courier", "Courier Bold Oblique", "Courier-Bold", 0.212557},
+ {"Courier-Oblique", "n022023l.pfb", "couri.ttf", "Courier", "Courier Oblique", "Courier", 0.212557},
+ {"Helvetica", "n019003l.pfb", "arial.ttf", "Helvetica", "Helvetica", NULL, 0},
+ {"Helvetica-Bold", "n019004l.pfb", "arialbd.ttf", "Helvetica", "Helvetica-Bold", NULL, 0},
+ {"Helvetica-BoldOblique", "n019024l.pfb", "arialbi.ttf", "Helvetica", "Helvetica Bold Oblique", "Helvetica-Bold", 0.212557},
+ {"Helvetica-Oblique", "n019023l.pfb", "ariali.ttf", "Helvetica", "Helvetica Oblique", "Helvetica", 0.212557},
+ {"Symbol", "s050000l.pfb", NULL, "Symbol", "Symbol", NULL, 0},
+ {"Times-Bold", "n021004l.pfb", "timesbd.ttf", "Times", "Times-Bold", NULL, 0},
+ {"Times-BoldItalic", "n021024l.pfb", "timesbi.ttf", "Times", "Times-BoldItalic", NULL, 0},
+ {"Times-Italic", "n021023l.pfb", "timesi.ttf", "Times", "Times-Italic", NULL, 0},
+ {"Times-Roman", "n021003l.pfb", "times.ttf", "Times", "Times-Roman", NULL, 0},
+ {"ZapfDingbats", "d050000l.pfb", NULL, "ZapfDingbats", "Zapf Dingbats", NULL, 0},
{NULL}
};
-#ifdef WIN32
+#ifdef _WIN32
static const char *displayFontDirs[] = {
"c:/windows/fonts",
"c:/winnt/fonts",
@@ -115,10 +125,31 @@ static const char *displayFontDirs[] = {
"/usr/share/fonts/default/Type1",
"/usr/share/fonts/default/ghostscript",
"/usr/share/fonts/type1/gsfonts",
+#if defined(__sun) && defined(__SVR4)
+ "/usr/sfw/share/ghostscript/fonts",
+#endif
NULL
};
#endif
+#ifdef __APPLE__
+static const char *macSystemFontPath = "/System/Library/Fonts";
+#endif
+
+struct Base14FontInfo {
+ Base14FontInfo(GString *fileNameA, int fontNumA, double obliqueA) {
+ fileName = fileNameA;
+ fontNum = fontNumA;
+ oblique = obliqueA;
+ }
+ ~Base14FontInfo() {
+ delete fileName;
+ }
+ GString *fileName;
+ int fontNum;
+ double oblique;
+};
+
//------------------------------------------------------------------------
GlobalParams *globalParams = NULL;
@@ -198,13 +229,13 @@ public:
~SysFontList();
SysFontInfo *find(GString *name);
-#ifdef WIN32
+#ifdef _WIN32
void scanWindowsFonts(char *winFontDir);
#endif
private:
-#ifdef WIN32
+#ifdef _WIN32
SysFontInfo *makeWindowsFont(char *name, int fontNum,
char *path);
#endif
@@ -241,40 +272,36 @@ SysFontInfo *SysFontList::find(GString *name) {
}
n = name2->getLength();
- // remove trailing "MT" (Foo-MT, Foo-BoldMT, etc.)
- if (n > 2 && !strcmp(name2->getCString() + n - 2, "MT")) {
- name2->del(n - 2, 2);
- n -= 2;
- }
+ // font names like "Arial-BoldMT,Bold" are occasionally used,
+ // so run this loop twice
+ bold = italic = gFalse;
+ for (i = 0; i < 2; ++i) {
- // look for "Regular"
- if (n > 7 && !strcmp(name2->getCString() + n - 7, "Regular")) {
- name2->del(n - 7, 7);
- n -= 7;
- }
+ // remove trailing "MT" (Foo-MT, Foo-BoldMT, etc.)
+ if (n > 2 && !strcmp(name2->getCString() + n - 2, "MT")) {
+ name2->del(n - 2, 2);
+ n -= 2;
+ }
- // look for "Italic"
- if (n > 6 && !strcmp(name2->getCString() + n - 6, "Italic")) {
- name2->del(n - 6, 6);
- italic = gTrue;
- n -= 6;
- } else {
- italic = gFalse;
- }
+ // look for "Regular"
+ if (n > 7 && !strcmp(name2->getCString() + n - 7, "Regular")) {
+ name2->del(n - 7, 7);
+ n -= 7;
+ }
- // look for "Bold"
- if (n > 4 && !strcmp(name2->getCString() + n - 4, "Bold")) {
- name2->del(n - 4, 4);
- bold = gTrue;
- n -= 4;
- } else {
- bold = gFalse;
- }
+ // look for "Italic"
+ if (n > 6 && !strcmp(name2->getCString() + n - 6, "Italic")) {
+ name2->del(n - 6, 6);
+ italic = gTrue;
+ n -= 6;
+ }
- // remove trailing "MT" (FooMT-Bold, etc.)
- if (n > 2 && !strcmp(name2->getCString() + n - 2, "MT")) {
- name2->del(n - 2, 2);
- n -= 2;
+ // look for "Bold"
+ if (n > 4 && !strcmp(name2->getCString() + n - 4, "Bold")) {
+ name2->del(n - 4, 4);
+ bold = gTrue;
+ n -= 4;
+ }
}
// remove trailing "PS"
@@ -323,7 +350,7 @@ SysFontInfo *SysFontList::find(GString *name) {
return fi;
}
-#ifdef WIN32
+#ifdef _WIN32
void SysFontList::scanWindowsFonts(char *winFontDir) {
OSVERSIONINFO version;
char *path;
@@ -341,15 +368,15 @@ void SysFontList::scanWindowsFonts(char *winFontDir) {
} else {
path = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Fonts\\";
}
- if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, path, 0,
- KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS,
- &regKey) == ERROR_SUCCESS) {
+ if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, path, 0,
+ KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS,
+ &regKey) == ERROR_SUCCESS) {
idx = 0;
while (1) {
valNameLen = sizeof(valName) - 1;
dataLen = sizeof(data) - 1;
- if (RegEnumValue(regKey, idx, valName, &valNameLen, NULL,
- &type, (LPBYTE)data, &dataLen) != ERROR_SUCCESS) {
+ if (RegEnumValueA(regKey, idx, valName, &valNameLen, NULL,
+ &type, (LPBYTE)data, &dataLen) != ERROR_SUCCESS) {
break;
}
if (type == REG_SZ &&
@@ -357,7 +384,7 @@ void SysFontList::scanWindowsFonts(char *winFontDir) {
dataLen > 0 && dataLen < sizeof(data)) {
valName[valNameLen] = '\0';
data[dataLen] = '\0';
- n = strlen(data);
+ n = (int)strlen(data);
if (!strcasecmp(data + n - 4, ".ttf") ||
!strcasecmp(data + n - 4, ".ttc")) {
fontPath = new GString(data);
@@ -398,7 +425,7 @@ SysFontInfo *SysFontList::makeWindowsFont(char *name, int fontNum,
int i;
SysFontType type;
- n = strlen(name);
+ n = (int)strlen(name);
bold = italic = gFalse;
// remove trailing ' (TrueType)'
@@ -419,7 +446,7 @@ SysFontInfo *SysFontList::makeWindowsFont(char *name, int fontNum,
}
// remove trailing ' Regular'
- if (n > 5 && !strncmp(name + n - 8, " Regular", 8)) {
+ if (n > 8 && !strncmp(name + n - 8, " Regular", 8)) {
n -= 8;
}
@@ -490,7 +517,7 @@ public:
private:
-#ifdef WIN32
+#ifdef _WIN32
Plugin(HMODULE libA);
HMODULE lib;
#else
@@ -504,7 +531,7 @@ Plugin *Plugin::load(char *type, char *name) {
Plugin *plugin;
XpdfPluginVecTable *vt;
XpdfBool (*xpdfInitPlugin)(void);
-#ifdef WIN32
+#ifdef _WIN32
HMODULE libA;
#else
void *dlA;
@@ -515,9 +542,9 @@ Plugin *Plugin::load(char *type, char *name) {
appendToPath(path, type);
appendToPath(path, name);
-#ifdef WIN32
+#ifdef _WIN32
path->append(".dll");
- if (!(libA = LoadLibrary(path->getCString()))) {
+ if (!(libA = LoadLibraryA(path->getCString()))) {
error(errIO, -1, "Failed to load plugin '{0:t}'", path);
goto err1;
}
@@ -548,7 +575,7 @@ Plugin *Plugin::load(char *type, char *name) {
}
memcpy(vt, &xpdfPluginVecTable, sizeof(xpdfPluginVecTable));
-#ifdef WIN32
+#ifdef _WIN32
if (!(xpdfInitPlugin = (XpdfBool (*)(void))
GetProcAddress(libA, "xpdfInitPlugin"))) {
error(errIO, -1, "Failed to find xpdfInitPlugin in plugin '{0:t}'",
@@ -568,7 +595,7 @@ Plugin *Plugin::load(char *type, char *name) {
goto err2;
}
-#ifdef WIN32
+#ifdef _WIN32
plugin = new Plugin(libA);
#else
plugin = new Plugin(dlA);
@@ -578,7 +605,7 @@ Plugin *Plugin::load(char *type, char *name) {
return plugin;
err2:
-#ifdef WIN32
+#ifdef _WIN32
FreeLibrary(libA);
#else
dlclose(dlA);
@@ -588,7 +615,7 @@ Plugin *Plugin::load(char *type, char *name) {
return NULL;
}
-#ifdef WIN32
+#ifdef _WIN32
Plugin::Plugin(HMODULE libA) {
lib = libA;
}
@@ -601,7 +628,7 @@ Plugin::Plugin(void *dlA) {
Plugin::~Plugin() {
void (*xpdfFreePlugin)(void);
-#ifdef WIN32
+#ifdef _WIN32
if ((xpdfFreePlugin = (void (*)(void))
GetProcAddress(lib, "xpdfFreePlugin"))) {
(*xpdfFreePlugin)();
@@ -621,7 +648,7 @@ Plugin::~Plugin() {
// parsing
//------------------------------------------------------------------------
-GlobalParams::GlobalParams(char *cfgFileName) {
+GlobalParams::GlobalParams(const char *cfgFileName) {
UnicodeMap *map;
GString *fileName;
FILE *f;
@@ -644,7 +671,7 @@ GlobalParams::GlobalParams(char *cfgFileName) {
}
}
-#ifdef WIN32
+#ifdef _WIN32
// baseDir will be set by a call to setBaseDir
baseDir = new GString();
#else
@@ -660,6 +687,7 @@ GlobalParams::GlobalParams(char *cfgFileName) {
fontFiles = new GHash(gTrue);
fontDirs = new GList();
ccFontFiles = new GHash(gTrue);
+ base14SysFonts = new GHash(gTrue);
sysFonts = new SysFontList();
#if HAVE_PAPER_H
char *paperName;
@@ -683,6 +711,7 @@ GlobalParams::GlobalParams(char *cfgFileName) {
psImageableURX = psPaperWidth;
psImageableURY = psPaperHeight;
psCrop = gTrue;
+ psUseCropBoxAsPage = gFalse;
psExpandSmaller = gFalse;
psShrinkLarger = gTrue;
psCenter = gTrue;
@@ -700,12 +729,15 @@ GlobalParams::GlobalParams(char *cfgFileName) {
psPreload = gFalse;
psOPI = gFalse;
psASCIIHex = gFalse;
+ psLZW = gTrue;
psUncompressPreloadedImages = gFalse;
+ psMinLineWidth = 0;
psRasterResolution = 300;
psRasterMono = gFalse;
+ psRasterSliceSize = 20000000;
psAlwaysRasterize = gFalse;
textEncoding = new GString("Latin1");
-#if defined(WIN32)
+#if defined(_WIN32)
textEOL = eolDOS;
#elif defined(MACOS)
textEOL = eolMac;
@@ -713,10 +745,9 @@ GlobalParams::GlobalParams(char *cfgFileName) {
textEOL = eolUnix;
#endif
textPageBreaks = gTrue;
- textKeepTinyChars = gFalse;
+ textKeepTinyChars = gTrue;
initialZoom = new GString("125");
continuousView = gFalse;
- enableT1lib = gTrue;
enableFreeType = gTrue;
disableFreeTypeHinting = gFalse;
antialias = gTrue;
@@ -737,6 +768,8 @@ GlobalParams::GlobalParams(char *cfgFileName) {
movieCommand = NULL;
mapNumericCharNames = gTrue;
mapUnknownCharNames = gFalse;
+ mapExtTrueTypeFontsViaUnicode = gTrue;
+ enableXFA = gTrue;
createDefaultKeyBindings();
printCommands = gFalse;
errQuiet = gFalse;
@@ -791,9 +824,9 @@ GlobalParams::GlobalParams(char *cfgFileName) {
}
}
if (!f) {
-#ifdef WIN32
+#ifdef _WIN32
char buf[512];
- i = GetModuleFileName(NULL, buf, sizeof(buf));
+ i = GetModuleFileNameA(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';
@@ -928,7 +961,7 @@ void GlobalParams::createDefaultKeyBindings() {
keyBindings->append(new KeyBinding('l', xpdfKeyModCtrl,
xpdfKeyContextAny, "redraw"));
keyBindings->append(new KeyBinding('w', xpdfKeyModCtrl,
- xpdfKeyContextAny, "closeWindow"));
+ xpdfKeyContextAny, "closeWindowOrQuit"));
keyBindings->append(new KeyBinding('?', xpdfKeyModNone,
xpdfKeyContextAny, "about"));
keyBindings->append(new KeyBinding('q', xpdfKeyModNone,
@@ -1017,6 +1050,9 @@ void GlobalParams::parseLine(char *buf, GString *fileName, int line) {
parsePSImageableArea(tokens, fileName, line);
} else if (!cmd->cmp("psCrop")) {
parseYesNo("psCrop", &psCrop, tokens, fileName, line);
+ } else if (!cmd->cmp("psUseCropBoxAsPage")) {
+ parseYesNo("psUseCropBoxAsPage", &psUseCropBoxAsPage,
+ tokens, fileName, line);
} else if (!cmd->cmp("psExpandSmaller")) {
parseYesNo("psExpandSmaller", &psExpandSmaller,
tokens, fileName, line);
@@ -1054,14 +1090,22 @@ void GlobalParams::parseLine(char *buf, GString *fileName, int line) {
parseYesNo("psOPI", &psOPI, tokens, fileName, line);
} else if (!cmd->cmp("psASCIIHex")) {
parseYesNo("psASCIIHex", &psASCIIHex, tokens, fileName, line);
+ } else if (!cmd->cmp("psLZW")) {
+ parseYesNo("psLZW", &psLZW, tokens, fileName, line);
} else if (!cmd->cmp("psUncompressPreloadedImages")) {
parseYesNo("psUncompressPreloadedImages", &psUncompressPreloadedImages,
tokens, fileName, line);
+ } else if (!cmd->cmp("psMinLineWidth")) {
+ parseFloat("psMinLineWidth", &psMinLineWidth,
+ tokens, fileName, line);
} else if (!cmd->cmp("psRasterResolution")) {
parseFloat("psRasterResolution", &psRasterResolution,
tokens, fileName, line);
} else if (!cmd->cmp("psRasterMono")) {
parseYesNo("psRasterMono", &psRasterMono, tokens, fileName, line);
+ } else if (!cmd->cmp("psRasterSliceSize")) {
+ parseInteger("psRasterSliceSize", &psRasterSliceSize,
+ tokens, fileName, line);
} else if (!cmd->cmp("psAlwaysRasterize")) {
parseYesNo("psAlwaysRasterize", &psAlwaysRasterize,
tokens, fileName, line);
@@ -1079,8 +1123,6 @@ void GlobalParams::parseLine(char *buf, GString *fileName, int line) {
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("disableFreeTypeHinting")) {
@@ -1133,6 +1175,12 @@ void GlobalParams::parseLine(char *buf, GString *fileName, int line) {
} else if (!cmd->cmp("mapUnknownCharNames")) {
parseYesNo("mapUnknownCharNames", &mapUnknownCharNames,
tokens, fileName, line);
+ } else if (!cmd->cmp("mapExtTrueTypeFontsViaUnicode")) {
+ parseYesNo("mapExtTrueTypeFontsViaUnicode",
+ &mapExtTrueTypeFontsViaUnicode,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("enableXFA")) {
+ parseYesNo("enableXFA", &enableXFA, tokens, fileName, line);
} else if (!cmd->cmp("bind")) {
parseBind(tokens, fileName, line);
} else if (!cmd->cmp("unbind")) {
@@ -1148,6 +1196,8 @@ void GlobalParams::parseLine(char *buf, GString *fileName, int line) {
!cmd->cmp("displayNamedCIDFontX") ||
!cmd->cmp("displayCIDFontX")) {
error(errConfig, -1, "Xpdf no longer supports X fonts");
+ } else if (!cmd->cmp("enableT1lib")) {
+ error(errConfig, -1, "Xpdf no longer uses t1lib");
} else if (!cmd->cmp("t1libControl") || !cmd->cmp("freetypeControl")) {
error(errConfig, -1,
"The t1libControl and freetypeControl options have been replaced by the enableT1lib, enableFreeType, and antialias options");
@@ -1520,6 +1570,7 @@ void GlobalParams::parseScreenType(GList *tokens, GString *fileName,
}
}
+
void GlobalParams::parseBind(GList *tokens, GString *fileName, int line) {
KeyBinding *binding;
GList *cmds;
@@ -1836,6 +1887,7 @@ GlobalParams::~GlobalParams() {
deleteGHash(fontFiles, GString);
deleteGList(fontDirs, GString);
deleteGHash(ccFontFiles, GString);
+ deleteGHash(base14SysFonts, Base14FontInfo);
delete sysFonts;
if (psFile) {
delete psFile;
@@ -1886,35 +1938,77 @@ void GlobalParams::setBaseDir(char *dir) {
baseDir = new GString(dir);
}
-void GlobalParams::setupBaseFonts(char *dir) {
- GString *fontName;
- GString *fileName;
-#ifdef WIN32
+#ifdef _WIN32
+static void getWinFontDir(char *winFontDir) {
HMODULE shell32Lib;
BOOL (__stdcall *SHGetSpecialFolderPathFunc)(HWND hwndOwner,
- LPTSTR lpszPath,
+ LPSTR lpszPath,
int nFolder,
BOOL fCreate);
- char winFontDir[MAX_PATH];
-#endif
- FILE *f;
- int i, j;
+ char *p;
+ int i;
-#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 ((shell32Lib = LoadLibraryA("shell32.dll"))) {
if ((SHGetSpecialFolderPathFunc =
- (BOOL (__stdcall *)(HWND hwndOwner, LPTSTR lpszPath,
+ (BOOL (__stdcall *)(HWND hwndOwner, LPSTR lpszPath,
int nFolder, BOOL fCreate))
GetProcAddress(shell32Lib, "SHGetSpecialFolderPathA"))) {
if (!(*SHGetSpecialFolderPathFunc)(NULL, winFontDir,
CSIDL_FONTS, FALSE)) {
winFontDir[0] = '\0';
}
+ // kludge: Terminal Server changes CSIDL_FONTS to something like
+ // "C:\Users\whatever\Windows\Fonts", which doesn't actually
+ // contain any fonts -- kill that, so we hit the fallback code
+ // below.
+ for (p = winFontDir; *p; ++p) {
+ if (!strncasecmp(p, "\\Users\\", 7)) {
+ winFontDir[0] = '\0';
+ break;
+ }
+ }
+ }
+ }
+ // if something went wrong, or we're on a Terminal Server, try using
+ // %SYSTEMROOT%\Fonts
+ if (!winFontDir[0]) {
+ GetSystemWindowsDirectoryA(winFontDir, MAX_PATH - 6);
+ winFontDir[MAX_PATH - 7] = '\0';
+ i = (int)strlen(winFontDir);
+ if (winFontDir[i-1] != '\\') {
+ winFontDir[i++] = '\\';
}
+ strcpy(winFontDir + i, "Fonts");
}
+}
+#endif
+
+void GlobalParams::setupBaseFonts(char *dir) {
+ GString *fontName;
+ GString *fileName;
+ int fontNum;
+ const char *s;
+ Base14FontInfo *base14;
+#ifdef _WIN32
+ char winFontDir[MAX_PATH];
+#endif
+#ifdef __APPLE__
+ static const char *macFontExts[3] = { "dfont", "ttc", "ttf" };
+ GList *dfontFontNames;
+ GBool found;
+ int k;
+#endif
+ FILE *f;
+ int i, j;
+
+#ifdef _WIN32
+ getWinFontDir(winFontDir);
+#endif
+#ifdef __APPLE__
+ dfontFontNames = NULL;
#endif
for (i = 0; displayFontTab[i].name; ++i) {
if (fontFiles->lookup(displayFontTab[i].name)) {
@@ -1922,6 +2016,7 @@ void GlobalParams::setupBaseFonts(char *dir) {
}
fontName = new GString(displayFontTab[i].name);
fileName = NULL;
+ fontNum = 0;
if (dir) {
fileName = appendToPath(new GString(dir), displayFontTab[i].t1FileName);
if ((f = fopen(fileName->getCString(), "rb"))) {
@@ -1931,7 +2026,7 @@ void GlobalParams::setupBaseFonts(char *dir) {
fileName = NULL;
}
}
-#ifdef WIN32
+#ifdef _WIN32
if (!fileName && winFontDir[0] && displayFontTab[i].ttFileName) {
fileName = appendToPath(new GString(winFontDir),
displayFontTab[i].ttFileName);
@@ -1942,13 +2037,67 @@ void GlobalParams::setupBaseFonts(char *dir) {
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) {
+#endif
+#ifdef __APPLE__
+ // Check for Mac OS X system fonts.
+ s = displayFontTab[i].macFileName;
+ if (dfontFontNames && i > 0 &&
+ (!s || strcmp(s, displayFontTab[i-1].macFileName))) {
+ deleteGList(dfontFontNames, GString);
+ dfontFontNames = NULL;
+ }
+ if (!fileName && s) {
+ for (j = 0; j < 3; ++j) {
+ fileName = GString::format("{0:s}/{1:s}.{2:s}",
+ macSystemFontPath, s, macFontExts[j]);
+ if (!(f = fopen(fileName->getCString(), "rb"))) {
+ delete fileName;
+ fileName = NULL;
+ } else {
+ fclose(f);
+ found = gFalse;
+ // for .dfont or .ttc, we need to scan the font list
+ if (j < 2) {
+ if (!dfontFontNames) {
+ dfontFontNames =
+ FoFiIdentifier::getFontList(fileName->getCString());
+ }
+ if (dfontFontNames) {
+ for (k = 0; k < dfontFontNames->getLength(); ++k) {
+ if (!((GString *)dfontFontNames->get(k))
+ ->cmp(displayFontTab[i].macFontName)) {
+ fontNum = k;
+ found = gTrue;
+ break;
+ }
+ }
+ }
+ // for .ttf, we just use the font
+ } else {
+ found = gTrue;
+ }
+ if (!found) {
+ delete fileName;
+ fileName = NULL;
+ }
+ break;
+ }
+ }
+ }
+#endif // __APPLE__
+ // On Linux, this checks the "standard" ghostscript font
+ // directories. On Windows, it checks the "standard" system font
+ // directories (because SHGetSpecialFolderPath(CSIDL_FONTS)
+ // doesn't work on Win 2k Server or Win2003 Server, or with older
+ // versions of shell32.dll).
+#ifdef _WIN32
+ s = displayFontTab[i].ttFileName;
+#else
+ s = displayFontTab[i].t1FileName;
+#endif
+ if (!fileName && s) {
for (j = 0; !fileName && displayFontDirs[j]; ++j) {
- fileName = appendToPath(new GString(displayFontDirs[j]),
- displayFontTab[i].ttFileName);
+ fileName = appendToPath(new GString(displayFontDirs[j]), s);
if ((f = fopen(fileName->getCString(), "rb"))) {
fclose(f);
} else {
@@ -1957,28 +2106,35 @@ void GlobalParams::setupBaseFonts(char *dir) {
}
}
}
-#else // WIN32
- for (j = 0; !fileName && displayFontDirs[j]; ++j) {
- fileName = appendToPath(new GString(displayFontDirs[j]),
- displayFontTab[i].t1FileName);
- if ((f = fopen(fileName->getCString(), "rb"))) {
- fclose(f);
- } else {
- delete fileName;
- fileName = NULL;
- }
- }
-#endif // WIN32
if (!fileName) {
- error(errConfig, -1, "No display font for '{0:s}'",
- displayFontTab[i].name);
delete fontName;
continue;
}
- addFontFile(fontName, fileName);
+ base14SysFonts->add(fontName, new Base14FontInfo(fileName, fontNum, 0));
}
-
-#ifdef WIN32
+#ifdef __APPLE__
+ if (dfontFontNames) {
+ deleteGList(dfontFontNames, GString);
+ }
+#endif
+ for (i = 0; displayFontTab[i].name; ++i) {
+ if (!base14SysFonts->lookup(displayFontTab[i].name) &&
+ !fontFiles->lookup(displayFontTab[i].name)) {
+ if (displayFontTab[i].obliqueFont &&
+ ((base14 = (Base14FontInfo *)base14SysFonts
+ ->lookup(displayFontTab[i].obliqueFont)))) {
+ base14SysFonts->add(
+ new GString(displayFontTab[i].name),
+ new Base14FontInfo(base14->fileName->copy(),
+ base14->fontNum,
+ displayFontTab[i].obliqueFactor));
+ } else {
+ error(errConfig, -1, "No display font for '{0:s}'",
+ displayFontTab[i].name);
+ }
+ }
+ }
+#ifdef _WIN32
if (winFontDir[0]) {
sysFonts->scanWindowsFonts(winFontDir);
}
@@ -2083,7 +2239,7 @@ FILE *GlobalParams::findToUnicodeFile(GString *name) {
GString *GlobalParams::findFontFile(GString *fontName) {
static const char *exts[] = { ".pfa", ".pfb", ".ttf", ".ttc" };
GString *path, *dir;
-#ifdef WIN32
+#ifdef _WIN32
GString *fontNameU;
#endif
const char *ext;
@@ -2100,7 +2256,7 @@ GString *GlobalParams::findFontFile(GString *fontName) {
dir = (GString *)fontDirs->get(i);
for (j = 0; j < (int)(sizeof(exts) / sizeof(exts[0])); ++j) {
ext = exts[j];
-#ifdef WIN32
+#ifdef _WIN32
fontNameU = fileNameToUTF8(fontName->getCString());
path = appendToPath(dir->copy(), fontNameU->getCString());
delete fontNameU;
@@ -2120,6 +2276,25 @@ GString *GlobalParams::findFontFile(GString *fontName) {
return NULL;
}
+GString *GlobalParams::findBase14FontFile(GString *fontName, int *fontNum,
+ double *oblique) {
+ Base14FontInfo *fi;
+ GString *path;
+
+ lockGlobalParams;
+ if ((fi = (Base14FontInfo *)base14SysFonts->lookup(fontName))) {
+ path = fi->fileName->copy();
+ *fontNum = fi->fontNum;
+ *oblique = fi->oblique;
+ unlockGlobalParams;
+ return path;
+ }
+ unlockGlobalParams;
+ *fontNum = 0;
+ *oblique = 0;
+ return findFontFile(fontName);
+}
+
GString *GlobalParams::findSystemFontFile(GString *fontName,
SysFontType *type,
int *fontNum) {
@@ -2193,6 +2368,15 @@ GBool GlobalParams::getPSCrop() {
return f;
}
+GBool GlobalParams::getPSUseCropBoxAsPage() {
+ GBool f;
+
+ lockGlobalParams;
+ f = psUseCropBoxAsPage;
+ unlockGlobalParams;
+ return f;
+}
+
GBool GlobalParams::getPSExpandSmaller() {
GBool f;
@@ -2242,7 +2426,9 @@ GString *GlobalParams::getPSResidentFont(GString *fontName) {
GString *psName;
lockGlobalParams;
- psName = (GString *)psResidentFonts->lookup(fontName);
+ if ((psName = (GString *)psResidentFonts->lookup(fontName))) {
+ psName = psName->copy();
+ }
unlockGlobalParams;
return psName;
}
@@ -2371,6 +2557,15 @@ GBool GlobalParams::getPSASCIIHex() {
return ah;
}
+GBool GlobalParams::getPSLZW() {
+ GBool ah;
+
+ lockGlobalParams;
+ ah = psLZW;
+ unlockGlobalParams;
+ return ah;
+}
+
GBool GlobalParams::getPSUncompressPreloadedImages() {
GBool ah;
@@ -2380,6 +2575,15 @@ GBool GlobalParams::getPSUncompressPreloadedImages() {
return ah;
}
+double GlobalParams::getPSMinLineWidth() {
+ double w;
+
+ lockGlobalParams;
+ w = psMinLineWidth;
+ unlockGlobalParams;
+ return w;
+}
+
double GlobalParams::getPSRasterResolution() {
double res;
@@ -2398,6 +2602,15 @@ GBool GlobalParams::getPSRasterMono() {
return mono;
}
+int GlobalParams::getPSRasterSliceSize() {
+ int slice;
+
+ lockGlobalParams;
+ slice = psRasterSliceSize;
+ unlockGlobalParams;
+ return slice;
+}
+
GBool GlobalParams::getPSAlwaysRasterize() {
GBool rast;
@@ -2461,15 +2674,6 @@ GBool GlobalParams::getContinuousView() {
return f;
}
-GBool GlobalParams::getEnableT1lib() {
- GBool f;
-
- lockGlobalParams;
- f = enableT1lib;
- unlockGlobalParams;
- return f;
-}
-
GBool GlobalParams::getEnableFreeType() {
GBool f;
@@ -2597,6 +2801,7 @@ GBool GlobalParams::getDrawAnnotations() {
return draw;
}
+
GBool GlobalParams::getMapNumericCharNames() {
GBool map;
@@ -2615,6 +2820,24 @@ GBool GlobalParams::getMapUnknownCharNames() {
return map;
}
+GBool GlobalParams::getMapExtTrueTypeFontsViaUnicode() {
+ GBool map;
+
+ lockGlobalParams;
+ map = mapExtTrueTypeFontsViaUnicode;
+ unlockGlobalParams;
+ return map;
+}
+
+GBool GlobalParams::getEnableXFA() {
+ GBool enable;
+
+ lockGlobalParams;
+ enable = enableXFA;
+ unlockGlobalParams;
+ return enable;
+}
+
GList *GlobalParams::getKeyBinding(int code, int mods, int context) {
KeyBinding *binding;
GList *cmds;
@@ -2804,6 +3027,12 @@ void GlobalParams::setPSCrop(GBool crop) {
unlockGlobalParams;
}
+void GlobalParams::setPSUseCropBoxAsPage(GBool crop) {
+ lockGlobalParams;
+ psUseCropBoxAsPage = crop;
+ unlockGlobalParams;
+}
+
void GlobalParams::setPSExpandSmaller(GBool expand) {
lockGlobalParams;
psExpandSmaller = expand;
@@ -2882,7 +3111,7 @@ void GlobalParams::setPSASCIIHex(GBool hex) {
unlockGlobalParams;
}
-void GlobalParams::setTextEncoding(char *encodingName) {
+void GlobalParams::setTextEncoding(const char *encodingName) {
lockGlobalParams;
delete textEncoding;
textEncoding = new GString(encodingName);
@@ -2930,15 +3159,6 @@ void GlobalParams::setContinuousView(GBool cont) {
unlockGlobalParams;
}
-GBool GlobalParams::setEnableT1lib(char *s) {
- GBool ok;
-
- lockGlobalParams;
- ok = parseYesNo2(s, &enableT1lib);
- unlockGlobalParams;
- return ok;
-}
-
GBool GlobalParams::setEnableFreeType(char *s) {
GBool ok;
@@ -3015,6 +3235,18 @@ void GlobalParams::setMapUnknownCharNames(GBool map) {
unlockGlobalParams;
}
+void GlobalParams::setMapExtTrueTypeFontsViaUnicode(GBool map) {
+ lockGlobalParams;
+ mapExtTrueTypeFontsViaUnicode = map;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setEnableXFA(GBool enable) {
+ lockGlobalParams;
+ enableXFA = enable;
+ unlockGlobalParams;
+}
+
void GlobalParams::setPrintCommands(GBool printCommandsA) {
lockGlobalParams;
printCommands = printCommandsA;
diff --git a/xpdf/GlobalParams.h b/xpdf/GlobalParams.h
index e2da5ca..07a6f83 100644
--- a/xpdf/GlobalParams.h
+++ b/xpdf/GlobalParams.h
@@ -173,7 +173,7 @@ public:
// Initialize the global parameters by attempting to read a config
// file.
- GlobalParams(char *cfgFileName);
+ GlobalParams(const char *cfgFileName);
~GlobalParams();
@@ -193,6 +193,8 @@ public:
FILE *findCMapFile(GString *collection, GString *cMapName);
FILE *findToUnicodeFile(GString *name);
GString *findFontFile(GString *fontName);
+ GString *findBase14FontFile(GString *fontName, int *fontNum,
+ double *oblique);
GString *findSystemFontFile(GString *fontName, SysFontType *type,
int *fontNum);
GString *findCCFontFile(GString *collection);
@@ -202,6 +204,7 @@ public:
void getPSImageableArea(int *llx, int *lly, int *urx, int *ury);
GBool getPSDuplex();
GBool getPSCrop();
+ GBool getPSUseCropBoxAsPage();
GBool getPSExpandSmaller();
GBool getPSShrinkLarger();
GBool getPSCenter();
@@ -218,9 +221,12 @@ public:
GBool getPSPreload();
GBool getPSOPI();
GBool getPSASCIIHex();
+ GBool getPSLZW();
GBool getPSUncompressPreloadedImages();
+ double getPSMinLineWidth();
double getPSRasterResolution();
GBool getPSRasterMono();
+ int getPSRasterSliceSize();
GBool getPSAlwaysRasterize();
GString *getTextEncodingName();
EndOfLineKind getTextEOL();
@@ -228,7 +234,6 @@ public:
GBool getTextKeepTinyChars();
GString *getInitialZoom();
GBool getContinuousView();
- GBool getEnableT1lib();
GBool getEnableFreeType();
GBool getDisableFreeTypeHinting();
GBool getAntialias();
@@ -249,6 +254,8 @@ public:
GString *getMovieCommand() { return movieCommand; }
GBool getMapNumericCharNames();
GBool getMapUnknownCharNames();
+ GBool getMapExtTrueTypeFontsViaUnicode();
+ GBool getEnableXFA();
GList *getKeyBinding(int code, int mods, int context);
GBool getPrintCommands();
GBool getErrQuiet();
@@ -269,6 +276,7 @@ public:
void setPSImageableArea(int llx, int lly, int urx, int ury);
void setPSDuplex(GBool duplex);
void setPSCrop(GBool crop);
+ void setPSUseCropBoxAsPage(GBool crop);
void setPSExpandSmaller(GBool expand);
void setPSShrinkLarger(GBool shrink);
void setPSCenter(GBool center);
@@ -281,13 +289,12 @@ public:
void setPSPreload(GBool preload);
void setPSOPI(GBool opi);
void setPSASCIIHex(GBool hex);
- void setTextEncoding(char *encodingName);
+ void setTextEncoding(const 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);
@@ -299,6 +306,8 @@ public:
void setScreenWhiteThreshold(double thresh);
void setMapNumericCharNames(GBool map);
void setMapUnknownCharNames(GBool map);
+ void setMapExtTrueTypeFontsViaUnicode(GBool map);
+ void setEnableXFA(GBool enable);
void setPrintCommands(GBool printCommandsA);
void setErrQuiet(GBool errQuietA);
@@ -379,6 +388,8 @@ private:
GList *fontDirs; // list of font dirs [GString]
GHash *ccFontFiles; // character collection font files:
// collection name mapped to path [GString]
+ GHash *base14SysFonts; // Base-14 system font files: font name
+ // mapped to path [Base14FontInfo]
SysFontList *sysFonts; // system fonts
GString *psFile; // PostScript file or command (for xpdf)
int psPaperWidth; // paper size, in PostScript points, for
@@ -388,6 +399,7 @@ private:
psImageableURX,
psImageableURY;
GBool psCrop; // crop PS output to CropBox
+ GBool psUseCropBoxAsPage; // use CropBox as page size
GBool psExpandSmaller; // expand smaller pages to fill paper
GBool psShrinkLarger; // shrink larger pages to fit paper
GBool psCenter; // center pages on the paper
@@ -411,11 +423,15 @@ private:
// memory
GBool psOPI; // generate PostScript OPI comments?
GBool psASCIIHex; // use ASCIIHex instead of ASCII85?
+ GBool psLZW; // false to use RLE instead of LZW
GBool psUncompressPreloadedImages; // uncompress all preloaded images
+ double psMinLineWidth; // minimum line width for PostScript output
double psRasterResolution; // PostScript rasterization resolution (dpi)
GBool psRasterMono; // true to do PostScript rasterization
// in monochrome (gray); false to do it
// in color (RGB/CMYK)
+ int psRasterSliceSize; // maximum size (pixels) of PostScript
+ // rasterization slice
GBool psAlwaysRasterize; // force PostScript rasterization
GString *textEncoding; // encoding (unicodeMap) to use for text
// output
@@ -425,7 +441,6 @@ private:
GBool textKeepTinyChars; // keep all characters in text output
GString *initialZoom; // initial zoom level
GBool continuousView; // continuous view mode
- GBool enableT1lib; // t1lib enable flag
GBool enableFreeType; // FreeType enable flag
GBool disableFreeTypeHinting; // FreeType hinting disable flag
GBool antialias; // font anti-aliasing enable flag
@@ -446,6 +461,9 @@ private:
GString *movieCommand; // command executed for movie annotations
GBool mapNumericCharNames; // map numeric char names (from font subsets)?
GBool mapUnknownCharNames; // map unknown char names?
+ GBool mapExtTrueTypeFontsViaUnicode; // map char codes to GID via Unicode
+ // for external TrueType fonts?
+ GBool enableXFA; // enable XFA form rendering
GList *keyBindings; // key & mouse button bindings [KeyBinding]
GBool printCommands; // print the drawing commands
GBool errQuiet; // suppress error messages?
diff --git a/xpdf/HTMLGen.cc b/xpdf/HTMLGen.cc
new file mode 100644
index 0000000..6372e13
--- /dev/null
+++ b/xpdf/HTMLGen.cc
@@ -0,0 +1,583 @@
+//========================================================================
+//
+// HTMLGen.cc
+//
+// Copyright 2010 Glyph & Cog, LLC
+//
+//========================================================================
+
+//~ to do:
+//~ - fonts
+//~ - underlined? (underlines are present in the background image)
+//~ - include the original font name in the CSS entry (before the
+//~ generic serif/sans-serif/monospace name)
+//~ - check that htmlDir exists and is a directory
+//~ - links:
+//~ - links to pages
+//~ - links to named destinations
+//~ - links to URLs
+//~ - rotated text should go in the background image
+//~ - metadata
+//~ - PDF outline
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <png.h>
+#include "gmem.h"
+#include "GString.h"
+#include "GList.h"
+#include "SplashBitmap.h"
+#include "PDFDoc.h"
+#include "TextOutputDev.h"
+#include "SplashOutputDev.h"
+#include "ErrorCodes.h"
+#if EVAL_MODE
+# include "SplashMath.h"
+# include "Splash.h"
+# include "BuiltinFontTables.h"
+# include "FontEncodingTables.h"
+#endif
+#include "HTMLGen.h"
+
+#ifdef _WIN32
+# define strcasecmp stricmp
+# define strncasecmp strnicmp
+#endif
+
+//------------------------------------------------------------------------
+
+struct FontStyleTagInfo {
+ const char *tag;
+ int tagLen;
+ GBool bold;
+ GBool italic;
+};
+
+// NB: these are compared, in order, against the tail of the font
+// name, so "BoldItalic" must come before "Italic", etc.
+static FontStyleTagInfo fontStyleTags[] = {
+ {"Roman", 5, gFalse, gFalse},
+ {"Regular", 7, gFalse, gFalse},
+ {"Condensed", 9, gFalse, gFalse},
+ {"CondensedBold", 13, gTrue, gFalse},
+ {"CondensedLight", 14, gFalse, gFalse},
+ {"SemiBold", 8, gTrue, gFalse},
+ {"BoldItalic", 10, gTrue, gTrue},
+ {"Bold_Italic", 11, gTrue, gTrue},
+ {"BoldOblique", 11, gTrue, gTrue},
+ {"Bold_Oblique", 12, gTrue, gTrue},
+ {"Bold", 4, gTrue, gFalse},
+ {"Italic", 6, gFalse, gTrue},
+ {"Oblique", 7, gFalse, gTrue},
+ {NULL, 0, gFalse, gFalse}
+};
+
+struct StandardFontInfo {
+ const char *name;
+ GBool fixedWidth;
+ GBool serif;
+};
+
+static StandardFontInfo standardFonts[] = {
+ {"Arial", gFalse, gFalse},
+ {"Courier", gTrue, gFalse},
+ {"Futura", gFalse, gFalse},
+ {"Helvetica", gFalse, gFalse},
+ {"Minion", gFalse, gTrue},
+ {"NewCenturySchlbk", gFalse, gTrue},
+ {"Times", gFalse, gTrue},
+ {"TimesNew", gFalse, gTrue},
+ {"Times_New", gFalse, gTrue},
+ {"Verdana", gFalse, gFalse},
+ {"LucidaSans", gFalse, gFalse},
+ {NULL, gFalse, gFalse}
+};
+
+struct SubstFontInfo {
+ double mWidth;
+};
+
+// index: {fixed:8, serif:4, sans-serif:0} + bold*2 + italic
+static SubstFontInfo substFonts[16] = {
+ {0.833},
+ {0.833},
+ {0.889},
+ {0.889},
+ {0.788},
+ {0.722},
+ {0.833},
+ {0.778},
+ {0.600},
+ {0.600},
+ {0.600},
+ {0.600}
+};
+
+// Map Unicode indexes from the private use area, following the Adobe
+// Glyph list.
+#define privateUnicodeMapStart 0xf6f9
+#define privateUnicodeMapEnd 0xf7ff
+static int
+privateUnicodeMap[privateUnicodeMapEnd - privateUnicodeMapStart + 1] = {
+ 0x0141, 0x0152, 0, 0, 0x0160, 0, 0x017d, // f6f9
+ 0, 0, 0, 0, 0, 0, 0, 0, // f700
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, // f710
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0x0021, 0, 0, 0x0024, 0, 0x0026, 0, // f720
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, // f730
+ 0x0038, 0x0039, 0, 0, 0, 0, 0, 0x003f,
+ 0, 0, 0, 0, 0, 0, 0, 0, // f740
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, // f750
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, // f760
+ 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, // f770
+ 0x0058, 0x0059, 0x005a, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, // f780
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, // f790
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0x00a1, 0x00a2, 0, 0, 0, 0, 0, // f7a0
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, // f7b0
+ 0, 0, 0, 0, 0, 0, 0, 0x00bf,
+ 0, 0, 0, 0, 0, 0, 0, 0, // f7c0
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, // f7d0
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, // f7e0
+ 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,
+ 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0, // f7f0
+ 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x0178
+};
+
+//------------------------------------------------------------------------
+
+#if EVAL_MODE
+
+#define EVAL_MODE_MSG "XpdfHTML evaluation - www.glyphandcog.com"
+
+static void drawEvalModeMsg(SplashOutputDev *out) {
+ BuiltinFont *bf;
+ SplashFont *font;
+ GString *fontName;
+ char *msg;
+ SplashCoord mat[4], ident[6];
+ SplashCoord diag, size, textW, x, y;
+ Gushort cw;
+ int w, h, n, i;
+
+ // get the Helvetica font info
+ bf = builtinFontSubst[4];
+
+ msg = EVAL_MODE_MSG;
+ n = strlen(msg);
+
+ w = out->getBitmap()->getWidth();
+ h = out->getBitmap()->getHeight();
+
+ ident[0] = 1; ident[1] = 0;
+ ident[2] = 0; ident[3] = -1;
+ ident[4] = 0; ident[5] = h;
+ out->getSplash()->setMatrix(ident);
+
+ diag = splashSqrt((SplashCoord)(w*w + h*h));
+ size = diag / (0.67 * n);
+ if (size < 8) {
+ size = 8;
+ }
+ mat[0] = size * (SplashCoord)w / diag;
+ mat[3] = mat[0];
+ mat[1] = size * (SplashCoord)h / diag;
+ mat[2] = -mat[1];
+ fontName = new GString(bf->name);
+ font = out->getFont(fontName, mat);
+ delete fontName;
+ if (!font) {
+ return;
+ }
+
+ textW = 0;
+ for (i = 0; i < n; ++i) {
+ bf->widths->getWidth(winAnsiEncoding[msg[i] & 0xff], &cw);
+ textW += size * cw * 0.001;
+ }
+
+ out->setFillColor(255, 0, 0);
+ x = 0.5 * (diag - textW) * (SplashCoord)w / diag;
+ y = 0.5 * (diag - textW) * (SplashCoord)h / diag;
+ for (i = 0; i < n; ++i) {
+ out->getSplash()->fillChar(x, y, msg[i], font);
+ bf->widths->getWidth(winAnsiEncoding[msg[i] & 0xff], &cw);
+ x += mat[0] * cw * 0.001;
+ y += mat[1] * cw * 0.001;
+ }
+}
+#endif
+
+//------------------------------------------------------------------------
+
+HTMLGen::HTMLGen(double backgroundResolutionA) {
+ TextOutputControl textOutControl;
+ SplashColor paperColor;
+
+ ok = gTrue;
+
+ backgroundResolution = backgroundResolutionA;
+ drawInvisibleText = gTrue;
+
+ // set up the TextOutputDev
+ textOutControl.mode = textOutReadingOrder;
+ textOutControl.html = gTrue;
+ textOut = new TextOutputDev(NULL, &textOutControl, gFalse);
+ if (!textOut->isOk()) {
+ ok = gFalse;
+ }
+
+ // set up the SplashOutputDev
+ paperColor[0] = paperColor[1] = paperColor[2] = 0xff;
+ splashOut = new SplashOutputDev(splashModeRGB8, 1, gFalse, paperColor);
+ splashOut->setSkipText(gTrue, gFalse);
+}
+
+HTMLGen::~HTMLGen() {
+ delete textOut;
+ delete splashOut;
+}
+
+void HTMLGen::startDoc(PDFDoc *docA) {
+ doc = docA;
+ splashOut->startDoc(doc->getXRef());
+}
+
+static inline int pr(int (*writeFunc)(void *stream, const char *data, int size),
+ void *stream, const char *data) {
+ return writeFunc(stream, data, (int)strlen(data));
+}
+
+static int pf(int (*writeFunc)(void *stream, const char *data, int size),
+ void *stream, const char *fmt, ...) {
+ va_list args;
+ GString *s;
+ int ret;
+
+ va_start(args, fmt);
+ s = GString::formatv(fmt, args);
+ va_end(args);
+ ret = writeFunc(stream, s->getCString(), s->getLength());
+ delete s;
+ return ret;
+}
+
+struct PNGWriteInfo {
+ int (*writePNG)(void *stream, const char *data, int size);
+ void *pngStream;
+};
+
+static void pngWriteFunc(png_structp png, png_bytep data, png_size_t size) {
+ PNGWriteInfo *info;
+
+ info = (PNGWriteInfo *)png_get_progressive_ptr(png);
+ info->writePNG(info->pngStream, (char *)data, (int)size);
+}
+
+int HTMLGen::convertPage(
+ int pg, const char *pngURL,
+ int (*writeHTML)(void *stream, const char *data, int size),
+ void *htmlStream,
+ int (*writePNG)(void *stream, const char *data, int size),
+ void *pngStream) {
+ png_structp png;
+ png_infop pngInfo;
+ PNGWriteInfo writeInfo;
+ SplashBitmap *bitmap;
+ Guchar *p;
+ double pageW, pageH;
+ TextPage *text;
+ GList *fonts, *cols, *pars, *lines, *words;
+ double *fontScales;
+ TextFontInfo *font;
+ TextColumn *col;
+ TextParagraph *par;
+ TextLine *line;
+ TextWord *word0, *word1;
+ GString *s;
+ double base, base1;
+ int subSuper0, subSuper1;
+ double r0, g0, b0, r1, g1, b1;
+ int colIdx, parIdx, lineIdx, wordIdx;
+ int y, i, u;
+
+ // generate the background bitmap
+ doc->displayPage(splashOut, pg, backgroundResolution, backgroundResolution,
+ 0, gFalse, gTrue, gFalse);
+#if EVAL_MODE
+ drawEvalModeMsg(splashOut);
+#endif
+ bitmap = splashOut->getBitmap();
+ if (!(png = png_create_write_struct(PNG_LIBPNG_VER_STRING,
+ NULL, NULL, NULL)) ||
+ !(pngInfo = png_create_info_struct(png))) {
+ return errFileIO;
+ }
+ if (setjmp(png_jmpbuf(png))) {
+ return errFileIO;
+ }
+ writeInfo.writePNG = writePNG;
+ writeInfo.pngStream = pngStream;
+ png_set_write_fn(png, &writeInfo, pngWriteFunc, NULL);
+ png_set_IHDR(png, pngInfo, bitmap->getWidth(), bitmap->getHeight(),
+ 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+ png_write_info(png, pngInfo);
+ p = bitmap->getDataPtr();
+ for (y = 0; y < bitmap->getHeight(); ++y) {
+ png_write_row(png, (png_bytep)p);
+ p += bitmap->getRowSize();
+ }
+ png_write_end(png, pngInfo);
+ png_destroy_write_struct(&png, &pngInfo);
+
+ // page size
+ pageW = doc->getPageCropWidth(pg);
+ pageH = doc->getPageCropHeight(pg);
+
+ // get the PDF text
+ doc->displayPage(textOut, pg, 72, 72, 0, gFalse, gTrue, gFalse);
+ doc->processLinks(textOut, pg);
+ text = textOut->takeText();
+
+ // HTML header
+ pr(writeHTML, htmlStream, "<html>\n");
+ pr(writeHTML, htmlStream, "<head>\n");
+ pr(writeHTML, htmlStream, "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n");
+ pr(writeHTML, htmlStream, "<style type=\"text/css\">\n");
+ pr(writeHTML, htmlStream, ".txt { white-space:nowrap; }\n");
+ fonts = text->getFonts();
+ fontScales = (double *)gmallocn(fonts->getLength(), sizeof(double));
+ for (i = 0; i < fonts->getLength(); ++i) {
+ font = (TextFontInfo *)fonts->get(i);
+ s = getFontDefn(font, &fontScales[i]);
+ pf(writeHTML, htmlStream, "#f{0:d} {{ {1:t} }}\n", i, s);
+ delete s;
+ }
+ pr(writeHTML, htmlStream, "</style>\n");
+ pr(writeHTML, htmlStream, "</head>\n");
+ pr(writeHTML, htmlStream, "<body onload=\"start()\">\n");
+ pf(writeHTML, htmlStream, "<img id=\"background\" style=\"position:absolute; left:0px; top:0px;\" width=\"{0:d}\" height=\"{1:d}\" src=\"{2:s}\">\n",
+ (int)pageW, (int)pageH, pngURL);
+
+ // generate the HTML text
+ cols = text->makeColumns();
+ for (colIdx = 0; colIdx < cols->getLength(); ++colIdx) {
+ col = (TextColumn *)cols->get(colIdx);
+ pars = col->getParagraphs();
+ for (parIdx = 0; parIdx < pars->getLength(); ++parIdx) {
+ par = (TextParagraph *)pars->get(parIdx);
+ lines = par->getLines();
+ for (lineIdx = 0; lineIdx < lines->getLength(); ++lineIdx) {
+ line = (TextLine *)lines->get(lineIdx);
+ if (line->getRotation() != 0) {
+ continue;
+ }
+ words = line->getWords();
+ base = line->getBaseline();
+ s = new GString();
+ word0 = NULL;
+ subSuper0 = 0; // make gcc happy
+ r0 = g0 = b0 = 0; // make gcc happy
+ for (wordIdx = 0; wordIdx < words->getLength(); ++wordIdx) {
+ word1 = (TextWord *)words->get(wordIdx);
+ if (!drawInvisibleText && word1->isInvisible()) {
+ continue;
+ }
+ word1->getColor(&r1, &g1, &b1);
+ base1 = word1->getBaseline();
+ if (base1 - base < -1) {
+ subSuper1 = -1; // superscript
+ } else if (base1 - base > 1) {
+ subSuper1 = 1; // subscript
+ } else {
+ subSuper1 = 0;
+ }
+ if (!word0 ||
+ word1->getFontInfo() != word0->getFontInfo() ||
+ word1->getFontSize() != word0->getFontSize() ||
+ subSuper1 != subSuper0 ||
+ r1 != r0 || g1 != g0 || b1 != b0) {
+ if (word0) {
+ s->append("</span>");
+ }
+ for (i = 0; i < fonts->getLength(); ++i) {
+ if (word1->getFontInfo() == (TextFontInfo *)fonts->get(i)) {
+ break;
+ }
+ }
+ s->appendf("<span id=\"f{0:d}\" style=\"font-size:{1:d}px;vertical-align:{2:s};color:#{3:02x}{4:02x}{5:02x};\">",
+ i, (int)(fontScales[i] * word1->getFontSize()),
+ subSuper1 < 0 ? "super"
+ : subSuper1 > 0 ? "sub"
+ : "baseline",
+ (int)(r1 * 255), (int)(g1 * 255), (int)(b1 * 255));
+ }
+ for (i = 0; i < word1->getLength(); ++i) {
+ u = word1->getChar(i);
+ if (u >= privateUnicodeMapStart &&
+ u <= privateUnicodeMapEnd &&
+ privateUnicodeMap[u - privateUnicodeMapStart]) {
+ u = privateUnicodeMap[u - privateUnicodeMapStart];
+ }
+ if (u <= 0x7f) {
+ if (u == '&') {
+ s->append("&amp;");
+ } else if (u == '<') {
+ s->append("&lt;");
+ } else if (u == '>') {
+ s->append("&gt;");
+ } else {
+ s->append((char)u);
+ }
+ } else if (u <= 0x7ff) {
+ s->append((char)(0xc0 + (u >> 6)));
+ s->append((char)(0x80 + (u & 0x3f)));
+ } else if (u <= 0xffff) {
+ s->append((char)0xe0 + (u >> 12));
+ s->append((char)0x80 + ((u >> 6) & 0x3f));
+ s->append((char)0x80 + (u & 0x3f));
+ } else if (u <= 0x1fffff) {
+ s->append((char)0xf0 + (u >> 18));
+ s->append((char)0x80 + ((u >> 12) & 0x3f));
+ s->append((char)0x80 + ((u >> 6) & 0x3f));
+ s->append((char)0x80 + (u & 0x3f));
+ } else if (u <= 0x3ffffff) {
+ s->append((char)0xf8 + (u >> 24));
+ s->append((char)0x80 + ((u >> 18) & 0x3f));
+ s->append((char)0x80 + ((u >> 12) & 0x3f));
+ s->append((char)0x80 + ((u >> 6) & 0x3f));
+ s->append((char)0x80 + (u & 0x3f));
+ } else if (u <= 0x7fffffff) {
+ s->append((char)0xfc + (u >> 30));
+ s->append((char)0x80 + ((u >> 24) & 0x3f));
+ s->append((char)0x80 + ((u >> 18) & 0x3f));
+ s->append((char)0x80 + ((u >> 12) & 0x3f));
+ s->append((char)0x80 + ((u >> 6) & 0x3f));
+ s->append((char)0x80 + (u & 0x3f));
+ }
+ }
+ if (word1->getSpaceAfter()) {
+ s->append(' ');
+ }
+ word0 = word1;
+ subSuper0 = subSuper1;
+ r0 = r1;
+ g0 = g1;
+ b0 = b1;
+ }
+ s->append("</span>");
+ pf(writeHTML, htmlStream, "<div class=\"txt\" style=\"position:absolute; left:{0:d}px; top:{1:d}px;\">{2:t}</div>\n",
+ (int)line->getXMin(), (int)line->getYMin(), s);
+ delete s;
+ }
+ }
+ }
+ gfree(fontScales);
+ delete text;
+ deleteGList(cols, TextColumn);
+
+ // HTML trailer
+ pr(writeHTML, htmlStream, "</body>\n");
+ pr(writeHTML, htmlStream, "</html>\n");
+
+ return errNone;
+}
+
+GString *HTMLGen::getFontDefn(TextFontInfo *font, double *scale) {
+ GString *fontName;
+ char *fontName2;
+ FontStyleTagInfo *fst;
+ StandardFontInfo *sf;
+ GBool fixedWidth, serif, bold, italic;
+ double s;
+ int n, i;
+
+ // get the font name, remove any subset tag
+ fontName = font->getFontName();
+ if (fontName) {
+ fontName2 = fontName->getCString();
+ n = fontName->getLength();
+ for (i = 0; i < n && i < 7; ++i) {
+ if (fontName2[i] < 'A' || fontName2[i] > 'Z') {
+ break;
+ }
+ }
+ if (i == 6 && n > 7 && fontName2[6] == '+') {
+ fontName2 += 7;
+ n -= 7;
+ }
+ } else {
+ fontName2 = NULL;
+ n = 0;
+ }
+
+ // get the style info from the font descriptor flags
+ fixedWidth = font->isFixedWidth();
+ serif = font->isSerif();
+ bold = font->isBold();
+ italic = font->isItalic();
+
+ if (fontName2) {
+
+ // look for a style tag at the end of the font name -- this
+ // overrides the font descriptor bold/italic flags
+ for (fst = fontStyleTags; fst->tag; ++fst) {
+ if (n > fst->tagLen &&
+ !strcasecmp(fontName2 + n - fst->tagLen, fst->tag)) {
+ bold = fst->bold;
+ italic = fst->italic;
+ n -= fst->tagLen;
+ if (n > 1 && (fontName2[n-1] == '-' ||
+ fontName2[n-1] == ',' ||
+ fontName2[n-1] == '.' ||
+ fontName2[n-1] == '_')) {
+ --n;
+ }
+ break;
+ }
+ }
+
+ // look for a known font name -- this overrides the font descriptor
+ // fixedWidth/serif flags
+ for (sf = standardFonts; sf->name; ++sf) {
+ if (!strncasecmp(fontName2, sf->name, n)) {
+ fixedWidth = sf->fixedWidth;
+ serif = sf->serif;
+ break;
+ }
+ }
+ }
+
+ // compute the scaling factor
+ *scale = 1;
+ if ((s = font->getMWidth())) {
+ i = (fixedWidth ? 8 : serif ? 4 : 0) + (bold ? 2 : 0) + (italic ? 1 : 0);
+ if (s < substFonts[i].mWidth) {
+ *scale = s / substFonts[i].mWidth;
+ }
+ }
+
+ // generate the CSS markup
+ return GString::format("font-family:{0:s}; font-weight:{1:s}; font-style:{2:s};",
+ fixedWidth ? "monospace"
+ : serif ? "serif"
+ : "sans-serif",
+ bold ? "bold" : "normal",
+ italic ? "italic" : "normal");
+}
diff --git a/xpdf/HTMLGen.h b/xpdf/HTMLGen.h
new file mode 100644
index 0000000..7271c67
--- /dev/null
+++ b/xpdf/HTMLGen.h
@@ -0,0 +1,63 @@
+//========================================================================
+//
+// HTMLGen.h
+//
+// Copyright 2010 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef HTMLGEN_H
+#define HTMLGEN_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+class GString;
+class PDFDoc;
+class TextOutputDev;
+class TextFontInfo;
+class SplashOutputDev;
+
+//------------------------------------------------------------------------
+
+class HTMLGen {
+public:
+
+ HTMLGen(double backgroundResolutionA);
+ ~HTMLGen();
+
+ GBool isOk() { return ok; }
+
+ double getBackgroundResolution() { return backgroundResolution; }
+ void setBackgroundResolution(double backgroundResolutionA)
+ { backgroundResolution = backgroundResolutionA; }
+
+ GBool getDrawInvisibleText() { return drawInvisibleText; }
+ void setDrawInvisibleText(GBool drawInvisibleTextA)
+ { drawInvisibleText = drawInvisibleTextA; }
+
+ void startDoc(PDFDoc *docA);
+ int convertPage(int pg, const char *pngURL,
+ int (*writeHTML)(void *stream, const char *data, int size),
+ void *htmlStream,
+ int (*writePNG)(void *stream, const char *data, int size),
+ void *pngStream);
+
+private:
+
+ GString *getFontDefn(TextFontInfo *font, double *scale);
+
+ double backgroundResolution;
+ GBool drawInvisibleText;
+
+ PDFDoc *doc;
+ TextOutputDev *textOut;
+ SplashOutputDev *splashOut;
+
+ GBool ok;
+};
+
+#endif
diff --git a/xpdf/ImageOutputDev.cc b/xpdf/ImageOutputDev.cc
index a5c9314..3f6b1a1 100644
--- a/xpdf/ImageOutputDev.cc
+++ b/xpdf/ImageOutputDev.cc
@@ -37,7 +37,8 @@ ImageOutputDev::~ImageOutputDev() {
gfree(fileRoot);
}
-void ImageOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
+void ImageOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx,
+ Object *strRef,
int paintType, Dict *resDict,
double *mat, double *bbox,
int x0, int y0, int x1, int y1,
@@ -47,16 +48,16 @@ void ImageOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
void ImageOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
int width, int height, GBool invert,
- GBool inlineImg) {
+ GBool inlineImg, GBool interpolate) {
FILE *f;
- int c;
- int size, i;
+ char buf[4096];
+ int size, n, i;
// dump JPEG file
if (dumpJPEG && str->getKind() == strDCT && !inlineImg) {
// open the image file
- sprintf(fileName, "%s-%03d.jpg", fileRoot, imgNum);
+ sprintf(fileName, "%s-%04d.jpg", fileRoot, imgNum);
++imgNum;
if (!(f = fopen(fileName, "wb"))) {
error(errIO, -1, "Couldn't open image file '{0:s}'", fileName);
@@ -68,8 +69,9 @@ void ImageOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
str->reset();
// copy the stream
- while ((c = str->getChar()) != EOF)
- fputc(c, f);
+ while ((n = str->getBlock(buf, sizeof(buf))) > 0) {
+ fwrite(buf, 1, n, f);
+ }
str->close();
fclose(f);
@@ -78,7 +80,7 @@ void ImageOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
} else {
// open the image file and write the PBM header
- sprintf(fileName, "%s-%03d.pbm", fileRoot, imgNum);
+ sprintf(fileName, "%s-%04d.pbm", fileRoot, imgNum);
++imgNum;
if (!(f = fopen(fileName, "wb"))) {
error(errIO, -1, "Couldn't open image file '{0:s}'", fileName);
@@ -92,8 +94,14 @@ void ImageOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
// copy the stream
size = height * ((width + 7) / 8);
- for (i = 0; i < size; ++i) {
- fputc(str->getChar(), f);
+ while (size > 0) {
+ i = size < (int)sizeof(buf) ? size : (int)sizeof(buf);
+ n = str->getBlock(buf, i);
+ fwrite(buf, 1, n, f);
+ if (n < i) {
+ break;
+ }
+ size -= n;
}
str->close();
@@ -104,14 +112,15 @@ void ImageOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
- int *maskColors, GBool inlineImg) {
+ int *maskColors, GBool inlineImg,
+ GBool interpolate) {
FILE *f;
ImageStream *imgStr;
Guchar *p;
GfxRGB rgb;
int x, y;
- int c;
- int size, i;
+ char buf[4096];
+ int size, n, i;
// dump JPEG file
if (dumpJPEG && str->getKind() == strDCT &&
@@ -120,7 +129,7 @@ void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
!inlineImg) {
// open the image file
- sprintf(fileName, "%s-%03d.jpg", fileRoot, imgNum);
+ sprintf(fileName, "%s-%04d.jpg", fileRoot, imgNum);
++imgNum;
if (!(f = fopen(fileName, "wb"))) {
error(errIO, -1, "Couldn't open image file '{0:s}'", fileName);
@@ -132,8 +141,9 @@ void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
str->reset();
// copy the stream
- while ((c = str->getChar()) != EOF)
- fputc(c, f);
+ while ((n = str->getBlock(buf, sizeof(buf))) > 0) {
+ fwrite(buf, 1, n, f);
+ }
str->close();
fclose(f);
@@ -143,7 +153,7 @@ void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
colorMap->getBits() == 1) {
// open the image file and write the PBM header
- sprintf(fileName, "%s-%03d.pbm", fileRoot, imgNum);
+ sprintf(fileName, "%s-%04d.pbm", fileRoot, imgNum);
++imgNum;
if (!(f = fopen(fileName, "wb"))) {
error(errIO, -1, "Couldn't open image file '{0:s}'", fileName);
@@ -157,8 +167,14 @@ void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
// copy the stream
size = height * ((width + 7) / 8);
- for (i = 0; i < size; ++i) {
- fputc(str->getChar() ^ 0xff, f);
+ while (size > 0) {
+ i = size < (int)sizeof(buf) ? size : (int)sizeof(buf);
+ n = str->getBlock(buf, i);
+ fwrite(buf, 1, n, f);
+ if (n < i) {
+ break;
+ }
+ size -= n;
}
str->close();
@@ -168,7 +184,7 @@ void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
} else {
// open the image file and write the PPM header
- sprintf(fileName, "%s-%03d.ppm", fileRoot, imgNum);
+ sprintf(fileName, "%s-%04d.ppm", fileRoot, imgNum);
++imgNum;
if (!(f = fopen(fileName, "wb"))) {
error(errIO, -1, "Couldn't open image file '{0:s}'", fileName);
@@ -203,8 +219,36 @@ void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
}
}
}
+
+ imgStr->close();
delete imgStr;
fclose(f);
}
}
+
+void ImageOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height,
+ GfxImageColorMap *colorMap,
+ Stream *maskStr,
+ int maskWidth, int maskHeight,
+ GBool maskInvert, GBool interpolate) {
+ drawImage(state, ref, str, width, height, colorMap,
+ NULL, gFalse, interpolate);
+ drawImageMask(state, ref, maskStr, maskWidth, maskHeight, maskInvert,
+ gFalse, interpolate);
+}
+
+void ImageOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
+ Stream *str,
+ int width, int height,
+ GfxImageColorMap *colorMap,
+ Stream *maskStr,
+ int maskWidth, int maskHeight,
+ GfxImageColorMap *maskColorMap,
+ GBool interpolate) {
+ drawImage(state, ref, str, width, height, colorMap,
+ NULL, gFalse, interpolate);
+ drawImage(state, ref, maskStr, maskWidth, maskHeight, maskColorMap,
+ NULL, gFalse, interpolate);
+}
diff --git a/xpdf/ImageOutputDev.h b/xpdf/ImageOutputDev.h
index 5496225..2984686 100644
--- a/xpdf/ImageOutputDev.h
+++ b/xpdf/ImageOutputDev.h
@@ -62,7 +62,7 @@ public:
virtual GBool useDrawChar() { return gFalse; }
//----- path painting
- virtual void tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
+ virtual void tilingPatternFill(GfxState *state, Gfx *gfx, Object *strRef,
int paintType, Dict *resDict,
double *mat, double *bbox,
int x0, int y0, int x1, int y1,
@@ -71,10 +71,22 @@ public:
//----- image drawing
virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
int width, int height, GBool invert,
- GBool inlineImg);
+ GBool inlineImg, GBool interpolate);
virtual void drawImage(GfxState *state, Object *ref, Stream *str,
int width, int height, GfxImageColorMap *colorMap,
- int *maskColors, GBool inlineImg);
+ int *maskColors, GBool inlineImg, GBool interpolate);
+ virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height,
+ GfxImageColorMap *colorMap,
+ Stream *maskStr, int maskWidth, int maskHeight,
+ GBool maskInvert, GBool interpolate);
+ virtual void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height,
+ GfxImageColorMap *colorMap,
+ Stream *maskStr,
+ int maskWidth, int maskHeight,
+ GfxImageColorMap *maskColorMap,
+ GBool interpolate);
private:
diff --git a/xpdf/JArithmeticDecoder.cc b/xpdf/JArithmeticDecoder.cc
index 56ddc31..92db326 100644
--- a/xpdf/JArithmeticDecoder.cc
+++ b/xpdf/JArithmeticDecoder.cc
@@ -92,10 +92,18 @@ JArithmeticDecoder::JArithmeticDecoder() {
dataLen = 0;
limitStream = gFalse;
nBytesRead = 0;
+ readBuf = -1;
}
inline Guint JArithmeticDecoder::readByte() {
+ Guint x;
+
if (limitStream) {
+ if (readBuf >= 0) {
+ x = (Guint)readBuf;
+ readBuf = -1;
+ return x;
+ }
--dataLen;
if (dataLen < 0) {
return 0xff;
@@ -162,9 +170,15 @@ void JArithmeticDecoder::restart(int dataLenA) {
void JArithmeticDecoder::cleanup() {
if (limitStream) {
+ // This saves one extra byte of data from the end of packet i, to
+ // be used in packet i+1. It's not clear from the JPEG 2000 spec
+ // exactly how this should work, but this kludge does seem to fix
+ // decode of some problematic JPEG 2000 streams. It may actually
+ // be necessary to buffer an arbitrary number of bytes (not just
+ // one byte), but I haven't run into that case yet.
while (dataLen > 0) {
- buf0 = buf1;
- buf1 = readByte();
+ readBuf = -1;
+ readBuf = readByte();
}
}
}
diff --git a/xpdf/JArithmeticDecoder.h b/xpdf/JArithmeticDecoder.h
index 7217984..c0f773f 100644
--- a/xpdf/JArithmeticDecoder.h
+++ b/xpdf/JArithmeticDecoder.h
@@ -108,6 +108,7 @@ private:
Guint nBytesRead;
int dataLen;
GBool limitStream;
+ int readBuf;
};
#endif
diff --git a/xpdf/JBIG2Stream.cc b/xpdf/JBIG2Stream.cc
index eb2719a..8588931 100644
--- a/xpdf/JBIG2Stream.cc
+++ b/xpdf/JBIG2Stream.cc
@@ -623,11 +623,11 @@ Guint JBIG2MMRDecoder::get24Bits() {
}
void JBIG2MMRDecoder::skipTo(Guint length) {
- while (nBytesRead < length) {
- str->getChar();
- ++nBytesRead;
- ++byteCounter;
- }
+ int n;
+
+ n = str->discardChars(length - nBytesRead);
+ nBytesRead += n;
+ byteCounter += n;
}
//------------------------------------------------------------------------
@@ -1310,10 +1310,9 @@ void JBIG2Stream::readSegments() {
}
refFlags = (refFlags << 24) | (c1 << 16) | (c2 << 8) | c3;
nRefSegs = refFlags & 0x1fffffff;
- for (i = 0; i < (nRefSegs + 9) >> 3; ++i) {
- if ((c1 = curStr->getChar()) == EOF) {
- goto eofError1;
- }
+ i = (nRefSegs + 9) >> 3;
+ if (curStr->discardChars(i) != i) {
+ goto eofError1;
}
}
@@ -1436,10 +1435,8 @@ void JBIG2Stream::readSegments() {
break;
default:
error(errSyntaxError, getPos(), "Unknown segment type in JBIG2 stream");
- for (i = 0; i < segLength; ++i) {
- if ((c1 = curStr->getChar()) == EOF) {
- goto eofError2;
- }
+ if (curStr->discardChars(segLength) != segLength) {
+ goto eofError2;
}
break;
}
@@ -1460,12 +1457,7 @@ void JBIG2Stream::readSegments() {
gfree(refSegs);
break;
}
- while (byteCounter < segLength) {
- if (curStr->getChar() == EOF) {
- break;
- }
- ++byteCounter;
- }
+ byteCounter += curStr->discardChars(segLength - byteCounter);
}
gfree(refSegs);
@@ -1502,9 +1494,8 @@ GBool JBIG2Stream::readSymbolDictSeg(Guint segNum, Guint length,
Guint symHeight, symWidth, totalWidth, x, symID;
int dh, dw, refAggNum, refDX, refDY, bmSize;
GBool ex;
- int run, cnt, c;
+ int run, cnt;
Guint i, j, k;
- Guchar *p;
symWidths = NULL;
@@ -1814,14 +1805,8 @@ GBool JBIG2Stream::readSymbolDictSeg(Guint segNum, Guint length,
if (bmSize == 0) {
collBitmap = new JBIG2Bitmap(0, totalWidth, symHeight);
bmSize = symHeight * ((totalWidth + 7) >> 3);
- p = collBitmap->getDataPtr();
- for (k = 0; k < (Guint)bmSize; ++k) {
- if ((c = curStr->getChar()) == EOF) {
- break;
- }
- *p++ = (Guchar)c;
- ++byteCounter;
- }
+ byteCounter += curStr->getBlock((char *)collBitmap->getDataPtr(),
+ bmSize);
} else {
collBitmap = readGenericBitmap(gTrue, totalWidth, symHeight,
0, gFalse, gFalse, NULL, NULL, NULL,
@@ -2781,7 +2766,6 @@ JBIG2Bitmap *JBIG2Stream::readGenericBitmap(GBool mmr, int w, int h,
Guchar mask;
int x, y, x0, x1, a0i, b1i, blackPixels, pix, i;
-
bitmap = new JBIG2Bitmap(0, w, h);
bitmap->clearToZero();
@@ -2994,7 +2978,7 @@ JBIG2Bitmap *JBIG2Stream::readGenericBitmap(GBool mmr, int w, int h,
ltpCX = 0x0e3; // 001 1100 01 1
break;
case 3:
- ltpCX = 0x18a; // 01100 0101 1
+ ltpCX = 0x18b; // 01100 0101 1
break;
}
}
@@ -3821,27 +3805,13 @@ void JBIG2Stream::readPageInfoSeg(Guint length) {
}
void JBIG2Stream::readEndOfStripeSeg(Guint length) {
- Guint i;
-
// skip the segment
- for (i = 0; i < length; ++i) {
- if (curStr->getChar() == EOF) {
- break;
- }
- ++byteCounter;
- }
+ byteCounter += curStr->discardChars(length);
}
void JBIG2Stream::readProfilesSeg(Guint length) {
- Guint i;
-
// skip the segment
- for (i = 0; i < length; ++i) {
- if (curStr->getChar() == EOF) {
- break;
- }
- ++byteCounter;
- }
+ byteCounter += curStr->discardChars(length);
}
void JBIG2Stream::readCodeTableSeg(Guint segNum, Guint length) {
@@ -3909,15 +3879,8 @@ void JBIG2Stream::readCodeTableSeg(Guint segNum, Guint length) {
}
void JBIG2Stream::readExtensionSeg(Guint length) {
- Guint i;
-
// skip the segment
- for (i = 0; i < length; ++i) {
- if (curStr->getChar() == EOF) {
- break;
- }
- ++byteCounter;
- }
+ byteCounter += curStr->discardChars(length);
}
JBIG2Segment *JBIG2Stream::findSegment(Guint segNum) {
diff --git a/xpdf/JPXStream.cc b/xpdf/JPXStream.cc
index 98dcfef..7e2bc6b 100644
--- a/xpdf/JPXStream.cc
+++ b/xpdf/JPXStream.cc
@@ -42,7 +42,7 @@
#define jpxContextSigProp 0 // 0 - 8: significance prop and cleanup
#define jpxContextSign 9 // 9 - 13: sign
-#define jpxContextMagRef 14 // 14 -16: magnitude refinement
+#define jpxContextMagRef 14 // 14 - 16: magnitude refinement
#define jpxContextRunLength 17 // cleanup: run length
#define jpxContextUniform 18 // cleanup: first signif coeff
@@ -152,9 +152,10 @@ static Guint signContext[5][5][2] = {
#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
+// sum of the sample size (number of bits) and the number of bits to
+// the right of the decimal point for the fixed point arithmetic used
+// in the IDWT
+#define fracBits 24
//------------------------------------------------------------------------
@@ -233,12 +234,25 @@ JPXStream::JPXStream(Stream *strA):
nComps = 0;
bpc = NULL;
width = height = 0;
+ reduction = 0;
haveCS = gFalse;
+
+ palette.bpc = NULL;
+ palette.c = NULL;
havePalette = gFalse;
+
+ compMap.comp = NULL;
+ compMap.type = NULL;
+ compMap.pComp = NULL;
haveCompMap = gFalse;
+
+ channelDefn.idx = NULL;
+ channelDefn.type = NULL;
+ channelDefn.assoc = NULL;
haveChannelDefn = gFalse;
img.tiles = NULL;
+
bitBuf = 0;
bitBufLen = 0;
bitBufSkip = gFalse;
@@ -252,13 +266,13 @@ JPXStream::~JPXStream() {
void JPXStream::reset() {
bufStr->reset();
- if (readBoxes()) {
- curY = img.yOffset;
- } else {
+ if (readBoxes() == jpxDecodeFatalError) {
// readBoxes reported an error, so we go immediately to EOF
- curY = img.ySize;
+ curY = img.ySizeR;
+ } else {
+ curY = img.yOffsetR;
}
- curX = img.xOffset;
+ curX = img.xOffsetR;
curComp = 0;
readBufLen = 0;
}
@@ -387,23 +401,25 @@ int JPXStream::lookChar() {
void JPXStream::fillReadBuf() {
JPXTileComp *tileComp;
Guint tileIdx, tx, ty;
- int pix, pixBits;
+ int pix, pixBits, k;
+ GBool eol;
do {
- if (curY >= img.ySize) {
+ if (curY >= img.ySizeR) {
return;
}
- tileIdx = ((curY - img.yTileOffset) / img.yTileSize) * img.nXTiles
- + (curX - img.xTileOffset) / img.xTileSize;
+ tileIdx = ((curY - img.yTileOffsetR) / img.yTileSizeR) * img.nXTiles
+ + (curX - img.xTileOffsetR) / img.xTileSizeR;
#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];
+ tx = jpxCeilDiv((curX - img.xTileOffsetR) % img.xTileSizeR, tileComp->hSep);
+ ty = jpxCeilDiv((curY - img.yTileOffsetR) % img.yTileSizeR, tileComp->vSep);
+ pix = (int)tileComp->data[ty * tileComp->w + tx];
pixBits = tileComp->prec;
+ eol = gFalse;
#if 1 //~ ignore the palette, assume the PDF ColorSpace object is valid
if (++curComp == img.nComps) {
#else
@@ -418,13 +434,10 @@ void JPXStream::fillReadBuf() {
if (++curComp == (Guint)(havePalette ? palette.nComps : img.nComps)) {
#endif
curComp = 0;
- if (++curX == img.xSize) {
- curX = img.xOffset;
+ if (++curX == img.xSizeR) {
+ curX = img.xOffsetR;
++curY;
- if (pixBits < 8) {
- pix <<= 8 - pixBits;
- pixBits = 8;
- }
+ eol = gTrue;
}
}
if (pixBits == 8) {
@@ -433,6 +446,10 @@ void JPXStream::fillReadBuf() {
readBuf = (readBuf << pixBits) | (pix & ((1 << pixBits) - 1));
}
readBufLen += pixBits;
+ if (eol && (k = readBufLen & 7)) {
+ readBuf <<= 8 - k;
+ readBufLen += 8 - k;
+ }
} while (readBufLen < 8);
}
@@ -447,7 +464,7 @@ GBool JPXStream::isBinary(GBool last) {
void JPXStream::getImageParams(int *bitsPerComponent,
StreamColorSpaceMode *csMode) {
Guint boxType, boxLen, dataLen, csEnum;
- Guint bpc1, dummy, i;
+ Guint bpc1, dummy;
int csMeth, csPrec, csPrec1, dummy2;
StreamColorSpaceMode csMode1;
GBool haveBPC, haveCSMode;
@@ -498,13 +515,13 @@ void JPXStream::getImageParams(int *bitsPerComponent,
csPrec = csPrec1;
haveCSMode = gTrue;
}
- for (i = 0; i < dataLen - 7; ++i) {
- bufStr->getChar();
+ if (dataLen > 7) {
+ bufStr->discardChars(dataLen - 7);
}
}
} else {
- for (i = 0; i < dataLen - 3; ++i) {
- bufStr->getChar();
+ if (dataLen > 3) {
+ bufStr->discardChars(dataLen - 3);
}
}
}
@@ -516,9 +533,7 @@ void JPXStream::getImageParams(int *bitsPerComponent,
break;
} else {
cover(4);
- for (i = 0; i < dataLen; ++i) {
- bufStr->getChar();
- }
+ bufStr->discardChars(dataLen);
}
}
}
@@ -529,7 +544,7 @@ void JPXStream::getImageParams(int *bitsPerComponent,
void JPXStream::getImageParams2(int *bitsPerComponent,
StreamColorSpaceMode *csMode) {
int segType;
- Guint segLen, nComps1, bpc1, dummy, i;
+ Guint segLen, nComps1, bpc1, dummy;
while (readMarkerHdr(&segType, &segLen)) {
if (segType == 0x51) { // SIZ - image and tile size
@@ -559,15 +574,14 @@ void JPXStream::getImageParams2(int *bitsPerComponent,
} else {
cover(6);
if (segLen > 2) {
- for (i = 0; i < segLen - 2; ++i) {
- bufStr->getChar();
- }
+ bufStr->discardChars(segLen - 2);
}
}
}
}
-GBool JPXStream::readBoxes() {
+JPXDecodeResult JPXStream::readBoxes() {
+ JPXDecodeResult result;
Guint boxType, boxLen, dataLen;
Guint bpc1, compression, unknownColorspace, ipr;
Guint i, j;
@@ -581,8 +595,8 @@ GBool JPXStream::readBoxes() {
cover(7);
error(errSyntaxWarning, getPos(),
"Naked JPEG 2000 codestream, missing JP2/JPX wrapper");
- if (!readCodestream(0)) {
- return gFalse;
+ if ((result = readCodestream(0)) == jpxDecodeFatalError) {
+ return result;
}
nComps = img.nComps;
bpc = (Guint *)gmallocn(nComps, sizeof(Guint));
@@ -591,7 +605,7 @@ GBool JPXStream::readBoxes() {
}
width = img.xSize - img.xOffset;
height = img.ySize - img.yOffset;
- return gTrue;
+ return result;
}
while (readBoxHdr(&boxType, &boxLen, &dataLen)) {
@@ -614,12 +628,12 @@ GBool JPXStream::readBoxes() {
!readUByte(&unknownColorspace) ||
!readUByte(&ipr)) {
error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
if (compression != 7) {
error(errSyntaxError, getPos(),
"Unknown compression type in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
bpc = (Guint *)gmallocn(nComps, sizeof(Guint));
for (i = 0; i < nComps; ++i) {
@@ -632,24 +646,24 @@ GBool JPXStream::readBoxes() {
if (!haveImgHdr) {
error(errSyntaxError, getPos(),
"Found bits per component box before image header box in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
if (dataLen != nComps) {
error(errSyntaxError, getPos(),
"Invalid bits per component box in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
for (i = 0; i < nComps; ++i) {
if (!readUByte(&bpc[i])) {
error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
}
break;
case 0x636F6C72: // color specification
cover(11);
if (!readColorSpecBox(dataLen)) {
- return gFalse;
+ return jpxDecodeFatalError;
}
break;
case 0x70636c72: // palette
@@ -657,15 +671,16 @@ GBool JPXStream::readBoxes() {
if (!readUWord(&palette.nEntries) ||
!readUByte(&palette.nComps)) {
error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
+ havePalette = gTrue;
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(errSyntaxError, getPos(), "Unexpected EOF in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
++palette.bpc[i];
}
@@ -675,14 +690,14 @@ GBool JPXStream::readBoxes() {
(palette.bpc[j] & 0x80) ? gTrue : gFalse,
&palette.c[i * palette.nComps + j])) {
error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
}
}
- havePalette = gTrue;
break;
case 0x636d6170: // component mapping
cover(13);
+ haveCompMap = gTrue;
compMap.nChannels = dataLen / 4;
compMap.comp = (Guint *)gmallocn(compMap.nChannels, sizeof(Guint));
compMap.type = (Guint *)gmallocn(compMap.nChannels, sizeof(Guint));
@@ -692,17 +707,17 @@ GBool JPXStream::readBoxes() {
!readUByte(&compMap.type[i]) ||
!readUByte(&compMap.pComp[i])) {
error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
}
- haveCompMap = gTrue;
break;
case 0x63646566: // channel definition
cover(14);
if (!readUWord(&channelDefn.nChannels)) {
error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
+ haveChannelDefn = gTrue;
channelDefn.idx =
(Guint *)gmallocn(channelDefn.nChannels, sizeof(Guint));
channelDefn.type =
@@ -714,10 +729,9 @@ GBool JPXStream::readBoxes() {
!readUWord(&channelDefn.type[i]) ||
!readUWord(&channelDefn.assoc[i])) {
error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
}
- haveChannelDefn = gTrue;
break;
case 0x6A703263: // contiguous codestream
cover(15);
@@ -729,28 +743,25 @@ GBool JPXStream::readBoxes() {
error(errSyntaxError, getPos(),
"JPX stream has no supported color spec");
}
- if (!readCodestream(dataLen)) {
- return gFalse;
+ if ((result = readCodestream(dataLen)) != jpxDecodeOk) {
+ return result;
}
break;
default:
cover(16);
- for (i = 0; i < dataLen; ++i) {
- if (bufStr->getChar() == EOF) {
- error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream");
- return gFalse;
- }
+ if (bufStr->discardChars(dataLen) != dataLen) {
+ error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream");
+ return jpxDecodeFatalError;
}
break;
}
}
- return gTrue;
+ return jpxDecodeOk;
}
GBool JPXStream::readColorSpecBox(Guint dataLen) {
JPXColorSpec newCS;
Guint csApprox, csEnum;
- Guint i;
GBool ok;
ok = gFalse;
@@ -852,10 +863,9 @@ GBool JPXStream::readColorSpecBox(Guint dataLen) {
case 3: // any ICC profile (JPX)
case 4: // vendor color (JPX)
cover(18);
- for (i = 0; i < dataLen - 3; ++i) {
- if (bufStr->getChar() == EOF) {
- goto err;
- }
+ if (dataLen > 3 &&
+ bufStr->discardChars(dataLen - 3) != dataLen - 3) {
+ goto err;
}
break;
}
@@ -872,11 +882,11 @@ GBool JPXStream::readColorSpecBox(Guint dataLen) {
return gFalse;
}
-GBool JPXStream::readCodestream(Guint len) {
+JPXDecodeResult JPXStream::readCodestream(Guint len) {
JPXTile *tile;
JPXTileComp *tileComp;
int segType;
- GBool haveSIZ, haveCOD, haveQCD, haveSOT;
+ GBool haveSIZ, haveCOD, haveQCD, haveSOT, ok;
Guint precinctSize, style;
Guint segLen, capabilities, comp, i, j, r;
@@ -885,7 +895,7 @@ GBool JPXStream::readCodestream(Guint len) {
do {
if (!readMarkerHdr(&segType, &segLen)) {
error(errSyntaxError, getPos(), "Error in JPX codestream");
- return gFalse;
+ return jpxDecodeFatalError;
}
switch (segType) {
case 0x4f: // SOC - start of codestream
@@ -897,7 +907,7 @@ GBool JPXStream::readCodestream(Guint len) {
if (haveSIZ) {
error(errSyntaxError, getPos(),
"Duplicate SIZ marker segment in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
if (!readUWord(&capabilities) ||
!readULong(&img.xSize) ||
@@ -910,12 +920,12 @@ GBool JPXStream::readCodestream(Guint len) {
!readULong(&img.yTileOffset) ||
!readUWord(&img.nComps)) {
error(errSyntaxError, getPos(), "Error in JPX SIZ marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
if (haveImgHdr && img.nComps != nComps) {
error(errSyntaxError, getPos(),
"Different number of components in JPX SIZ marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
if (img.xSize == 0 || img.ySize == 0 ||
img.xOffset >= img.xSize || img.yOffset >= img.ySize ||
@@ -925,8 +935,16 @@ GBool JPXStream::readCodestream(Guint len) {
img.xTileSize + img.xTileOffset <= img.xOffset ||
img.yTileSize + img.yTileOffset <= img.yOffset) {
error(errSyntaxError, getPos(), "Error in JPX SIZ marker segment");
- return gFalse;
- }
+ return jpxDecodeFatalError;
+ }
+ img.xSizeR = img.xSize >> reduction;
+ img.ySizeR = img.ySize >> reduction;
+ img.xOffsetR = img.xOffset >> reduction;
+ img.yOffsetR = img.yOffset >> reduction;
+ img.xTileSizeR = img.xTileSize >> reduction;
+ img.yTileSizeR = img.yTileSize >> reduction;
+ img.xTileOffsetR = img.xTileOffset >> reduction;
+ img.yTileOffsetR = img.yTileOffset >> reduction;
img.nXTiles = (img.xSize - img.xTileOffset + img.xTileSize - 1)
/ img.xTileSize;
img.nYTiles = (img.ySize - img.yTileOffset + img.yTileSize - 1)
@@ -936,12 +954,16 @@ GBool JPXStream::readCodestream(Guint len) {
img.nXTiles >= INT_MAX / img.nYTiles) {
error(errSyntaxError, getPos(),
"Bad tile count in JPX SIZ marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
img.tiles = (JPXTile *)gmallocn(img.nXTiles * img.nYTiles,
sizeof(JPXTile));
for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
img.tiles[i].init = gFalse;
+ img.tiles[i].nextTilePart = 0;
+ img.tiles[i].tileComps = NULL;
+ }
+ 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) {
@@ -956,12 +978,12 @@ GBool JPXStream::readCodestream(Guint len) {
!readUByte(&img.tiles[0].tileComps[comp].hSep) ||
!readUByte(&img.tiles[0].tileComps[comp].vSep)) {
error(errSyntaxError, getPos(), "Error in JPX SIZ marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
if (img.tiles[0].tileComps[comp].hSep == 0 ||
img.tiles[0].tileComps[comp].vSep == 0) {
error(errSyntaxError, getPos(), "Error in JPX SIZ marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
img.tiles[0].tileComps[comp].sgned =
(img.tiles[0].tileComps[comp].prec & 0x80) ? gTrue : gFalse;
@@ -978,7 +1000,7 @@ GBool JPXStream::readCodestream(Guint len) {
if (!haveSIZ) {
error(errSyntaxError, getPos(),
"JPX COD marker segment before SIZ segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
if (!readUByte(&img.tiles[0].tileComps[0].style) ||
!readUByte(&img.tiles[0].progOrder) ||
@@ -990,14 +1012,21 @@ GBool JPXStream::readCodestream(Guint len) {
!readUByte(&img.tiles[0].tileComps[0].codeBlockStyle) ||
!readUByte(&img.tiles[0].tileComps[0].transform)) {
error(errSyntaxError, getPos(), "Error in JPX COD marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
if (img.tiles[0].tileComps[0].nDecompLevels > 32 ||
img.tiles[0].tileComps[0].codeBlockW > 8 ||
img.tiles[0].tileComps[0].codeBlockH > 8) {
error(errSyntaxError, getPos(), "Error in JPX COD marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
+#if 1 //~ progression orders 2-4 are unimplemented
+ if (img.tiles[0].progOrder >= 2) {
+ error(errUnimplemented, -1,
+ "JPX progression order {0:d} is unimplemented",
+ img.tiles[0].progOrder);
+ }
+#endif
img.tiles[0].tileComps[0].codeBlockW += 2;
img.tiles[0].tileComps[0].codeBlockH += 2;
for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
@@ -1035,7 +1064,7 @@ GBool JPXStream::readCodestream(Guint len) {
cover(91);
if (!readUByte(&precinctSize)) {
error(errSyntaxError, getPos(), "Error in JPX COD marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
img.tiles[0].tileComps[0].resLevels[r].precinctWidth =
precinctSize & 0x0f;
@@ -1065,7 +1094,7 @@ GBool JPXStream::readCodestream(Guint len) {
if (!haveCOD) {
error(errSyntaxError, getPos(),
"JPX COC marker segment before COD segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
if ((img.nComps > 256 && !readUWord(&comp)) ||
(img.nComps <= 256 && !readUByte(&comp)) ||
@@ -1077,13 +1106,13 @@ GBool JPXStream::readCodestream(Guint len) {
!readUByte(&img.tiles[0].tileComps[comp].codeBlockStyle) ||
!readUByte(&img.tiles[0].tileComps[comp].transform)) {
error(errSyntaxError, getPos(), "Error in JPX COC marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
if (img.tiles[0].tileComps[comp].nDecompLevels > 32 ||
img.tiles[0].tileComps[comp].codeBlockW > 8 ||
img.tiles[0].tileComps[comp].codeBlockH > 8) {
error(errSyntaxError, getPos(), "Error in JPX COC marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
img.tiles[0].tileComps[comp].style =
(img.tiles[0].tileComps[comp].style & ~1) | (style & 1);
@@ -1117,7 +1146,7 @@ GBool JPXStream::readCodestream(Guint len) {
if (img.tiles[0].tileComps[comp].style & 0x01) {
if (!readUByte(&precinctSize)) {
error(errSyntaxError, getPos(), "Error in JPX COD marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
img.tiles[0].tileComps[comp].resLevels[r].precinctWidth =
precinctSize & 0x0f;
@@ -1142,16 +1171,16 @@ GBool JPXStream::readCodestream(Guint len) {
if (!haveSIZ) {
error(errSyntaxError, getPos(),
"JPX QCD marker segment before SIZ segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
if (!readUByte(&img.tiles[0].tileComps[0].quantStyle)) {
error(errSyntaxError, getPos(), "Error in JPX QCD marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
if ((img.tiles[0].tileComps[0].quantStyle & 0x1f) == 0x00) {
if (segLen <= 3) {
error(errSyntaxError, getPos(), "Error in JPX QCD marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
img.tiles[0].tileComps[0].nQuantSteps = segLen - 3;
img.tiles[0].tileComps[0].quantSteps =
@@ -1161,7 +1190,7 @@ GBool JPXStream::readCodestream(Guint len) {
for (i = 0; i < img.tiles[0].tileComps[0].nQuantSteps; ++i) {
if (!readUByte(&img.tiles[0].tileComps[0].quantSteps[i])) {
error(errSyntaxError, getPos(), "Error in JPX QCD marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
}
} else if ((img.tiles[0].tileComps[0].quantStyle & 0x1f) == 0x01) {
@@ -1172,12 +1201,12 @@ GBool JPXStream::readCodestream(Guint len) {
sizeof(Guint));
if (!readUWord(&img.tiles[0].tileComps[0].quantSteps[0])) {
error(errSyntaxError, getPos(), "Error in JPX QCD marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
} else if ((img.tiles[0].tileComps[0].quantStyle & 0x1f) == 0x02) {
if (segLen < 5) {
error(errSyntaxError, getPos(), "Error in JPX QCD marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
img.tiles[0].tileComps[0].nQuantSteps = (segLen - 3) / 2;
img.tiles[0].tileComps[0].quantSteps =
@@ -1187,12 +1216,12 @@ GBool JPXStream::readCodestream(Guint len) {
for (i = 0; i < img.tiles[0].tileComps[0].nQuantSteps; ++i) {
if (!readUWord(&img.tiles[0].tileComps[0].quantSteps[i])) {
error(errSyntaxError, getPos(), "Error in JPX QCD marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
}
} else {
error(errSyntaxError, getPos(), "Error in JPX QCD marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
for (comp = 0; comp < img.nComps; ++comp) {
@@ -1219,19 +1248,19 @@ GBool JPXStream::readCodestream(Guint len) {
if (!haveQCD) {
error(errSyntaxError, getPos(),
"JPX QCC marker segment before QCD segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
if ((img.nComps > 256 && !readUWord(&comp)) ||
(img.nComps <= 256 && !readUByte(&comp)) ||
comp >= img.nComps ||
!readUByte(&img.tiles[0].tileComps[comp].quantStyle)) {
error(errSyntaxError, getPos(), "Error in JPX QCC marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
if ((img.tiles[0].tileComps[comp].quantStyle & 0x1f) == 0x00) {
if (segLen <= (img.nComps > 256 ? 5U : 4U)) {
error(errSyntaxError, getPos(), "Error in JPX QCC marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
img.tiles[0].tileComps[comp].nQuantSteps =
segLen - (img.nComps > 256 ? 5 : 4);
@@ -1242,7 +1271,7 @@ GBool JPXStream::readCodestream(Guint len) {
for (i = 0; i < img.tiles[0].tileComps[comp].nQuantSteps; ++i) {
if (!readUByte(&img.tiles[0].tileComps[comp].quantSteps[i])) {
error(errSyntaxError, getPos(), "Error in JPX QCC marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
}
} else if ((img.tiles[0].tileComps[comp].quantStyle & 0x1f) == 0x01) {
@@ -1253,12 +1282,12 @@ GBool JPXStream::readCodestream(Guint len) {
sizeof(Guint));
if (!readUWord(&img.tiles[0].tileComps[comp].quantSteps[0])) {
error(errSyntaxError, getPos(), "Error in JPX QCC marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
} else if ((img.tiles[0].tileComps[comp].quantStyle & 0x1f) == 0x02) {
if (segLen < (img.nComps > 256 ? 5U : 4U) + 2) {
error(errSyntaxError, getPos(), "Error in JPX QCC marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
img.tiles[0].tileComps[comp].nQuantSteps =
(segLen - (img.nComps > 256 ? 5 : 4)) / 2;
@@ -1269,12 +1298,12 @@ GBool JPXStream::readCodestream(Guint len) {
for (i = 0; i < img.tiles[0].tileComps[comp].nQuantSteps; ++i) {
if (!readUWord(&img.tiles[0].tileComps[comp].quantSteps[i])) {
error(errSyntaxError, getPos(), "Error in JPX QCD marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
}
} else {
error(errSyntaxError, getPos(), "Error in JPX QCC marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
for (i = 1; i < img.nXTiles * img.nYTiles; ++i) {
img.tiles[i].tileComps[comp].quantStyle =
@@ -1295,11 +1324,10 @@ GBool JPXStream::readCodestream(Guint len) {
cover(25);
#if 1 //~ ROI is unimplemented
error(errUnimplemented, -1, "got a JPX RGN segment");
- for (i = 0; i < segLen - 2; ++i) {
- if (bufStr->getChar() == EOF) {
- error(errSyntaxError, getPos(), "Error in JPX RGN marker segment");
- return gFalse;
- }
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX RGN marker segment");
+ return jpxDecodeFatalError;
}
#else
if ((img.nComps > 256 && !readUWord(&comp)) ||
@@ -1308,7 +1336,7 @@ GBool JPXStream::readCodestream(Guint len) {
!readUByte(&compInfo[comp].defROI.style) ||
!readUByte(&compInfo[comp].defROI.shift)) {
error(errSyntaxError, getPos(), "Error in JPX RGN marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
#endif
break;
@@ -1316,11 +1344,10 @@ GBool JPXStream::readCodestream(Guint len) {
cover(26);
#if 1 //~ progression order changes are unimplemented
error(errUnimplemented, -1, "got a JPX POC segment");
- for (i = 0; i < segLen - 2; ++i) {
- if (bufStr->getChar() == EOF) {
- error(errSyntaxError, getPos(), "Error in JPX POC marker segment");
- return gFalse;
- }
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX POC marker segment");
+ return jpxDecodeFatalError;
}
#else
nProgs = (segLen - 2) / (img.nComps > 256 ? 9 : 7);
@@ -1335,7 +1362,7 @@ GBool JPXStream::readCodestream(Guint len) {
!(img.nComps <= 256 && readUByte(&progs[i].endComp)) ||
!readUByte(&progs[i].progOrder)) {
error(errSyntaxError, getPos(), "Error in JPX POC marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
}
#endif
@@ -1344,52 +1371,47 @@ GBool JPXStream::readCodestream(Guint len) {
cover(27);
#if 1 //~ packed packet headers are unimplemented
error(errUnimplemented, -1, "Got a JPX PPM segment");
- for (i = 0; i < segLen - 2; ++i) {
- if (bufStr->getChar() == EOF) {
- error(errSyntaxError, getPos(), "Error in JPX PPM marker segment");
- return gFalse;
- }
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX PPM marker segment");
+ return jpxDecodeFatalError;
}
#endif
break;
case 0x55: // TLM - tile-part lengths
// skipped
cover(28);
- for (i = 0; i < segLen - 2; ++i) {
- if (bufStr->getChar() == EOF) {
- error(errSyntaxError, getPos(), "Error in JPX TLM marker segment");
- return gFalse;
- }
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX TLM marker segment");
+ return jpxDecodeFatalError;
}
break;
case 0x57: // PLM - packet length, main header
// skipped
cover(29);
- for (i = 0; i < segLen - 2; ++i) {
- if (bufStr->getChar() == EOF) {
- error(errSyntaxError, getPos(), "Error in JPX PLM marker segment");
- return gFalse;
- }
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX PLM marker segment");
+ return jpxDecodeFatalError;
}
break;
case 0x63: // CRG - component registration
// skipped
cover(30);
- for (i = 0; i < segLen - 2; ++i) {
- if (bufStr->getChar() == EOF) {
- error(errSyntaxError, getPos(), "Error in JPX CRG marker segment");
- return gFalse;
- }
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX CRG marker segment");
+ return jpxDecodeFatalError;
}
break;
case 0x64: // COM - comment
// skipped
cover(31);
- for (i = 0; i < segLen - 2; ++i) {
- if (bufStr->getChar() == EOF) {
- error(errSyntaxError, getPos(), "Error in JPX COM marker segment");
- return gFalse;
- }
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX COM marker segment");
+ return jpxDecodeFatalError;
}
break;
case 0x90: // SOT - start of tile
@@ -1400,10 +1422,8 @@ GBool JPXStream::readCodestream(Guint len) {
cover(33);
error(errSyntaxError, getPos(),
"Unknown marker segment {0:02x} in JPX stream", segType);
- for (i = 0; i < segLen - 2; ++i) {
- if (bufStr->getChar() == EOF) {
- break;
- }
+ if (segLen > 2) {
+ bufStr->discardChars(segLen - 2);
}
break;
}
@@ -1412,27 +1432,30 @@ GBool JPXStream::readCodestream(Guint len) {
if (!haveSIZ) {
error(errSyntaxError, getPos(),
"Missing SIZ marker segment in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
if (!haveCOD) {
error(errSyntaxError, getPos(),
"Missing COD marker segment in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
if (!haveQCD) {
error(errSyntaxError, getPos(),
"Missing QCD marker segment in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
//----- read the tile-parts
+ ok = gTrue;
while (1) {
if (!readTilePart()) {
- return gFalse;
+ ok = gFalse;
+ break;
}
if (!readMarkerHdr(&segType, &segLen)) {
error(errSyntaxError, getPos(), "Error in JPX codestream");
- return gFalse;
+ ok = gFalse;
+ break;
}
if (segType != 0x90) { // SOT - start of tile
break;
@@ -1441,7 +1464,7 @@ GBool JPXStream::readCodestream(Guint len) {
if (segType != 0xd9) { // EOC - end of codestream
error(errSyntaxError, getPos(), "Missing EOC marker in JPX codestream");
- return gFalse;
+ ok = gFalse;
}
//----- finish decoding the image
@@ -1449,20 +1472,20 @@ GBool JPXStream::readCodestream(Guint len) {
tile = &img.tiles[i];
if (!tile->init) {
error(errSyntaxError, getPos(), "Uninitialized tile in JPX codestream");
- return gFalse;
+ return jpxDecodeFatalError;
}
for (comp = 0; comp < img.nComps; ++comp) {
tileComp = &tile->tileComps[comp];
inverseTransform(tileComp);
}
if (!inverseMultiCompAndDC(tile)) {
- return gFalse;
+ return jpxDecodeFatalError;
}
}
//~ can free memory below tileComps here, and also tileComp.buf
- return gTrue;
+ return ok ? jpxDecodeOk : jpxDecodeNonFatalError;
}
GBool JPXStream::readTilePart() {
@@ -1490,11 +1513,16 @@ GBool JPXStream::readTilePart() {
return gFalse;
}
- if ((tilePartIdx > 0 && !img.tiles[tileIdx].init) ||
- tileIdx >= img.nXTiles * img.nYTiles) {
- error(errSyntaxError, getPos(), "Weird tile index in JPX stream");
+ // check tileIdx and tilePartIdx
+ // (this ignores nTileParts, because some encoders get it wrong)
+ if (tileIdx >= img.nXTiles * img.nYTiles ||
+ tilePartIdx != img.tiles[tileIdx].nextTilePart ||
+ (tilePartIdx > 0 && !img.tiles[tileIdx].init) ||
+ (tilePartIdx == 0 && img.tiles[tileIdx].init)) {
+ error(errSyntaxError, getPos(), "Weird tile-part header in JPX stream");
return gFalse;
}
+ ++img.tiles[tileIdx].nextTilePart;
tilePartToEOC = tilePartLen == 0;
tilePartLen -= 12; // subtract size of SOT segment
@@ -1527,6 +1555,13 @@ GBool JPXStream::readTilePart() {
error(errSyntaxError, getPos(), "Error in JPX COD marker segment");
return gFalse;
}
+#if 1 //~ progression orders 2-4 are unimplemented
+ if (img.tiles[tileIdx].progOrder >= 2) {
+ error(errUnimplemented, -1,
+ "JPX progression order {0:d} is unimplemented",
+ img.tiles[tileIdx].progOrder);
+ }
+#endif
img.tiles[tileIdx].tileComps[0].codeBlockW += 2;
img.tiles[tileIdx].tileComps[0].codeBlockH += 2;
for (comp = 0; comp < img.nComps; ++comp) {
@@ -1760,11 +1795,10 @@ GBool JPXStream::readTilePart() {
cover(38);
#if 1 //~ ROI is unimplemented
error(errUnimplemented, -1, "Got a JPX RGN segment");
- for (i = 0; i < segLen - 2; ++i) {
- if (bufStr->getChar() == EOF) {
- error(errSyntaxError, getPos(), "Error in JPX RGN marker segment");
- return gFalse;
- }
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX RGN marker segment");
+ return gFalse;
}
#else
if ((img.nComps > 256 && !readUWord(&comp)) ||
@@ -1781,11 +1815,10 @@ GBool JPXStream::readTilePart() {
cover(39);
#if 1 //~ progression order changes are unimplemented
error(errUnimplemented, -1, "Got a JPX POC segment");
- for (i = 0; i < segLen - 2; ++i) {
- if (bufStr->getChar() == EOF) {
- error(errSyntaxError, getPos(), "Error in JPX POC marker segment");
- return gFalse;
- }
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX POC marker segment");
+ return gFalse;
}
#else
nTileProgs = (segLen - 2) / (img.nComps > 256 ? 9 : 7);
@@ -1809,31 +1842,28 @@ GBool JPXStream::readTilePart() {
cover(40);
#if 1 //~ packed packet headers are unimplemented
error(errUnimplemented, -1, "Got a JPX PPT segment");
- for (i = 0; i < segLen - 2; ++i) {
- if (bufStr->getChar() == EOF) {
- error(errSyntaxError, getPos(), "Error in JPX PPT marker segment");
- return gFalse;
- }
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, 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 (bufStr->getChar() == EOF) {
- error(errSyntaxError, getPos(), "Error in JPX PLT marker segment");
- return gFalse;
- }
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, 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 (bufStr->getChar() == EOF) {
- error(errSyntaxError, getPos(), "Error in JPX COM marker segment");
- return gFalse;
- }
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX COM marker segment");
+ return gFalse;
}
break;
case 0x93: // SOD - start of data
@@ -1845,10 +1875,8 @@ GBool JPXStream::readTilePart() {
error(errSyntaxError, getPos(),
"Unknown marker segment {0:02x} in JPX tile-part stream",
segType);
- for (i = 0; i < segLen - 2; ++i) {
- if (bufStr->getChar() == EOF) {
- break;
- }
+ if (segLen > 2) {
+ bufStr->discardChars(segLen - 2);
}
break;
}
@@ -1886,12 +1914,13 @@ GBool JPXStream::readTilePart() {
tileComp->y0 = jpxCeilDiv(tile->y0, tileComp->vSep);
tileComp->x1 = jpxCeilDiv(tile->x1, tileComp->hSep);
tileComp->y1 = jpxCeilDiv(tile->y1, tileComp->vSep);
- tileComp->w = tileComp->x1 - tileComp->x0;
tileComp->cbW = 1 << tileComp->codeBlockW;
tileComp->cbH = 1 << tileComp->codeBlockH;
- tileComp->data = (int *)gmallocn((tileComp->x1 - tileComp->x0) *
- (tileComp->y1 - tileComp->y0),
- sizeof(int));
+ tileComp->w = (tileComp->x1 - tileComp->x0 + (1 << reduction) - 1)
+ >> reduction;
+ tileComp->h = (tileComp->y1 - tileComp->y0 + (1 << reduction) - 1)
+ >> reduction;
+ tileComp->data = (int *)gmallocn(tileComp->w * tileComp->h, sizeof(int));
if (tileComp->x1 - tileComp->x0 > tileComp->y1 - tileComp->y0) {
n = tileComp->x1 - tileComp->x0;
} else {
@@ -1927,6 +1956,9 @@ GBool JPXStream::readTilePart() {
}
resLevel->precincts = (JPXPrecinct *)gmallocn(1, sizeof(JPXPrecinct));
for (pre = 0; pre < 1; ++pre) {
+ resLevel->precincts[pre].subbands = NULL;
+ }
+ for (pre = 0; pre < 1; ++pre) {
precinct = &resLevel->precincts[pre];
precinct->x0 = resLevel->x0;
precinct->y0 = resLevel->y0;
@@ -1936,6 +1968,11 @@ GBool JPXStream::readTilePart() {
precinct->subbands =
(JPXSubband *)gmallocn(nSBs, sizeof(JPXSubband));
for (sb = 0; sb < nSBs; ++sb) {
+ precinct->subbands[sb].inclusion = NULL;
+ precinct->subbands[sb].zeroBitPlane = NULL;
+ precinct->subbands[sb].cbs = NULL;
+ }
+ for (sb = 0; sb < nSBs; ++sb) {
subband = &precinct->subbands[sb];
subband->x0 = resLevel->bx0[sb];
subband->y0 = resLevel->by0[sb];
@@ -1973,6 +2010,12 @@ GBool JPXStream::readTilePart() {
subband->cbs = (JPXCodeBlock *)gmallocn(subband->nXCBs *
subband->nYCBs,
sizeof(JPXCodeBlock));
+ for (k = 0; k < subband->nXCBs * subband->nYCBs; ++k) {
+ subband->cbs[k].dataLen = NULL;
+ subband->cbs[k].touched = NULL;
+ subband->cbs[k].arithDecoder = NULL;
+ subband->cbs[k].stats = NULL;
+ }
sbx0 = jpxFloorDivPow2(subband->x0, tileComp->codeBlockW);
sby0 = jpxFloorDivPow2(subband->y0, tileComp->codeBlockH);
if (r == 0) { // (NL)LL
@@ -2013,21 +2056,25 @@ GBool JPXStream::readTilePart() {
cb->nZeroBitPlanes = 0;
cb->dataLenSize = 1;
cb->dataLen = (Guint *)gmalloc(sizeof(Guint));
- cb->coeffs = sbCoeffs
- + (cb->y0 - subband->y0) * tileComp->w
- + (cb->x0 - subband->x0);
- cb->touched = (char *)gmalloc(1 << (tileComp->codeBlockW
- + tileComp->codeBlockH));
- cb->len = 0;
- for (cbj = 0; cbj < cb->y1 - cb->y0; ++cbj) {
- for (cbi = 0; cbi < cb->x1 - cb->x0; ++cbi) {
- cb->coeffs[cbj * tileComp->w + cbi] = 0;
+ if (r <= tileComp->nDecompLevels - reduction) {
+ cb->coeffs = sbCoeffs
+ + (cb->y0 - subband->y0) * tileComp->w
+ + (cb->x0 - subband->x0);
+ cb->touched = (char *)gmalloc(1 << (tileComp->codeBlockW
+ + tileComp->codeBlockH));
+ cb->len = 0;
+ for (cbj = 0; cbj < cb->y1 - cb->y0; ++cbj) {
+ for (cbi = 0; cbi < cb->x1 - cb->x0; ++cbi) {
+ cb->coeffs[cbj * tileComp->w + cbi] = 0;
+ }
}
+ memset(cb->touched, 0,
+ (1 << (tileComp->codeBlockW + tileComp->codeBlockH)));
+ } else {
+ cb->coeffs = NULL;
+ cb->touched = NULL;
+ cb->len = 0;
}
- memset(cb->touched, 0,
- (1 << (tileComp->codeBlockW + tileComp->codeBlockH)));
- cb->arithDecoder = NULL;
- cb->stats = NULL;
++cb;
}
}
@@ -2381,7 +2428,21 @@ GBool JPXStream::readCodeBlockData(JPXTileComp *tileComp,
Guint horiz, vert, diag, all, cx, xorBit;
int horizSign, vertSign, bit;
int segSym;
- Guint i, x, y0, y1;
+ Guint n, i, x, y0, y1;
+
+ if (res > tileComp->nDecompLevels - reduction) {
+ // skip the codeblock data
+ if (tileComp->codeBlockStyle & 0x04) {
+ n = 0;
+ for (i = 0; i < cb->nCodingPasses; ++i) {
+ n += cb->dataLen[i];
+ }
+ } else {
+ n = cb->dataLen[0];
+ }
+ bufStr->discardChars(n);
+ return gTrue;
+ }
if (cb->arithDecoder) {
cover(63);
@@ -2735,7 +2796,7 @@ void JPXStream::inverseTransform(JPXTileComp *tileComp) {
}
if (tileComp->transform == 0) {
cover(71);
- shift += fracBits;
+ shift += fracBits - tileComp->prec;
}
// do fixed point adjustment and dequantization on (NL)LL
@@ -2766,7 +2827,7 @@ void JPXStream::inverseTransform(JPXTileComp *tileComp) {
cover(96);
if (tileComp->transform == 0) {
cover(97);
- val &= -1 << fracBits;
+ val &= -1 << (fracBits - tileComp->prec);
}
} else {
cover(98);
@@ -2782,7 +2843,7 @@ void JPXStream::inverseTransform(JPXTileComp *tileComp) {
//----- IDWT for each level
- for (r = 1; r <= tileComp->nDecompLevels; ++r) {
+ for (r = 1; r <= tileComp->nDecompLevels - reduction; ++r) {
resLevel = &tileComp->resLevels[r];
// (n)LL is already in the upper-left corner of the
@@ -2837,7 +2898,7 @@ void JPXStream::inverseTransformLevel(JPXTileComp *tileComp,
}
if (tileComp->transform == 0) {
cover(103);
- shift += fracBits;
+ shift += fracBits - tileComp->prec;
}
// fixed point adjustment and dequantization
@@ -2868,7 +2929,7 @@ void JPXStream::inverseTransformLevel(JPXTileComp *tileComp,
if (qStyle == 0) {
cover(76);
if (tileComp->transform == 0) {
- val &= -1 << fracBits;
+ val &= -1 << (fracBits - tileComp->prec);
}
} else {
cover(77);
@@ -3103,8 +3164,8 @@ GBool JPXStream::inverseMultiCompAndDC(JPXTile *tile) {
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) {
+ for (y = 0; y < tile->tileComps[0].h; ++y) {
+ for (x = 0; x < tile->tileComps[0].w; ++x) {
d0 = tile->tileComps[0].data[j];
d1 = tile->tileComps[1].data[j];
d2 = tile->tileComps[2].data[j];
@@ -3120,8 +3181,8 @@ GBool JPXStream::inverseMultiCompAndDC(JPXTile *tile) {
} 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) {
+ for (y = 0; y < tile->tileComps[0].h; ++y) {
+ for (x = 0; x < tile->tileComps[0].w; ++x) {
d0 = tile->tileComps[0].data[j];
d1 = tile->tileComps[1].data[j];
d2 = tile->tileComps[2].data[j];
@@ -3144,12 +3205,12 @@ GBool JPXStream::inverseMultiCompAndDC(JPXTile *tile) {
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) {
+ for (y = 0; y < tileComp->h; ++y) {
+ for (x = 0; x < tileComp->w; ++x) {
coeff = *dataPtr;
if (tileComp->transform == 0) {
cover(109);
- coeff >>= fracBits;
+ coeff >>= fracBits - tileComp->prec;
}
if (coeff < minVal) {
cover(110);
@@ -3168,12 +3229,12 @@ GBool JPXStream::inverseMultiCompAndDC(JPXTile *tile) {
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) {
+ for (y = 0; y < tileComp->h; ++y) {
+ for (x = 0; x < tileComp->w; ++x) {
coeff = *dataPtr;
if (tileComp->transform == 0) {
cover(112);
- coeff >>= fracBits;
+ coeff >>= fracBits - tileComp->prec;
}
coeff += zeroVal;
if (coeff < 0) {
@@ -3339,16 +3400,12 @@ GBool JPXStream::readBits(int nBits, Guint *x) {
}
void JPXStream::skipSOP() {
- int i;
-
// SOP occurs at the start of the packet header, so we don't need to
// worry about bit-stuff prior to it
if (byteCount >= 6 &&
bufStr->lookChar(0) == 0xff &&
bufStr->lookChar(1) == 0x91) {
- for (i = 0; i < 6; ++i) {
- bufStr->getChar();
- }
+ bufStr->discardChars(6);
byteCount -= 6;
bitBufLen = 0;
bitBufSkip = gFalse;
@@ -3356,15 +3413,13 @@ void JPXStream::skipSOP() {
}
void JPXStream::skipEPH() {
- int i, k;
+ int k;
k = bitBufSkip ? 1 : 0;
if (byteCount >= (Guint)(k + 2) &&
bufStr->lookChar(k) == 0xff &&
bufStr->lookChar(k + 1) == 0x92) {
- for (i = 0; i < k + 2; ++i) {
- bufStr->getChar();
- }
+ bufStr->discardChars(k + 2);
byteCount -= k + 2;
bitBufLen = 0;
bitBufSkip = gFalse;
diff --git a/xpdf/JPXStream.h b/xpdf/JPXStream.h
index 2c46ca0..d00a55e 100644
--- a/xpdf/JPXStream.h
+++ b/xpdf/JPXStream.h
@@ -199,7 +199,7 @@ struct JPXTileComp {
//----- computed
Guint x0, y0, x1, y1; // bounds of the tile-comp, in ref coords
- Guint w; // x1 - x0
+ Guint w, h; // data size = {x1 - x0, y1 - y0} >> reduction
Guint cbW; // code-block width
Guint cbH; // code-block height
@@ -234,6 +234,9 @@ struct JPXTile {
Guint precinct; // precinct
Guint layer; // layer
+ //----- tile part info
+ Guint nextTilePart; // next expected tile-part
+
//----- children
JPXTileComp *tileComps; // the tile-components (len = JPXImage.nComps)
};
@@ -247,6 +250,11 @@ struct JPXImage {
Guint xTileSize, yTileSize; // size of tiles
Guint xTileOffset, // offset of first tile
yTileOffset;
+ Guint xSizeR, ySizeR; // size of reference grid >> reduction
+ Guint xOffsetR, yOffsetR; // image offset >> reduction
+ Guint xTileSizeR, yTileSizeR; // size of tiles >> reduction
+ Guint xTileOffsetR, // offset of first tile >> reduction
+ yTileOffsetR;
Guint nComps; // number of components
//----- computed
@@ -259,6 +267,14 @@ struct JPXImage {
//------------------------------------------------------------------------
+enum JPXDecodeResult {
+ jpxDecodeOk,
+ jpxDecodeNonFatalError,
+ jpxDecodeFatalError
+};
+
+//------------------------------------------------------------------------
+
class JPXStream: public FilterStream {
public:
@@ -273,14 +289,15 @@ public:
virtual GBool isBinary(GBool last = gTrue);
virtual void getImageParams(int *bitsPerComponent,
StreamColorSpaceMode *csMode);
+ void reduceResolution(int reductionA) { reduction = reductionA; }
private:
void fillReadBuf();
void getImageParams2(int *bitsPerComponent, StreamColorSpaceMode *csMode);
- GBool readBoxes();
+ JPXDecodeResult readBoxes();
GBool readColorSpecBox(Guint dataLen);
- GBool readCodestream(Guint len);
+ JPXDecodeResult readCodestream(Guint len);
GBool readTilePart();
GBool readTilePartData(Guint tileIdx,
Guint tilePartLen, GBool tilePartToEOC);
@@ -314,6 +331,7 @@ private:
Guint nComps; // number of components
Guint *bpc; // bits per component, for each component
Guint width, height; // image size
+ int reduction; // log2(reduction in resolution)
GBool haveImgHdr; // set if a JP2/JPX image header has been
// found
JPXColorSpec cs; // color specification
diff --git a/xpdf/Lexer.h b/xpdf/Lexer.h
index f6ad9ce..3f46689 100644
--- a/xpdf/Lexer.h
+++ b/xpdf/Lexer.h
@@ -53,13 +53,12 @@ public:
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(); }
+ // Get current position in file.
+ GFileOffset getPos()
+ { return curStr.isNone() ? -1 : curStr.streamGetPos(); }
// Set position in file.
- void setPos(Guint pos, int dir = 0)
+ void setPos(GFileOffset pos, int dir = 0)
{ if (!curStr.isNone()) curStr.streamSetPos(pos, dir); }
// Returns true if <c> is a whitespace character.
diff --git a/xpdf/Link.cc b/xpdf/Link.cc
index 56a2963..86a3c41 100644
--- a/xpdf/Link.cc
+++ b/xpdf/Link.cc
@@ -39,7 +39,7 @@ LinkAction *LinkAction::parseDest(Object *obj) {
LinkAction *LinkAction::parseAction(Object *obj, GString *baseURI) {
LinkAction *action;
- Object obj2, obj3, obj4;
+ Object obj2, obj3, obj4, obj5;
if (!obj->isDict()) {
error(errSyntaxWarning, -1, "Bad annotation action");
@@ -86,6 +86,30 @@ LinkAction *LinkAction::parseAction(Object *obj, GString *baseURI) {
obj3.free();
obj4.free();
+ // JavaScript action
+ } else if (obj2.isName("JavaScript")) {
+ obj->dictLookup("JS", &obj3);
+ action = new LinkJavaScript(&obj3);
+ obj3.free();
+
+ // SubmitForm action
+ } else if (obj2.isName("SubmitForm")) {
+ obj->dictLookup("F", &obj3);
+ obj->dictLookup("Fields", &obj4);
+ obj->dictLookup("Flags", &obj5);
+ action = new LinkSubmitForm(&obj3, &obj4, &obj5);
+ obj3.free();
+ obj4.free();
+ obj5.free();
+
+ // Hide action
+ } else if (obj2.isName("Hide")) {
+ obj->dictLookupNF("T", &obj3);
+ obj->dictLookup("H", &obj4);
+ action = new LinkHide(&obj3, &obj4);
+ obj3.free();
+ obj4.free();
+
// unknown action
} else if (obj2.isName()) {
action = new LinkUnknown(obj2.getName());
@@ -117,7 +141,7 @@ GString *LinkAction::getFileSpecName(Object *fileSpecObj) {
// dictionary
} else if (fileSpecObj->isDict()) {
-#ifdef WIN32
+#ifdef _WIN32
if (!fileSpecObj->dictLookup("DOS", &obj1)->isString()) {
#else
if (!fileSpecObj->dictLookup("Unix", &obj1)->isString()) {
@@ -139,7 +163,7 @@ GString *LinkAction::getFileSpecName(Object *fileSpecObj) {
// system-dependent path manipulation
if (name) {
-#ifdef WIN32
+#ifdef _WIN32
int i, j;
// "//...." --> "\...."
@@ -517,7 +541,7 @@ LinkLaunch::LinkLaunch(Object *actionObj) {
fileName = getFileSpecName(&obj1);
} else {
obj1.free();
-#ifdef WIN32
+#ifdef _WIN32
if (actionObj->dictLookup("Win", &obj1)->isDict()) {
obj1.dictLookup("F", &obj2);
fileName = getFileSpecName(&obj2);
@@ -644,6 +668,98 @@ LinkMovie::~LinkMovie() {
}
//------------------------------------------------------------------------
+// LinkJavaScript
+//------------------------------------------------------------------------
+
+LinkJavaScript::LinkJavaScript(Object *jsObj) {
+ char buf[4096];
+ int n;
+
+ if (jsObj->isString()) {
+ js = jsObj->getString()->copy();
+ } else if (jsObj->isStream()) {
+ js = new GString();
+ jsObj->streamReset();
+ while ((n = jsObj->getStream()->getBlock(buf, sizeof(buf))) > 0) {
+ js->append(buf, n);
+ }
+ jsObj->streamClose();
+ } else {
+ error(errSyntaxError, -1, "JavaScript action JS key is wrong type");
+ js = NULL;
+ }
+}
+
+LinkJavaScript::~LinkJavaScript() {
+ if (js) {
+ delete js;
+ }
+}
+
+//------------------------------------------------------------------------
+// LinkSubmitForm
+//------------------------------------------------------------------------
+
+LinkSubmitForm::LinkSubmitForm(Object *urlObj, Object *fieldsObj,
+ Object *flagsObj) {
+ if (urlObj->isString()) {
+ url = urlObj->getString()->copy();
+ } else {
+ error(errSyntaxError, -1, "SubmitForm action URL is wrong type");
+ url = NULL;
+ }
+
+ if (fieldsObj->isArray()) {
+ fieldsObj->copy(&fields);
+ } else {
+ if (!fieldsObj->isNull()) {
+ error(errSyntaxError, -1, "SubmitForm action Fields value is wrong type");
+ }
+ fields.initNull();
+ }
+
+ if (flagsObj->isInt()) {
+ flags = flagsObj->getInt();
+ } else {
+ if (!flagsObj->isNull()) {
+ error(errSyntaxError, -1, "SubmitForm action Flags value is wrong type");
+ }
+ flags = 0;
+ }
+}
+
+LinkSubmitForm::~LinkSubmitForm() {
+ if (url) {
+ delete url;
+ }
+ fields.free();
+}
+
+//------------------------------------------------------------------------
+// LinkHide
+//------------------------------------------------------------------------
+
+LinkHide::LinkHide(Object *fieldsObj, Object *hideFlagObj) {
+ if (fieldsObj->isRef() || fieldsObj->isString() || fieldsObj->isArray()) {
+ fieldsObj->copy(&fields);
+ } else {
+ error(errSyntaxError, -1, "Hide action T value is wrong type");
+ fields.initNull();
+ }
+
+ if (hideFlagObj->isBool()) {
+ hideFlag = hideFlagObj->getBool();
+ } else {
+ error(errSyntaxError, -1, "Hide action H value is wrong type");
+ hideFlag = gFalse;
+ }
+}
+
+LinkHide::~LinkHide() {
+ fields.free();
+}
+
+//------------------------------------------------------------------------
// LinkUnknown
//------------------------------------------------------------------------
@@ -745,7 +861,7 @@ Link::~Link() {
Links::Links(Object *annots, GString *baseURI) {
Link *link;
- Object obj1, obj2;
+ Object obj1, obj2, obj3;
int size;
int i;
@@ -756,7 +872,10 @@ Links::Links(Object *annots, GString *baseURI) {
if (annots->isArray()) {
for (i = 0; i < annots->arrayGetLength(); ++i) {
if (annots->arrayGet(i, &obj1)->isDict()) {
- if (obj1.dictLookup("Subtype", &obj2)->isName("Link")) {
+ obj1.dictLookup("Subtype", &obj2);
+ obj1.dictLookup("FT", &obj3);
+ if (obj2.isName("Link") ||
+ (obj2.isName("Widget") && (obj3.isName("Btn") || obj3.isNull()))) {
link = new Link(obj1.getDict(), baseURI);
if (link->isOk()) {
if (numLinks >= size) {
@@ -768,6 +887,7 @@ Links::Links(Object *annots, GString *baseURI) {
delete link;
}
}
+ obj3.free();
obj2.free();
}
obj1.free();
diff --git a/xpdf/Link.h b/xpdf/Link.h
index 55fbfa1..480496a 100644
--- a/xpdf/Link.h
+++ b/xpdf/Link.h
@@ -32,6 +32,9 @@ enum LinkActionKind {
actionURI, // URI
actionNamed, // named action
actionMovie, // movie action
+ actionJavaScript, // run JavaScript
+ actionSubmitForm, // submit form
+ actionHide, // hide annotation
actionUnknown // anything else
};
@@ -244,8 +247,10 @@ public:
virtual ~LinkNamed();
+ // Was the LinkNamed created successfully?
virtual GBool isOk() { return name != NULL; }
+ // Accessors.
virtual LinkActionKind getKind() { return actionNamed; }
GString *getName() { return name; }
@@ -265,8 +270,10 @@ public:
virtual ~LinkMovie();
+ // Was the LinkMovie created successfully?
virtual GBool isOk() { return annotRef.num >= 0 || title != NULL; }
+ // Accessors.
virtual LinkActionKind getKind() { return actionMovie; }
GBool hasAnnotRef() { return annotRef.num >= 0; }
Ref *getAnnotRef() { return &annotRef; }
@@ -279,6 +286,81 @@ private:
};
//------------------------------------------------------------------------
+// LinkJavaScript
+//------------------------------------------------------------------------
+
+class LinkJavaScript: public LinkAction {
+public:
+
+ LinkJavaScript(Object *jsObj);
+
+ virtual ~LinkJavaScript();
+
+ // Was the LinkJavaScript created successfully?
+ virtual GBool isOk() { return js != NULL; }
+
+ // Accessors.
+ virtual LinkActionKind getKind() { return actionJavaScript; }
+ GString *getJS() { return js; }
+
+private:
+
+ GString *js;
+};
+
+//------------------------------------------------------------------------
+// LinkSubmitForm
+//------------------------------------------------------------------------
+
+class LinkSubmitForm: public LinkAction {
+public:
+
+ LinkSubmitForm(Object *urlObj, Object *fieldsObj, Object *flagsObj);
+
+ virtual ~LinkSubmitForm();
+
+ // Was the LinkSubmitForm created successfully?
+ virtual GBool isOk() { return url != NULL; }
+
+ // Accessors.
+ virtual LinkActionKind getKind() { return actionSubmitForm; }
+ GString *getURL() { return url; }
+ Object *getFields() { return &fields; }
+ int getFlags() { return flags; }
+
+private:
+
+ GString *url;
+ Object fields;
+ int flags;
+};
+
+//------------------------------------------------------------------------
+// LinkHide
+//------------------------------------------------------------------------
+
+class LinkHide: public LinkAction {
+public:
+
+ LinkHide(Object *fieldsObj, Object *hideFlagObj);
+
+ virtual ~LinkHide();
+
+ // Was the LinkHide created successfully?
+ virtual GBool isOk() { return !fields.isNull(); }
+
+ // Accessors.
+ virtual LinkActionKind getKind() { return actionHide; }
+ Object *getFields() { return &fields; }
+ GBool getHideFlag() { return hideFlag; }
+
+private:
+
+ Object fields;
+ GBool hideFlag;
+};
+
+//------------------------------------------------------------------------
// LinkUnknown
//------------------------------------------------------------------------
diff --git a/xpdf/Makefile.in b/xpdf/Makefile.in
index 5b48eff..de3e676 100644
--- a/xpdf/Makefile.in
+++ b/xpdf/Makefile.in
@@ -19,18 +19,19 @@ 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@
+CXXFLAGS = @CXXFLAGS@ @DEFS@ -I.. -I$(srcdir)/.. -I$(GOOSRCDIR) -I$(FOFISRCDIR) -I$(SPLASHSRCDIR) -I$(srcdir) @freetype2_CFLAGS@ @Sgm_CFLAGS@ @Xm_CFLAGS@ @Xt_CFLAGS@ @Xp_CFLAGS@ @Xext_CFLAGS@ @Xpm_CFLAGS@ @libpng_CFLAGS@ @libpaper_CFLAGS@ @X_CFLAGS@ @EXTRA_CFLAGS@
LDFLAGS = @LDFLAGS@
-T1LIBS = @t1_LIBS@
-FTLIBS = @freetype2_LIBS@
+FTLIBS = @freetype2_LIBS@ -lz
XLIBS = @Sgm_LIBS@ @Xm_LIBS@ @Xt_LIBS@ @Xp_LIBS@ @Xext_LIBS@ @Xpm_LIBS@ @X_PRE_LIBS@ @X_LIBS@ -lX11 @X_EXTRA_LIBS@
+PNGLIBS = @libpng_LIBS@
+
SPLASHLIBS = -L$(SPLASHLIBDIR) -lsplash
-OTHERLIBS = @LIBS@ @libpaper_LIBS@ \
+OTHERLIBS = @LIBS@ @libpaper_LIBS@ @EXTRA_LIBS@ \
-L$(FOFILIBDIR) -lfofi \
-L$(GOOLIBDIR) -lGoo
@@ -49,6 +50,7 @@ EXE = @EXE@
#------------------------------------------------------------------------
CXX_SRC = \
+ $(srcdir)/AcroForm.cc \
$(srcdir)/Annot.cc \
$(srcdir)/Array.cc \
$(srcdir)/BuiltinFont.cc \
@@ -61,11 +63,13 @@ CXX_SRC = \
$(srcdir)/Dict.cc \
$(srcdir)/Error.cc \
$(srcdir)/FontEncodingTables.cc \
+ $(srcdir)/Form.cc \
$(srcdir)/Function.cc \
$(srcdir)/Gfx.cc \
$(srcdir)/GfxFont.cc \
$(srcdir)/GfxState.cc \
$(srcdir)/GlobalParams.cc \
+ $(srcdir)/HTMLGen.cc \
$(srcdir)/ImageOutputDev.cc \
$(srcdir)/JArithmeticDecoder.cc \
$(srcdir)/JBIG2Stream.cc \
@@ -89,44 +93,94 @@ CXX_SRC = \
$(srcdir)/SplashOutputDev.cc \
$(srcdir)/Stream.cc \
$(srcdir)/TextOutputDev.cc \
+ $(srcdir)/TextString.cc \
$(srcdir)/UnicodeMap.cc \
$(srcdir)/UnicodeTypeTable.cc \
+ $(srcdir)/XFAForm.cc \
$(srcdir)/XPDFApp.cc \
$(srcdir)/XPDFCore.cc \
$(srcdir)/XPDFTree.cc \
$(srcdir)/XPDFViewer.cc \
$(srcdir)/XpdfPluginAPI.cc \
$(srcdir)/XRef.cc \
+ $(srcdir)/Zoox.cc \
$(srcdir)/pdftops.cc \
$(srcdir)/pdftotext.cc \
+ $(srcdir)/pdftohtml.cc \
$(srcdir)/pdfinfo.cc \
$(srcdir)/pdffonts.cc \
$(srcdir)/pdfdetach.cc \
$(srcdir)/pdftoppm.cc \
+ $(srcdir)/pdftopng.cc \
$(srcdir)/pdfimages.cc \
$(srcdir)/xpdf.cc
#------------------------------------------------------------------------
-all: xpdf$(EXE) pdftops$(EXE) pdftotext$(EXE) pdfinfo$(EXE) \
- pdffonts$(EXE) pdfdetach$(EXE) pdftoppm$(EXE) pdfimages$(EXE)
+all: xpdf$(EXE) pdftops$(EXE) pdftotext$(EXE) pdftohtml$(EXE) \
+ pdfinfo$(EXE) pdffonts$(EXE) pdfdetach$(EXE) pdftoppm$(EXE) \
+ pdftopng$(EXE) pdfimages$(EXE)
-all-no-x: pdftops$(EXE) pdftotext$(EXE) pdfinfo$(EXE) pdffonts$(EXE) \
- pdfdetach$(EXE) pdfimages$(EXE)
+all-no-x: pdftops$(EXE) pdftotext$(EXE) pdftohtml$(EXE) pdfinfo$(EXE) \
+ pdffonts$(EXE) pdfdetach$(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 \
- OptionalContent.o Outline.o OutputDev.o Page.o Parser.o PDFCore.o \
- PDFDoc.o PDFDocEncoding.o PreScanOutputDev.o PSOutputDev.o \
- PSTokenizer.o SecurityHandler.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) \
+XPDF_OBJS = \
+ AcroForm.o \
+ Annot.o \
+ Array.o \
+ BuiltinFont.o \
+ BuiltinFontTables.o \
+ Catalog.o \
+ CharCodeToUnicode.o \
+ CMap.o \
+ CoreOutputDev.o \
+ Decrypt.o \
+ Dict.o \
+ Error.o \
+ FontEncodingTables.o \
+ Form.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 \
+ OptionalContent.o \
+ Outline.o \
+ OutputDev.o \
+ Page.o \
+ Parser.o \
+ PDFCore.o \
+ PDFDoc.o \
+ PDFDocEncoding.o \
+ PreScanOutputDev.o \
+ PSOutputDev.o \
+ PSTokenizer.o \
+ SecurityHandler.o \
+ SplashOutputDev.o \
+ Stream.o \
+ TextOutputDev.o \
+ TextString.o \
+ UnicodeMap.o \
+ UnicodeTypeTable.o \
+ XFAForm.o \
+ XPDFApp.o \
+ XPDFCore.o \
+ XPDFTree.o \
+ XPDFViewer.o \
+ XpdfPluginAPI.o \
+ XRef.o \
+ Zoox.o \
+ xpdf.o
+XPDF_LIBS = -L$(GOOLIBDIR) -lGoo $(SPLASHLIBS) $(FTLIBS) \
$(XLIBS) $(OTHERLIBS) -lm
xpdf$(EXE): $(XPDF_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
@@ -134,16 +188,53 @@ xpdf$(EXE): $(XPDF_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
#------------------------------------------------------------------------
-PDFTOPS_OBJS = Annot.o Array.o BuiltinFont.o BuiltinFontTables.o \
- Catalog.o CharCodeToUnicode.o CMap.o Decrypt.o Dict.o Error.o \
- FontEncodingTables.o Function.o Gfx.o GfxFont.o \
- GfxState.o GlobalParams.o JArithmeticDecoder.o JBIG2Stream.o \
- JPXStream.o Lexer.o Link.o NameToCharCode.o OptionalContent.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) \
+PDFTOPS_OBJS = \
+ AcroForm.o \
+ Annot.o \
+ Array.o \
+ BuiltinFont.o \
+ BuiltinFontTables.o \
+ Catalog.o \
+ CharCodeToUnicode.o \
+ CMap.o \
+ Decrypt.o \
+ Dict.o \
+ Error.o \
+ FontEncodingTables.o \
+ Form.o \
+ Function.o \
+ Gfx.o \
+ GfxFont.o \
+ GfxState.o \
+ GlobalParams.o \
+ JArithmeticDecoder.o \
+ JBIG2Stream.o \
+ JPXStream.o \
+ Lexer.o \
+ Link.o \
+ NameToCharCode.o \
+ OptionalContent.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 \
+ TextString.o \
+ UnicodeMap.o \
+ XFAForm.o \
+ XpdfPluginAPI.o \
+ XRef.o \
+ Zoox.o \
+ pdftops.o
+PDFTOPS_LIBS = -L$(GOOLIBDIR) -lGoo $(SPLASHLIBS) $(FTLIBS) \
$(OTHERLIBS) -lm
pdftops$(EXE): $(PDFTOPS_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
@@ -152,15 +243,51 @@ pdftops$(EXE): $(PDFTOPS_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
#------------------------------------------------------------------------
-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 \
- OptionalContent.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_OBJS = \
+ AcroForm.o \
+ Annot.o \
+ Array.o \
+ BuiltinFont.o \
+ BuiltinFontTables.o \
+ Catalog.o \
+ CharCodeToUnicode.o \
+ CMap.o \
+ Decrypt.o \
+ Dict.o \
+ Error.o \
+ FontEncodingTables.o \
+ Form.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 \
+ OptionalContent.o \
+ Outline.o \
+ OutputDev.o \
+ Page.o \
+ Parser.o \
+ PDFDoc.o \
+ PDFDocEncoding.o \
+ PSTokenizer.o \
+ SecurityHandler.o \
+ Stream.o \
+ TextOutputDev.o \
+ TextString.o \
+ UnicodeMap.o \
+ UnicodeTypeTable.o \
+ XFAForm.o \
+ XpdfPluginAPI.o \
+ XRef.o \
+ Zoox.o \
+ pdftotext.o
PDFTOTEXT_LIBS = -L$(GOOLIBDIR) -lGoo $(OTHERLIBS) -lm
pdftotext$(EXE): $(PDFTOTEXT_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
@@ -169,14 +296,105 @@ pdftotext$(EXE): $(PDFTOTEXT_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
#------------------------------------------------------------------------
-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 \
- OptionalContent.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
+PDFTOHTML_OBJS = \
+ AcroForm.o \
+ Annot.o \
+ Array.o \
+ BuiltinFont.o \
+ BuiltinFontTables.o \
+ Catalog.o \
+ CharCodeToUnicode.o \
+ CMap.o \
+ Decrypt.o \
+ Dict.o \
+ Error.o \
+ FontEncodingTables.o \
+ Form.o \
+ Function.o \
+ Gfx.o \
+ GfxFont.o \
+ GfxState.o \
+ GlobalParams.o \
+ HTMLGen.o \
+ JArithmeticDecoder.o \
+ JBIG2Stream.o \
+ JPXStream.o \
+ Lexer.o \
+ Link.o \
+ NameToCharCode.o \
+ Object.o \
+ OptionalContent.o \
+ Outline.o \
+ OutputDev.o \
+ Page.o \
+ Parser.o \
+ PDFDoc.o \
+ PDFDocEncoding.o \
+ PSTokenizer.o \
+ SecurityHandler.o \
+ SplashOutputDev.o \
+ Stream.o \
+ TextOutputDev.o \
+ TextString.o \
+ UnicodeMap.o \
+ UnicodeTypeTable.o \
+ XFAForm.o \
+ XpdfPluginAPI.o \
+ XRef.o \
+ Zoox.o \
+ pdftohtml.o
+PDFTOHTML_LIBS = -L$(GOOLIBDIR) -lGoo $(SPLASHLIBS) $(FTLIBS) \
+ $(OTHERLIBS) $(PNGLIBS) -lm
+
+pdftohtml$(EXE): $(PDFTOHTML_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
+ $(CXX) $(CXXFLAGS) $(LDFLAGS) -o pdftohtml$(EXE) $(PDFTOHTML_OBJS) \
+ $(PDFTOHTML_LIBS)
+
+#------------------------------------------------------------------------
+
+PDFINFO_OBJS = \
+ AcroForm.o \
+ Annot.o \
+ Array.o \
+ BuiltinFont.o \
+ BuiltinFontTables.o \
+ Catalog.o \
+ CharCodeToUnicode.o \
+ CMap.o \
+ Decrypt.o \
+ Dict.o \
+ Error.o \
+ FontEncodingTables.o \
+ Form.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 \
+ OptionalContent.o \
+ Outline.o \
+ OutputDev.o \
+ Page.o \
+ Parser.o \
+ PDFDoc.o \
+ PDFDocEncoding.o \
+ PSTokenizer.o \
+ SecurityHandler.o \
+ Stream.o \
+ TextString.o \
+ UnicodeMap.o \
+ XFAForm.o \
+ XpdfPluginAPI.o \
+ XRef.o \
+ Zoox.o \
+ pdfinfo.o
PDFINFO_LIBS = -L$(GOOLIBDIR) -lGoo $(OTHERLIBS) -lm
pdfinfo$(EXE): $(PDFINFO_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
@@ -185,14 +403,49 @@ pdfinfo$(EXE): $(PDFINFO_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
#------------------------------------------------------------------------
-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 \
- OptionalContent.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_OBJS = \
+ AcroForm.o \
+ Annot.o \
+ Array.o \
+ BuiltinFont.o \
+ BuiltinFontTables.o \
+ Catalog.o \
+ CharCodeToUnicode.o \
+ CMap.o \
+ Decrypt.o \
+ Dict.o \
+ Error.o \
+ FontEncodingTables.o \
+ Form.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 \
+ OptionalContent.o \
+ Outline.o \
+ OutputDev.o \
+ Page.o \
+ Parser.o \
+ PDFDoc.o \
+ PDFDocEncoding.o \
+ PSTokenizer.o \
+ SecurityHandler.o \
+ Stream.o \
+ TextString.o \
+ UnicodeMap.o \
+ XFAForm.o \
+ XpdfPluginAPI.o \
+ XRef.o \
+ Zoox.o \
+ pdffonts.o
PDFFONTS_LIBS = -L$(GOOLIBDIR) -lGoo $(OTHERLIBS) -lm
pdffonts$(EXE): $(PDFFONTS_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
@@ -201,14 +454,49 @@ pdffonts$(EXE): $(PDFFONTS_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
#------------------------------------------------------------------------
-PDFDETACH_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 \
- OptionalContent.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 pdfdetach.o
+PDFDETACH_OBJS = \
+ AcroForm.o \
+ Annot.o \
+ Array.o \
+ BuiltinFont.o \
+ BuiltinFontTables.o \
+ Catalog.o \
+ CharCodeToUnicode.o \
+ CMap.o \
+ Decrypt.o \
+ Dict.o \
+ Error.o \
+ FontEncodingTables.o \
+ Form.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 \
+ OptionalContent.o \
+ Outline.o \
+ OutputDev.o \
+ Page.o \
+ Parser.o \
+ PDFDoc.o \
+ PDFDocEncoding.o \
+ PSTokenizer.o \
+ SecurityHandler.o \
+ Stream.o \
+ TextString.o \
+ UnicodeMap.o \
+ XFAForm.o \
+ XpdfPluginAPI.o \
+ XRef.o \
+ Zoox.o \
+ pdfdetach.o
PDFDETACH_LIBS = -L$(GOOLIBDIR) -lGoo $(OTHERLIBS) -lm
pdfdetach$(EXE): $(PDFDETACH_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
@@ -217,16 +505,53 @@ pdfdetach$(EXE): $(PDFDETACH_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
#------------------------------------------------------------------------
-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 OptionalContent.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) \
+PDFTOPPM_OBJS = \
+ AcroForm.o \
+ Annot.o \
+ Array.o \
+ BuiltinFont.o \
+ BuiltinFontTables.o \
+ Catalog.o \
+ CharCodeToUnicode.o \
+ CMap.o \
+ Decrypt.o \
+ Dict.o \
+ Error.o \
+ FontEncodingTables.o \
+ Form.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 \
+ OptionalContent.o \
+ Outline.o \
+ OutputDev.o \
+ Page.o \
+ Parser.o \
+ PDFDoc.o \
+ PDFDocEncoding.o \
+ PSTokenizer.o \
+ SecurityHandler.o \
+ SplashOutputDev.o \
+ Stream.o \
+ TextOutputDev.o \
+ TextString.o \
+ UnicodeMap.o \
+ UnicodeTypeTable.o \
+ XFAForm.o \
+ XpdfPluginAPI.o \
+ XRef.o \
+ Zoox.o \
+ pdftoppm.o
+PDFTOPPM_LIBS = -L$(GOOLIBDIR) -lGoo $(SPLASHLIBS) $(FTLIBS) \
$(OTHERLIBS) -lm
pdftoppm$(EXE): $(PDFTOPPM_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
@@ -235,14 +560,105 @@ pdftoppm$(EXE): $(PDFTOPPM_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
#------------------------------------------------------------------------
-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 \
- OptionalContent.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
+PDFTOPNG_OBJS = \
+ AcroForm.o \
+ Annot.o \
+ Array.o \
+ BuiltinFont.o \
+ BuiltinFontTables.o \
+ Catalog.o \
+ CharCodeToUnicode.o \
+ CMap.o \
+ Decrypt.o \
+ Dict.o \
+ Error.o \
+ FontEncodingTables.o \
+ Form.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 \
+ OptionalContent.o \
+ Outline.o \
+ OutputDev.o \
+ Page.o \
+ Parser.o \
+ PDFDoc.o \
+ PDFDocEncoding.o \
+ PSTokenizer.o \
+ SecurityHandler.o \
+ SplashOutputDev.o \
+ Stream.o \
+ TextOutputDev.o \
+ TextString.o \
+ UnicodeMap.o \
+ UnicodeTypeTable.o \
+ XFAForm.o \
+ XpdfPluginAPI.o \
+ XRef.o \
+ Zoox.o \
+ pdftopng.o
+PDFTOPNG_LIBS = -L$(GOOLIBDIR) -lGoo $(SPLASHLIBS) $(FTLIBS) \
+ $(OTHERLIBS) $(PNGLIBS) -lm
+
+pdftopng$(EXE): $(PDFTOPNG_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
+ $(CXX) $(CXXFLAGS) $(LDFLAGS) -o pdftopng$(EXE) $(PDFTOPNG_OBJS) \
+ $(PDFTOPNG_LIBS)
+
+#------------------------------------------------------------------------
+
+PDFIMAGES_OBJS = \
+ AcroForm.o \
+ Annot.o \
+ Array.o \
+ BuiltinFont.o \
+ BuiltinFontTables.o \
+ Catalog.o \
+ CharCodeToUnicode.o \
+ CMap.o \
+ Decrypt.o \
+ Dict.o \
+ Error.o \
+ FontEncodingTables.o \
+ Form.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 \
+ OptionalContent.o \
+ Outline.o \
+ OutputDev.o \
+ Page.o \
+ Parser.o \
+ PDFDoc.o \
+ PDFDocEncoding.o \
+ PSTokenizer.o \
+ SecurityHandler.o \
+ Stream.o \
+ TextString.o \
+ UnicodeMap.o \
+ XFAForm.o \
+ XpdfPluginAPI.o \
+ XRef.o \
+ Zoox.o \
+ pdfimages.o
PDFIMAGES_LIBS = -L$(GOOLIBDIR) -lGoo $(OTHERLIBS) -lm
pdfimages$(EXE): $(PDFIMAGES_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
@@ -255,10 +671,12 @@ clean:
rm -f $(XPDF_OBJS) xpdf$(EXE)
rm -f $(PDFTOPS_OBJS) pdftops$(EXE)
rm -f $(PDFTOTEXT_OBJS) pdftotext$(EXE)
+ rm -f $(PDFTOHTML_OBJS) pdftohtml$(EXE)
rm -f $(PDFINFO_OBJS) pdfinfo$(EXE)
rm -f $(PDFFONTS_OBJS) pdffonts$(EXE)
rm -f $(PDFDETACH_OBJS) pdfdetach$(EXE)
rm -f $(PDFTOPPM_OBJS) pdftoppm$(EXE)
+ rm -f $(PDFTOPNG_OBJS) pdftopng$(EXE)
rm -f $(PDFIMAGES_OBJS) pdfimages$(EXE)
#------------------------------------------------------------------------
@@ -266,4 +684,4 @@ clean:
depend:
$(CXX) $(CXXFLAGS) -MM $(CXX_SRC) >Makefile.dep
-include Makefile.dep
+-include Makefile.dep
diff --git a/xpdf/Object.h b/xpdf/Object.h
index e1abed9..64c75f7 100644
--- a/xpdf/Object.h
+++ b/xpdf/Object.h
@@ -19,6 +19,7 @@
#include <string.h>
#include "gtypes.h"
#include "gmem.h"
+#include "gfile.h"
#include "GString.h"
class XRef;
@@ -179,9 +180,10 @@ public:
void streamClose();
int streamGetChar();
int streamLookChar();
+ int streamGetBlock(char *blk, int size);
char *streamGetLine(char *buf, int size);
- Guint streamGetPos();
- void streamSetPos(Guint pos, int dir = 0);
+ GFileOffset streamGetPos();
+ void streamSetPos(GFileOffset pos, int dir = 0);
Dict *streamGetDict();
// Output.
@@ -288,13 +290,16 @@ inline int Object::streamGetChar()
inline int Object::streamLookChar()
{ return stream->lookChar(); }
+inline int Object::streamGetBlock(char *blk, int size)
+ { return stream->getBlock(blk, size); }
+
inline char *Object::streamGetLine(char *buf, int size)
{ return stream->getLine(buf, size); }
-inline Guint Object::streamGetPos()
+inline GFileOffset Object::streamGetPos()
{ return stream->getPos(); }
-inline void Object::streamSetPos(Guint pos, int dir)
+inline void Object::streamSetPos(GFileOffset pos, int dir)
{ stream->setPos(pos, dir); }
inline Dict *Object::streamGetDict()
diff --git a/xpdf/OptionalContent.cc b/xpdf/OptionalContent.cc
index 589719c..bebb674 100644
--- a/xpdf/OptionalContent.cc
+++ b/xpdf/OptionalContent.cc
@@ -2,7 +2,7 @@
//
// OptionalContent.cc
//
-// Copyright 2008 Glyph & Cog, LLC
+// Copyright 2008-2013 Glyph & Cog, LLC
//
//========================================================================
@@ -17,7 +17,7 @@
#include "Error.h"
#include "Object.h"
#include "PDFDoc.h"
-#include "PDFDocEncoding.h"
+#include "TextString.h"
#include "OptionalContent.h"
//------------------------------------------------------------------------
@@ -150,70 +150,79 @@ GBool OptionalContent::evalOCObject(Object *obj, GBool *visible) {
}
}
obj->fetch(xref, &obj2);
- if (obj2.isDict("OCMD")) {
- if (obj2.dictLookup("VE", &obj3)->isArray()) {
- *visible = evalOCVisibilityExpr(&obj3, 0);
- obj3.free();
- } else {
- obj3.free();
- policy = ocPolicyAnyOn;
- if (obj2.dictLookup("P", &obj3)->isName()) {
- if (obj3.isName("AllOn")) {
- policy = ocPolicyAllOn;
- } else if (obj3.isName("AnyOn")) {
- policy = ocPolicyAnyOn;
- } else if (obj3.isName("AnyOff")) {
- policy = ocPolicyAnyOff;
- } else if (obj3.isName("AllOff")) {
- policy = ocPolicyAllOff;
- }
+ if (!obj2.isDict("OCMD")) {
+ obj2.free();
+ return gFalse;
+ }
+ if (obj2.dictLookup("VE", &obj3)->isArray()) {
+ *visible = evalOCVisibilityExpr(&obj3, 0);
+ obj3.free();
+ } else {
+ obj3.free();
+ policy = ocPolicyAnyOn;
+ if (obj2.dictLookup("P", &obj3)->isName()) {
+ if (obj3.isName("AllOn")) {
+ policy = ocPolicyAllOn;
+ } else if (obj3.isName("AnyOn")) {
+ policy = ocPolicyAnyOn;
+ } else if (obj3.isName("AnyOff")) {
+ policy = ocPolicyAnyOff;
+ } else if (obj3.isName("AllOff")) {
+ policy = ocPolicyAllOff;
}
- obj3.free();
- obj2.dictLookupNF("OCGs", &obj3);
- ocg = NULL;
- if (obj3.isRef()) {
- ref = obj3.getRef();
- ocg = findOCG(&ref);
+ }
+ obj3.free();
+ obj2.dictLookupNF("OCGs", &obj3);
+ ocg = NULL;
+ if (obj3.isRef()) {
+ ref = obj3.getRef();
+ ocg = findOCG(&ref);
+ }
+ if (ocg) {
+ *visible = (policy == ocPolicyAllOn || policy == ocPolicyAnyOn) ?
+ ocg->getState() : !ocg->getState();
+ } else {
+ *visible = policy == ocPolicyAllOn || policy == ocPolicyAllOff;
+ if (!obj3.fetch(xref, &obj4)->isArray()) {
+ obj4.free();
+ obj3.free();
+ obj2.free();
+ return gFalse;
}
- if (ocg) {
- *visible = (policy == ocPolicyAllOn || policy == ocPolicyAnyOn) ?
- ocg->getState() : !ocg->getState();
- } else {
- *visible = gTrue;
- if (obj3.fetch(xref, &obj4)->isArray()) {
- for (i = 0; i < obj4.arrayGetLength(); ++i) {
- obj4.arrayGetNF(i, &obj5);
- if (obj5.isRef()) {
- ref = obj5.getRef();
- if ((ocg = findOCG(&ref))) {
- switch (policy) {
- case ocPolicyAllOn:
- *visible = *visible && ocg->getState();
- break;
- case ocPolicyAnyOn:
- *visible = *visible || ocg->getState();
- break;
- case ocPolicyAnyOff:
- *visible = *visible || !ocg->getState();
- break;
- case ocPolicyAllOff:
- *visible = *visible && !ocg->getState();
- break;
- }
- }
- }
+ for (i = 0; i < obj4.arrayGetLength(); ++i) {
+ obj4.arrayGetNF(i, &obj5);
+ if (obj5.isRef()) {
+ ref = obj5.getRef();
+ if (!(ocg = findOCG(&ref))) {
obj5.free();
+ obj4.free();
+ obj3.free();
+ obj2.free();
+ return gFalse;
+ }
+ switch (policy) {
+ case ocPolicyAllOn:
+ *visible = *visible && ocg->getState();
+ break;
+ case ocPolicyAnyOn:
+ *visible = *visible || ocg->getState();
+ break;
+ case ocPolicyAnyOff:
+ *visible = *visible || !ocg->getState();
+ break;
+ case ocPolicyAllOff:
+ *visible = *visible && !ocg->getState();
+ break;
}
}
- obj4.free();
+ obj5.free();
}
- obj3.free();
+ obj4.free();
}
- obj2.free();
- return gTrue;
+ obj3.free();
}
obj2.free();
- return gFalse;
+ return gTrue;
}
GBool OptionalContent::evalOCVisibilityExpr(Object *expr, int recursion) {
@@ -279,12 +288,9 @@ GBool OptionalContent::evalOCVisibilityExpr(Object *expr, int recursion) {
//------------------------------------------------------------------------
OptionalContentGroup *OptionalContentGroup::parse(Ref *refA, Object *obj) {
- Unicode *nameA;
- int nameLenA;
+ TextString *nameA;
Object obj1, obj2, obj3;
- GString *s;
OCUsageState viewStateA, printStateA;
- int i;
if (!obj->isDict()) {
return NULL;
@@ -294,22 +300,7 @@ OptionalContentGroup *OptionalContentGroup::parse(Ref *refA, Object *obj) {
obj1.free();
return NULL;
}
- s = obj1.getString();
- if ((s->getChar(0) & 0xff) == 0xfe &&
- (s->getChar(1) & 0xff) == 0xff) {
- nameLenA = (s->getLength() - 2) / 2;
- nameA = (Unicode *)gmallocn(nameLenA, sizeof(Unicode));
- for (i = 0; i < nameLenA; ++i) {
- nameA[i] = ((s->getChar(2 + 2*i) & 0xff) << 8) |
- (s->getChar(3 + 2*i) & 0xff);
- }
- } else {
- nameLenA = s->getLength();
- nameA = (Unicode *)gmallocn(nameLenA, sizeof(Unicode));
- for (i = 0; i < nameLenA; ++i) {
- nameA[i] = pdfDocEncoding[s->getChar(i) & 0xff];
- }
- }
+ nameA = new TextString(obj1.getString());
obj1.free();
viewStateA = printStateA = ocUsageUnset;
@@ -339,30 +330,35 @@ OptionalContentGroup *OptionalContentGroup::parse(Ref *refA, Object *obj) {
}
obj1.free();
- return new OptionalContentGroup(refA, nameA, nameLenA,
- viewStateA, printStateA);
+ return new OptionalContentGroup(refA, nameA, viewStateA, printStateA);
}
-OptionalContentGroup::OptionalContentGroup(Ref *refA, Unicode *nameA,
- int nameLenA,
+OptionalContentGroup::OptionalContentGroup(Ref *refA, TextString *nameA,
OCUsageState viewStateA,
OCUsageState printStateA) {
ref = *refA;
name = nameA;
- nameLen = nameLenA;
viewState = viewStateA;
printState = printStateA;
state = gTrue;
}
OptionalContentGroup::~OptionalContentGroup() {
- gfree(name);
+ delete name;
}
GBool OptionalContentGroup::matches(Ref *refA) {
return refA->num == ref.num && refA->gen == ref.gen;
}
+Unicode *OptionalContentGroup::getName() {
+ return name->getUnicode();
+}
+
+int OptionalContentGroup::getNameLength() {
+ return name->getLength();
+}
+
//------------------------------------------------------------------------
OCDisplayNode *OCDisplayNode::parse(Object *obj, OptionalContent *oc,
@@ -404,8 +400,10 @@ OCDisplayNode *OCDisplayNode::parse(Object *obj, OptionalContent *oc,
obj2.arrayGetNF(i, &obj3);
if ((child = OCDisplayNode::parse(&obj3, oc, xref, recursion + 1))) {
if (!child->ocg && !child->name && node->getNumChildren() > 0) {
- node->getChild(node->getNumChildren() - 1)->
- addChildren(child->takeChildren());
+ if (child->getNumChildren() > 0) {
+ node->getChild(node->getNumChildren() - 1)->
+ addChildren(child->takeChildren());
+ }
delete child;
} else {
node->addChild(child);
@@ -418,42 +416,19 @@ OCDisplayNode *OCDisplayNode::parse(Object *obj, OptionalContent *oc,
}
OCDisplayNode::OCDisplayNode() {
- name = NULL;
- nameLen = 0;
+ name = new TextString();
ocg = NULL;
children = NULL;
}
OCDisplayNode::OCDisplayNode(GString *nameA) {
- int i;
-
- if ((nameA->getChar(0) & 0xff) == 0xfe &&
- (nameA->getChar(1) & 0xff) == 0xff) {
- nameLen = (nameA->getLength() - 2) / 2;
- name = (Unicode *)gmallocn(nameLen, sizeof(Unicode));
- for (i = 0; i < nameLen; ++i) {
- name[i] = ((nameA->getChar(2 + 2*i) & 0xff) << 8) |
- (nameA->getChar(3 + 2*i) & 0xff);
- }
- } else {
- nameLen = nameA->getLength();
- name = (Unicode *)gmallocn(nameLen, sizeof(Unicode));
- for (i = 0; i < nameLen; ++i) {
- name[i] = pdfDocEncoding[nameA->getChar(i) & 0xff];
- }
- }
+ name = new TextString(nameA);
ocg = NULL;
children = NULL;
}
OCDisplayNode::OCDisplayNode(OptionalContentGroup *ocgA) {
- nameLen = ocgA->getNameLength();
- if (nameLen) {
- name = (Unicode *)gmallocn(nameLen, sizeof(Unicode));
- memcpy(name, ocgA->getName(), nameLen * sizeof(Unicode));
- } else {
- name = NULL;
- }
+ name = new TextString(ocgA->name);
ocg = ocgA;
children = NULL;
}
@@ -482,12 +457,20 @@ GList *OCDisplayNode::takeChildren() {
}
OCDisplayNode::~OCDisplayNode() {
- gfree(name);
+ delete name;
if (children) {
deleteGList(children, OCDisplayNode);
}
}
+Unicode *OCDisplayNode::getName() {
+ return name->getUnicode();
+}
+
+int OCDisplayNode::getNameLength() {
+ return name->getLength();
+}
+
int OCDisplayNode::getNumChildren() {
if (!children) {
return 0;
diff --git a/xpdf/OptionalContent.h b/xpdf/OptionalContent.h
index 82d3e0e..e2ea244 100644
--- a/xpdf/OptionalContent.h
+++ b/xpdf/OptionalContent.h
@@ -2,7 +2,7 @@
//
// OptionalContent.h
//
-// Copyright 2008 Glyph & Cog, LLC
+// Copyright 2008-2013 Glyph & Cog, LLC
//
//========================================================================
@@ -22,6 +22,7 @@
class GString;
class GList;
class PDFDoc;
+class TextString;
class XRef;
class OptionalContentGroup;
class OCDisplayNode;
@@ -78,8 +79,8 @@ public:
GBool matches(Ref *refA);
- Unicode *getName() { return name; }
- int getNameLength() { return nameLen; }
+ Unicode *getName();
+ int getNameLength();
OCUsageState getViewState() { return viewState; }
OCUsageState getPrintState() { return printState; }
GBool getState() { return state; }
@@ -87,15 +88,16 @@ public:
private:
- OptionalContentGroup(Ref *refA, Unicode *nameA, int nameLenA,
+ OptionalContentGroup(Ref *refA, TextString *nameA,
OCUsageState viewStateA, OCUsageState printStateA);
Ref ref;
- Unicode *name;
- int nameLen;
+ TextString *name;
OCUsageState viewState, // suggested state when viewing
printState; // suggested state when printing
GBool state; // current state (on/off)
+
+ friend class OCDisplayNode;
};
//------------------------------------------------------------------------
@@ -108,8 +110,8 @@ public:
OCDisplayNode();
~OCDisplayNode();
- Unicode *getName() { return name; }
- int getNameLength() { return nameLen; }
+ Unicode *getName();
+ int getNameLength();
OptionalContentGroup *getOCG() { return ocg; }
int getNumChildren();
OCDisplayNode *getChild(int idx);
@@ -122,8 +124,7 @@ private:
void addChildren(GList *childrenA);
GList *takeChildren();
- Unicode *name; // display name (may be NULL)
- int nameLen;
+ TextString *name; // display name
OptionalContentGroup *ocg; // NULL for display labels
GList *children; // NULL if there are no children
// [OCDisplayNode]
diff --git a/xpdf/Outline.cc b/xpdf/Outline.cc
index 30ca85d..e87f61e 100644
--- a/xpdf/Outline.cc
+++ b/xpdf/Outline.cc
@@ -2,7 +2,7 @@
//
// Outline.cc
//
-// Copyright 2002-2003 Glyph & Cog, LLC
+// Copyright 2002-2013 Glyph & Cog, LLC
//
//========================================================================
@@ -17,7 +17,7 @@
#include "GList.h"
#include "Error.h"
#include "Link.h"
-#include "PDFDocEncoding.h"
+#include "TextString.h"
#include "Outline.h"
//------------------------------------------------------------------------
@@ -32,7 +32,7 @@ Outline::Outline(Object *outlineObj, XRef *xref) {
outlineObj->dictLookupNF("First", &first);
outlineObj->dictLookupNF("Last", &last);
if (first.isRef() && last.isRef()) {
- items = OutlineItem::readItemList(&first, &last, xref);
+ items = OutlineItem::readItemList(&first, &last, NULL, xref);
}
first.free();
last.free();
@@ -46,35 +46,18 @@ Outline::~Outline() {
//------------------------------------------------------------------------
-OutlineItem::OutlineItem(Dict *dict, XRef *xrefA) {
+OutlineItem::OutlineItem(Object *itemRefA, Dict *dict,
+ OutlineItem *parentA, XRef *xrefA) {
Object obj1;
- GString *s;
- int i;
xref = xrefA;
title = NULL;
action = NULL;
kids = NULL;
+ parent = parentA;
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;
+ title = new TextString(obj1.getString());
}
obj1.free();
@@ -88,6 +71,7 @@ OutlineItem::OutlineItem(Dict *dict, XRef *xrefA) {
}
obj1.free();
+ itemRefA->copy(&itemRef);
dict->lookupNF("First", &firstRef);
dict->lookupNF("Last", &lastRef);
dict->lookupNF("Next", &nextRef);
@@ -104,22 +88,24 @@ OutlineItem::OutlineItem(Dict *dict, XRef *xrefA) {
OutlineItem::~OutlineItem() {
close();
if (title) {
- gfree(title);
+ delete title;
}
if (action) {
delete action;
}
+ itemRef.free();
firstRef.free();
lastRef.free();
nextRef.free();
}
GList *OutlineItem::readItemList(Object *firstItemRef, Object *lastItemRef,
- XRef *xrefA) {
+ OutlineItem *parentA, XRef *xrefA) {
GList *items;
- OutlineItem *item;
+ OutlineItem *item, *sibling;
Object obj;
- Object *p, *refObj;
+ Object *p;
+ OutlineItem *ancestor;
int i;
items = new GList();
@@ -132,8 +118,36 @@ GList *OutlineItem::readItemList(Object *firstItemRef, Object *lastItemRef,
obj.free();
break;
}
- item = new OutlineItem(obj.getDict(), xrefA);
+ item = new OutlineItem(p, obj.getDict(), parentA, xrefA);
obj.free();
+
+ // check for loops with parents
+ for (ancestor = parentA; ancestor; ancestor = ancestor->parent) {
+ if (p->getRefNum() == ancestor->itemRef.getRefNum() &&
+ p->getRefGen() == ancestor->itemRef.getRefGen()) {
+ error(errSyntaxError, -1, "Loop detected in outline");
+ break;
+ }
+ }
+ if (ancestor) {
+ delete item;
+ break;
+ }
+
+ // check for loops with siblings
+ for (i = 0; i < items->getLength(); ++i) {
+ sibling = (OutlineItem *)items->get(i);
+ if (p->getRefNum() == sibling->itemRef.getRefNum() &&
+ p->getRefGen() == sibling->itemRef.getRefGen()) {
+ error(errSyntaxError, -1, "Loop detected in outline");
+ break;
+ }
+ }
+ if (i < items->getLength()) {
+ delete item;
+ break;
+ }
+
items->append(item);
if (p->getRefNum() == lastItemRef->getRef().num &&
p->getRefGen() == lastItemRef->getRef().gen) {
@@ -143,23 +157,13 @@ GList *OutlineItem::readItemList(Object *firstItemRef, Object *lastItemRef,
if (!p->isRef()) {
break;
}
- for (i = 0; i < items->getLength(); ++i) {
- refObj = (i == 0) ? firstItemRef
- : &((OutlineItem *)items->get(i - 1))->nextRef;
- if (refObj->getRefNum() == p->getRefNum() &&
- refObj->getRefGen() == p->getRefGen()) {
- error(errSyntaxError, -1, "Loop detected in outline item list");
- p = NULL;
- break;
- }
- }
} while (p);
return items;
}
void OutlineItem::open() {
if (!kids) {
- kids = readItemList(&firstRef, &lastRef, xref);
+ kids = readItemList(&firstRef, &lastRef, this, xref);
}
}
@@ -169,3 +173,11 @@ void OutlineItem::close() {
kids = NULL;
}
}
+
+Unicode *OutlineItem::getTitle() {
+ return title ? title->getUnicode() : (Unicode *)NULL;
+}
+
+int OutlineItem::getTitleLength() {
+ return title ? title->getLength() : 0;
+}
diff --git a/xpdf/Outline.h b/xpdf/Outline.h
index f38f8d1..a9c2089 100644
--- a/xpdf/Outline.h
+++ b/xpdf/Outline.h
@@ -2,7 +2,7 @@
//
// Outline.h
//
-// Copyright 2002-2003 Glyph & Cog, LLC
+// Copyright 2002-2013 Glyph & Cog, LLC
//
//========================================================================
@@ -22,6 +22,7 @@ class GString;
class GList;
class XRef;
class LinkAction;
+class TextString;
//------------------------------------------------------------------------
@@ -44,17 +45,18 @@ private:
class OutlineItem {
public:
- OutlineItem(Dict *dict, XRef *xrefA);
+ OutlineItem(Object *itemRefA, Dict *dict, OutlineItem *parentA, XRef *xrefA);
~OutlineItem();
static GList *readItemList(Object *firstItemRef, Object *lastItemRef,
- XRef *xrefA);
+ OutlineItem *parentA, XRef *xrefA);
void open();
void close();
- Unicode *getTitle() { return title; }
- int getTitleLength() { return titleLen; }
+ Unicode *getTitle();
+ int getTitleLength();
+ TextString *getTitleTextString() { return title; }
LinkAction *getAction() { return action; }
GBool isOpen() { return startsOpen; }
GBool hasKids() { return firstRef.isRef(); }
@@ -63,14 +65,15 @@ public:
private:
XRef *xref;
- Unicode *title;
- int titleLen;
+ TextString *title; // may be NULL
LinkAction *action;
+ Object itemRef;
Object firstRef;
Object lastRef;
Object nextRef;
GBool startsOpen;
GList *kids; // NULL unless this item is open [OutlineItem]
+ OutlineItem *parent;
};
#endif
diff --git a/xpdf/OutputDev.cc b/xpdf/OutputDev.cc
index d2ef286..2d17547 100644
--- a/xpdf/OutputDev.cc
+++ b/xpdf/OutputDev.cc
@@ -43,6 +43,11 @@ void OutputDev::cvtDevToUser(double dx, double dy, double *ux, double *uy) {
*uy = defICTM[1] * dx + defICTM[3] * dy + defICTM[5];
}
+void OutputDev::cvtUserToDev(double ux, double uy, double *dx, double *dy) {
+ *dx = defCTM[0] * ux + defCTM[2] * uy + defCTM[4];
+ *dy = defCTM[1] * ux + defCTM[3] * uy + defCTM[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);
@@ -78,14 +83,10 @@ GBool OutputDev::beginType3Char(GfxState *state, double x, double y,
void OutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
int width, int height, GBool invert,
- GBool inlineImg) {
- int i, j;
-
+ GBool inlineImg, GBool interpolate) {
if (inlineImg) {
str->reset();
- j = height * ((width + 7) / 8);
- for (i = 0; i < j; ++i)
- str->getChar();
+ str->discardChars(height * ((width + 7) / 8));
str->close();
}
}
@@ -93,21 +94,17 @@ void OutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
void OutputDev::setSoftMaskFromImageMask(GfxState *state,
Object *ref, Stream *str,
int width, int height, GBool invert,
- GBool inlineImg) {
- drawImageMask(state, ref, str, width, height, invert, inlineImg);
+ GBool inlineImg, GBool interpolate) {
+ drawImageMask(state, ref, str, width, height, invert, inlineImg, interpolate);
}
void OutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
int width, int height, GfxImageColorMap *colorMap,
- int *maskColors, GBool inlineImg) {
- int i, j;
-
+ int *maskColors, GBool inlineImg, GBool interpolate) {
if (inlineImg) {
str->reset();
- j = height * ((width * colorMap->getNumPixelComps() *
- colorMap->getBits() + 7) / 8);
- for (i = 0; i < j; ++i)
- str->getChar();
+ str->discardChars(height * ((width * colorMap->getNumPixelComps() *
+ colorMap->getBits() + 7) / 8));
str->close();
}
}
@@ -117,8 +114,9 @@ void OutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
GfxImageColorMap *colorMap,
Stream *maskStr,
int maskWidth, int maskHeight,
- GBool maskInvert) {
- drawImage(state, ref, str, width, height, colorMap, NULL, gFalse);
+ GBool maskInvert, GBool interpolate) {
+ drawImage(state, ref, str, width, height, colorMap, NULL, gFalse,
+ interpolate);
}
void OutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
@@ -126,8 +124,10 @@ void OutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
GfxImageColorMap *colorMap,
Stream *maskStr,
int maskWidth, int maskHeight,
- GfxImageColorMap *maskColorMap) {
- drawImage(state, ref, str, width, height, colorMap, NULL, gFalse);
+ GfxImageColorMap *maskColorMap,
+ GBool interpolate) {
+ drawImage(state, ref, str, width, height, colorMap, NULL, gFalse,
+ interpolate);
}
#if OPI_SUPPORT
diff --git a/xpdf/OutputDev.h b/xpdf/OutputDev.h
index 138aa34..759e356 100644
--- a/xpdf/OutputDev.h
+++ b/xpdf/OutputDev.h
@@ -112,6 +112,7 @@ public:
// Convert between device and user coordinates.
virtual void cvtDevToUser(double dx, double dy, double *ux, double *uy);
+ virtual void cvtUserToDev(double ux, double uy, double *dx, double *dy);
virtual void cvtUserToDev(double ux, double uy, int *dx, int *dy);
double *getDefCTM() { return defCTM; }
@@ -161,7 +162,7 @@ public:
virtual void stroke(GfxState *state) {}
virtual void fill(GfxState *state) {}
virtual void eoFill(GfxState *state) {}
- virtual void tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
+ virtual void tilingPatternFill(GfxState *state, Gfx *gfx, Object *strRef,
int paintType, Dict *resDict,
double *mat, double *bbox,
int x0, int y0, int x1, int y1,
@@ -201,25 +202,26 @@ public:
//----- image drawing
virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
int width, int height, GBool invert,
- GBool inlineImg);
+ GBool inlineImg, GBool interpolate);
virtual void setSoftMaskFromImageMask(GfxState *state,
Object *ref, Stream *str,
int width, int height, GBool invert,
- GBool inlineImg);
+ GBool inlineImg, GBool interpolate);
virtual void drawImage(GfxState *state, Object *ref, Stream *str,
int width, int height, GfxImageColorMap *colorMap,
- int *maskColors, GBool inlineImg);
+ int *maskColors, GBool inlineImg, GBool interpolate);
virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
Stream *maskStr, int maskWidth, int maskHeight,
- GBool maskInvert);
+ GBool maskInvert, GBool interpolate);
virtual void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
Stream *maskStr,
int maskWidth, int maskHeight,
- GfxImageColorMap *maskColorMap);
+ GfxImageColorMap *maskColorMap,
+ GBool interpolate);
#if OPI_SUPPORT
//----- OPI functions
diff --git a/xpdf/PDFCore.cc b/xpdf/PDFCore.cc
index 6435815..34b6483 100644
--- a/xpdf/PDFCore.cc
+++ b/xpdf/PDFCore.cc
@@ -2,7 +2,7 @@
//
// PDFCore.cc
//
-// Copyright 2004 Glyph & Cog, LLC
+// Copyright 2004-2013 Glyph & Cog, LLC
//
//========================================================================
@@ -100,7 +100,8 @@ PDFCore::PDFCore(SplashColorMode colorModeA, int bitmapRowPadA,
selectULY = selectLRY = 0;
dragging = gFalse;
lastDragLeft = lastDragTop = gTrue;
- selectXorColor[0] = selectXorColor[1] = selectXorColor[2] = 0;
+ selectXorColor[0] = selectXorColor[1] = selectXorColor[2] =
+ reverseVideoA ? 0xff : 0x00;
splashColorXor(selectXorColor, paperColorA);
historyCur = pdfHistorySize - 1;
@@ -128,7 +129,11 @@ PDFCore::~PDFCore() {
}
for (i = 0; i < pdfHistorySize; ++i) {
if (history[i].fileName) {
+#ifdef _WIN32
+ delete[] history[i].fileName;
+#else
delete history[i].fileName;
+#endif
}
}
gfree(pageY);
@@ -147,7 +152,7 @@ int PDFCore::loadFile(GString *fileName, GString *ownerPassword,
return err;
}
-#ifdef WIN32
+#ifdef _WIN32
int PDFCore::loadFile(wchar_t *fileName, int fileNameLen,
GString *ownerPassword, GString *userPassword) {
int err;
@@ -423,6 +428,7 @@ void PDFCore::update(int topPageA, int scrollXA, int scrollYA,
// check for changes to the PDF file
if ((force || (!continuousMode && topPage != topPageA)) &&
+ doc->getFileName() &&
checkForNewFile()) {
if (loadFile(doc->getFileName()) == errNone) {
if (topPageA > doc->getNumPages()) {
@@ -758,13 +764,30 @@ void PDFCore::update(int topPageA, int scrollXA, int scrollYA,
}
hist = &history[historyCur];
if (hist->fileName) {
+#ifdef _WIN32
+ delete[] hist->fileName;
+#else
delete hist->fileName;
+#endif
+ }
+#ifdef _WIN32
+ if (doc->getFileNameU()) {
+ hist->fileName = (wchar_t *)gmallocn(MAX_PATH + 1, sizeof(wchar_t));
+ if (GetFullPathNameW(doc->getFileNameU(), MAX_PATH + 1,
+ hist->fileName, NULL) == 0) {
+ delete[] hist->fileName;
+ hist->fileName = NULL;
+ }
+ } else {
+ hist->fileName = NULL;
}
+#else
if (doc->getFileName()) {
hist->fileName = doc->getFileName()->copy();
} else {
hist->fileName = NULL;
}
+#endif
hist->page = topPage;
if (historyBLen < pdfHistorySize) {
++historyBLen;
@@ -807,6 +830,7 @@ void PDFCore::addPage(int pg, int rot) {
void PDFCore::needTile(PDFCorePage *page, int x, int y) {
PDFCoreTile *tile;
+ TextOutputControl textOutCtrl;
TextOutputDev *textOut;
int xDest, yDest, sliceW, sliceH;
int i;
@@ -893,7 +917,8 @@ void PDFCore::needTile(PDFCorePage *page, int x, int y) {
page->links = doc->getLinks(page->page);
}
if (!page->text) {
- if ((textOut = new TextOutputDev(NULL, gTrue, 0, gFalse, gFalse))) {
+ textOutCtrl.mode = textOutPhysLayout;
+ if ((textOut = new TextOutputDev(NULL, &textOutCtrl, gFalse))) {
doc->displayPage(textOut, page->page, dpi, dpi, rotate,
gFalse, gTrue, gFalse);
page->text = textOut->takeText();
@@ -977,11 +1002,27 @@ GBool PDFCore::goForward() {
}
--historyFLen;
++historyBLen;
- if (!doc || history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
+ if (!history[historyCur].fileName) {
+ return gFalse;
+ }
+#ifdef _WIN32
+ if (!doc ||
+ !doc->getFileNameU() ||
+ wcscmp(history[historyCur].fileName, doc->getFileNameU()) != 0) {
+ if (loadFile(history[historyCur].fileName,
+ wcslen(history[historyCur].fileName)) != errNone) {
+ return gFalse;
+ }
+ }
+#else
+ if (!doc ||
+ !doc->getFileName() ||
+ history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
if (loadFile(history[historyCur].fileName) != errNone) {
return gFalse;
}
}
+#endif
pg = history[historyCur].page;
update(pg, scrollX, continuousMode ? -1 : scrollY,
zoom, rotate, gFalse, gFalse, gTrue);
@@ -999,11 +1040,27 @@ GBool PDFCore::goBackward() {
}
--historyBLen;
++historyFLen;
- if (!doc || history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
+ if (!history[historyCur].fileName) {
+ return gFalse;
+ }
+#ifdef _WIN32
+ if (!doc ||
+ !doc->getFileNameU() ||
+ wcscmp(history[historyCur].fileName, doc->getFileNameU()) != 0) {
+ if (loadFile(history[historyCur].fileName,
+ wcslen(history[historyCur].fileName)) != errNone) {
+ return gFalse;
+ }
+ }
+#else
+ if (!doc ||
+ !doc->getFileName() ||
+ history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
if (loadFile(history[historyCur].fileName) != errNone) {
return gFalse;
}
}
+#endif
pg = history[historyCur].page;
update(pg, scrollX, continuousMode ? -1 : scrollY,
zoom, rotate, gFalse, gFalse, gTrue);
@@ -1615,6 +1672,7 @@ GBool PDFCore::getSelection(int *pg, double *ulx, double *uly,
GString *PDFCore::extractText(int pg, double xMin, double yMin,
double xMax, double yMax) {
PDFCorePage *page;
+ TextOutputControl textOutCtrl;
TextOutputDev *textOut;
int x0, y0, x1, y1, t;
GString *s;
@@ -1633,7 +1691,8 @@ GString *PDFCore::extractText(int pg, double xMin, double yMin,
}
s = page->text->getText(x0, y0, x1, y1);
} else {
- textOut = new TextOutputDev(NULL, gTrue, 0, gFalse, gFalse);
+ textOutCtrl.mode = textOutPhysLayout;
+ textOut = new TextOutputDev(NULL, &textOutCtrl, gFalse);
if (textOut->isOk()) {
doc->displayPage(textOut, pg, dpi, dpi, rotate, gFalse, gTrue, gFalse);
textOut->cvtUserToDev(xMin, yMin, &x0, &y0);
@@ -1675,10 +1734,10 @@ GBool PDFCore::find(char *s, GBool caseSensitive, GBool next, GBool backward,
GBool PDFCore::findU(Unicode *u, int len, GBool caseSensitive,
GBool next, GBool backward, GBool wholeWord,
GBool onePageOnly) {
+ TextOutputControl textOutCtrl;
TextOutputDev *textOut;
double xMin, yMin, xMax, yMax;
PDFCorePage *page;
- PDFCoreTile *tile;
int pg;
GBool startAtTop, startAtLast, stopAtLast;
@@ -1721,7 +1780,8 @@ GBool PDFCore::findU(Unicode *u, int len, GBool caseSensitive,
if (!onePageOnly) {
// search following/previous pages
- textOut = new TextOutputDev(NULL, gTrue, 0, gFalse, gFalse);
+ textOutCtrl.mode = textOutPhysLayout;
+ textOut = new TextOutputDev(NULL, &textOutCtrl, gFalse);
if (!textOut->isOk()) {
delete textOut;
goto notFound;
@@ -1791,7 +1851,6 @@ GBool PDFCore::findU(Unicode *u, int len, GBool caseSensitive,
// 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));
diff --git a/xpdf/PDFCore.h b/xpdf/PDFCore.h
index 264756f..1be1010 100644
--- a/xpdf/PDFCore.h
+++ b/xpdf/PDFCore.h
@@ -100,7 +100,11 @@ public:
//------------------------------------------------------------------------
struct PDFHistory {
+#ifdef _WIN32
+ wchar_t *fileName;
+#else
GString *fileName;
+#endif
int page;
};
@@ -125,7 +129,7 @@ public:
virtual int loadFile(GString *fileName, GString *ownerPassword = NULL,
GString *userPassword = NULL);
-#ifdef WIN32
+#ifdef _WIN32
// Load a new file. Returns pdfOk or error code.
virtual int loadFile(wchar_t *fileName, int fileNameLen,
GString *ownerPassword = NULL,
diff --git a/xpdf/PDFDoc.cc b/xpdf/PDFDoc.cc
index b20ef2c..5126289 100644
--- a/xpdf/PDFDoc.cc
+++ b/xpdf/PDFDoc.cc
@@ -16,7 +16,7 @@
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
-#ifdef WIN32
+#ifdef _WIN32
# include <windows.h>
#endif
#include "GString.h"
@@ -52,7 +52,7 @@ PDFDoc::PDFDoc(GString *fileNameA, GString *ownerPassword,
GString *userPassword, PDFCore *coreA) {
Object obj;
GString *fileName1, *fileName2;
-#ifdef WIN32
+#ifdef _WIN32
int n, i;
#endif
@@ -71,7 +71,7 @@ PDFDoc::PDFDoc(GString *fileNameA, GString *ownerPassword,
optContent = NULL;
fileName = fileNameA;
-#ifdef WIN32
+#ifdef _WIN32
n = fileName->getLength();
fileNameU = (wchar_t *)gmallocn(n + 1, sizeof(wchar_t));
for (i = 0; i < n; ++i) {
@@ -114,7 +114,7 @@ PDFDoc::PDFDoc(GString *fileNameA, GString *ownerPassword,
ok = setup(ownerPassword, userPassword);
}
-#ifdef WIN32
+#ifdef _WIN32
PDFDoc::PDFDoc(wchar_t *fileNameA, int fileNameLen, GString *ownerPassword,
GString *userPassword, PDFCore *coreA) {
OSVERSIONINFO version;
@@ -169,7 +169,7 @@ PDFDoc::PDFDoc(wchar_t *fileNameA, int fileNameLen, GString *ownerPassword,
PDFDoc::PDFDoc(BaseStream *strA, GString *ownerPassword,
GString *userPassword, PDFCore *coreA) {
-#ifdef WIN32
+#ifdef _WIN32
int n, i;
#endif
@@ -178,7 +178,7 @@ PDFDoc::PDFDoc(BaseStream *strA, GString *ownerPassword,
core = coreA;
if (strA->getFileName()) {
fileName = strA->getFileName()->copy();
-#ifdef WIN32
+#ifdef _WIN32
n = fileName->getLength();
fileNameU = (wchar_t *)gmallocn(n + 1, sizeof(wchar_t));
for (i = 0; i < n; ++i) {
@@ -188,7 +188,7 @@ PDFDoc::PDFDoc(BaseStream *strA, GString *ownerPassword,
#endif
} else {
fileName = NULL;
-#ifdef WIN32
+#ifdef _WIN32
fileNameU = NULL;
#endif
}
@@ -231,6 +231,7 @@ GBool PDFDoc::setup(GString *ownerPassword, GString *userPassword) {
// read the optional content info
optContent = new OptionalContent(this);
+
// done
return gTrue;
}
@@ -294,7 +295,7 @@ PDFDoc::~PDFDoc() {
if (fileName) {
delete fileName;
}
-#ifdef WIN32
+#ifdef _WIN32
if (fileNameU) {
gfree(fileNameU);
}
@@ -309,10 +310,8 @@ void PDFDoc::checkHeader() {
int i;
pdfVersion = 0;
- for (i = 0; i < headerSearchSize; ++i) {
- hdrBuf[i] = str->getChar();
- }
- hdrBuf[headerSearchSize] = '\0';
+ memset(hdrBuf, 0, headerSearchSize + 1);
+ str->getBlock(hdrBuf, headerSearchSize);
for (i = 0; i < headerSearchSize - 5; ++i) {
if (!strncmp(&hdrBuf[i], "%PDF-", 5)) {
break;
@@ -455,15 +454,16 @@ GBool PDFDoc::isLinearized() {
GBool PDFDoc::saveAs(GString *name) {
FILE *f;
- int c;
+ char buf[4096];
+ int n;
if (!(f = fopen(name->getCString(), "wb"))) {
error(errIO, -1, "Couldn't open file '{0:t}'", name);
return gFalse;
}
str->reset();
- while ((c = str->getChar()) != EOF) {
- fputc(c, f);
+ while ((n = str->getBlock(buf, sizeof(buf))) > 0) {
+ fwrite(buf, 1, n, f);
}
str->close();
fclose(f);
@@ -482,7 +482,7 @@ GBool PDFDoc::saveEmbeddedFile(int idx, char *path) {
return ret;
}
-#ifdef WIN32
+#ifdef _WIN32
GBool PDFDoc::saveEmbeddedFile(int idx, wchar_t *path, int pathLen) {
FILE *f;
OSVERSIONINFO version;
@@ -518,14 +518,15 @@ GBool PDFDoc::saveEmbeddedFile(int idx, wchar_t *path, int pathLen) {
GBool PDFDoc::saveEmbeddedFile2(int idx, FILE *f) {
Object strObj;
- int c;
+ char buf[4096];
+ int n;
if (!catalog->getEmbeddedFileStreamObj(idx, &strObj)) {
return gFalse;
}
strObj.streamReset();
- while ((c = strObj.streamGetChar()) != EOF) {
- fputc(c, f);
+ while ((n = strObj.streamGetBlock(buf, sizeof(buf))) > 0) {
+ fwrite(buf, 1, n, f);
}
strObj.streamClose();
strObj.free();
@@ -535,24 +536,28 @@ GBool PDFDoc::saveEmbeddedFile2(int idx, FILE *f) {
char *PDFDoc::getEmbeddedFileMem(int idx, int *size) {
Object strObj;
char *buf;
- int bufSize, len, c;
+ int bufSize, sizeInc, n;
if (!catalog->getEmbeddedFileStreamObj(idx, &strObj)) {
return NULL;
}
strObj.streamReset();
- bufSize = 1024;
- buf = (char *)gmalloc(bufSize);
- len = 0;
- while ((c = strObj.streamGetChar()) != EOF) {
- if (len == bufSize) {
- bufSize *= 2;
- buf = (char *)grealloc(buf, bufSize);
+ bufSize = 0;
+ buf = NULL;
+ do {
+ sizeInc = bufSize ? bufSize : 1024;
+ if (bufSize > INT_MAX - sizeInc) {
+ error(errIO, -1, "embedded file is too large");
+ *size = 0;
+ return NULL;
}
- buf[len++] = (char)c;
- }
+ buf = (char *)grealloc(buf, bufSize + sizeInc);
+ n = strObj.streamGetBlock(buf + bufSize, sizeInc);
+ bufSize += n;
+ } while (n == sizeInc);
strObj.streamClose();
strObj.free();
- *size = len;
+ *size = bufSize;
return buf;
}
+
diff --git a/xpdf/PDFDoc.h b/xpdf/PDFDoc.h
index 94fcfb7..509c1d5 100644
--- a/xpdf/PDFDoc.h
+++ b/xpdf/PDFDoc.h
@@ -39,7 +39,7 @@ public:
PDFDoc(GString *fileNameA, GString *ownerPassword = NULL,
GString *userPassword = NULL, PDFCore *coreA = NULL);
-#ifdef WIN32
+#ifdef _WIN32
PDFDoc(wchar_t *fileNameA, int fileNameLen, GString *ownerPassword = NULL,
GString *userPassword = NULL, PDFCore *coreA = NULL);
#endif
@@ -55,7 +55,7 @@ public:
// Get file name.
GString *getFileName() { return fileName; }
-#ifdef WIN32
+#ifdef _WIN32
wchar_t *getFileNameU() { return fileNameU; }
#endif
@@ -172,11 +172,12 @@ public:
int getEmbeddedFileNameLength(int idx)
{ return catalog->getEmbeddedFileNameLength(idx); }
GBool saveEmbeddedFile(int idx, char *path);
-#ifdef WIN32
+#ifdef _WIN32
GBool saveEmbeddedFile(int idx, wchar_t *path, int pathLen);
#endif
char *getEmbeddedFileMem(int idx, int *size);
+
private:
GBool setup(GString *ownerPassword, GString *userPassword);
@@ -187,7 +188,7 @@ private:
GBool saveEmbeddedFile2(int idx, FILE *f);
GString *fileName;
-#ifdef WIN32
+#ifdef _WIN32
wchar_t *fileNameU;
#endif
FILE *file;
diff --git a/xpdf/PSOutputDev.cc b/xpdf/PSOutputDev.cc
index 55e576b..9521f6c 100644
--- a/xpdf/PSOutputDev.cc
+++ b/xpdf/PSOutputDev.cc
@@ -2,7 +2,7 @@
//
// PSOutputDev.cc
//
-// Copyright 1996-2003 Glyph & Cog, LLC
+// Copyright 1996-2013 Glyph & Cog, LLC
//
//========================================================================
@@ -39,6 +39,8 @@
#include "XRef.h"
#include "PreScanOutputDev.h"
#include "CharCodeToUnicode.h"
+#include "Form.h"
+#include "TextString.h"
#if HAVE_SPLASH
# include "Splash.h"
# include "SplashBitmap.h"
@@ -57,11 +59,6 @@
#endif
//------------------------------------------------------------------------
-
-// Max size of a slice when rasterizing pages, in pixels.
-#define rasterizationSliceSize 20000000
-
-//------------------------------------------------------------------------
// PostScript prolog and setup
//------------------------------------------------------------------------
@@ -85,24 +82,29 @@ static const char *prolog[] = {
" } for",
"~123sn",
"/pdfSetup {",
+ " /pdfDuplex exch def",
" /setpagedevice where {",
" pop 2 dict begin",
" /Policies 1 dict dup begin /PageSize 6 def end def",
- " { /Duplex true def } if",
+ " pdfDuplex { /Duplex true def } if",
" currentdict end setpagedevice",
- " } {",
- " pop",
- " } ifelse",
+ " } if",
+ " /pdfPageW 0 def",
+ " /pdfPageH 0 def",
"} def",
"/pdfSetupPaper {",
- " 2 array astore",
- " /setpagedevice where {",
- " pop 2 dict begin",
- " /PageSize exch def",
- " /ImagingBBox null def",
- " currentdict end setpagedevice",
+ " 2 copy pdfPageH ne exch pdfPageW ne or {",
+ " /pdfPageH exch def",
+ " /pdfPageW exch def",
+ " /setpagedevice where {",
+ " pop 3 dict begin",
+ " /PageSize [pdfPageW pdfPageH] def",
+ " pdfDuplex { /Duplex true def } if",
+ " /ImagingBBox null def",
+ " currentdict end setpagedevice",
+ " } if",
" } {",
- " pop",
+ " pop pop",
" } ifelse",
"} def",
"~1sn",
@@ -571,17 +573,14 @@ static const char *prolog[] = {
"} def",
"~23sn",
"/pdfImM { fCol imagemask skipEOD } 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",
+ "/pr {",
+ " 4 2 roll exch 5 index div exch 4 index div moveto",
+ " exch 3 index div dup 0 rlineto",
+ " exch 2 index div 0 exch rlineto",
+ " neg 0 rlineto",
+ " closepath",
"} def",
+ "/pdfImClip { gsave clip } def",
"/pdfImClipEnd { grestore } def",
"~23sn",
"% shading operators",
@@ -736,6 +735,19 @@ static const char *prolog[] = {
NULL
};
+static const char *minLineWidthProlog[] = {
+ "/pdfDist { dup dtransform dup mul exch dup mul add 0.5 mul sqrt } def",
+ "/pdfIDist { dup idtransform dup mul exch dup mul add 0.5 mul sqrt } def",
+ "/pdfMinLineDist pdfMinLineWidth pdfDist def",
+ "/setlinewidth {",
+ " dup pdfDist pdfMinLineDist lt {",
+ " pop pdfMinLineDist pdfIDist",
+ " } if",
+ " setlinewidth",
+ "} bind def",
+ NULL
+};
+
static const char *cmapProlog[] = {
"/CIDInit /ProcSet findresource begin",
"10 dict begin",
@@ -809,25 +821,68 @@ static PSSubstFont psBase14SubstFonts[14] = {
{"ZapfDingbats", 0}
};
-// Mapping from Type 1/1C font file to PS font name.
-struct PST1FontName {
- Ref fontFileID;
- GString *psName; // PostScript font name used for this
- // embedded font file
-};
+class PSFontInfo {
+public:
+
+ PSFontInfo(Ref fontIDA)
+ { fontID = fontIDA; ff = NULL; }
-// Info for 8-bit fonts
-struct PSFont8Info {
Ref fontID;
- int *codeToGID; // code-to-GID mapping for TrueType fonts
+ PSFontFileInfo *ff; // pointer to font file info; NULL indicates
+ // font mapping failed
};
-// Encoding info for substitute 16-bit font
-struct PSFont16Enc {
- Ref fontID;
- GString *enc; // NULL means font wasn't correctly substituted
+enum PSFontFileLocation {
+ psFontFileResident,
+ psFontFileEmbedded,
+ psFontFileExternal
};
+class PSFontFileInfo {
+public:
+
+ PSFontFileInfo(GString *psNameA, GfxFontType typeA,
+ PSFontFileLocation locA);
+ ~PSFontFileInfo();
+
+ GString *psName; // name under which font is defined
+ GfxFontType type; // font type
+ PSFontFileLocation loc; // font location
+ Ref embFontID; // object ID for the embedded font file
+ // (for all embedded fonts)
+ GString *extFileName; // external font file path
+ // (for all external fonts)
+ GString *encoding; // encoding name (for resident CID fonts)
+ int *codeToGID; // mapping from code/CID to GID
+ // (for TrueType, OpenType-TrueType, and
+ // CID OpenType-CFF fonts)
+ int codeToGIDLen; // length of codeToGID array
+};
+
+PSFontFileInfo::PSFontFileInfo(GString *psNameA, GfxFontType typeA,
+ PSFontFileLocation locA) {
+ psName = psNameA;
+ type = typeA;
+ loc = locA;
+ embFontID.num = embFontID.gen = -1;
+ extFileName = NULL;
+ encoding = NULL;
+ codeToGID = NULL;
+}
+
+PSFontFileInfo::~PSFontFileInfo() {
+ delete psName;
+ if (extFileName) {
+ delete extFileName;
+ }
+ if (encoding) {
+ delete encoding;
+ }
+ if (codeToGID) {
+ gfree(codeToGID);
+ }
+}
+
//------------------------------------------------------------------------
// process colors
//------------------------------------------------------------------------
@@ -998,11 +1053,8 @@ PSOutputDev::PSOutputDev(char *fileName, PDFDoc *docA,
customCodeCbk = customCodeCbkA;
customCodeCbkData = customCodeCbkDataA;
- fontIDs = NULL;
- fontNames = new GHash(gTrue);
- t1FontNames = NULL;
- font8Info = NULL;
- font16Enc = NULL;
+ fontInfo = new GList();
+ fontFileInfo = new GHash();
imgIDs = NULL;
formIDs = NULL;
xobjStack = NULL;
@@ -1019,7 +1071,7 @@ PSOutputDev::PSOutputDev(char *fileName, PDFDoc *docA,
} else if (fileName[0] == '|') {
fileTypeA = psPipe;
#ifdef HAVE_POPEN
-#ifndef WIN32
+#ifndef _WIN32
signal(SIGPIPE, (SignalFunc)SIG_IGN);
#endif
if (!(f = popen(fileName + 1, "w"))) {
@@ -1060,11 +1112,8 @@ PSOutputDev::PSOutputDev(PSOutputFunc outputFuncA, void *outputStreamA,
customCodeCbk = customCodeCbkA;
customCodeCbkData = customCodeCbkDataA;
- fontIDs = NULL;
- fontNames = new GHash(gTrue);
- t1FontNames = NULL;
- font8Info = NULL;
- font16Enc = NULL;
+ fontInfo = new GList();
+ fontFileInfo = new GHash();
imgIDs = NULL;
formIDs = NULL;
xobjStack = NULL;
@@ -1088,6 +1137,7 @@ void PSOutputDev::init(PSOutputFunc outputFuncA, void *outputStreamA,
Page *page;
PDFRectangle *box;
PSOutPaperSize *size;
+ PSFontFileInfo *ff;
GList *names;
int pg, w, h, i;
@@ -1118,8 +1168,13 @@ void PSOutputDev::init(PSOutputFunc outputFuncA, void *outputStreamA,
pg <= lastPage && pg <= catalog->getNumPages();
++pg) {
page = catalog->getPage(pg);
- w = (int)ceil(page->getMediaWidth());
- h = (int)ceil(page->getMediaHeight());
+ if (globalParams->getPSUseCropBoxAsPage()) {
+ w = (int)ceil(page->getCropWidth());
+ h = (int)ceil(page->getCropHeight());
+ } else {
+ w = (int)ceil(page->getMediaWidth());
+ h = (int)ceil(page->getMediaHeight());
+ }
for (i = 0; i < paperSizes->getLength(); ++i) {
size = (PSOutPaperSize *)paperSizes->get(i);
if (size->w == w && size->h == h) {
@@ -1160,25 +1215,21 @@ void PSOutputDev::init(PSOutputFunc outputFuncA, void *outputStreamA,
clipLLX0 = clipLLY0 = 0;
clipURX0 = clipURY0 = -1;
- // initialize fontIDs and fontNames lists
- fontIDSize = 64;
- fontIDLen = 0;
- fontIDs = (Ref *)gmallocn(fontIDSize, sizeof(Ref));
+ // initialize font lists, etc.
for (i = 0; i < 14; ++i) {
- fontNames->add(new GString(psBase14SubstFonts[i].psName), 1);
+ ff = new PSFontFileInfo(new GString(psBase14SubstFonts[i].psName),
+ fontType1, psFontFileResident);
+ fontFileInfo->add(ff->psName, ff);
}
names = globalParams->getPSResidentFonts();
for (i = 0; i < names->getLength(); ++i) {
- fontNames->add((GString *)names->get(i), 1);
+ if (!fontFileInfo->lookup((GString *)names->get(i))) {
+ ff = new PSFontFileInfo((GString *)names->get(i), fontType1,
+ psFontFileResident);
+ fontFileInfo->add(ff->psName, ff);
+ }
}
delete names;
- t1FontNameSize = 64;
- t1FontNameLen = 0;
- t1FontNames = (PST1FontName *)gmallocn(t1FontNameSize, sizeof(PST1FontName));
- font8InfoLen = 0;
- font8InfoSize = 0;
- font16EncLen = 0;
- font16EncSize = 0;
imgIDLen = 0;
imgIDSize = 0;
formIDLen = 0;
@@ -1224,7 +1275,6 @@ void PSOutputDev::init(PSOutputFunc outputFuncA, void *outputStreamA,
PSOutputDev::~PSOutputDev() {
PSOutCustomColor *cc;
- int i;
if (ok) {
if (!manualCtrl) {
@@ -1243,7 +1293,7 @@ PSOutputDev::~PSOutputDev() {
#ifdef HAVE_POPEN
else if (fileType == psPipe) {
pclose((FILE *)outputStream);
-#ifndef WIN32
+#ifndef _WIN32
signal(SIGPIPE, (SignalFunc)SIG_DFL);
#endif
}
@@ -1255,30 +1305,8 @@ PSOutputDev::~PSOutputDev() {
if (embFontList) {
delete embFontList;
}
- if (fontIDs) {
- gfree(fontIDs);
- }
- delete fontNames;
- if (t1FontNames) {
- for (i = 0; i < t1FontNameLen; ++i) {
- delete t1FontNames[i].psName;
- }
- gfree(t1FontNames);
- }
- if (font8Info) {
- for (i = 0; i < font8InfoLen; ++i) {
- gfree(font8Info[i].codeToGID);
- }
- gfree(font8Info);
- }
- if (font16Enc) {
- for (i = 0; i < font16EncLen; ++i) {
- if (font16Enc[i].enc) {
- delete font16Enc[i].enc;
- }
- }
- gfree(font16Enc);
- }
+ deleteGList(fontInfo, PSFontInfo);
+ deleteGHash(fontFileInfo, PSFontFileInfo);
gfree(imgIDs);
gfree(formIDs);
if (xobjStack) {
@@ -1291,6 +1319,16 @@ PSOutputDev::~PSOutputDev() {
}
}
+GBool PSOutputDev::checkIO() {
+ if (fileType == psFile || fileType == psPipe || fileType == psStdout) {
+ if (ferror((FILE *)outputStream)) {
+ error(errIO, -1, "Error writing to PostScript file");
+ return gFalse;
+ }
+ }
+ return gTrue;
+}
+
void PSOutputDev::writeHeader(int firstPage, int lastPage,
PDFRectangle *mediaBox, PDFRectangle *cropBox,
int pageRotate) {
@@ -1395,6 +1433,7 @@ void PSOutputDev::writeXpdfProcset() {
GBool lev1, lev2, lev3, sep, nonSep;
const char **p;
const char *q;
+ double w;
writePSFmt("%%BeginResource: procset xpdf {0:s} 0\n", xpdfVersion);
writePSFmt("%%Copyright: {0:s}\n", xpdfCopyright);
@@ -1420,6 +1459,12 @@ void PSOutputDev::writeXpdfProcset() {
writePSFmt("{0:s}\n", *p);
}
}
+ if ((w = globalParams->getPSMinLineWidth()) > 0) {
+ writePSFmt("/pdfMinLineWidth {0:.4g} def\n", w);
+ for (p = minLineWidthProlog; *p; ++p) {
+ writePSFmt("{0:s}\n", *p);
+ }
+ }
writePS("%%EndResource\n");
if (level >= psLevel3) {
@@ -1434,10 +1479,10 @@ void PSOutputDev::writeDocSetup(Catalog *catalog,
Page *page;
Dict *resDict;
Annots *annots;
- Object *acroForm;
+ Form *form;
Object obj1, obj2, obj3;
GString *s;
- int pg, i;
+ int pg, i, j;
if (mode == psModeForm) {
// swap the form and xpdf dicts
@@ -1464,23 +1509,22 @@ void PSOutputDev::writeDocSetup(Catalog *catalog,
}
delete annots;
}
- if ((acroForm = catalog->getAcroForm()) && acroForm->isDict()) {
- if (acroForm->dictLookup("DR", &obj1)->isDict()) {
- setupResources(obj1.getDict());
- }
- obj1.free();
- if (acroForm->dictLookup("Fields", &obj1)->isArray()) {
- for (i = 0; i < obj1.arrayGetLength(); ++i) {
- if (obj1.arrayGet(i, &obj2)->isDict()) {
- if (obj2.dictLookup("DR", &obj3)->isDict()) {
- setupResources(obj3.getDict());
+ if ((form = catalog->getForm())) {
+ for (i = 0; i < form->getNumFields(); ++i) {
+ form->getField(i)->getResources(&obj1);
+ if (obj1.isArray()) {
+ for (j = 0; j < obj1.arrayGetLength(); ++j) {
+ obj1.arrayGet(j, &obj2);
+ if (obj2.isDict()) {
+ setupResources(obj2.getDict());
}
- obj3.free();
+ obj2.free();
}
- obj2.free();
+ } else if (obj1.isDict()) {
+ setupResources(obj1.getDict());
}
+ obj1.free();
}
- obj1.free();
}
if (mode != psModeForm) {
if (mode != psModeEPS && !manualCtrl) {
@@ -1503,6 +1547,9 @@ void PSOutputDev::writeDocSetup(Catalog *catalog,
delete s;
}
}
+ if (mode != psModeForm) {
+ writePS("end\n");
+ }
}
void PSOutputDev::writePageTrailer() {
@@ -1517,7 +1564,6 @@ void PSOutputDev::writeTrailer() {
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 ||
@@ -1554,14 +1600,14 @@ void PSOutputDev::writeTrailer() {
}
void PSOutputDev::setupResources(Dict *resDict) {
- Object xObjDict, xObjRef, xObj, patDict, patRef, pat, resObj;
+ Object xObjDict, xObjRef, xObj, patDict, patRef, pat;
+ Object gsDict, gsRef, gs, smask, smaskGroup, resObj;
Ref ref0, ref1;
GBool skip;
int i, j;
setupFonts(resDict);
setupImages(resDict);
- setupForms(resDict);
//----- recursively scan XObjects
resDict->lookup("XObject", &xObjDict);
@@ -1648,6 +1694,55 @@ void PSOutputDev::setupResources(Dict *resDict) {
inType3Char = gFalse;
}
patDict.free();
+
+ //----- recursively scan SMask transparency groups in ExtGState dicts
+ resDict->lookup("ExtGState", &gsDict);
+ if (gsDict.isDict()) {
+ for (i = 0; i < gsDict.dictGetLength(); ++i) {
+
+ // avoid infinite recursion on ExtGStates
+ skip = gFalse;
+ if ((gsDict.dictGetValNF(i, &gsRef)->isRef())) {
+ ref0 = gsRef.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 ExtGState's SMask's transparency group's resource dict
+ if (gsDict.dictGetVal(i, &gs)->isDict()) {
+ if (gs.dictLookup("SMask", &smask)->isDict()) {
+ if (smask.dictLookup("G", &smaskGroup)->isStream()) {
+ smaskGroup.streamGetDict()->lookup("Resources", &resObj);
+ if (resObj.isDict()) {
+ setupResources(resObj.getDict());
+ }
+ resObj.free();
+ }
+ smaskGroup.free();
+ }
+ smask.free();
+ }
+ gs.free();
+ }
+
+ if (gsRef.isRef() && !skip) {
+ xobjStack->del(xobjStack->getLength() - 1);
+ }
+ gsRef.free();
+ }
+ }
+ gsDict.free();
+
+ setupForms(resDict);
}
void PSOutputDev::setupFonts(Dict *resDict) {
@@ -1681,8 +1776,8 @@ void PSOutputDev::setupFonts(Dict *resDict) {
}
void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) {
+ PSFontInfo *fi;
GfxFontLoc *fontLoc;
- GString *psName;
GBool subst;
char buf[16];
UnicodeMap *uMap;
@@ -1693,123 +1788,101 @@ void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) {
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) {
+ for (i = 0; i < fontInfo->getLength(); ++i) {
+ fi = (PSFontInfo *)fontInfo->get(i);
+ if (fi->fontID.num == font->getID()->num &&
+ fi->fontID.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();
+ // add fontInfo entry
+ fi = new PSFontInfo(*font->getID());
+ fontInfo->append(fi);
- psName = NULL;
xs = ys = 1;
subst = gFalse;
if (font->getType() == fontType3) {
- psName = GString::format("T3_{0:d}_{1:d}",
- font->getID()->num, font->getID()->gen);
- setupType3Font(font, psName, parentResDict);
+ fi->ff = setupType3Font(font, parentResDict);
} else {
- fontLoc = font->locateFont(xref, gTrue);
- switch (fontLoc->locType) {
- case gfxFontLocEmbedded:
- switch (fontLoc->fontType) {
- case fontType1:
- // this assumes that the PS font name matches the PDF font name
- psName = font->getEmbeddedFontName()->copy();
- setupEmbeddedType1Font(&fontLoc->embFontID, psName);
- break;
- case fontType1C:
- psName = makePSFontName(font, &fontLoc->embFontID);
- setupEmbeddedType1CFont(font, &fontLoc->embFontID, psName);
- break;
- case fontType1COT:
- psName = makePSFontName(font, &fontLoc->embFontID);
- setupEmbeddedOpenTypeT1CFont(font, &fontLoc->embFontID, psName);
- break;
- case fontTrueType:
- case fontTrueTypeOT:
- psName = makePSFontName(font, font->getID());
- setupEmbeddedTrueTypeFont(font, &fontLoc->embFontID, psName);
- break;
- case fontCIDType0C:
- psName = makePSFontName(font, &fontLoc->embFontID);
- setupEmbeddedCIDType0Font(font, &fontLoc->embFontID, psName);
- break;
- case fontCIDType2:
- case fontCIDType2OT:
- psName = makePSFontName(font, font->getID());
- //~ should check to see if font actually uses vertical mode
- setupEmbeddedCIDTrueTypeFont(font, &fontLoc->embFontID, psName, gTrue);
- break;
- case fontCIDType0COT:
- psName = makePSFontName(font, &fontLoc->embFontID);
- setupEmbeddedOpenTypeCFFFont(font, &fontLoc->embFontID, psName);
- break;
- default:
- break;
- }
- break;
- case gfxFontLocExternal:
- //~ add cases for external 16-bit fonts
- switch (fontLoc->fontType) {
- case fontType1:
- if (font->getName()) {
- // this assumes that the PS font name matches the PDF font name
- psName = font->getName()->copy();
- } else {
- //~ this won't work -- the PS font name won't match
- psName = makePSFontName(font, font->getID());
+ if ((fontLoc = font->locateFont(xref, gTrue))) {
+ switch (fontLoc->locType) {
+ case gfxFontLocEmbedded:
+ switch (fontLoc->fontType) {
+ case fontType1:
+ fi->ff = setupEmbeddedType1Font(font, &fontLoc->embFontID);
+ break;
+ case fontType1C:
+ fi->ff = setupEmbeddedType1CFont(font, &fontLoc->embFontID);
+ break;
+ case fontType1COT:
+ fi->ff = setupEmbeddedOpenTypeT1CFont(font, &fontLoc->embFontID);
+ break;
+ case fontTrueType:
+ case fontTrueTypeOT:
+ fi->ff = setupEmbeddedTrueTypeFont(font, &fontLoc->embFontID);
+ break;
+ case fontCIDType0C:
+ fi->ff = setupEmbeddedCIDType0Font(font, &fontLoc->embFontID);
+ break;
+ case fontCIDType2:
+ case fontCIDType2OT:
+ //~ should check to see if font actually uses vertical mode
+ fi->ff = setupEmbeddedCIDTrueTypeFont(font, &fontLoc->embFontID,
+ gTrue);
+ break;
+ case fontCIDType0COT:
+ fi->ff = setupEmbeddedOpenTypeCFFFont(font, &fontLoc->embFontID);
+ break;
+ default:
+ break;
}
- setupExternalType1Font(fontLoc->path, psName);
- break;
- case fontTrueType:
- case fontTrueTypeOT:
- psName = makePSFontName(font, font->getID());
- setupExternalTrueTypeFont(font, fontLoc->path, psName);
break;
- case fontCIDType2:
- case fontCIDType2OT:
- psName = makePSFontName(font, font->getID());
- //~ should check to see if font actually uses vertical mode
- setupExternalCIDTrueTypeFont(font, fontLoc->path, psName, gTrue);
+ case gfxFontLocExternal:
+ //~ add cases for other external 16-bit fonts
+ switch (fontLoc->fontType) {
+ case fontType1:
+ fi->ff = setupExternalType1Font(font, fontLoc->path);
+ break;
+ case fontTrueType:
+ case fontTrueTypeOT:
+ fi->ff = setupExternalTrueTypeFont(font, fontLoc->path,
+ fontLoc->fontNum);
+ break;
+ case fontCIDType2:
+ case fontCIDType2OT:
+ //~ should check to see if font actually uses vertical mode
+ fi->ff = setupExternalCIDTrueTypeFont(font, fontLoc->path,
+ fontLoc->fontNum, gTrue);
+ break;
+ default:
+ break;
+ }
break;
- default:
+ case gfxFontLocResident:
+ if (!(fi->ff = (PSFontFileInfo *)fontFileInfo->lookup(fontLoc->path))) {
+ // handle psFontPassthrough
+ fi->ff = new PSFontFileInfo(fontLoc->path->copy(), fontLoc->fontType,
+ psFontFileResident);
+ fontFileInfo->add(fi->ff->psName, fi->ff);
+ }
break;
}
- break;
- case gfxFontLocResident:
- psName = fontLoc->path->copy();
- break;
}
- if (!psName) {
+ if (!fi->ff) {
if (font->isCIDFont()) {
error(errSyntaxError, -1,
- "Couldn't find a font to substitute for '{0:s}' ('{1:s}' character collection)",
+ "Couldn't find a font for '{0:s}' ('{1:s}' character collection)",
font->getName() ? font->getName()->getCString()
: "(unnamed)",
((GfxCIDFont *)font)->getCollection()
? ((GfxCIDFont *)font)->getCollection()->getCString()
: "(unknown)");
- if (font16EncLen >= font16EncSize) {
- font16EncSize += 16;
- font16Enc = (PSFont16Enc *)greallocn(font16Enc,
- font16EncSize,
- sizeof(PSFont16Enc));
- }
- font16Enc[font16EncLen].fontID = *font->getID();
- font16Enc[font16EncLen].enc = NULL;
- ++font16EncLen;
} else {
error(errSyntaxError, -1,
- "Couldn't find a font to substitute for '{0:s}'",
+ "Couldn't find a font for '{0:s}'",
font->getName() ? font->getName()->getCString()
: "(unnamed)");
}
@@ -1843,23 +1916,14 @@ void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) {
if (fontLoc->locType == gfxFontLocResident &&
fontLoc->fontType >= fontCIDType0) {
subst = gTrue;
- if (font16EncLen >= font16EncSize) {
- font16EncSize += 16;
- font16Enc = (PSFont16Enc *)greallocn(font16Enc,
- font16EncSize,
- sizeof(PSFont16Enc));
- }
- font16Enc[font16EncLen].fontID = *font->getID();
if ((uMap = globalParams->getUnicodeMap(fontLoc->encoding))) {
- font16Enc[font16EncLen].enc = fontLoc->encoding->copy();
+ fi->ff->encoding = fontLoc->encoding->copy();
uMap->decRefCnt();
} else {
error(errSyntaxError, -1,
"Couldn't find Unicode map for 16-bit font encoding '{0:t}'",
fontLoc->encoding);
- font16Enc[font16EncLen].enc = NULL;
}
- ++font16EncLen;
}
delete fontLoc;
@@ -1869,16 +1933,16 @@ void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) {
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->getID()->num, font->getID()->gen, fi->ff->psName,
font->getWMode());
} else {
writePSFmt("/F{0:d}_{1:d} /{2:t} {3:d} pdfMakeFont16\n",
- font->getID()->num, font->getID()->gen, psName,
+ font->getID()->num, font->getID()->gen, fi->ff->psName,
font->getWMode());
}
} else {
writePSFmt("/F{0:d}_{1:d} /{2:t} {3:.6g} {4:.6g}\n",
- font->getID()->num, font->getID()->gen, psName, xs, ys);
+ font->getID()->num, font->getID()->gen, fi->ff->psName, xs, ys);
for (i = 0; i < 256; i += 8) {
writePS((char *)((i == 0) ? "[ " : " "));
for (j = 0; j < 8; ++j) {
@@ -1903,25 +1967,29 @@ void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) {
}
writePS("pdfMakeFont\n");
}
-
- delete psName;
}
-void PSOutputDev::setupEmbeddedType1Font(Ref *id, GString *psName) {
+PSFontFileInfo *PSOutputDev::setupEmbeddedType1Font(GfxFont *font, Ref *id) {
static char hexChar[17] = "0123456789abcdef";
- Object refObj, strObj, obj1, obj2, obj3;
+ GString *psName;
+ PSFontFileInfo *ff;
+ Object refObj, strObj, obj1, obj2;
Dict *dict;
- int length1, length2, length3;
+ int length1, length2;
int c;
- int start[4];
+ int start[6];
GBool binMode;
- int i;
+ int n, i;
// check if font is already embedded
- if (fontNames->lookupInt(psName)) {
- return;
+ if ((ff = (PSFontFileInfo *)
+ fontFileInfo->lookup(font->getEmbeddedFontName()))) {
+ return ff;
}
- fontNames->add(psName->copy(), 1);
+
+ // generate name
+ // (this assumes that the PS font name matches the PDF font name)
+ psName = font->getEmbeddedFontName()->copy();
// get the font stream and info
refObj.initRef(id->num, id->gen);
@@ -1938,21 +2006,17 @@ void PSOutputDev::setupEmbeddedType1Font(Ref *id, GString *psName) {
}
dict->lookup("Length1", &obj1);
dict->lookup("Length2", &obj2);
- dict->lookup("Length3", &obj3);
- if (!obj1.isInt() || !obj2.isInt() || !obj3.isInt()) {
+ if (!obj1.isInt() || !obj2.isInt()) {
error(errSyntaxError, -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);
@@ -1960,91 +2024,164 @@ void PSOutputDev::setupEmbeddedType1Font(Ref *id, GString *psName) {
embFontList->append(psName->getCString());
embFontList->append("\n");
- // copy ASCII portion of font
+ // check for PFB format
strObj.streamReset();
- for (i = 0; i < length1 && (c = strObj.streamGetChar()) != EOF; ++i) {
- writePSChar(c);
- }
+ start[0] = strObj.streamGetChar();
+ start[1] = strObj.streamGetChar();
+ if (start[0] == 0x80 && start[1] == 0x01) {
+ error(errSyntaxWarning, -1, "Embedded Type 1 font is in PFB format");
+ while (1) {
+ for (i = 2; i < 6; ++i) {
+ start[i] = strObj.streamGetChar();
+ }
+ if (start[2] == EOF || start[3] == EOF ||
+ start[4] == EOF || start[5] == EOF) {
+ break;
+ }
+ n = start[2] + (start[3] << 8) + (start[4] << 16) + (start[5] << 24);
+ if (start[1] == 0x01) {
+ for (i = 0; i < n; ++i) {
+ if ((c = strObj.streamGetChar()) == EOF) {
+ break;
+ }
+ writePSChar(c);
+ }
+ } else {
+ for (i = 0; i < n; ++i) {
+ if ((c = strObj.streamGetChar()) == EOF) {
+ break;
+ }
+ writePSChar(hexChar[(c >> 4) & 0x0f]);
+ writePSChar(hexChar[c & 0x0f]);
+ if (i % 32 == 31) {
+ writePSChar('\n');
+ }
+ }
+ }
+ start[0] = strObj.streamGetChar();
+ start[1] = strObj.streamGetChar();
+ if (start[0] == EOF || start[1] == EOF ||
+ (start[0] == 0x80 && start[1] == 0x03)) {
+ break;
+ } else if (!(start[0] == 0x80 &&
+ (start[1] == 0x01 || start[1] == 0x02))) {
+ error(errSyntaxError, -1,
+ "Invalid PFB header in embedded font stream");
+ break;
+ }
+ }
+ writePSChar('\n');
- // 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(errSyntaxError, -1,
- "Unexpected end of file in embedded font stream");
- goto err1;
+ // plain text (PFA) format
+ } else {
+
+ // copy ASCII portion of font
+ writePSChar(start[0]);
+ writePSChar(start[1]);
+ for (i = 2; i < length1 && (c = strObj.streamGetChar()) != EOF; ++i) {
+ writePSChar(c);
}
- 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) {
+ // figure out if encrypted portion is binary or ASCII
+ binMode = gFalse;
for (i = 0; i < 4; ++i) {
- writePSChar(hexChar[(start[i] >> 4) & 0x0f]);
- writePSChar(hexChar[start[i] & 0x0f]);
+ start[i] = strObj.streamGetChar();
+ if (start[i] == EOF) {
+ error(errSyntaxError, -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;
+ // 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;
+ while (i < length2) {
+ if ((c = strObj.streamGetChar()) == EOF) {
+ break;
+ }
+ writePSChar(hexChar[(c >> 4) & 0x0f]);
+ writePSChar(hexChar[c & 0x0f]);
+ if (++i % 32 == 0) {
+ writePSChar('\n');
+ }
}
- writePSChar(hexChar[(c >> 4) & 0x0f]);
- writePSChar(hexChar[c & 0x0f]);
- if (++i % 32 == 0) {
+ 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;
+ // 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);
}
- writePSChar(c);
}
- }
- // write padding and "cleartomark"
- for (i = 0; i < 8; ++i) {
- writePS("00000000000000000000000000000000"
- "00000000000000000000000000000000\n");
+ // write padding and "cleartomark"
+ for (i = 0; i < 8; ++i) {
+ writePS("00000000000000000000000000000000"
+ "00000000000000000000000000000000\n");
+ }
+ writePS("cleartomark\n");
}
- writePS("cleartomark\n");
// ending comment
writePS("%%EndResource\n");
+ strObj.streamClose();
+ strObj.free();
+
+ ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded);
+ ff->embFontID = *id;
+ fontFileInfo->add(ff->psName, ff);
+ return ff;
+
err1:
strObj.streamClose();
strObj.free();
+ delete psName;
+ return NULL;
}
-//~ This doesn't handle .pfb files or binary eexec data (which only
-//~ happens in pfb files?).
-void PSOutputDev::setupExternalType1Font(GString *fileName, GString *psName) {
+PSFontFileInfo *PSOutputDev::setupExternalType1Font(GfxFont *font,
+ GString *fileName) {
+ static char hexChar[17] = "0123456789abcdef";
+ GString *psName;
+ PSFontFileInfo *ff;
FILE *fontFile;
- int c;
+ int buf[6];
+ int c, n, i;
- // check if font is already embedded
- if (fontNames->lookupInt(psName)) {
- return;
+ if (font->getName()) {
+ // check if font is already embedded
+ if ((ff = (PSFontFileInfo *)fontFileInfo->lookup(font->getName()))) {
+ return ff;
+ }
+ // this assumes that the PS font name matches the PDF font name
+ psName = font->getName()->copy();
+ } else {
+ // generate name
+ //~ this won't work -- the PS font name won't match
+ psName = makePSFontName(font, font->getID());
}
- fontNames->add(psName->copy(), 1);
// beginning comment
writePSFmt("%%BeginResource: font {0:t}\n", psName);
@@ -2052,44 +2189,98 @@ void PSOutputDev::setupExternalType1Font(GString *fileName, GString *psName) {
embFontList->append(psName->getCString());
embFontList->append("\n");
- // copy the font file
+ // open the font file
if (!(fontFile = fopen(fileName->getCString(), "rb"))) {
error(errIO, -1, "Couldn't open external font file");
- return;
+ return NULL;
}
- while ((c = fgetc(fontFile)) != EOF) {
- writePSChar(c);
+
+ // check for PFB format
+ buf[0] = fgetc(fontFile);
+ buf[1] = fgetc(fontFile);
+ if (buf[0] == 0x80 && buf[1] == 0x01) {
+ while (1) {
+ for (i = 2; i < 6; ++i) {
+ buf[i] = fgetc(fontFile);
+ }
+ if (buf[2] == EOF || buf[3] == EOF || buf[4] == EOF || buf[5] == EOF) {
+ break;
+ }
+ n = buf[2] + (buf[3] << 8) + (buf[4] << 16) + (buf[5] << 24);
+ if (buf[1] == 0x01) {
+ for (i = 0; i < n; ++i) {
+ if ((c = fgetc(fontFile)) == EOF) {
+ break;
+ }
+ writePSChar(c);
+ }
+ } else {
+ for (i = 0; i < n; ++i) {
+ if ((c = fgetc(fontFile)) == EOF) {
+ break;
+ }
+ writePSChar(hexChar[(c >> 4) & 0x0f]);
+ writePSChar(hexChar[c & 0x0f]);
+ if (i % 32 == 31) {
+ writePSChar('\n');
+ }
+ }
+ }
+ buf[0] = fgetc(fontFile);
+ buf[1] = fgetc(fontFile);
+ if (buf[0] == EOF || buf[1] == EOF ||
+ (buf[0] == 0x80 && buf[1] == 0x03)) {
+ break;
+ } else if (!(buf[0] == 0x80 &&
+ (buf[1] == 0x01 || buf[1] == 0x02))) {
+ error(errSyntaxError, -1,
+ "Invalid PFB header in external font file");
+ break;
+ }
+ }
+ writePSChar('\n');
+
+ // plain text (PFA) format
+ } else {
+ writePSChar(buf[0]);
+ writePSChar(buf[1]);
+ while ((c = fgetc(fontFile)) != EOF) {
+ writePSChar(c);
+ }
}
+
fclose(fontFile);
// ending comment
writePS("%%EndResource\n");
+
+ ff = new PSFontFileInfo(psName, font->getType(), psFontFileExternal);
+ ff->extFileName = fileName->copy();
+ fontFileInfo->add(ff->psName, ff);
+ return ff;
}
-void PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id,
- GString *psName) {
+PSFontFileInfo *PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id) {
+ GString *psName;
+ PSFontFileInfo *ff;
char *fontBuf;
int fontLen;
FoFiType1C *ffT1C;
- int i;
+ GHashIter *iter;
// check if font is already embedded
- for (i = 0; i < t1FontNameLen; ++i) {
- if (t1FontNames[i].fontFileID.num == id->num &&
- t1FontNames[i].fontFileID.gen == id->gen) {
- psName->clear();
- psName->insert(0, t1FontNames[i].psName);
- return;
+ fontFileInfo->startIter(&iter);
+ while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) {
+ if (ff->loc == psFontFileEmbedded &&
+ ff->embFontID.num == id->num &&
+ ff->embFontID.gen == id->gen) {
+ fontFileInfo->killIter(&iter);
+ return ff;
}
}
- if (t1FontNameLen == t1FontNameSize) {
- t1FontNameSize *= 2;
- t1FontNames = (PST1FontName *)greallocn(t1FontNames, t1FontNameSize,
- sizeof(PST1FontName));
- }
- t1FontNames[t1FontNameLen].fontFileID = *id;
- t1FontNames[t1FontNameLen].psName = psName->copy();
- ++t1FontNameLen;
+
+ // generate name
+ psName = makePSFontName(font, id);
// beginning comment
writePSFmt("%%BeginResource: font {0:t}\n", psName);
@@ -2109,32 +2300,35 @@ void PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id,
// ending comment
writePS("%%EndResource\n");
+
+ ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded);
+ ff->embFontID = *id;
+ fontFileInfo->add(ff->psName, ff);
+ return ff;
}
-void PSOutputDev::setupEmbeddedOpenTypeT1CFont(GfxFont *font, Ref *id,
- GString *psName) {
+PSFontFileInfo *PSOutputDev::setupEmbeddedOpenTypeT1CFont(GfxFont *font,
+ Ref *id) {
+ GString *psName;
+ PSFontFileInfo *ff;
char *fontBuf;
int fontLen;
FoFiTrueType *ffTT;
- int i;
+ GHashIter *iter;
// check if font is already embedded
- for (i = 0; i < t1FontNameLen; ++i) {
- if (t1FontNames[i].fontFileID.num == id->num &&
- t1FontNames[i].fontFileID.gen == id->gen) {
- psName->clear();
- psName->insert(0, t1FontNames[i].psName);
- return;
+ fontFileInfo->startIter(&iter);
+ while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) {
+ if (ff->loc == psFontFileEmbedded &&
+ ff->embFontID.num == id->num &&
+ ff->embFontID.gen == id->gen) {
+ fontFileInfo->killIter(&iter);
+ return ff;
}
}
- if (t1FontNameLen == t1FontNameSize) {
- t1FontNameSize *= 2;
- t1FontNames = (PST1FontName *)greallocn(t1FontNames, t1FontNameSize,
- sizeof(PST1FontName));
- }
- t1FontNames[t1FontNameLen].fontFileID = *id;
- t1FontNames[t1FontNameLen].psName = psName->copy();
- ++t1FontNameLen;
+
+ // generate name
+ psName = makePSFontName(font, id);
// beginning comment
writePSFmt("%%BeginResource: font {0:t}\n", psName);
@@ -2144,7 +2338,7 @@ void PSOutputDev::setupEmbeddedOpenTypeT1CFont(GfxFont *font, Ref *id,
// convert it to a Type 1 font
if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) {
- if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) {
+ if ((ffTT = FoFiTrueType::make(fontBuf, fontLen, 0))) {
if (ffTT->isOpenTypeCFF()) {
ffTT->convertToType1(psName->getCString(), NULL, gTrue,
outputFunc, outputStream);
@@ -2156,14 +2350,50 @@ void PSOutputDev::setupEmbeddedOpenTypeT1CFont(GfxFont *font, Ref *id,
// ending comment
writePS("%%EndResource\n");
+
+ ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded);
+ ff->embFontID = *id;
+ fontFileInfo->add(ff->psName, ff);
+ return ff;
}
-void PSOutputDev::setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id,
- GString *psName) {
+PSFontFileInfo *PSOutputDev::setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id) {
+ GString *psName;
+ PSFontFileInfo *ff;
char *fontBuf;
int fontLen;
FoFiTrueType *ffTT;
int *codeToGID;
+ GHashIter *iter;
+
+ // get the code-to-GID mapping
+ if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
+ return NULL;
+ }
+ if (!(ffTT = FoFiTrueType::make(fontBuf, fontLen, 0))) {
+ gfree(fontBuf);
+ return NULL;
+ }
+ codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT);
+
+ // check if font is already embedded
+ fontFileInfo->startIter(&iter);
+ while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) {
+ if (ff->loc == psFontFileEmbedded &&
+ ff->embFontID.num == id->num &&
+ ff->embFontID.gen == id->gen &&
+ ff->codeToGIDLen == 256 &&
+ !memcmp(ff->codeToGID, codeToGID, 256 * sizeof(int))) {
+ fontFileInfo->killIter(&iter);
+ gfree(codeToGID);
+ delete ffTT;
+ gfree(fontBuf);
+ return ff;
+ }
+ }
+
+ // generate name
+ psName = makePSFontName(font, id);
// beginning comment
writePSFmt("%%BeginResource: font {0:t}\n", psName);
@@ -2172,38 +2402,57 @@ void PSOutputDev::setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id,
embFontList->append("\n");
// convert it to a Type 42 font
- if ((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);
- }
+ ffTT->convertToType42(psName->getCString(),
+ ((Gfx8BitFont *)font)->getHasEncoding()
+ ? ((Gfx8BitFont *)font)->getEncoding()
+ : (char **)NULL,
+ codeToGID, outputFunc, outputStream);
+ delete ffTT;
+ gfree(fontBuf);
// ending comment
writePS("%%EndResource\n");
+
+ ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded);
+ ff->embFontID = *id;
+ ff->codeToGID = codeToGID;
+ ff->codeToGIDLen = 256;
+ fontFileInfo->add(ff->psName, ff);
+ return ff;
}
-void PSOutputDev::setupExternalTrueTypeFont(GfxFont *font, GString *fileName,
- GString *psName) {
+PSFontFileInfo *PSOutputDev::setupExternalTrueTypeFont(GfxFont *font,
+ GString *fileName,
+ int fontNum) {
+ GString *psName;
+ PSFontFileInfo *ff;
FoFiTrueType *ffTT;
int *codeToGID;
+ GHashIter *iter;
+
+ // get the code-to-GID mapping
+ if (!(ffTT = FoFiTrueType::load(fileName->getCString(), fontNum))) {
+ return NULL;
+ }
+ codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT);
+
+ // check if font is already embedded
+ fontFileInfo->startIter(&iter);
+ while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) {
+ if (ff->loc == psFontFileExternal &&
+ ff->type == font->getType() &&
+ !ff->extFileName->cmp(fileName) &&
+ ff->codeToGIDLen == 256 &&
+ !memcmp(ff->codeToGID, codeToGID, 256 * sizeof(int))) {
+ fontFileInfo->killIter(&iter);
+ gfree(codeToGID);
+ delete ffTT;
+ return ff;
+ }
+ }
+
+ // generate name
+ psName = makePSFontName(font, font->getID());
// beginning comment
writePSFmt("%%BeginResource: font {0:t}\n", psName);
@@ -2212,55 +2461,45 @@ void PSOutputDev::setupExternalTrueTypeFont(GfxFont *font, GString *fileName,
embFontList->append("\n");
// convert it to a Type 42 font
- if ((ffTT = FoFiTrueType::load(fileName->getCString()))) {
- 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;
- }
+ ffTT->convertToType42(psName->getCString(),
+ ((Gfx8BitFont *)font)->getHasEncoding()
+ ? ((Gfx8BitFont *)font)->getEncoding()
+ : (char **)NULL,
+ codeToGID, outputFunc, outputStream);
+ delete ffTT;
// ending comment
writePS("%%EndResource\n");
+
+ ff = new PSFontFileInfo(psName, font->getType(), psFontFileExternal);
+ ff->extFileName = fileName->copy();
+ ff->codeToGID = codeToGID;
+ ff->codeToGIDLen = 256;
+ fontFileInfo->add(ff->psName, ff);
+ return ff;
}
-void PSOutputDev::setupEmbeddedCIDType0Font(GfxFont *font, Ref *id,
- GString *psName) {
+PSFontFileInfo *PSOutputDev::setupEmbeddedCIDType0Font(GfxFont *font, Ref *id) {
+ GString *psName;
+ PSFontFileInfo *ff;
char *fontBuf;
int fontLen;
FoFiType1C *ffT1C;
- int i;
+ GHashIter *iter;
// check if font is already embedded
- for (i = 0; i < t1FontNameLen; ++i) {
- if (t1FontNames[i].fontFileID.num == id->num &&
- t1FontNames[i].fontFileID.gen == id->gen) {
- psName->clear();
- psName->insert(0, t1FontNames[i].psName);
- return;
+ fontFileInfo->startIter(&iter);
+ while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) {
+ if (ff->loc == psFontFileEmbedded &&
+ ff->embFontID.num == id->num &&
+ ff->embFontID.gen == id->gen) {
+ fontFileInfo->killIter(&iter);
+ return ff;
}
}
- if (t1FontNameLen == t1FontNameSize) {
- t1FontNameSize *= 2;
- t1FontNames = (PST1FontName *)greallocn(t1FontNames, t1FontNameSize,
- sizeof(PST1FontName));
- }
- t1FontNames[t1FontNameLen].fontFileID = *id;
- t1FontNames[t1FontNameLen].psName = psName->copy();
- ++t1FontNameLen;
+
+ // generate name
+ psName = makePSFontName(font, id);
// beginning comment
writePSFmt("%%BeginResource: font {0:t}\n", psName);
@@ -2287,14 +2526,46 @@ void PSOutputDev::setupEmbeddedCIDType0Font(GfxFont *font, Ref *id,
// ending comment
writePS("%%EndResource\n");
+
+ ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded);
+ ff->embFontID = *id;
+ fontFileInfo->add(ff->psName, ff);
+ return ff;
}
-void PSOutputDev::setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id,
- GString *psName,
- GBool needVerticalMetrics) {
+PSFontFileInfo *PSOutputDev::setupEmbeddedCIDTrueTypeFont(
+ GfxFont *font, Ref *id,
+ GBool needVerticalMetrics) {
+ GString *psName;
+ PSFontFileInfo *ff;
char *fontBuf;
int fontLen;
FoFiTrueType *ffTT;
+ int *codeToGID;
+ int codeToGIDLen;
+ GHashIter *iter;
+
+ // get the code-to-GID mapping
+ codeToGID = ((GfxCIDFont *)font)->getCIDToGID();
+ codeToGIDLen = ((GfxCIDFont *)font)->getCIDToGIDLen();
+
+ // check if font is already embedded
+ fontFileInfo->startIter(&iter);
+ while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) {
+ if (ff->loc == psFontFileEmbedded &&
+ ff->embFontID.num == id->num &&
+ ff->embFontID.gen == id->gen &&
+ ff->codeToGIDLen == codeToGIDLen &&
+ ((!ff->codeToGID && !codeToGID) ||
+ (ff->codeToGID && codeToGID &&
+ !memcmp(ff->codeToGID, codeToGID, codeToGIDLen * sizeof(int))))) {
+ fontFileInfo->killIter(&iter);
+ return ff;
+ }
+ }
+
+ // generate name
+ psName = makePSFontName(font, id);
// beginning comment
writePSFmt("%%BeginResource: font {0:t}\n", psName);
@@ -2304,19 +2575,17 @@ void PSOutputDev::setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id,
// convert it to a Type 0 font
if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) {
- if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) {
+ if ((ffTT = FoFiTrueType::make(fontBuf, fontLen, 0))) {
if (globalParams->getPSLevel() >= psLevel3) {
// Level 3: use a CID font
ffTT->convertToCIDType2(psName->getCString(),
- ((GfxCIDFont *)font)->getCIDToGID(),
- ((GfxCIDFont *)font)->getCIDToGIDLen(),
+ codeToGID, codeToGIDLen,
needVerticalMetrics,
outputFunc, outputStream);
} else {
// otherwise: use a non-CID composite font
ffTT->convertToType0(psName->getCString(),
- ((GfxCIDFont *)font)->getCIDToGID(),
- ((GfxCIDFont *)font)->getCIDToGIDLen(),
+ codeToGID, codeToGIDLen,
needVerticalMetrics,
outputFunc, outputStream);
}
@@ -2327,18 +2596,104 @@ void PSOutputDev::setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id,
// ending comment
writePS("%%EndResource\n");
+
+ ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded);
+ ff->embFontID = *id;
+ if (codeToGIDLen) {
+ ff->codeToGID = (int *)gmallocn(codeToGIDLen, sizeof(int));
+ memcpy(ff->codeToGID, codeToGID, codeToGIDLen * sizeof(int));
+ ff->codeToGIDLen = codeToGIDLen;
+ }
+ fontFileInfo->add(ff->psName, ff);
+ return ff;
}
-void PSOutputDev::setupExternalCIDTrueTypeFont(GfxFont *font,
- GString *fileName,
- GString *psName,
- GBool needVerticalMetrics) {
+PSFontFileInfo *PSOutputDev::setupExternalCIDTrueTypeFont(
+ GfxFont *font,
+ GString *fileName,
+ int fontNum,
+ GBool needVerticalMetrics) {
+ GString *psName;
+ PSFontFileInfo *ff;
FoFiTrueType *ffTT;
int *codeToGID;
int codeToGIDLen;
CharCodeToUnicode *ctu;
Unicode uBuf[8];
int cmap, code;
+ GHashIter *iter;
+
+ // create a code-to-GID mapping, via Unicode
+ if (!(ffTT = FoFiTrueType::load(fileName->getCString(), fontNum))) {
+ return NULL;
+ }
+ if (!(ctu = ((GfxCIDFont *)font)->getToUnicode())) {
+ error(errSyntaxError, -1,
+ "Couldn't find a mapping to Unicode for font '{0:s}'",
+ font->getName() ? font->getName()->getCString() : "(unnamed)");
+ delete ffTT;
+ return NULL;
+ }
+ // look for a Unicode cmap
+ for (cmap = 0; cmap < ffTT->getNumCmaps(); ++cmap) {
+ if ((ffTT->getCmapPlatform(cmap) == 3 &&
+ ffTT->getCmapEncoding(cmap) == 1) ||
+ ffTT->getCmapPlatform(cmap) == 0) {
+ break;
+ }
+ }
+ if (cmap >= ffTT->getNumCmaps()) {
+ error(errSyntaxError, -1,
+ "Couldn't find a Unicode cmap in font '{0:s}'",
+ font->getName() ? font->getName()->getCString() : "(unnamed)");
+ ctu->decRefCnt();
+ delete ffTT;
+ return NULL;
+ }
+ // map CID -> Unicode -> GID
+ if (ctu->isIdentity()) {
+ codeToGIDLen = 65536;
+ } else {
+ codeToGIDLen = ctu->getLength();
+ }
+ codeToGID = (int *)gmallocn(codeToGIDLen, sizeof(int));
+ for (code = 0; code < codeToGIDLen; ++code) {
+ if (ctu->mapToUnicode(code, uBuf, 8) > 0) {
+ codeToGID[code] = ffTT->mapCodeToGID(cmap, uBuf[0]);
+ } else {
+ codeToGID[code] = 0;
+ }
+ }
+ ctu->decRefCnt();
+
+ // check if font is already embedded
+ fontFileInfo->startIter(&iter);
+ while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) {
+ if (ff->loc == psFontFileExternal &&
+ ff->type == font->getType() &&
+ !ff->extFileName->cmp(fileName) &&
+ ff->codeToGIDLen == codeToGIDLen &&
+ ff->codeToGID &&
+ !memcmp(ff->codeToGID, codeToGID, codeToGIDLen * sizeof(int))) {
+ fontFileInfo->killIter(&iter);
+ gfree(codeToGID);
+ delete ffTT;
+ return ff;
+ }
+ }
+
+ // check for embedding permission
+ if (ffTT->getEmbeddingRights() < 1) {
+ error(errSyntaxError, -1,
+ "TrueType font '{0:s}' does not allow embedding",
+ font->getName() ? font->getName()->getCString() : "(unnamed)");
+ gfree(codeToGID);
+ delete ffTT;
+ return NULL;
+ }
+
+ // generate name
+ psName = makePSFontName(font, font->getID());
// beginning comment
writePSFmt("%%BeginResource: font {0:t}\n", psName);
@@ -2348,90 +2703,55 @@ void PSOutputDev::setupExternalCIDTrueTypeFont(GfxFont *font,
// convert it to a Type 0 font
//~ this should use fontNum to load the correct font
- if ((ffTT = FoFiTrueType::load(fileName->getCString()))) {
-
- // check for embedding permission
- if (ffTT->getEmbeddingRights() >= 1) {
-
- // create a CID-to-GID mapping, via Unicode
- if ((ctu = ((GfxCIDFont *)font)->getToUnicode())) {
- // look for a Unicode cmap
- for (cmap = 0; cmap < ffTT->getNumCmaps(); ++cmap) {
- if ((ffTT->getCmapPlatform(cmap) == 3 &&
- ffTT->getCmapEncoding(cmap) == 1) ||
- ffTT->getCmapPlatform(cmap) == 0) {
- break;
- }
- }
- if (cmap < ffTT->getNumCmaps()) {
- // map CID -> Unicode -> GID
- codeToGIDLen = ctu->getLength();
- codeToGID = (int *)gmallocn(codeToGIDLen, sizeof(int));
- for (code = 0; code < codeToGIDLen; ++code) {
- if (ctu->mapToUnicode(code, uBuf, 8) > 0) {
- codeToGID[code] = ffTT->mapCodeToGID(cmap, uBuf[0]);
- } else {
- codeToGID[code] = 0;
- }
- }
- if (globalParams->getPSLevel() >= psLevel3) {
- // Level 3: use a CID font
- ffTT->convertToCIDType2(psName->getCString(),
- codeToGID, codeToGIDLen,
- needVerticalMetrics,
- outputFunc, outputStream);
- } else {
- // otherwise: use a non-CID composite font
- ffTT->convertToType0(psName->getCString(),
- codeToGID, codeToGIDLen,
- needVerticalMetrics,
- outputFunc, outputStream);
- }
- gfree(codeToGID);
- }
- ctu->decRefCnt();
- } else {
- error(errSyntaxError, -1,
- "Couldn't find a mapping to Unicode for font '{0:s}'",
- font->getName() ? font->getName()->getCString() : "(unnamed)");
- }
- } else {
- error(errSyntaxError, -1,
- "TrueType font '%s' does not allow embedding",
- font->getName() ? font->getName()->getCString() : "(unnamed)");
-
- }
- delete ffTT;
+ if (globalParams->getPSLevel() >= psLevel3) {
+ // Level 3: use a CID font
+ ffTT->convertToCIDType2(psName->getCString(),
+ codeToGID, codeToGIDLen,
+ needVerticalMetrics,
+ outputFunc, outputStream);
+ } else {
+ // otherwise: use a non-CID composite font
+ ffTT->convertToType0(psName->getCString(),
+ codeToGID, codeToGIDLen,
+ needVerticalMetrics,
+ outputFunc, outputStream);
}
+ delete ffTT;
// ending comment
writePS("%%EndResource\n");
+
+ ff = new PSFontFileInfo(psName, font->getType(), psFontFileExternal);
+ ff->extFileName = fileName->copy();
+ ff->codeToGID = codeToGID;
+ ff->codeToGIDLen = codeToGIDLen;
+ fontFileInfo->add(ff->psName, ff);
+ return ff;
}
-void PSOutputDev::setupEmbeddedOpenTypeCFFFont(GfxFont *font, Ref *id,
- GString *psName) {
+PSFontFileInfo *PSOutputDev::setupEmbeddedOpenTypeCFFFont(GfxFont *font,
+ Ref *id) {
+ GString *psName;
+ PSFontFileInfo *ff;
char *fontBuf;
int fontLen;
FoFiTrueType *ffTT;
- int i;
+ GHashIter *iter;
+ int n;
// check if font is already embedded
- for (i = 0; i < t1FontNameLen; ++i) {
- if (t1FontNames[i].fontFileID.num == id->num &&
- t1FontNames[i].fontFileID.gen == id->gen) {
- psName->clear();
- psName->insert(0, t1FontNames[i].psName);
- return;
+ fontFileInfo->startIter(&iter);
+ while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) {
+ if (ff->loc == psFontFileEmbedded &&
+ ff->embFontID.num == id->num &&
+ ff->embFontID.gen == id->gen) {
+ fontFileInfo->killIter(&iter);
+ return ff;
}
}
- if (t1FontNameLen == t1FontNameSize) {
- t1FontNameSize *= 2;
- t1FontNames = (PST1FontName *)greallocn(t1FontNames, t1FontNameSize,
- sizeof(PST1FontName));
- }
- t1FontNames[t1FontNameLen].fontFileID = *id;
- t1FontNames[t1FontNameLen].psName = psName->copy();
- ++t1FontNameLen;
+
+ // generate name
+ psName = makePSFontName(font, id);
// beginning comment
writePSFmt("%%BeginResource: font {0:t}\n", psName);
@@ -2441,7 +2761,7 @@ void PSOutputDev::setupEmbeddedOpenTypeCFFFont(GfxFont *font, Ref *id,
// convert it to a Type 0 font
if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) {
- if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) {
+ if ((ffTT = FoFiTrueType::make(fontBuf, fontLen, 0))) {
if (ffTT->isOpenTypeCFF()) {
if (globalParams->getPSLevel() >= psLevel3) {
// Level 3: use a CID font
@@ -2464,10 +2784,22 @@ void PSOutputDev::setupEmbeddedOpenTypeCFFFont(GfxFont *font, Ref *id,
// ending comment
writePS("%%EndResource\n");
+
+ ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded);
+ ff->embFontID = *id;
+ if ((n = ((GfxCIDFont *)font)->getCIDToGIDLen())) {
+ ff->codeToGID = (int *)gmallocn(n, sizeof(int));
+ memcpy(ff->codeToGID, ((GfxCIDFont *)font)->getCIDToGID(), n * sizeof(int));
+ ff->codeToGIDLen = n;
+ }
+ fontFileInfo->add(ff->psName, ff);
+ return ff;
}
-void PSOutputDev::setupType3Font(GfxFont *font, GString *psName,
- Dict *parentResDict) {
+PSFontFileInfo *PSOutputDev::setupType3Font(GfxFont *font,
+ Dict *parentResDict) {
+ PSFontFileInfo *ff;
+ GString *psName;
Dict *resDict;
Dict *charProcs;
Object charProc;
@@ -2477,6 +2809,10 @@ void PSOutputDev::setupType3Font(GfxFont *font, GString *psName,
GString *buf;
int i;
+ // generate name
+ psName = GString::format("T3_{0:d}_{1:d}",
+ font->getID()->num, font->getID()->gen);
+
// set up resources used by font
if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
inType3Char = gTrue;
@@ -2528,7 +2864,7 @@ void PSOutputDev::setupType3Font(GfxFont *font, GString *psName,
writePS("/");
writePSName(charProcs->getKey(i));
writePS(" {\n");
- gfx->display(charProcs->getVal(i, &charProc));
+ gfx->display(charProcs->getValNF(i, &charProc));
charProc.free();
if (t3String) {
if (t3Cacheable) {
@@ -2558,6 +2894,10 @@ void PSOutputDev::setupType3Font(GfxFont *font, GString *psName,
// ending comment
writePS("%%EndResource\n");
+
+ ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded);
+ fontFileInfo->add(ff->psName, ff);
+ return ff;
}
// Make a unique PS font name, based on the names given in the PDF
@@ -2567,16 +2907,14 @@ GString *PSOutputDev::makePSFontName(GfxFont *font, Ref *id) {
if ((s = font->getEmbeddedFontName())) {
psName = filterPSName(s);
- if (!fontNames->lookupInt(psName)) {
- fontNames->add(psName->copy(), 1);
+ if (!fontFileInfo->lookup(psName)) {
return psName;
}
delete psName;
}
if ((s = font->getName())) {
psName = filterPSName(s);
- if (!fontNames->lookupInt(psName)) {
- fontNames->add(psName->copy(), 1);
+ if (!fontFileInfo->lookup(psName)) {
return psName;
}
delete psName;
@@ -2591,7 +2929,6 @@ GString *PSOutputDev::makePSFontName(GfxFont *font, Ref *id) {
psName->append('_')->append(s);
delete s;
}
- fontNames->add(psName->copy(), 1);
return psName;
}
@@ -2651,7 +2988,7 @@ void PSOutputDev::setupImages(Dict *resDict) {
}
void PSOutputDev::setupImage(Ref id, Stream *str, GBool mask) {
- GBool useRLE, useCompressed, useASCIIHex;
+ GBool useLZW, useRLE, useCompressed, useASCIIHex;
GString *s;
int c;
int size, line, col, i;
@@ -2660,21 +2997,27 @@ void PSOutputDev::setupImage(Ref id, Stream *str, GBool mask) {
//~ this does not correctly handle the DeviceN color space
//~ -- need to use DeviceNRecoder
if (level < psLevel2) {
- useRLE = gFalse;
+ useLZW = useRLE = gFalse;
useCompressed = gFalse;
useASCIIHex = gTrue;
} else {
if (globalParams->getPSUncompressPreloadedImages()) {
- useRLE = gFalse;
+ useLZW = useRLE = gFalse;
useCompressed = gFalse;
} else {
s = str->getPSFilter(level < psLevel3 ? 2 : 3, "");
if (s) {
- useRLE = gFalse;
+ useLZW = useRLE = gFalse;
useCompressed = gTrue;
delete s;
} else {
- useRLE = gTrue;
+ if (globalParams->getPSLZW()) {
+ useLZW = gTrue;
+ useRLE = gFalse;
+ } else {
+ useRLE = gTrue;
+ useLZW = gFalse;
+ }
useCompressed = gFalse;
}
}
@@ -2683,7 +3026,9 @@ void PSOutputDev::setupImage(Ref id, Stream *str, GBool mask) {
if (useCompressed) {
str = str->getUndecodedStream();
}
- if (useRLE) {
+ if (useLZW) {
+ str = new LZWEncoder(str);
+ } else if (useRLE) {
str = new RunLengthEncoder(str);
}
if (useASCIIHex) {
@@ -2722,9 +3067,9 @@ void PSOutputDev::setupImage(Ref id, Stream *str, GBool mask) {
}
} 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
+ // because the LZWDecode/RunLengthDecode filter may read past the end
++size;
- if (useRLE) {
+ if (useLZW || useRLE) {
++size;
}
writePSFmt("{0:d} array dup /{1:s}Data_{2:d}_{3:d} exch def\n",
@@ -2771,7 +3116,7 @@ void PSOutputDev::setupImage(Ref id, Stream *str, GBool mask) {
}
} while (c != (useASCIIHex ? '>' : '~') && c != EOF);
writePS((char *)(useASCIIHex ? "> put\n" : "~> put\n"));
- if (useRLE) {
+ if (useLZW || useRLE) {
++line;
writePSFmt("{0:d} <> put\n", line);
} else {
@@ -2799,7 +3144,7 @@ void PSOutputDev::setupForms(Dict *resDict) {
xObj.streamGetDict()->lookup("Subtype", &subtypeObj);
if (subtypeObj.isName("Form")) {
if (xObjRef.isRef()) {
- setupForm(xObjRef.getRef(), &xObj);
+ setupForm(&xObjRef, &xObj);
} else {
error(errSyntaxError, -1,
"Form in resource dict is not an indirect reference");
@@ -2814,7 +3159,7 @@ void PSOutputDev::setupForms(Dict *resDict) {
xObjDict.free();
}
-void PSOutputDev::setupForm(Ref id, Object *strObj) {
+void PSOutputDev::setupForm(Object *strRef, Object *strObj) {
Dict *dict, *resDict;
Object matrixObj, bboxObj, resObj, obj1;
double m[6], bbox[4];
@@ -2824,7 +3169,8 @@ void PSOutputDev::setupForm(Ref id, Object *strObj) {
// check if form is already defined
for (i = 0; i < formIDLen; ++i) {
- if (formIDs[i].num == id.num && formIDs[i].gen == id.gen) {
+ if (formIDs[i].num == strRef->getRefNum() &&
+ formIDs[i].gen == strRef->getRefGen()) {
return;
}
}
@@ -2838,7 +3184,7 @@ void PSOutputDev::setupForm(Ref id, Object *strObj) {
}
formIDs = (Ref *)greallocn(formIDs, formIDSize, sizeof(Ref));
}
- formIDs[formIDLen++] = id;
+ formIDs[formIDLen++] = strRef->getRef();
dict = strObj->streamGetDict();
@@ -2875,7 +3221,7 @@ void PSOutputDev::setupForm(Ref id, Object *strObj) {
dict->lookup("Resources", &resObj);
resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
- writePSFmt("/f_{0:d}_{1:d} {{\n", id.num, id.gen);
+ writePSFmt("/f_{0:d}_{1:d} {{\n", strRef->getRefNum(), strRef->getRefGen());
writePS("q\n");
writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] cm\n",
m[0], m[1], m[2], m[3], m[4], m[5]);
@@ -2885,7 +3231,7 @@ void PSOutputDev::setupForm(Ref id, Object *strObj) {
box.x2 = bbox[2];
box.y2 = bbox[3];
gfx = new Gfx(doc, this, resDict, &box, &box);
- gfx->display(strObj);
+ gfx->display(strRef);
delete gfx;
writePS("Q\n");
@@ -2905,6 +3251,7 @@ GBool PSOutputDev::checkPageSlice(Page *page, double hDPI, double vDPI,
GBool rasterize;
#if HAVE_SPLASH
GBool mono;
+ GBool useLZW;
double dpi;
SplashOutputDev *splashOut;
SplashColor paperColor;
@@ -2915,10 +3262,11 @@ GBool PSOutputDev::checkPageSlice(Page *page, double hDPI, double vDPI,
Object obj;
Guchar *p;
Guchar col[4];
+ char buf[4096];
double hDPI2, vDPI2;
double m0, m1, m2, m3, m4, m5;
int nStripes, stripeH, stripeY;
- int c, w, h, x, y, comp, i;
+ int w, h, x, y, comp, i, n;
#endif
if (globalParams->getPSAlwaysRasterize()) {
@@ -2939,6 +3287,7 @@ GBool PSOutputDev::checkPageSlice(Page *page, double hDPI, double vDPI,
// get the rasterization parameters
dpi = globalParams->getPSRasterResolution();
mono = globalParams->getPSRasterMono();
+ useLZW = globalParams->getPSLZW();
// start the PS page
page->makeBox(dpi, dpi, rotateA, useMediaBox, gFalse,
@@ -2987,8 +3336,8 @@ GBool PSOutputDev::checkPageSlice(Page *page, double hDPI, double vDPI,
sliceW = (int)((box.x2 - box.x1) * hDPI2 / 72.0);
sliceH = (int)((box.y2 - box.y1) * vDPI2 / 72.0);
}
- nStripes = (int)ceil((double)(sliceW * sliceH) /
- (double)rasterizationSliceSize);
+ nStripes = (int)ceil(((double)sliceW * (double)sliceH) /
+ (double)globalParams->getPSRasterSliceSize());
stripeH = (sliceH + nStripes - 1) / nStripes;
// render the stripes
@@ -3094,21 +3443,29 @@ GBool PSOutputDev::checkPageSlice(Page *page, double hDPI, double vDPI,
} else {
writePS(" /ASCII85Decode filter\n");
}
- writePS(" /RunLengthDecode filter\n");
+ if (useLZW) {
+ writePS(" /LZWDecode filter\n");
+ } else {
+ writePS(" /RunLengthDecode filter\n");
+ }
writePS(">>\n");
writePS("image\n");
obj.initNull();
p = bitmap->getDataPtr() + (h - 1) * bitmap->getRowSize();
str0 = new MemStream((char *)p, 0, w * h * (mono ? 1 : 3), &obj);
- str = new RunLengthEncoder(str0);
+ if (useLZW) {
+ str = new LZWEncoder(str0);
+ } else {
+ 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);
+ while ((n = str->getBlock(buf, sizeof(buf))) > 0) {
+ writePSBlock(buf, n);
}
str->close();
delete str;
@@ -3148,8 +3505,13 @@ void PSOutputDev::startPage(int pageNum, GfxState *state) {
if (paperMatch) {
page = doc->getCatalog()->getPage(pageNum);
imgLLX = imgLLY = 0;
- imgURX = (int)ceil(page->getMediaWidth());
- imgURY = (int)ceil(page->getMediaHeight());
+ if (globalParams->getPSUseCropBoxAsPage()) {
+ imgURX = (int)ceil(page->getCropWidth());
+ imgURY = (int)ceil(page->getCropHeight());
+ } else {
+ imgURX = (int)ceil(page->getMediaWidth());
+ imgURY = (int)ceil(page->getMediaHeight());
+ }
if (state->getRotate() == 90 || state->getRotate() == 270) {
t = imgURX;
imgURX = imgURY;
@@ -3160,6 +3522,9 @@ void PSOutputDev::startPage(int pageNum, GfxState *state) {
}
writePS("%%BeginPageSetup\n");
}
+ if (mode != psModeForm) {
+ writePS("xpdf begin\n");
+ }
// underlays
if (underlayCbk) {
@@ -3356,6 +3721,7 @@ void PSOutputDev::endPage() {
}
writePS("%%PageTrailer\n");
writePageTrailer();
+ writePS("end\n");
}
}
@@ -3371,8 +3737,13 @@ void PSOutputDev::restoreState(GfxState *state) {
void PSOutputDev::updateCTM(GfxState *state, double m11, double m12,
double m21, double m22, double m31, double m32) {
- writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] cm\n",
- m11, m12, m21, m22, m31, m32);
+ if (fabs(m11 * m22 - m12 * m21) < 0.00001) {
+ // avoid a singular (or close-to-singular) matrix
+ writePSFmt("[0.00001 0 0 0.00001 {0:.6g} {1:.6g}] Tm\n", m31, m32);
+ } else {
+ writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] cm\n",
+ m11, m12, m21, m22, m31, m32);
+ }
}
void PSOutputDev::updateLineDash(GfxState *state) {
@@ -3737,7 +4108,7 @@ void PSOutputDev::eoFill(GfxState *state) {
writePS("f*\n");
}
-void PSOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
+void PSOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *strRef,
int paintType, Dict *resDict,
double *mat, double *bbox,
int x0, int y0, int x1, int y1,
@@ -3770,6 +4141,7 @@ void PSOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
box.x2 = bbox[2];
box.y2 = bbox[3];
gfx2 = new Gfx(doc, this, resDict, &box, NULL);
+ gfx2->takeContentStreamStack(gfx);
writePS("/x {\n");
if (paintType == 2) {
writePSFmt("{0:.6g} 0 {1:.6g} {2:.6g} {3:.6g} {4:.6g} setcachedevice\n",
@@ -3785,7 +4157,7 @@ void PSOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
}
inType3Char = gTrue;
++numTilingPatterns;
- gfx2->display(str);
+ gfx2->display(strRef);
--numTilingPatterns;
inType3Char = gFalse;
writePS("} def\n");
@@ -3944,7 +4316,7 @@ GBool PSOutputDev::radialShadedFill(GfxState *state,
double xMin, yMin, xMax, yMax;
double x0, y0, r0, x1, y1, r1, t0, t1;
double xa, ya, ra;
- double sz, sMin, sMax, h, ta;
+ double sMin, sMax, h, ta;
double sLeft, sRight, sTop, sBottom, sZero, sDiag;
GBool haveSLeft, haveSRight, haveSTop, haveSBottom, haveSZero;
GBool haveSMin, haveSMax;
@@ -3970,18 +4342,14 @@ GBool PSOutputDev::radialShadedFill(GfxState *state,
if (h == 0) {
enclosed = gTrue;
theta = 0; // make gcc happy
- sz = 0; // make gcc happy
} else if (r1 - r0 == 0) {
enclosed = gFalse;
theta = 0;
- sz = 0; // make gcc happy
} else if (fabs(r1 - r0) >= h) {
enclosed = gTrue;
theta = 0; // make gcc happy
- sz = 0; // make gcc happy
} else {
enclosed = gFalse;
- sz = -r0 / (r1 - r0);
theta = asin((r1 - r0) / h);
}
if (enclosed) {
@@ -4267,8 +4635,9 @@ void PSOutputDev::drawString(GfxState *state, GString *s) {
int wMode;
int *codeToGID;
GString *s2;
- double dx, dy, originX, originY;
+ double dx, dy, originX, originY, originX0, originY0, tOriginX0, tOriginY0;
char *p;
+ PSFontInfo *fi;
UnicodeMap *uMap;
CharCode code;
Unicode u[8];
@@ -4292,30 +4661,32 @@ void PSOutputDev::drawString(GfxState *state, GString *s) {
}
wMode = font->getWMode();
+ fi = NULL;
+ for (i = 0; i < fontInfo->getLength(); ++i) {
+ fi = (PSFontInfo *)fontInfo->get(i);
+ if (fi->fontID.num == font->getID()->num &&
+ fi->fontID.gen == font->getID()->gen) {
+ break;
+ }
+ fi = NULL;
+ }
+
// 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) {
- if (!font16Enc[i].enc) {
- // font substitution failed, so don't output any text
- return;
- }
- uMap = globalParams->getUnicodeMap(font16Enc[i].enc);
- break;
- }
+ if (!(fi && fi->ff)) {
+ // font substitution failed, so don't output any text
+ return;
+ }
+ if (fi->ff->encoding) {
+ uMap = globalParams->getUnicodeMap(fi->ff->encoding);
}
- // check for a code-to-GID map
+ // check for an 8-bit 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;
- }
+ if (fi && fi->ff) {
+ codeToGID = fi->ff->codeToGID;
}
}
@@ -4326,10 +4697,18 @@ void PSOutputDev::drawString(GfxState *state, GString *s) {
s2 = new GString();
dxdySize = font->isCIDFont() ? 8 : s->getLength();
dxdy = (double *)gmallocn(2 * dxdySize, sizeof(double));
+ originX0 = originY0 = 0; // make gcc happy
while (len > 0) {
n = font->getNextChar(p, len, &code,
u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
&dx, &dy, &originX, &originY);
+ //~ this doesn't handle the case where the origin offset changes
+ //~ within a string of characters -- which could be fixed by
+ //~ modifying dx,dy as needed for each character
+ if (p == s->getCString()) {
+ originX0 = originX;
+ originY0 = originY;
+ }
dx *= state->getFontSize();
dy *= state->getFontSize();
if (wMode) {
@@ -4389,8 +4768,14 @@ void PSOutputDev::drawString(GfxState *state, GString *s) {
if (uMap) {
uMap->decRefCnt();
}
+ originX0 *= state->getFontSize();
+ originY0 *= state->getFontSize();
+ state->textTransformDelta(originX0, originY0, &tOriginX0, &tOriginY0);
if (nChars > 0) {
+ if (wMode) {
+ writePSFmt("{0:.6g} {1:.6g} rmoveto\n", -tOriginX0, -tOriginY0);
+ }
writePSString(s2);
writePS("\n[");
for (i = 0; i < 2 * nChars; ++i) {
@@ -4400,6 +4785,9 @@ void PSOutputDev::drawString(GfxState *state, GString *s) {
writePSFmt("{0:.6g}", dxdy[i]);
}
writePS("] Tj\n");
+ if (wMode) {
+ writePSFmt("{0:.6g} {1:.6g} rmoveto\n", tOriginX0, tOriginY0);
+ }
}
gfree(dxdy);
delete s2;
@@ -4418,7 +4806,7 @@ void PSOutputDev::endTextObject(GfxState *state) {
void PSOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
int width, int height, GBool invert,
- GBool inlineImg) {
+ GBool inlineImg, GBool interpolate) {
int len;
len = height * ((width + 7) / 8);
@@ -4442,7 +4830,8 @@ void PSOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
void PSOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
int width, int height, GfxImageColorMap *colorMap,
- int *maskColors, GBool inlineImg) {
+ int *maskColors, GBool inlineImg,
+ GBool interpolate) {
int len;
len = height * ((width * colorMap->getNumPixelComps() *
@@ -4474,7 +4863,7 @@ void PSOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
GfxImageColorMap *colorMap,
Stream *maskStr,
int maskWidth, int maskHeight,
- GBool maskInvert) {
+ GBool maskInvert, GBool interpolate) {
int len;
len = height * ((width * colorMap->getNumPixelComps() *
@@ -4684,10 +5073,11 @@ void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
GBool emitRect, addRect, extendRect;
GString *s;
int n, numComps;
- GBool useRLE, useASCII, useASCIIHex, useCompressed;
+ GBool useLZW, useRLE, useASCII, useASCIIHex, useCompressed;
GfxSeparationColorSpace *sepCS;
GfxColor color;
GfxCMYK cmyk;
+ char buf[4096];
int c;
int col, i, j, x0, x1, y, maskXor;
@@ -4821,14 +5211,14 @@ void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
rectsOut[rectsOutLen].y1 = height - rects0[i].y0 - 1;
++rectsOutLen;
}
- writePSFmt("{0:d} array 0\n", rectsOutLen * 4);
+ writePSFmt("{0:d} {1:d}\n", maskWidth, maskHeight);
for (i = 0; i < rectsOutLen; ++i) {
- writePSFmt("[{0:d} {1:d} {2:d} {3:d}] pr\n",
+ 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);
+ writePS("pop pop pdfImClip\n");
gfree(rectsOut);
gfree(rects0);
gfree(rects1);
@@ -4923,14 +5313,14 @@ void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
rectsOut[rectsOutLen].y1 = maskHeight - rects0[i].y0 - 1;
++rectsOutLen;
}
- writePSFmt("{0:d} array 0\n", rectsOutLen * 4);
+ writePSFmt("{0:d} {1:d}\n", maskWidth, maskHeight);
for (i = 0; i < rectsOutLen; ++i) {
- writePSFmt("[{0:d} {1:d} {2:d} {3:d}] pr\n",
+ 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);
+ writePS("pop pop pdfImClip\n");
gfree(rectsOut);
gfree(rects0);
gfree(rects1);
@@ -4951,7 +5341,11 @@ void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
if (inlineImg) {
// create an array
str2 = new FixedLengthEncoder(str, len);
- str2 = new RunLengthEncoder(str2);
+ if (globalParams->getPSLZW()) {
+ str2 = new LZWEncoder(str2);
+ } else {
+ str2 = new RunLengthEncoder(str2);
+ }
if (useASCIIHex) {
str2 = new ASCIIHexEncoder(str2);
} else {
@@ -4994,8 +5388,8 @@ void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
}
} while (c != (useASCIIHex ? '>' : '~') && c != EOF);
writePS((char *)(useASCIIHex ? ">\n" : "~>\n"));
- // add an extra entry because the RunLengthDecode filter may
- // read past the end
+ // add an extra entry because the LZWDecode/RunLengthDecode
+ // filter may read past the end
writePS("<>]\n");
writePS("0\n");
str2->close();
@@ -5065,7 +5459,7 @@ void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
if ((mode == psModeForm || inType3Char || preload) &&
globalParams->getPSUncompressPreloadedImages()) {
s = NULL;
- useRLE = gFalse;
+ useLZW = useRLE = gFalse;
useCompressed = gFalse;
useASCII = gFalse;
} else {
@@ -5073,11 +5467,17 @@ void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
" ");
if ((colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) ||
inlineImg || !s) {
- useRLE = gTrue;
+ if (globalParams->getPSLZW()) {
+ useLZW = gTrue;
+ useRLE = gFalse;
+ } else {
+ useRLE = gTrue;
+ useLZW = gFalse;
+ }
useASCII = !(mode == psModeForm || inType3Char || preload);
useCompressed = gFalse;
} else {
- useRLE = gFalse;
+ useLZW = useRLE = gFalse;
useASCII = str->isBinary() &&
!(mode == psModeForm || inType3Char || preload);
useCompressed = gTrue;
@@ -5087,7 +5487,9 @@ void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
writePSFmt(" /ASCII{0:s}Decode filter\n",
useASCIIHex ? "Hex" : "85");
}
- if (useRLE) {
+ if (useLZW) {
+ writePS(" /LZWDecode filter\n");
+ } else if (useRLE) {
writePS(" /RunLengthDecode filter\n");
}
if (useCompressed) {
@@ -5119,8 +5521,10 @@ void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
str = new DeviceNRecoder(str, width, height, colorMap);
}
- // add RunLengthEncode and ASCIIHex/85 encode filters
- if (useRLE) {
+ // add LZWEncode/RunLengthEncode and ASCIIHex/85 encode filters
+ if (useLZW) {
+ str = new LZWEncoder(str);
+ } else if (useRLE) {
str = new RunLengthEncoder(str);
}
if (useASCII) {
@@ -5141,12 +5545,14 @@ void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
n = 0;
} else {
// need to read the stream to count characters -- the length
- // is data-dependent (because of ASCII and RLE filters)
+ // is data-dependent (because of ASCII and LZW/RunLength
+ // filters)
str->reset();
n = 0;
- while ((c = str->getChar()) != EOF) {
- ++n;
- }
+ do {
+ i = str->discardChars(4096);
+ n += i;
+ } while (i == 4096);
str->close();
}
// +6/7 for "pdfIm\n" / "pdfImM\n"
@@ -5170,8 +5576,8 @@ void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
// copy the stream data
str->reset();
- while ((c = str->getChar()) != EOF) {
- writePSChar(c);
+ while ((n = str->getBlock(buf, sizeof(buf))) > 0) {
+ writePSBlock(buf, n);
}
str->close();
@@ -5185,7 +5591,7 @@ void PSOutputDev::doImageL2(Object *ref, GfxImageColorMap *colorMap,
#endif
// delete encoders
- if (useRLE || useASCII || inlineImg) {
+ if (useLZW || useRLE || useASCII || inlineImg) {
delete str;
}
}
@@ -5204,18 +5610,20 @@ void PSOutputDev::doImageL3(Object *ref, GfxImageColorMap *colorMap,
Stream *str2;
GString *s;
int n, numComps;
- GBool useRLE, useASCII, useASCIIHex, useCompressed;
- GBool maskUseRLE, maskUseASCII, maskUseCompressed;
+ GBool useLZW, useRLE, useASCII, useASCIIHex, useCompressed;
+ GBool maskUseLZW, maskUseRLE, maskUseASCII, maskUseCompressed;
GString *maskFilters;
GfxSeparationColorSpace *sepCS;
GfxColor color;
GfxCMYK cmyk;
+ char buf[4096];
int c;
int col, i;
useASCIIHex = globalParams->getPSASCIIHex();
- useRLE = useASCII = useCompressed = gFalse; // make gcc happy
- maskUseRLE = maskUseASCII = maskUseCompressed = gFalse; // make gcc happy
+ useLZW = useRLE = useASCII = useCompressed = gFalse; // make gcc happy
+ maskUseLZW = maskUseRLE = maskUseASCII = gFalse; // make gcc happy
+ maskUseCompressed = gFalse; // make gcc happy
maskFilters = NULL; // make gcc happy
// explicit masking
@@ -5225,17 +5633,23 @@ void PSOutputDev::doImageL3(Object *ref, GfxImageColorMap *colorMap,
if ((mode == psModeForm || inType3Char || preload) &&
globalParams->getPSUncompressPreloadedImages()) {
s = NULL;
- maskUseRLE = gFalse;
+ maskUseLZW = maskUseRLE = gFalse;
maskUseCompressed = gFalse;
maskUseASCII = gFalse;
} else {
s = maskStr->getPSFilter(3, " ");
if (!s) {
- maskUseRLE = gTrue;
+ if (globalParams->getPSLZW()) {
+ maskUseLZW = gTrue;
+ maskUseRLE = gFalse;
+ } else {
+ maskUseRLE = gTrue;
+ maskUseLZW = gFalse;
+ }
maskUseASCII = !(mode == psModeForm || inType3Char || preload);
maskUseCompressed = gFalse;
} else {
- maskUseRLE = gFalse;
+ maskUseLZW = maskUseRLE = gFalse;
maskUseASCII = maskStr->isBinary() &&
!(mode == psModeForm || inType3Char || preload);
maskUseCompressed = gTrue;
@@ -5246,7 +5660,9 @@ void PSOutputDev::doImageL3(Object *ref, GfxImageColorMap *colorMap,
maskFilters->appendf(" /ASCII{0:s}Decode filter\n",
useASCIIHex ? "Hex" : "85");
}
- if (maskUseRLE) {
+ if (maskUseLZW) {
+ maskFilters->append(" /LZWDecode filter\n");
+ } else if (maskUseRLE) {
maskFilters->append(" /RunLengthDecode filter\n");
}
if (maskUseCompressed) {
@@ -5263,11 +5679,13 @@ void PSOutputDev::doImageL3(Object *ref, GfxImageColorMap *colorMap,
writePS(maskFilters->getCString());
writePS("pdfMask\n");
- // add RunLengthEncode and ASCIIHex/85 encode filters
+ // add LZWEncode/RunLengthEncode and ASCIIHex/85 encode filters
if (maskUseCompressed) {
maskStr = maskStr->getUndecodedStream();
}
- if (maskUseRLE) {
+ if (maskUseLZW) {
+ maskStr = new LZWEncoder(maskStr);
+ } else if (maskUseRLE) {
maskStr = new RunLengthEncoder(maskStr);
}
if (maskUseASCII) {
@@ -5280,15 +5698,15 @@ void PSOutputDev::doImageL3(Object *ref, GfxImageColorMap *colorMap,
// copy the stream data
maskStr->reset();
- while ((c = maskStr->getChar()) != EOF) {
- writePSChar(c);
+ while ((n = maskStr->getBlock(buf, sizeof(buf))) > 0) {
+ writePSBlock(buf, n);
}
maskStr->close();
writePSChar('\n');
writePS("%-EOD-\n");
// delete encoders
- if (maskUseRLE || maskUseASCII) {
+ if (maskUseLZW || maskUseRLE || maskUseASCII) {
delete maskStr;
}
}
@@ -5305,7 +5723,11 @@ void PSOutputDev::doImageL3(Object *ref, GfxImageColorMap *colorMap,
if (inlineImg) {
// create an array
str2 = new FixedLengthEncoder(str, len);
- str2 = new RunLengthEncoder(str2);
+ if (globalParams->getPSLZW()) {
+ str2 = new LZWEncoder(str2);
+ } else {
+ str2 = new RunLengthEncoder(str2);
+ }
if (useASCIIHex) {
str2 = new ASCIIHexEncoder(str2);
} else {
@@ -5348,8 +5770,8 @@ void PSOutputDev::doImageL3(Object *ref, GfxImageColorMap *colorMap,
}
} while (c != (useASCIIHex ? '>' : '~') && c != EOF);
writePS((char *)(useASCIIHex ? ">\n" : "~>\n"));
- // add an extra entry because the RunLengthDecode filter may
- // read past the end
+ // add an extra entry because the LZWDecode/RunLengthDecode
+ // filter may read past the end
writePS("<>]\n");
writePS("0\n");
str2->close();
@@ -5436,7 +5858,7 @@ void PSOutputDev::doImageL3(Object *ref, GfxImageColorMap *colorMap,
if ((mode == psModeForm || inType3Char || preload) &&
globalParams->getPSUncompressPreloadedImages()) {
s = NULL;
- useRLE = gFalse;
+ useLZW = useRLE = gFalse;
useCompressed = gFalse;
useASCII = gFalse;
} else {
@@ -5444,11 +5866,17 @@ void PSOutputDev::doImageL3(Object *ref, GfxImageColorMap *colorMap,
" ");
if ((colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) ||
inlineImg || !s) {
- useRLE = gTrue;
+ if (globalParams->getPSLZW()) {
+ useLZW = gTrue;
+ useRLE = gFalse;
+ } else {
+ useRLE = gTrue;
+ useLZW = gFalse;
+ }
useASCII = !(mode == psModeForm || inType3Char || preload);
useCompressed = gFalse;
} else {
- useRLE = gFalse;
+ useLZW = useRLE = gFalse;
useASCII = str->isBinary() &&
!(mode == psModeForm || inType3Char || preload);
useCompressed = gTrue;
@@ -5458,7 +5886,9 @@ void PSOutputDev::doImageL3(Object *ref, GfxImageColorMap *colorMap,
writePSFmt(" /ASCII{0:s}Decode filter\n",
useASCIIHex ? "Hex" : "85");
}
- if (useRLE) {
+ if (useLZW) {
+ writePS(" /LZWDecode filter\n");
+ } else if (useRLE) {
writePS(" /RunLengthDecode filter\n");
}
if (useCompressed) {
@@ -5538,8 +5968,10 @@ void PSOutputDev::doImageL3(Object *ref, GfxImageColorMap *colorMap,
str = new DeviceNRecoder(str, width, height, colorMap);
}
- // add RunLengthEncode and ASCIIHex/85 encode filters
- if (useRLE) {
+ // add LZWEncode/RunLengthEncode and ASCIIHex/85 encode filters
+ if (useLZW) {
+ str = new LZWEncoder(str);
+ } else if (useRLE) {
str = new RunLengthEncoder(str);
}
if (useASCII) {
@@ -5552,8 +5984,8 @@ void PSOutputDev::doImageL3(Object *ref, GfxImageColorMap *colorMap,
// copy the stream data
str->reset();
- while ((c = str->getChar()) != EOF) {
- writePSChar(c);
+ while ((n = str->getBlock(buf, sizeof(buf))) > 0) {
+ writePSBlock(buf, n);
}
str->close();
@@ -5562,7 +5994,7 @@ void PSOutputDev::doImageL3(Object *ref, GfxImageColorMap *colorMap,
writePS("%-EOD-\n");
// delete encoders
- if (useRLE || useASCII || inlineImg) {
+ if (useLZW || useRLE || useASCII || inlineImg) {
delete str;
}
}
@@ -6267,6 +6699,10 @@ void PSOutputDev::type3D0(GfxState *state, double wx, double wy) {
void PSOutputDev::type3D1(GfxState *state, double wx, double wy,
double llx, double lly, double urx, double ury) {
+ if (t3String) {
+ error(errSyntaxError, -1, "Multiple 'd1' operators in Type 3 CharProc");
+ return;
+ }
t3WX = wx;
t3WY = wy;
t3LLX = llx;
@@ -6286,7 +6722,8 @@ void PSOutputDev::drawForm(Ref id) {
void PSOutputDev::psXObject(Stream *psStream, Stream *level1Stream) {
Stream *str;
- int c;
+ char buf[4096];
+ int n;
if ((level == psLevel1 || level == psLevel1Sep) && level1Stream) {
str = level1Stream;
@@ -6294,8 +6731,8 @@ void PSOutputDev::psXObject(Stream *psStream, Stream *level1Stream) {
str = psStream;
}
str->reset();
- while ((c = str->getChar()) != EOF) {
- writePSChar(c);
+ while ((n = str->getBlock(buf, sizeof(buf))) > 0) {
+ writePSBlock(buf, n);
}
str->close();
}
@@ -6462,6 +6899,14 @@ void PSOutputDev::writePSChar(char c) {
}
}
+void PSOutputDev::writePSBlock(char *s, int len) {
+ if (t3String) {
+ t3String->append(s, len);
+ } else {
+ (*outputFunc)(outputStream, s, len);
+ }
+}
+
void PSOutputDev::writePS(const char *s) {
if (t3String) {
t3String->append(s);
@@ -6564,7 +7009,9 @@ GString *PSOutputDev::filterPSName(GString *name) {
// Write a DSC-compliant <textline>.
void PSOutputDev::writePSTextLine(GString *s) {
- int i, j, step;
+ TextString *ts;
+ Unicode *u;
+ int i, j;
int c;
// - DSC comments must be printable ASCII; control chars and
@@ -6574,17 +7021,10 @@ void PSOutputDev::writePSTextLine(GString *s) {
// 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;
+ ts = new TextString(s);
+ u = ts->getUnicode();
+ for (i = 0, j = 0; i < ts->getLength() && j < 200; ++i) {
+ c = u[i] & 0xff;
if (c == '\\') {
writePS("\\\\");
j += 2;
@@ -6597,4 +7037,5 @@ void PSOutputDev::writePSTextLine(GString *s) {
}
}
writePS("\n");
+ delete ts;
}
diff --git a/xpdf/PSOutputDev.h b/xpdf/PSOutputDev.h
index e528f8c..7118830 100644
--- a/xpdf/PSOutputDev.h
+++ b/xpdf/PSOutputDev.h
@@ -30,11 +30,9 @@ class GfxFont;
class GfxColorSpace;
class GfxSeparationColorSpace;
class PDFRectangle;
-struct PST1FontName;
-struct PSFont8Info;
-struct PSFont16Enc;
class PSOutCustomColor;
class PSOutputDev;
+class PSFontFileInfo;
//------------------------------------------------------------------------
// PSOutputDev
@@ -92,6 +90,9 @@ public:
// Check if file was successfully created.
virtual GBool isOk() { return ok; }
+ // Returns false if there have been any errors on the output stream.
+ GBool checkIO();
+
//---- get info about output device
// Does this device use upside-down coordinates?
@@ -196,7 +197,7 @@ public:
virtual void stroke(GfxState *state);
virtual void fill(GfxState *state);
virtual void eoFill(GfxState *state);
- virtual void tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
+ virtual void tilingPatternFill(GfxState *state, Gfx *gfx, Object *strRef,
int paintType, Dict *resDict,
double *mat, double *bbox,
int x0, int y0, int x1, int y1,
@@ -218,15 +219,15 @@ public:
//----- image drawing
virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
int width, int height, GBool invert,
- GBool inlineImg);
+ GBool inlineImg, GBool interpolate);
virtual void drawImage(GfxState *state, Object *ref, Stream *str,
int width, int height, GfxImageColorMap *colorMap,
- int *maskColors, GBool inlineImg);
+ int *maskColors, GBool inlineImg, GBool interpolate);
virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
Stream *maskStr, int maskWidth, int maskHeight,
- GBool maskInvert);
+ GBool maskInvert, GBool interpolate);
#if OPI_SUPPORT
//----- OPI functions
@@ -262,6 +263,7 @@ public:
{ overlayCbk = cbk; overlayCbkData = data; }
void writePSChar(char c);
+ void writePSBlock(char *s, int len);
void writePS(const char *s);
void writePSFmt(const char *fmt, ...);
void writePSString(GString *s);
@@ -277,27 +279,27 @@ private:
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 *fileName,
- GString *psName);
- void setupEmbeddedCIDType0Font(GfxFont *font, Ref *id, GString *psName);
- void setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id, GString *psName,
- GBool needVerticalMetrics);
- void setupExternalCIDTrueTypeFont(GfxFont *font,
- GString *fileName,
- GString *psName,
- GBool needVerticalMetrics);
- void setupEmbeddedOpenTypeCFFFont(GfxFont *font, Ref *id, GString *psName);
- void setupType3Font(GfxFont *font, GString *psName, Dict *parentResDict);
+ PSFontFileInfo *setupEmbeddedType1Font(GfxFont *font, Ref *id);
+ PSFontFileInfo *setupExternalType1Font(GfxFont *font, GString *fileName);
+ PSFontFileInfo *setupEmbeddedType1CFont(GfxFont *font, Ref *id);
+ PSFontFileInfo *setupEmbeddedOpenTypeT1CFont(GfxFont *font, Ref *id);
+ PSFontFileInfo *setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id);
+ PSFontFileInfo *setupExternalTrueTypeFont(GfxFont *font, GString *fileName,
+ int fontNum);
+ PSFontFileInfo *setupEmbeddedCIDType0Font(GfxFont *font, Ref *id);
+ PSFontFileInfo *setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id,
+ GBool needVerticalMetrics);
+ PSFontFileInfo *setupExternalCIDTrueTypeFont(GfxFont *font,
+ GString *fileName,
+ int fontNum,
+ GBool needVerticalMetrics);
+ PSFontFileInfo *setupEmbeddedOpenTypeCFFFont(GfxFont *font, Ref *id);
+ PSFontFileInfo *setupType3Font(GfxFont *font, Dict *parentResDict);
GString *makePSFontName(GfxFont *font, Ref *id);
void setupImages(Dict *resDict);
void setupImage(Ref id, Stream *str, GBool mask);
void setupForms(Dict *resDict);
- void setupForm(Ref id, Object *strObj);
+ void setupForm(Object *strRef, Object *strObj);
void addProcessColor(double c, double m, double y, double k);
void addCustomColor(GfxSeparationColorSpace *sepCS);
void doPath(GfxPath *path);
@@ -358,19 +360,8 @@ private:
PDFDoc *doc;
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
- GHash *fontNames; // all used font names
- PST1FontName *t1FontNames; // font names for Type 1/1C fonts
- int t1FontNameLen; // number of entries in t1FontNames array
- int t1FontNameSize; // size of t1FontNames array
- 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
+ GList *fontInfo; // info for each font [PSFontInfo]
+ GHash *fontFileInfo; // info for each font file [PSFontFileInfo]
Ref *imgIDs; // list of image IDs for in-memory images
int imgIDLen; // number of entries in imgIDs array
int imgIDSize; // size of imgIDs array
diff --git a/xpdf/Page.cc b/xpdf/Page.cc
index 189d2a2..635ee7a 100644
--- a/xpdf/Page.cc
+++ b/xpdf/Page.cc
@@ -25,6 +25,7 @@
#include "Gfx.h"
#include "GfxState.h"
#include "Annot.h"
+#include "Form.h"
#endif
#include "Error.h"
#include "Catalog.h"
@@ -123,6 +124,15 @@ PageAttrs::PageAttrs(PageAttrs *attrs, Dict *dict) {
dict->lookup("Metadata", &metadata);
dict->lookup("PieceInfo", &pieceInfo);
dict->lookup("SeparationInfo", &separationInfo);
+ if (dict->lookup("UserUnit", &obj1)->isNum()) {
+ userUnit = obj1.getNum();
+ if (userUnit < 1) {
+ userUnit = 1;
+ }
+ } else {
+ userUnit = 1;
+ }
+ obj1.free();
// resource dictionary
dict->lookup("Resources", &obj1);
@@ -312,7 +322,7 @@ void Page::displaySlice(OutputDev *out, double hDPI, double vDPI,
Gfx *gfx;
Object obj;
Annots *annotList;
- Dict *acroForm;
+ Form *form;
int i;
if (!out->checkPageSlice(this, hDPI, vDPI, rotate, useMediaBox, crop,
@@ -347,8 +357,10 @@ void Page::displaySlice(OutputDev *out, double hDPI, double vDPI,
contents.fetch(xref, &obj);
if (!obj.isNull()) {
gfx->saveState();
- gfx->display(&obj);
- gfx->restoreState();
+ gfx->display(&contents);
+ while (gfx->getState()->hasSaves()) {
+ gfx->restoreState();
+ }
} else {
// empty pages need to call dump to do any setup required by the
// OutputDev
@@ -356,20 +368,11 @@ void Page::displaySlice(OutputDev *out, double hDPI, double vDPI,
}
obj.free();
- // draw annotations
+ // draw (non-form) annotations
if (globalParams->getDrawAnnotations()) {
annotList = new Annots(doc, getAnnots(&obj));
obj.free();
- acroForm = doc->getCatalog()->getAcroForm()->isDict() ?
- doc->getCatalog()->getAcroForm()->getDict() : NULL;
- if (acroForm) {
- if (acroForm->lookup("NeedAppearances", &obj)) {
- if (obj.isBool() && obj.getBool()) {
- annotList->generateAppearances();
- }
- }
- obj.free();
- }
+ annotList->generateAnnotAppearances();
if (annotList->getNumAnnots() > 0) {
if (globalParams->getPrintCommands()) {
printf("***** Annotations\n");
@@ -382,6 +385,12 @@ void Page::displaySlice(OutputDev *out, double hDPI, double vDPI,
delete annotList;
}
+ // draw form fields
+ if ((form = doc->getCatalog()->getForm())) {
+ form->draw(num, gfx, printing);
+ out->dump();
+ }
+
delete gfx;
#endif
}
@@ -459,6 +468,7 @@ void Page::processLinks(OutputDev *out) {
delete links;
}
+#ifndef PDF_PARSER_ONLY
void Page::getDefaultCTM(double *ctm, double hDPI, double vDPI,
int rotate, GBool useMediaBox, GBool upsideDown) {
GfxState *state;
@@ -478,3 +488,4 @@ void Page::getDefaultCTM(double *ctm, double hDPI, double vDPI,
}
delete state;
}
+#endif
diff --git a/xpdf/Page.h b/xpdf/Page.h
index 102eca8..0e7729e 100644
--- a/xpdf/Page.h
+++ b/xpdf/Page.h
@@ -77,6 +77,7 @@ public:
Dict *getSeparationInfo()
{ return separationInfo.isDict()
? separationInfo.getDict() : (Dict *)NULL; }
+ double getUserUnit() { return userUnit; }
Dict *getResourceDict()
{ return resources.isDict() ? resources.getDict() : (Dict *)NULL; }
@@ -100,6 +101,7 @@ private:
Object metadata;
Object pieceInfo;
Object separationInfo;
+ double userUnit;
Object resources;
};
@@ -146,6 +148,7 @@ public:
Stream *getMetadata() { return attrs->getMetadata(); }
Dict *getPieceInfo() { return attrs->getPieceInfo(); }
Dict *getSeparationInfo() { return attrs->getSeparationInfo(); }
+ double getUserUnit() { return attrs->getUserUnit(); }
// Get resource dictionary.
Dict *getResourceDict() { return attrs->getResourceDict(); }
diff --git a/xpdf/Parser.cc b/xpdf/Parser.cc
index c43da26..dd37470 100644
--- a/xpdf/Parser.cc
+++ b/xpdf/Parser.cc
@@ -153,7 +153,7 @@ Stream *Parser::makeStream(Object *dict, Guchar *fileKey,
Object obj;
BaseStream *baseStr;
Stream *str;
- Guint pos, endPos, length;
+ GFileOffset pos, endPos, length;
// get stream start position
lexer->skipToNextLine();
@@ -162,20 +162,21 @@ Stream *Parser::makeStream(Object *dict, Guchar *fileKey,
}
pos = str->getPos();
- // get length
- dict->dictLookup("Length", &obj, recursion);
- if (obj.isInt()) {
- length = (Guint)obj.getInt();
- obj.free();
- } else {
- error(errSyntaxError, 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;
+
+ // get length from the stream object
+ } else {
+ dict->dictLookup("Length", &obj, recursion);
+ if (obj.isInt()) {
+ length = (GFileOffset)(Guint)obj.getInt();
+ obj.free();
+ } else {
+ error(errSyntaxError, getPos(), "Bad 'Length' attribute in stream");
+ obj.free();
+ return NULL;
+ }
}
// in badly damaged PDF files, we can run off the end of the input
@@ -210,7 +211,7 @@ Stream *Parser::makeStream(Object *dict, Guchar *fileKey,
}
// get filters
- str = str->addFilters(dict);
+ str = str->addFilters(dict, recursion);
return str;
}
diff --git a/xpdf/Parser.h b/xpdf/Parser.h
index b25d749..d5403d0 100644
--- a/xpdf/Parser.h
+++ b/xpdf/Parser.h
@@ -42,7 +42,7 @@ public:
Stream *getStream() { return lexer->getStream(); }
// Get current position in file.
- int getPos() { return lexer->getPos(); }
+ GFileOffset getPos() { return lexer->getPos(); }
private:
diff --git a/xpdf/PreScanOutputDev.cc b/xpdf/PreScanOutputDev.cc
index bf48df0..ced188e 100644
--- a/xpdf/PreScanOutputDev.cc
+++ b/xpdf/PreScanOutputDev.cc
@@ -61,13 +61,13 @@ void PreScanOutputDev::eoFill(GfxState *state) {
}
void PreScanOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx,
- Object *str,
+ Object *strRef,
int paintType, Dict *resDict,
double *mat, double *bbox,
int x0, int y0, int x1, int y1,
double xStep, double yStep) {
if (paintType == 1) {
- gfx->drawForm(str, resDict, mat, bbox);
+ gfx->drawForm(strRef, resDict, mat, bbox);
} else {
check(state->getFillColorSpace(), state->getFillColor(),
state->getFillOpacity(), state->getBlendMode());
@@ -174,9 +174,7 @@ 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;
-
+ GBool inlineImg, GBool interpolate) {
check(state->getFillColorSpace(), state->getFillColor(),
state->getFillOpacity(), state->getBlendMode());
if (state->getFillColorSpace()->getMode() == csPattern) {
@@ -186,9 +184,7 @@ void PreScanOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
if (inlineImg) {
str->reset();
- j = height * ((width + 7) / 8);
- for (i = 0; i < j; ++i)
- str->getChar();
+ str->discardChars(height * ((width + 7) / 8));
str->close();
}
}
@@ -196,9 +192,9 @@ void PreScanOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
void PreScanOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
- int *maskColors, GBool inlineImg) {
+ int *maskColors, GBool inlineImg,
+ GBool interpolate) {
GfxColorSpace *colorSpace;
- int i, j;
colorSpace = colorMap->getColorSpace();
if (colorSpace->getMode() == csIndexed) {
@@ -221,10 +217,8 @@ void PreScanOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
if (inlineImg) {
str->reset();
- j = height * ((width * colorMap->getNumPixelComps() *
- colorMap->getBits() + 7) / 8);
- for (i = 0; i < j; ++i)
- str->getChar();
+ str->discardChars(height * ((width * colorMap->getNumPixelComps() *
+ colorMap->getBits() + 7) / 8));
str->close();
}
}
@@ -235,7 +229,7 @@ void PreScanOutputDev::drawMaskedImage(GfxState *state, Object *ref,
GfxImageColorMap *colorMap,
Stream *maskStr,
int maskWidth, int maskHeight,
- GBool maskInvert) {
+ GBool maskInvert, GBool interpolate) {
GfxColorSpace *colorSpace;
colorSpace = colorMap->getColorSpace();
@@ -264,7 +258,8 @@ void PreScanOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
GfxImageColorMap *colorMap,
Stream *maskStr,
int maskWidth, int maskHeight,
- GfxImageColorMap *maskColorMap) {
+ GfxImageColorMap *maskColorMap,
+ GBool interpolate) {
GfxColorSpace *colorSpace;
colorSpace = colorMap->getColorSpace();
diff --git a/xpdf/PreScanOutputDev.h b/xpdf/PreScanOutputDev.h
index 3889cfc..6ca8b46 100644
--- a/xpdf/PreScanOutputDev.h
+++ b/xpdf/PreScanOutputDev.h
@@ -67,7 +67,7 @@ public:
virtual void stroke(GfxState *state);
virtual void fill(GfxState *state);
virtual void eoFill(GfxState *state);
- virtual void tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
+ virtual void tilingPatternFill(GfxState *state, Gfx *gfx, Object *strRef,
int paintType, Dict *resDict,
double *mat, double *bbox,
int x0, int y0, int x1, int y1,
@@ -92,21 +92,22 @@ public:
//----- image drawing
virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
int width, int height, GBool invert,
- GBool inlineImg);
+ GBool inlineImg, GBool interpolate);
virtual void drawImage(GfxState *state, Object *ref, Stream *str,
int width, int height, GfxImageColorMap *colorMap,
- int *maskColors, GBool inlineImg);
+ int *maskColors, GBool inlineImg, GBool interpolate);
virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
Stream *maskStr, int maskWidth, int maskHeight,
- GBool maskInvert);
+ GBool maskInvert, GBool interpolate);
virtual void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
Stream *maskStr,
int maskWidth, int maskHeight,
- GfxImageColorMap *maskColorMap);
+ GfxImageColorMap *maskColorMap,
+ GBool interpolate);
//----- transparency groups and soft masks
virtual void beginTransparencyGroup(GfxState *state, double *bbox,
diff --git a/xpdf/SecurityHandler.cc b/xpdf/SecurityHandler.cc
index 52607c2..88f2b65 100644
--- a/xpdf/SecurityHandler.cc
+++ b/xpdf/SecurityHandler.cc
@@ -158,7 +158,7 @@ StandardSecurityHandler::StandardSecurityHandler(PDFDoc *docA,
if ((encRevision <= 4 &&
ownerKeyObj.getString()->getLength() == 32 &&
userKeyObj.getString()->getLength() == 32) ||
- (encRevision == 5 &&
+ ((encRevision == 5 || encRevision == 6) &&
// the spec says 48 bytes, but Acrobat pads them out longer
ownerKeyObj.getString()->getLength() >= 48 &&
userKeyObj.getString()->getLength() >= 48 &&
@@ -180,7 +180,7 @@ StandardSecurityHandler::StandardSecurityHandler(PDFDoc *docA,
//~ doesn't handle the case where StmF, StrF, and EFF are not all the
//~ same)
if ((encVersion == 4 || encVersion == 5) &&
- (encRevision == 4 || encRevision == 5)) {
+ (encRevision == 4 || encRevision == 5 || encRevision == 6)) {
encryptDictA->dictLookup("CF", &cryptFiltersObj);
encryptDictA->dictLookup("StmF", &streamFilterObj);
encryptDictA->dictLookup("StrF", &stringFilterObj);
@@ -216,7 +216,9 @@ StandardSecurityHandler::StandardSecurityHandler(PDFDoc *docA,
cfLengthObj.free();
} else if (cfmObj.isName("AESV3")) {
encVersion = 5;
- encRevision = 5;
+ if (encRevision != 5 && encRevision != 6) {
+ encRevision = 6;
+ }
encAlgorithm = cryptAES256;
if (cryptFilterObj.dictLookup("Length",
&cfLengthObj)->isInt()) {
@@ -254,15 +256,15 @@ StandardSecurityHandler::StandardSecurityHandler(PDFDoc *docA,
} else {
fileID = new GString();
}
- if (fileKeyLength > 16 || fileKeyLength < 0) {
+ if (fileKeyLength > 16 || fileKeyLength <= 0) {
fileKeyLength = 16;
}
ok = gTrue;
- } else if (encVersion == 5 && encRevision == 5) {
+ } else if (encVersion == 5 && (encRevision == 5 || encRevision == 6)) {
fileID = new GString(); // unused for V=R=5
ownerEnc = ownerEncObj.getString()->copy();
userEnc = userEncObj.getString()->copy();
- if (fileKeyLength > 32 || fileKeyLength < 0) {
+ if (fileKeyLength > 32 || fileKeyLength <= 0) {
fileKeyLength = 32;
}
ok = gTrue;
diff --git a/xpdf/SplashOutputDev.cc b/xpdf/SplashOutputDev.cc
index 50e4801..d9befec 100644
--- a/xpdf/SplashOutputDev.cc
+++ b/xpdf/SplashOutputDev.cc
@@ -2,7 +2,7 @@
//
// SplashOutputDev.cc
//
-// Copyright 2003 Glyph & Cog, LLC
+// Copyright 2003-2013 Glyph & Cog, LLC
//
//========================================================================
@@ -27,6 +27,7 @@
#include "BuiltinFont.h"
#include "BuiltinFontTables.h"
#include "FoFiTrueType.h"
+#include "JPXStream.h"
#include "SplashBitmap.h"
#include "SplashGlyphBitmap.h"
#include "SplashPattern.h"
@@ -118,7 +119,9 @@ static void splashOutBlendColorDodge(SplashColorPtr src, SplashColorPtr dest,
int i, x;
for (i = 0; i < splashColorModeNComps[cm]; ++i) {
- if (src[i] == 255) {
+ if (dest[i] == 0) {
+ blend[i] = 0;
+ } else if (src[i] == 255) {
blend[i] = 255;
} else {
x = (dest[i] * 255) / (255 - src[i]);
@@ -132,7 +135,9 @@ static void splashOutBlendColorBurn(SplashColorPtr src, SplashColorPtr dest,
int i, x;
for (i = 0; i < splashColorModeNComps[cm]; ++i) {
- if (src[i] == 0) {
+ if (dest[i] == 255) {
+ blend[i] = 255;
+ } else if (src[i] == 0) {
blend[i] = 0;
} else {
x = ((255 - dest[i]) * 255) / src[i];
@@ -284,7 +289,10 @@ static void setSat(Guchar rIn, Guchar gIn, Guchar bIn, int sat,
static void splashOutBlendHue(SplashColorPtr src, SplashColorPtr dest,
SplashColorPtr blend, SplashColorMode cm) {
- Guchar r0, g0, b0, r1, g1, b1;
+ Guchar r0, g0, b0;
+#if SPLASH_CMYK
+ Guchar r1, g1, b1;
+#endif
switch (cm) {
case splashModeMono1:
@@ -317,7 +325,10 @@ static void splashOutBlendHue(SplashColorPtr src, SplashColorPtr dest,
static void splashOutBlendSaturation(SplashColorPtr src, SplashColorPtr dest,
SplashColorPtr blend,
SplashColorMode cm) {
- Guchar r0, g0, b0, r1, g1, b1;
+ Guchar r0, g0, b0;
+#if SPLASH_CMYK
+ Guchar r1, g1, b1;
+#endif
switch (cm) {
case splashModeMono1:
@@ -435,7 +446,11 @@ SplashBlendFunc splashOutBlendFuncs[] = {
class SplashOutFontFileID: public SplashFontFileID {
public:
- SplashOutFontFileID(Ref *rA) { r = *rA; substIdx = -1; }
+ SplashOutFontFileID(Ref *rA) {
+ r = *rA;
+ substIdx = -1;
+ oblique = 0;
+ }
~SplashOutFontFileID() {}
@@ -444,12 +459,15 @@ public:
((SplashOutFontFileID *)id)->r.gen == r.gen;
}
+ void setOblique(double obliqueA) { oblique = obliqueA; }
+ double getOblique() { return oblique; }
void setSubstIdx(int substIdxA) { substIdx = substIdxA; }
int getSubstIdx() { return substIdx; }
private:
Ref r;
+ double oblique;
int substIdx;
};
@@ -536,6 +554,10 @@ T3FontCache::~T3FontCache() {
struct T3GlyphStack {
Gushort code; // character code
+ GBool haveDx; // set after seeing a d0/d1 operator
+ GBool doNotCache; // set if we see a gsave/grestore before
+ // the d0/d1
+
//----- cache info
T3FontCache *cache; // font cache for the current font
T3FontCacheTag *cacheTag; // pointer to cache tag for the glyph
@@ -580,6 +602,7 @@ SplashOutputDev::SplashOutputDev(SplashColorMode colorModeA,
bitmapRowPad = bitmapRowPadA;
bitmapTopDown = bitmapTopDownA;
bitmapUpsideDown = gFalse;
+ noComposite = gFalse;
allowAntialias = allowAntialiasA;
vectorAntialias = allowAntialias &&
globalParams->getVectorAntialias() &&
@@ -596,6 +619,7 @@ SplashOutputDev::SplashOutputDev(SplashColorMode colorModeA,
colorMode != splashModeMono1, bitmapTopDown);
splash = new Splash(bitmap, vectorAntialias, &screenParams);
splash->setMinLineWidth(globalParams->getMinLineWidth());
+ splash->setStrokeAdjust(globalParams->getStrokeAdjust());
splash->clear(paperColor, 0);
fontEngine = NULL;
@@ -688,9 +712,6 @@ void SplashOutputDev::startDoc(XRef *xrefA) {
delete fontEngine;
}
fontEngine = new SplashFontEngine(
-#if HAVE_T1LIB_H
- globalParams->getEnableT1lib(),
-#endif
#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
globalParams->getEnableFreeType(),
globalParams->getDisableFreeTypeHinting()
@@ -777,18 +798,28 @@ void SplashOutputDev::startPage(int pageNum, GfxState *state) {
}
void SplashOutputDev::endPage() {
- if (colorMode != splashModeMono1) {
+ if (colorMode != splashModeMono1 && !noComposite) {
splash->compositeBackground(paperColor);
}
}
void SplashOutputDev::saveState(GfxState *state) {
splash->saveState();
+ if (t3GlyphStack && !t3GlyphStack->haveDx) {
+ t3GlyphStack->doNotCache = gTrue;
+ error(errSyntaxWarning, -1,
+ "Save (q) operator before d0/d1 in Type 3 glyph");
+ }
}
void SplashOutputDev::restoreState(GfxState *state) {
splash->restoreState();
needFontUpdate = gTrue;
+ if (t3GlyphStack && !t3GlyphStack->haveDx) {
+ t3GlyphStack->doNotCache = gTrue;
+ error(errSyntaxWarning, -1,
+ "Restore (Q) operator before d0/d1 in Type 3 glyph");
+ }
}
void SplashOutputDev::updateAll(GfxState *state) {
@@ -976,10 +1007,19 @@ void SplashOutputDev::setOverprintMask(GfxColorSpace *colorSpace,
if (overprintFlag && globalParams->getOverprintPreview()) {
mask = colorSpace->getOverprintMask();
+ // The OPM (overprintMode) setting is only relevant when the color
+ // space is DeviceCMYK or is "implicitly converted to DeviceCMYK".
+ // Per the PDF spec, this happens with ICCBased color spaces only
+ // if the profile matches the output device -- Acrobat's output
+ // preview mode does NOT honor OPM=1 for ICCBased CMYK color
+ // spaces. To change the behavior here, use:
+ // if (singleColor && overprintMode &&
+ // (colorSpace->getMode() == csDeviceCMYK ||
+ // (colorSpace->getMode() == csICCBased &&
+ // colorSpace->getNComps() == 4 &&
+ // <...the profile matches...>)))
if (singleColor && overprintMode &&
- (colorSpace->getMode() == csDeviceCMYK ||
- (colorSpace->getMode() == csICCBased &&
- colorSpace->getNComps() == 4))) {
+ colorSpace->getMode() == csDeviceCMYK) {
colorSpace->getCMYK(singleColor, &cmyk);
if (cmyk.c == 0) {
mask &= ~1;
@@ -1072,22 +1112,33 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
FoFiTrueType *ff;
Ref embRef;
Object refObj, strObj;
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf;
+ FILE *extFontFile;
+#else
GString *tmpFileName, *fileName;
FILE *tmpFile;
+#endif
+ char blk[4096];
int *codeToGID;
CharCodeToUnicode *ctu;
double *textMat;
- double m11, m12, m21, m22, fontSize;
- double w, fontScaleMin, fontScaleAvg, fontScale;
+ double m11, m12, m21, m22, fontSize, oblique;
+ double fsx, fsy, w, fontScaleMin, fontScaleAvg, fontScale;
Gushort ww;
SplashCoord mat[4];
char *name;
Unicode uBuf[8];
- int c, substIdx, n, code, cmap, i;
+ int substIdx, n, code, cmap, i;
needFontUpdate = gFalse;
font = NULL;
+#if LOAD_FONTS_FROM_MEM
+ fontBuf = NULL;
+#else
tmpFileName = NULL;
+ fileName = NULL;
+#endif
substIdx = -1;
if (!(gfxFont = state->getFont())) {
@@ -1098,10 +1149,13 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
goto err1;
}
- // sanity-check the font size - skip anything larger than 10 inches
+ // sanity-check the font size - skip anything larger than 20 inches
// (this avoids problems allocating memory for the font cache)
- if (state->getTransformedFontSize()
- > 10 * (state->getHDPI() + state->getVDPI())) {
+ state->textTransformDelta(state->getFontSize(), state->getFontSize(),
+ &fsx, &fsy);
+ state->transformDelta(fsx, fsy, &fsx, &fsy);
+ if (fabs(fsx) > 20 * state->getHDPI() ||
+ fabs(fsy) > 20 * state->getVDPI()) {
goto err1;
}
@@ -1112,7 +1166,6 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
} else {
- fileName = NULL;
fontNum = 0;
if (!(fontLoc = gfxFont->locateFont(xref, gFalse))) {
@@ -1125,6 +1178,24 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
// embedded font
if (fontLoc->locType == gfxFontLocEmbedded) {
gfxFont->getEmbeddedFontID(&embRef);
+#if LOAD_FONTS_FROM_MEM
+ fontBuf = new GString();
+ refObj.initRef(embRef.num, embRef.gen);
+ refObj.fetch(xref, &strObj);
+ refObj.free();
+ if (!strObj.isStream()) {
+ error(errSyntaxError, -1, "Embedded font object is wrong type");
+ strObj.free();
+ delete fontLoc;
+ goto err2;
+ }
+ strObj.streamReset();
+ while ((n = strObj.streamGetBlock(blk, sizeof(blk))) > 0) {
+ fontBuf->append(blk, n);
+ }
+ strObj.streamClose();
+ strObj.free();
+#else
if (!openTempFile(&tmpFileName, &tmpFile, "wb", NULL)) {
error(errIO, -1, "Couldn't create temporary font file");
delete fontLoc;
@@ -1141,21 +1212,39 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
goto err2;
}
strObj.streamReset();
- while ((c = strObj.streamGetChar()) != EOF) {
- fputc(c, tmpFile);
+ while ((n = strObj.streamGetBlock(blk, sizeof(blk))) > 0) {
+ fwrite(blk, 1, n, tmpFile);
}
strObj.streamClose();
strObj.free();
fclose(tmpFile);
fileName = tmpFileName;
+#endif
// external font
} else { // gfxFontLocExternal
+#if LOAD_FONTS_FROM_MEM
+ if (!(extFontFile = fopen(fontLoc->path->getCString(), "rb"))) {
+ error(errSyntaxError, -1, "Couldn't open external font file '{0:t}'",
+ fontLoc->path);
+ delete fontLoc;
+ goto err2;
+ }
+ fontBuf = new GString();
+ while ((n = fread(blk, 1, sizeof(blk), extFontFile)) > 0) {
+ fontBuf->append(blk, n);
+ }
+ fclose(extFontFile);
+#else
fileName = fontLoc->path;
+#endif
fontNum = fontLoc->fontNum;
if (fontLoc->substIdx >= 0) {
id->setSubstIdx(fontLoc->substIdx);
}
+ if (fontLoc->oblique != 0) {
+ id->setOblique(fontLoc->oblique);
+ }
}
// load the font file
@@ -1163,8 +1252,12 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
case fontType1:
if (!(fontFile = fontEngine->loadType1Font(
id,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
fileName->getCString(),
fileName == tmpFileName,
+#endif
(const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) {
error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
gfxFont->getName() ? gfxFont->getName()->getCString()
@@ -1176,8 +1269,12 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
case fontType1C:
if (!(fontFile = fontEngine->loadType1CFont(
id,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
fileName->getCString(),
fileName == tmpFileName,
+#endif
(const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) {
error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
gfxFont->getName() ? gfxFont->getName()->getCString()
@@ -1189,8 +1286,12 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
case fontType1COT:
if (!(fontFile = fontEngine->loadOpenTypeT1CFont(
id,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
fileName->getCString(),
fileName == tmpFileName,
+#endif
(const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) {
error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
gfxFont->getName() ? gfxFont->getName()->getCString()
@@ -1201,7 +1302,12 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
break;
case fontTrueType:
case fontTrueTypeOT:
- if ((ff = FoFiTrueType::load(fileName->getCString()))) {
+#if LOAD_FONTS_FROM_MEM
+ if ((ff = FoFiTrueType::make(fontBuf->getCString(), fontBuf->getLength(),
+ fontNum))) {
+#else
+ if ((ff = FoFiTrueType::load(fileName->getCString(), fontNum))) {
+#endif
codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff);
n = 256;
delete ff;
@@ -1222,9 +1328,13 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
}
if (!(fontFile = fontEngine->loadTrueTypeFont(
id,
- fileName->getCString(), fontNum,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
+ fileName->getCString(),
fileName == tmpFileName,
- codeToGID, n,
+#endif
+ fontNum, codeToGID, n,
gfxFont->getEmbeddedFontName()
? gfxFont->getEmbeddedFontName()->getCString()
: (char *)NULL))) {
@@ -1239,8 +1349,14 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
case fontCIDType0C:
if (!(fontFile = fontEngine->loadCIDFont(
id,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf
+#else
fileName->getCString(),
- fileName == tmpFileName))) {
+ fileName == tmpFileName
+#endif
+ ))) {
+
error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
gfxFont->getName() ? gfxFont->getName()->getCString()
: "(unnamed)");
@@ -1260,8 +1376,12 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
}
if (!(fontFile = fontEngine->loadOpenTypeCFFFont(
id,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
fileName->getCString(),
fileName == tmpFileName,
+#endif
codeToGID, n))) {
error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
gfxFont->getName() ? gfxFont->getName()->getCString()
@@ -1281,11 +1401,18 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(),
n * sizeof(int));
}
+ } else if (!globalParams->getMapExtTrueTypeFontsViaUnicode()) {
+ codeToGID = NULL;
+ n = 0;
} else {
// create a CID-to-GID mapping, via Unicode
if ((ctu = ((GfxCIDFont *)gfxFont)->getToUnicode())) {
- //~ this should use fontNum to load the correct font
- if ((ff = FoFiTrueType::load(fileName->getCString()))) {
+#if LOAD_FONTS_FROM_MEM
+ if ((ff = FoFiTrueType::make(fontBuf->getCString(),
+ fontBuf->getLength(), fontNum))) {
+#else
+ if ((ff = FoFiTrueType::load(fileName->getCString(), fontNum))) {
+#endif
// look for a Unicode cmap
for (cmap = 0; cmap < ff->getNumCmaps(); ++cmap) {
if ((ff->getCmapPlatform(cmap) == 3 &&
@@ -1296,7 +1423,11 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
}
if (cmap < ff->getNumCmaps()) {
// map CID -> Unicode -> GID
- n = ctu->getLength();
+ if (ctu->isIdentity()) {
+ n = 65536;
+ } else {
+ n = ctu->getLength();
+ }
codeToGID = (int *)gmallocn(n, sizeof(int));
for (code = 0; code < n; ++code) {
if (ctu->mapToUnicode(code, uBuf, 8) > 0) {
@@ -1318,9 +1449,13 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
}
if (!(fontFile = fontEngine->loadTrueTypeFont(
id,
- fileName->getCString(), fontNum,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
+ fileName->getCString(),
fileName == tmpFileName,
- codeToGID, n,
+#endif
+ fontNum, codeToGID, n,
gfxFont->getEmbeddedFontName()
? gfxFont->getEmbeddedFontName()->getCString()
: (char *)NULL))) {
@@ -1342,10 +1477,15 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
// 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;
+ oblique = ((SplashOutFontFileID *)fontFile->getID())->getOblique();
+ m11 = state->getHorizScaling() * textMat[0];
+ m12 = state->getHorizScaling() * textMat[1];
+ m21 = oblique * m11 + textMat[2];
+ m22 = oblique * m12 + textMat[3];
+ m11 *= fontSize;
+ m12 *= fontSize;
+ m21 *= fontSize;
+ m22 *= fontSize;
// for substituted fonts: adjust the font matrix -- compare the
// widths of letters and digits (A-Z, a-z, 0-9) in the original font
@@ -1393,18 +1533,26 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
mat[2] = m21; mat[3] = m22;
font = fontEngine->getFont(fontFile, mat, splash->getMatrix());
+#if !LOAD_FONTS_FROM_MEM
if (tmpFileName) {
delete tmpFileName;
}
+#endif
return;
err2:
delete id;
err1:
+#if LOAD_FONTS_FROM_MEM
+ if (fontBuf) {
+ delete fontBuf;
+ }
+#else
if (tmpFileName) {
unlink(tmpFileName->getCString());
delete tmpFileName;
}
+#endif
return;
}
@@ -1447,7 +1595,8 @@ void SplashOutputDev::eoFill(GfxState *state) {
delete path;
}
-void SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
+void SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx,
+ Object *strRef,
int paintType, Dict *resDict,
double *mat, double *bbox,
int x0, int y0, int x1, int y1,
@@ -1529,7 +1678,7 @@ void SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
ya = y * yStep;
mat1[4] = xa * mat[0] + ya * mat[2] + mat[4];
mat1[5] = xa * mat[1] + ya * mat[3] + mat[5];
- gfx->drawForm(str, resDict, mat1, bbox);
+ gfx->drawForm(strRef, resDict, mat1, bbox);
}
}
return;
@@ -1542,6 +1691,7 @@ void SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
colorMode, gTrue, bitmapTopDown);
splash = new Splash(bitmap, vectorAntialias, origSplash->getScreen());
splash->setMinLineWidth(globalParams->getMinLineWidth());
+ splash->setStrokeAdjust(globalParams->getStrokeAdjust());
for (i = 0; i < splashMaxColorComps; ++i) {
color[i] = 0;
}
@@ -1556,7 +1706,7 @@ void SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
// render the tile
state->shiftCTM(-tileX0, -tileY0);
updateCTM(state, 0, 0, 0, 0, 0, 0);
- gfx->drawForm(str, resDict, mat, bbox);
+ gfx->drawForm(strRef, resDict, mat, bbox);
state->shiftCTM(tileX0, tileY0);
updateCTM(state, 0, 0, 0, 0, 0, 0);
@@ -1889,8 +2039,8 @@ GBool SplashOutputDev::beginType3Char(GfxState *state, double x, double y,
t3GlyphStack->cache = t3Font;
t3GlyphStack->cacheTag = NULL;
t3GlyphStack->cacheData = NULL;
-
- haveT3Dx = gFalse;
+ t3GlyphStack->haveDx = gFalse;
+ t3GlyphStack->doNotCache = gFalse;
return gFalse;
}
@@ -1920,7 +2070,7 @@ void SplashOutputDev::endType3Char(GfxState *state) {
}
void SplashOutputDev::type3D0(GfxState *state, double wx, double wy) {
- haveT3Dx = gTrue;
+ t3GlyphStack->haveDx = gTrue;
}
void SplashOutputDev::type3D1(GfxState *state, double wx, double wy,
@@ -1932,10 +2082,14 @@ void SplashOutputDev::type3D1(GfxState *state, double wx, double wy,
int i, j;
// ignore multiple d0/d1 operators
- if (haveT3Dx) {
+ if (t3GlyphStack->haveDx) {
+ return;
+ }
+ t3GlyphStack->haveDx = gTrue;
+ // don't cache if we got a gsave/grestore before the d1
+ if (t3GlyphStack->doNotCache) {
return;
}
- haveT3Dx = gTrue;
t3Font = t3GlyphStack->cache;
@@ -2026,6 +2180,7 @@ void SplashOutputDev::type3D1(GfxState *state, double wx, double wy,
color[0] = 0xff;
}
splash->setMinLineWidth(globalParams->getMinLineWidth());
+ splash->setStrokeAdjust(t3GlyphStack->origSplash->getStrokeAdjust());
splash->setFillPattern(new SplashSolidColor(color));
splash->setStrokePattern(new SplashSolidColor(color));
//~ this should copy other state from t3GlyphStack->origSplash?
@@ -2072,10 +2227,9 @@ GBool SplashOutputDev::imageMaskSrc(void *data, SplashColorPtr line) {
SplashColorPtr q;
int x;
- if (imgMaskData->y == imgMaskData->height) {
- return gFalse;
- }
- if (!(p = imgMaskData->imgStr->getLine())) {
+ if (imgMaskData->y == imgMaskData->height ||
+ !(p = imgMaskData->imgStr->getLine())) {
+ memset(line, 0, imgMaskData->width);
return gFalse;
}
for (x = 0, q = line; x < imgMaskData->width; ++x) {
@@ -2087,7 +2241,7 @@ GBool SplashOutputDev::imageMaskSrc(void *data, SplashColorPtr line) {
void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
int width, int height, GBool invert,
- GBool inlineImg) {
+ GBool inlineImg, GBool interpolate) {
double *ctm;
SplashCoord mat[6];
SplashOutImageMaskData imgMaskData;
@@ -2106,6 +2260,8 @@ void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
mat[4] = ctm[2] + ctm[4];
mat[5] = ctm[3] + ctm[5];
+ reduceImageResolution(str, ctm, &width, &height);
+
imgMaskData.imgStr = new ImageStream(str, width, 1, 1);
imgMaskData.imgStr->reset();
imgMaskData.invert = invert ? 0 : 1;
@@ -2114,7 +2270,7 @@ void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
imgMaskData.y = 0;
splash->fillImageMask(&imageMaskSrc, &imgMaskData, width, height, mat,
- t3GlyphStack != NULL);
+ t3GlyphStack != NULL, interpolate);
if (inlineImg) {
while (imgMaskData.y < height) {
imgMaskData.imgStr->getLine();
@@ -2130,7 +2286,8 @@ void SplashOutputDev::setSoftMaskFromImageMask(GfxState *state,
Object *ref, Stream *str,
int width, int height,
GBool invert,
- GBool inlineImg) {
+ GBool inlineImg,
+ GBool interpolate) {
double *ctm;
SplashCoord mat[6];
SplashOutImageMaskData imgMaskData;
@@ -2145,6 +2302,7 @@ void SplashOutputDev::setSoftMaskFromImageMask(GfxState *state,
mat[3] = -ctm[3];
mat[4] = ctm[2] + ctm[4];
mat[5] = ctm[3] + ctm[5];
+ reduceImageResolution(str, ctm, &width, &height);
imgMaskData.imgStr = new ImageStream(str, width, 1, 1);
imgMaskData.imgStr->reset();
imgMaskData.invert = invert ? 0 : 1;
@@ -2154,12 +2312,12 @@ void SplashOutputDev::setSoftMaskFromImageMask(GfxState *state,
maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(),
1, splashModeMono8, gFalse);
maskSplash = new Splash(maskBitmap, gTrue);
- maskColor[0] = 0;
- maskSplash->clear(maskColor);
+ maskSplash->setStrokeAdjust(globalParams->getStrokeAdjust());
+ clearMaskRegion(state, maskSplash, 0, 0, 1, 1);
maskColor[0] = 0xff;
maskSplash->setFillPattern(new SplashSolidColor(maskColor));
maskSplash->fillImageMask(&imageMaskSrc, &imgMaskData,
- width, height, mat, gFalse);
+ width, height, mat, gFalse, interpolate);
delete imgMaskData.imgStr;
str->close();
delete maskSplash;
@@ -2180,17 +2338,12 @@ GBool SplashOutputDev::imageSrc(void *data, SplashColorPtr colorLine,
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;
- }
- if (!(p = imgData->imgStr->getLine())) {
+ if (imgData->y == imgData->height ||
+ !(p = imgData->imgStr->getLine())) {
+ memset(colorLine, 0,
+ imgData->width * splashColorModeNComps[imgData->colorMode]);
return gFalse;
}
@@ -2229,29 +2382,15 @@ GBool SplashOutputDev::imageSrc(void *data, SplashColorPtr colorLine,
switch (imgData->colorMode) {
case splashModeMono1:
case splashModeMono8:
- for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) {
- imgData->colorMap->getGray(p, &gray);
- *q++ = colToByte(gray);
- }
+ imgData->colorMap->getGrayByteLine(p, colorLine, imgData->width);
break;
case splashModeRGB8:
case splashModeBGR8:
- for (x = 0, 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);
- }
+ imgData->colorMap->getRGBByteLine(p, colorLine, imgData->width);
break;
#if SPLASH_CMYK
case splashModeCMYK8:
- for (x = 0, 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);
- }
+ imgData->colorMap->getCMYKByteLine(p, colorLine, imgData->width);
break;
#endif
}
@@ -2274,10 +2413,11 @@ GBool SplashOutputDev::alphaImageSrc(void *data, SplashColorPtr colorLine,
Guchar alpha;
int nComps, x, i;
- if (imgData->y == imgData->height) {
- return gFalse;
- }
- if (!(p = imgData->imgStr->getLine())) {
+ if (imgData->y == imgData->height ||
+ !(p = imgData->imgStr->getLine())) {
+ memset(colorLine, 0,
+ imgData->width * splashColorModeNComps[imgData->colorMode]);
+ memset(alphaLine, 0, imgData->width);
return gFalse;
}
@@ -2353,7 +2493,8 @@ GBool SplashOutputDev::alphaImageSrc(void *data, SplashColorPtr colorLine,
void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
- int *maskColors, GBool inlineImg) {
+ int *maskColors, GBool inlineImg,
+ GBool interpolate) {
double *ctm;
SplashCoord mat[6];
SplashOutImageData imgData;
@@ -2378,6 +2519,8 @@ void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
mat[4] = ctm[2] + ctm[4];
mat[5] = ctm[3] + ctm[5];
+ reduceImageResolution(str, ctm, &width, &height);
+
imgData.imgStr = new ImageStream(str, width,
colorMap->getNumPixelComps(),
colorMap->getBits());
@@ -2433,12 +2576,14 @@ void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
if (colorMode == splashModeMono1) {
srcMode = splashModeMono8;
+ } else if (colorMode == splashModeBGR8) {
+ srcMode = splashModeRGB8;
} else {
srcMode = colorMode;
}
src = maskColors ? &alphaImageSrc : &imageSrc;
splash->drawImage(src, &imgData, srcMode, maskColors ? gTrue : gFalse,
- width, height, mat);
+ width, height, mat, interpolate);
if (inlineImg) {
while (imgData.y < height) {
imgData.imgStr->getLine();
@@ -2470,15 +2615,17 @@ GBool SplashOutputDev::maskedImageSrc(void *data, SplashColorPtr colorLine,
#if SPLASH_CMYK
GfxCMYK cmyk;
#endif
+ static Guchar bitToByte[2] = {0x00, 0xff};
Guchar alpha;
Guchar *maskPtr;
- int maskBit;
+ int maskShift;
int nComps, x;
- if (imgData->y == imgData->height) {
- return gFalse;
- }
- if (!(p = imgData->imgStr->getLine())) {
+ if (imgData->y == imgData->height ||
+ !(p = imgData->imgStr->getLine())) {
+ memset(colorLine, 0,
+ imgData->width * splashColorModeNComps[imgData->colorMode]);
+ memset(alphaLine, 0, imgData->width);
return gFalse;
}
@@ -2486,15 +2633,13 @@ GBool SplashOutputDev::maskedImageSrc(void *data, SplashColorPtr colorLine,
maskPtr = imgData->mask->getDataPtr() +
imgData->y * imgData->mask->getRowSize();
- maskBit = 0x80;
+ maskShift = 7;
for (x = 0, q = colorLine, aq = alphaLine;
x < imgData->width;
++x, p += nComps) {
- alpha = (*maskPtr & maskBit) ? 0xff : 0x00;
- if (!(maskBit >>= 1)) {
- ++maskPtr;
- maskBit = 0x80;
- }
+ alpha = bitToByte[(*maskPtr >> maskShift) & 1];
+ maskPtr += (8 - maskShift) >> 3;
+ maskShift = (maskShift - 1) & 7;
if (imgData->lookup) {
switch (imgData->colorMode) {
case splashModeMono1:
@@ -2555,7 +2700,8 @@ void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref,
Stream *str, int width, int height,
GfxImageColorMap *colorMap,
Stream *maskStr, int maskWidth,
- int maskHeight, GBool maskInvert) {
+ int maskHeight, GBool maskInvert,
+ GBool interpolate) {
GfxImageColorMap *maskColorMap;
Object maskDecode, decodeLow, decodeHigh;
double *ctm;
@@ -2577,6 +2723,10 @@ void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref,
setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(),
state->getOverprintMode(), NULL);
+ ctm = state->getCTM();
+ reduceImageResolution(str, ctm, &width, &height);
+ reduceImageResolution(maskStr, ctm, &maskWidth, &maskHeight);
+
// If the mask is higher resolution than the image, use
// drawSoftMaskedImage() instead.
if (maskWidth > width || maskHeight > height) {
@@ -2589,7 +2739,8 @@ void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref,
new GfxDeviceGrayColorSpace());
maskDecode.free();
drawSoftMaskedImage(state, ref, str, width, height, colorMap,
- maskStr, maskWidth, maskHeight, maskColorMap);
+ maskStr, maskWidth, maskHeight, maskColorMap,
+ interpolate);
delete maskColorMap;
} else {
@@ -2610,19 +2761,20 @@ void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref,
imgMaskData.y = 0;
maskBitmap = new SplashBitmap(width, height, 1, splashModeMono1, gFalse);
maskSplash = new Splash(maskBitmap, gFalse);
+ maskSplash->setStrokeAdjust(globalParams->getStrokeAdjust());
maskColor[0] = 0;
maskSplash->clear(maskColor);
maskColor[0] = 0xff;
maskSplash->setFillPattern(new SplashSolidColor(maskColor));
+ // use "glyph mode" here to get the correct scaled size
maskSplash->fillImageMask(&imageMaskSrc, &imgMaskData,
- maskWidth, maskHeight, mat, gFalse);
+ maskWidth, maskHeight, mat, gTrue, interpolate);
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];
@@ -2685,11 +2837,13 @@ void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref,
if (colorMode == splashModeMono1) {
srcMode = splashModeMono8;
+ } else if (colorMode == splashModeBGR8) {
+ srcMode = splashModeRGB8;
} else {
srcMode = colorMode;
}
splash->drawImage(&maskedImageSrc, &imgData, srcMode, gTrue,
- width, height, mat);
+ width, height, mat, interpolate);
delete maskBitmap;
gfree(imgData.lookup);
@@ -2703,7 +2857,8 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
GfxImageColorMap *colorMap,
Stream *maskStr,
int maskWidth, int maskHeight,
- GfxImageColorMap *maskColorMap) {
+ GfxImageColorMap *maskColorMap,
+ GBool interpolate) {
double *ctm;
SplashCoord mat[6];
SplashOutImageData imgData;
@@ -2711,7 +2866,6 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
SplashColorMode srcMode;
SplashBitmap *maskBitmap;
Splash *maskSplash;
- SplashColor maskColor;
GfxGray gray;
GfxRGB rgb;
#if SPLASH_CMYK
@@ -2731,6 +2885,9 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
mat[4] = ctm[2] + ctm[4];
mat[5] = ctm[3] + ctm[5];
+ reduceImageResolution(str, ctm, &width, &height);
+ reduceImageResolution(maskStr, ctm, &maskWidth, &maskHeight);
+
//----- set up the soft mask
imgMaskData.imgStr = new ImageStream(maskStr, maskWidth,
@@ -2753,10 +2910,10 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(),
1, splashModeMono8, gFalse);
maskSplash = new Splash(maskBitmap, vectorAntialias);
- maskColor[0] = 0;
- maskSplash->clear(maskColor);
+ maskSplash->setStrokeAdjust(globalParams->getStrokeAdjust());
+ clearMaskRegion(state, maskSplash, 0, 0, 1, 1);
maskSplash->drawImage(&imageSrc, &imgMaskData, splashModeMono8, gFalse,
- maskWidth, maskHeight, mat);
+ maskWidth, maskHeight, mat, interpolate);
delete imgMaskData.imgStr;
maskStr->close();
gfree(imgMaskData.lookup);
@@ -2820,10 +2977,13 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
if (colorMode == splashModeMono1) {
srcMode = splashModeMono8;
+ } else if (colorMode == splashModeBGR8) {
+ srcMode = splashModeRGB8;
} else {
srcMode = colorMode;
}
- splash->drawImage(&imageSrc, &imgData, srcMode, gFalse, width, height, mat);
+ splash->drawImage(&imageSrc, &imgData, srcMode, gFalse, width, height, mat,
+ interpolate);
splash->setSoftMask(NULL);
gfree(imgData.lookup);
@@ -2831,6 +2991,98 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
str->close();
}
+void SplashOutputDev::reduceImageResolution(Stream *str, double *ctm,
+ int *width, int *height) {
+ double sw, sh;
+ int reduction;
+
+ if (str->getKind() == strJPX &&
+ *width * *height > 10000000) {
+ sw = (double)*width / (fabs(ctm[2]) + fabs(ctm[3]));
+ sh = (double)*height / (fabs(ctm[0]) + fabs(ctm[1]));
+ if (sw > 8 && sh > 8) {
+ reduction = 3;
+ } else if (sw > 4 && sh > 4) {
+ reduction = 2;
+ } else if (sw > 2 && sh > 2) {
+ reduction = 1;
+ } else {
+ reduction = 0;
+ }
+ if (reduction > 0) {
+ ((JPXStream *)str)->reduceResolution(reduction);
+ *width >>= reduction;
+ *height >>= reduction;
+ }
+ }
+}
+
+void SplashOutputDev::clearMaskRegion(GfxState *state,
+ Splash *maskSplash,
+ double xMin, double yMin,
+ double xMax, double yMax) {
+ SplashBitmap *maskBitmap;
+ double xxMin, yyMin, xxMax, yyMax, xx, yy;
+ int xxMinI, yyMinI, xxMaxI, yyMaxI, y, n;
+ Guchar *p;
+
+ maskBitmap = maskSplash->getBitmap();
+ xxMin = maskBitmap->getWidth();
+ xxMax = 0;
+ yyMin = maskBitmap->getHeight();
+ yyMax = 0;
+ state->transform(xMin, yMin, &xx, &yy);
+ if (xx < xxMin) { xxMin = xx; }
+ if (xx > xxMax) { xxMax = xx; }
+ if (yy < yyMin) { yyMin = yy; }
+ if (yy > yyMax) { yyMax = yy; }
+ state->transform(xMin, yMax, &xx, &yy);
+ if (xx < xxMin) { xxMin = xx; }
+ if (xx > xxMax) { xxMax = xx; }
+ if (yy < yyMin) { yyMin = yy; }
+ if (yy > yyMax) { yyMax = yy; }
+ state->transform(xMax, yMin, &xx, &yy);
+ if (xx < xxMin) { xxMin = xx; }
+ if (xx > xxMax) { xxMax = xx; }
+ if (yy < yyMin) { yyMin = yy; }
+ if (yy > yyMax) { yyMax = yy; }
+ state->transform(xMax, yMax, &xx, &yy);
+ if (xx < xxMin) { xxMin = xx; }
+ if (xx > xxMax) { xxMax = xx; }
+ if (yy < yyMin) { yyMin = yy; }
+ if (yy > yyMax) { yyMax = yy; }
+ xxMinI = (int)floor(xxMin);
+ if (xxMinI < 0) {
+ xxMinI = 0;
+ }
+ xxMaxI = (int)ceil(xxMax);
+ if (xxMaxI > maskBitmap->getWidth()) {
+ xxMaxI = maskBitmap->getWidth();
+ }
+ yyMinI = (int)floor(yyMin);
+ if (yyMinI < 0) {
+ yyMinI = 0;
+ }
+ yyMaxI = (int)ceil(yyMax);
+ if (yyMaxI > maskBitmap->getHeight()) {
+ yyMaxI = maskBitmap->getHeight();
+ }
+ p = maskBitmap->getDataPtr() + yyMinI * maskBitmap->getRowSize();
+ if (maskBitmap->getMode() == splashModeMono1) {
+ n = (xxMaxI + 7) / 8 - xxMinI / 8;
+ p += xxMinI / 8;
+ } else {
+ n = xxMaxI - xxMinI;
+ p += xxMinI;
+ }
+ if (xxMaxI > xxMinI) {
+ for (y = yyMinI; y < yyMaxI; ++y) {
+ memset(p, 0, n);
+ p += maskBitmap->getRowSize();
+ }
+ }
+}
+
void SplashOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
GfxColorSpace *blendingColorSpace,
GBool isolated, GBool knockout,
@@ -2921,7 +3173,7 @@ void SplashOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
//~ not yet for transparency groups
// switch to the blending color space
- if (forSoftMask && isolated && blendingColorSpace) {
+ if (forSoftMask && isolated && !knockout && blendingColorSpace) {
if (blendingColorSpace->getMode() == csDeviceGray ||
blendingColorSpace->getMode() == csCalGray ||
(blendingColorSpace->getMode() == csICCBased &&
@@ -2948,6 +3200,7 @@ void SplashOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
splash = new Splash(bitmap, vectorAntialias,
transpGroup->origSplash->getScreen());
splash->setMinLineWidth(globalParams->getMinLineWidth());
+ splash->setStrokeAdjust(globalParams->getStrokeAdjust());
//~ Acrobat apparently copies at least the fill and stroke colors, and
//~ maybe other state(?) -- but not the clipping path (and not sure
//~ what else)
@@ -2962,8 +3215,9 @@ void SplashOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
splash->clear(color, 0);
} else {
splash->blitTransparent(transpGroup->origBitmap, tx, ty, 0, 0, w, h);
- splash->setInNonIsolatedGroup(transpGroup->origBitmap, tx, ty);
}
+ splash->setInTransparencyGroup(transpGroup->origBitmap, tx, ty,
+ !isolated, knockout);
transpGroup->tBitmap = bitmap;
state->shiftCTM(-tx, -ty);
updateCTM(state, 0, 0, 0, 0, 0, 0);
@@ -3022,7 +3276,7 @@ void SplashOutputDev::setSoftMask(GfxState *state, double *bbox,
#if SPLASH_CMYK
GfxCMYK cmyk;
#endif
- double lum, lum2;
+ double backdrop, backdrop2, lum, lum2;
int tx, ty, x, y;
tx = transpGroupStack->tx;
@@ -3030,11 +3284,13 @@ void SplashOutputDev::setSoftMask(GfxState *state, double *bbox,
tBitmap = transpGroupStack->tBitmap;
// composite with backdrop color
+ backdrop = 0;
if (!alpha && tBitmap->getMode() != splashModeMono1) {
//~ need to correctly handle the case where no blending color
//~ space is given
tSplash = new Splash(tBitmap, vectorAntialias,
transpGroupStack->origSplash->getScreen());
+ tSplash->setStrokeAdjust(globalParams->getStrokeAdjust());
if (transpGroupStack->blendingColorSpace) {
switch (tBitmap->getMode()) {
case splashModeMono1:
@@ -3042,12 +3298,16 @@ void SplashOutputDev::setSoftMask(GfxState *state, double *bbox,
break;
case splashModeMono8:
transpGroupStack->blendingColorSpace->getGray(backdropColor, &gray);
+ backdrop = colToDbl(gray);
color[0] = colToByte(gray);
tSplash->compositeBackground(color);
break;
case splashModeRGB8:
case splashModeBGR8:
transpGroupStack->blendingColorSpace->getRGB(backdropColor, &rgb);
+ backdrop = 0.3 * colToDbl(rgb.r) +
+ 0.59 * colToDbl(rgb.g) +
+ 0.11 * colToDbl(rgb.b);
color[0] = colToByte(rgb.r);
color[1] = colToByte(rgb.g);
color[2] = colToByte(rgb.b);
@@ -3056,6 +3316,13 @@ void SplashOutputDev::setSoftMask(GfxState *state, double *bbox,
#if SPLASH_CMYK
case splashModeCMYK8:
transpGroupStack->blendingColorSpace->getCMYK(backdropColor, &cmyk);
+ backdrop = (1 - colToDbl(cmyk.k))
+ - 0.3 * colToDbl(cmyk.c)
+ - 0.59 * colToDbl(cmyk.m)
+ - 0.11 * colToDbl(cmyk.y);
+ if (backdrop < 0) {
+ backdrop = 0;
+ }
color[0] = colToByte(cmyk.c);
color[1] = colToByte(cmyk.m);
color[2] = colToByte(cmyk.y);
@@ -3067,10 +3334,15 @@ void SplashOutputDev::setSoftMask(GfxState *state, double *bbox,
delete tSplash;
}
}
+ if (transferFunc) {
+ transferFunc->transform(&backdrop, &backdrop2);
+ } else {
+ backdrop2 = backdrop;
+ }
softMask = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(),
1, splashModeMono8, gFalse);
- memset(softMask->getDataPtr(), 0,
+ memset(softMask->getDataPtr(), (int)(backdrop2 * 255.0 + 0.5),
softMask->getRowSize() * softMask->getHeight());
if (tx < softMask->getWidth() && ty < softMask->getHeight()) {
p = softMask->getDataPtr() + ty * softMask->getRowSize() + tx;
@@ -3198,12 +3470,19 @@ SplashFont *SplashOutputDev::getFont(GString *name, SplashCoord *textMatA) {
Ref ref;
SplashOutFontFileID *id;
GfxFontLoc *fontLoc;
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf;
+ FILE *extFontFile;
+ char blk[4096];
+ int n;
+#endif
SplashFontFile *fontFile;
SplashFont *fontObj;
FoFiTrueType *ff;
int *codeToGID;
Unicode u;
SplashCoord textMat[4];
+ SplashCoord oblique;
int cmap, i;
for (i = 0; i < nBuiltinFonts; ++i) {
@@ -3227,11 +3506,40 @@ SplashFont *SplashOutputDev::getFont(GString *name, SplashCoord *textMatA) {
if (!(fontLoc = GfxFont::locateBase14Font(name))) {
return NULL;
}
+#if LOAD_FONTS_FROM_MEM
+ fontBuf = NULL;
+ if (fontLoc->fontType == fontType1 ||
+ fontLoc->fontType == fontTrueType) {
+ if (!(extFontFile = fopen(fontLoc->path->getCString(), "rb"))) {
+ delete fontLoc;
+ delete id;
+ return NULL;
+ }
+ fontBuf = new GString();
+ while ((n = fread(blk, 1, sizeof(blk), extFontFile)) > 0) {
+ fontBuf->append(blk, n);
+ }
+ fclose(extFontFile);
+ }
+#endif
if (fontLoc->fontType == fontType1) {
- fontFile = fontEngine->loadType1Font(id, fontLoc->path->getCString(),
- gFalse, winAnsiEncoding);
+ fontFile = fontEngine->loadType1Font(id,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
+ fontLoc->path->getCString(),
+ gFalse,
+#endif
+ winAnsiEncoding);
} else if (fontLoc->fontType == fontTrueType) {
- if (!(ff = FoFiTrueType::load(fontLoc->path->getCString()))) {
+#if LOAD_FONTS_FROM_MEM
+ if (!(ff = FoFiTrueType::make(fontBuf->getCString(),
+ fontBuf->getLength(),
+ fontLoc->fontNum))) {
+#else
+ if (!(ff = FoFiTrueType::load(fontLoc->path->getCString(),
+ fontLoc->fontNum))) {
+#endif
delete fontLoc;
delete id;
return NULL;
@@ -3259,9 +3567,14 @@ SplashFont *SplashOutputDev::getFont(GString *name, SplashCoord *textMatA) {
}
delete ff;
fontFile = fontEngine->loadTrueTypeFont(id,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
fontLoc->path->getCString(),
+ gFalse,
+#endif
fontLoc->fontNum,
- gFalse, codeToGID, 256, NULL);
+ codeToGID, 256, NULL);
} else {
delete fontLoc;
delete id;
@@ -3274,10 +3587,12 @@ SplashFont *SplashOutputDev::getFont(GString *name, SplashCoord *textMatA) {
}
// create the scaled font
+ oblique = (SplashCoord)
+ ((SplashOutFontFileID *)fontFile->getID())->getOblique();
textMat[0] = (SplashCoord)textMatA[0];
textMat[1] = (SplashCoord)textMatA[1];
- textMat[2] = (SplashCoord)textMatA[2];
- textMat[3] = (SplashCoord)textMatA[3];
+ textMat[2] = oblique * textMatA[0] + textMatA[2];
+ textMat[3] = oblique * textMatA[1] + textMatA[3];
fontObj = fontEngine->getFont(fontFile, textMat, splash->getMatrix());
return fontObj;
diff --git a/xpdf/SplashOutputDev.h b/xpdf/SplashOutputDev.h
index c48b786..ebbf8a1 100644
--- a/xpdf/SplashOutputDev.h
+++ b/xpdf/SplashOutputDev.h
@@ -110,7 +110,7 @@ public:
virtual void stroke(GfxState *state);
virtual void fill(GfxState *state);
virtual void eoFill(GfxState *state);
- virtual void tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
+ virtual void tilingPatternFill(GfxState *state, Gfx *gfx, Object *strRef,
int paintType, Dict *resDict,
double *mat, double *bbox,
int x0, int y0, int x1, int y1,
@@ -135,25 +135,26 @@ public:
//----- image drawing
virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
int width, int height, GBool invert,
- GBool inlineImg);
+ GBool inlineImg, GBool interpolate);
virtual void setSoftMaskFromImageMask(GfxState *state,
Object *ref, Stream *str,
int width, int height, GBool invert,
- GBool inlineImg);
+ GBool inlineImg, GBool interpolate);
virtual void drawImage(GfxState *state, Object *ref, Stream *str,
int width, int height, GfxImageColorMap *colorMap,
- int *maskColors, GBool inlineImg);
+ int *maskColors, GBool inlineImg, GBool interpolate);
virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
Stream *maskStr, int maskWidth, int maskHeight,
- GBool maskInvert);
+ GBool maskInvert, GBool interpolate);
virtual void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
Stream *maskStr,
int maskWidth, int maskHeight,
- GfxImageColorMap *maskColorMap);
+ GfxImageColorMap *maskColorMap,
+ GBool interpolate);
//----- Type 3 font operators
virtual void type3D0(GfxState *state, double wx, double wy);
@@ -194,6 +195,10 @@ public:
// for Windows BMP files).
void setBitmapUpsideDown(GBool f) { bitmapUpsideDown = f; }
+ // Setting this to true disables the final composite (with the
+ // opaque paper color), resulting in transparent output.
+ void setNoComposite(GBool f) { noComposite = f; }
+
// Get the Splash object.
Splash *getSplash() { return splash; }
@@ -245,11 +250,18 @@ private:
Guchar *alphaLine);
static GBool maskedImageSrc(void *data, SplashColorPtr line,
Guchar *alphaLine);
+ void reduceImageResolution(Stream *str, double *mat,
+ int *width, int *height);
+ void clearMaskRegion(GfxState *state,
+ Splash *maskSplash,
+ double xMin, double yMin,
+ double xMax, double yMax);
SplashColorMode colorMode;
int bitmapRowPad;
GBool bitmapTopDown;
GBool bitmapUpsideDown;
+ GBool noComposite;
GBool allowAntialias;
GBool vectorAntialias;
GBool reverseVideo; // reverse video mode
@@ -268,7 +280,6 @@ private:
t3FontCache[splashOutT3FontCacheSize];
int nT3Fonts; // number of valid entries in t3FontCache
T3GlyphStack *t3GlyphStack; // Type 3 glyph context stack
- GBool haveT3Dx; // set after seeing a d0/d1 operator
SplashFont *font; // current font
GBool needFontUpdate; // set when the font needs to be updated
diff --git a/xpdf/Stream.cc b/xpdf/Stream.cc
index d91b941..7102966 100644
--- a/xpdf/Stream.cc
+++ b/xpdf/Stream.cc
@@ -16,7 +16,7 @@
#include <stdlib.h>
#include <stddef.h>
#include <limits.h>
-#ifndef WIN32
+#ifndef _WIN32
#include <unistd.h>
#endif
#include <string.h>
@@ -98,11 +98,29 @@ char *Stream::getLine(char *buf, int size) {
return buf;
}
+Guint Stream::discardChars(Guint n) {
+ char buf[4096];
+ Guint count, i, j;
+
+ count = 0;
+ while (count < n) {
+ if ((i = n - count) > sizeof(buf)) {
+ i = (Guint)sizeof(buf);
+ }
+ j = (Guint)getBlock(buf, (int)i);
+ count += j;
+ if (j != i) {
+ break;
+ }
+ }
+ return count;
+}
+
GString *Stream::getPSFilter(int psLevel, const char *indent) {
return new GString();
}
-Stream *Stream::addFilters(Object *dict) {
+Stream *Stream::addFilters(Object *dict, int recursion) {
Object obj, obj2;
Object params, params2;
Stream *str;
@@ -120,7 +138,7 @@ Stream *Stream::addFilters(Object *dict) {
dict->dictLookup("DP", &params);
}
if (obj.isName()) {
- str = makeFilter(obj.getName(), str, &params);
+ str = makeFilter(obj.getName(), str, &params, recursion);
} else if (obj.isArray()) {
for (i = 0; i < obj.arrayGetLength(); ++i) {
obj.arrayGet(i, &obj2);
@@ -129,7 +147,7 @@ Stream *Stream::addFilters(Object *dict) {
else
params2.initNull();
if (obj2.isName()) {
- str = makeFilter(obj2.getName(), str, &params2);
+ str = makeFilter(obj2.getName(), str, &params2, recursion);
} else {
error(errSyntaxError, getPos(), "Bad filter name");
str = new EOFStream(str);
@@ -146,7 +164,8 @@ Stream *Stream::addFilters(Object *dict) {
return str;
}
-Stream *Stream::makeFilter(char *name, Stream *str, Object *params) {
+Stream *Stream::makeFilter(char *name, Stream *str, Object *params,
+ int recursion) {
int pred; // parameters
int colors;
int bits;
@@ -168,23 +187,23 @@ Stream *Stream::makeFilter(char *name, Stream *str, Object *params) {
bits = 8;
early = 1;
if (params->isDict()) {
- params->dictLookup("Predictor", &obj);
+ params->dictLookup("Predictor", &obj, recursion);
if (obj.isInt())
pred = obj.getInt();
obj.free();
- params->dictLookup("Columns", &obj);
+ params->dictLookup("Columns", &obj, recursion);
if (obj.isInt())
columns = obj.getInt();
obj.free();
- params->dictLookup("Colors", &obj);
+ params->dictLookup("Colors", &obj, recursion);
if (obj.isInt())
colors = obj.getInt();
obj.free();
- params->dictLookup("BitsPerComponent", &obj);
+ params->dictLookup("BitsPerComponent", &obj, recursion);
if (obj.isInt())
bits = obj.getInt();
obj.free();
- params->dictLookup("EarlyChange", &obj);
+ params->dictLookup("EarlyChange", &obj, recursion);
if (obj.isInt())
early = obj.getInt();
obj.free();
@@ -201,37 +220,37 @@ Stream *Stream::makeFilter(char *name, Stream *str, Object *params) {
endOfBlock = gTrue;
black = gFalse;
if (params->isDict()) {
- params->dictLookup("K", &obj);
+ params->dictLookup("K", &obj, recursion);
if (obj.isInt()) {
encoding = obj.getInt();
}
obj.free();
- params->dictLookup("EndOfLine", &obj);
+ params->dictLookup("EndOfLine", &obj, recursion);
if (obj.isBool()) {
endOfLine = obj.getBool();
}
obj.free();
- params->dictLookup("EncodedByteAlign", &obj);
+ params->dictLookup("EncodedByteAlign", &obj, recursion);
if (obj.isBool()) {
byteAlign = obj.getBool();
}
obj.free();
- params->dictLookup("Columns", &obj);
+ params->dictLookup("Columns", &obj, recursion);
if (obj.isInt()) {
columns = obj.getInt();
}
obj.free();
- params->dictLookup("Rows", &obj);
+ params->dictLookup("Rows", &obj, recursion);
if (obj.isInt()) {
rows = obj.getInt();
}
obj.free();
- params->dictLookup("EndOfBlock", &obj);
+ params->dictLookup("EndOfBlock", &obj, recursion);
if (obj.isBool()) {
endOfBlock = obj.getBool();
}
obj.free();
- params->dictLookup("BlackIs1", &obj);
+ params->dictLookup("BlackIs1", &obj, recursion);
if (obj.isBool()) {
black = obj.getBool();
}
@@ -242,7 +261,7 @@ Stream *Stream::makeFilter(char *name, Stream *str, Object *params) {
} else if (!strcmp(name, "DCTDecode") || !strcmp(name, "DCT")) {
colorXform = -1;
if (params->isDict()) {
- if (params->dictLookup("ColorTransform", &obj)->isInt()) {
+ if (params->dictLookup("ColorTransform", &obj, recursion)->isInt()) {
colorXform = obj.getInt();
}
obj.free();
@@ -254,19 +273,19 @@ Stream *Stream::makeFilter(char *name, Stream *str, Object *params) {
colors = 1;
bits = 8;
if (params->isDict()) {
- params->dictLookup("Predictor", &obj);
+ params->dictLookup("Predictor", &obj, recursion);
if (obj.isInt())
pred = obj.getInt();
obj.free();
- params->dictLookup("Columns", &obj);
+ params->dictLookup("Columns", &obj, recursion);
if (obj.isInt())
columns = obj.getInt();
obj.free();
- params->dictLookup("Colors", &obj);
+ params->dictLookup("Colors", &obj, recursion);
if (obj.isInt())
colors = obj.getInt();
obj.free();
- params->dictLookup("BitsPerComponent", &obj);
+ params->dictLookup("BitsPerComponent", &obj, recursion);
if (obj.isInt())
bits = obj.getInt();
obj.free();
@@ -274,7 +293,7 @@ Stream *Stream::makeFilter(char *name, Stream *str, Object *params) {
str = new FlateStream(str, pred, columns, colors, bits);
} else if (!strcmp(name, "JBIG2Decode")) {
if (params->isDict()) {
- params->dictLookup("JBIG2Globals", &globals);
+ params->dictLookup("JBIG2Globals", &globals, recursion);
}
str = new JBIG2Stream(str, &globals);
globals.free();
@@ -314,7 +333,7 @@ void FilterStream::close() {
str->close();
}
-void FilterStream::setPos(Guint pos, int dir) {
+void FilterStream::setPos(GFileOffset pos, int dir) {
error(errInternal, -1, "Called setPos() on FilterStream");
}
@@ -365,6 +384,10 @@ void ImageStream::reset() {
str->reset();
}
+void ImageStream::close() {
+ str->close();
+}
+
GBool ImageStream::getPixel(Guchar *pix) {
int i;
@@ -405,6 +428,10 @@ Guchar *ImageStream::getLine() {
}
} else if (nBits == 8) {
// special case: imgLine == inputLine
+ } else if (nBits == 16) {
+ for (i = 0; i < nVals; ++i) {
+ imgLine[i] = (Guchar)inputLine[2*i];
+ }
} else {
bitMask = (1 << nBits) - 1;
buf = 0;
@@ -451,8 +478,8 @@ StreamPredictor::StreamPredictor(Stream *strA, int predictorA,
return;
}
predLine = (Guchar *)gmalloc(rowBytes);
- memset(predLine, 0, rowBytes);
- predIdx = rowBytes;
+
+ reset();
ok = gTrue;
}
@@ -461,6 +488,11 @@ StreamPredictor::~StreamPredictor() {
gfree(predLine);
}
+void StreamPredictor::reset() {
+ memset(predLine, 0, rowBytes);
+ predIdx = rowBytes;
+}
+
int StreamPredictor::lookChar() {
if (predIdx >= rowBytes) {
if (!getNextLine()) {
@@ -573,19 +605,19 @@ GBool StreamPredictor::getNextLine() {
// 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) {
+ if (nBits == 8) {
for (i = pixBytes; i < rowBytes; ++i) {
predLine[i] += predLine[i - nComps];
}
+ } else if (nBits == 16) {
+ for (i = pixBytes; i < rowBytes; i += 2) {
+ c = ((predLine[i] + predLine[i - 2*nComps]) << 8) +
+ predLine[i + 1] + predLine[i + 1 - 2*nComps];
+ predLine[i] = (Guchar)(c >> 8);
+ predLine[i+1] = (Guchar)(c & 0xff);
+ }
} else {
- memset(upLeftBuf, 0, nComps + 1);
+ memset(upLeftBuf, 0, nComps);
bitMask = (1 << nBits) - 1;
inBuf = outBuf = 0;
inBits = outBits = 0;
@@ -624,8 +656,8 @@ GBool StreamPredictor::getNextLine() {
// FileStream
//------------------------------------------------------------------------
-FileStream::FileStream(FILE *fA, Guint startA, GBool limitedA,
- Guint lengthA, Object *dictA):
+FileStream::FileStream(FILE *fA, GFileOffset startA, GBool limitedA,
+ GFileOffset lengthA, Object *dictA):
BaseStream(dictA) {
f = fA;
start = startA;
@@ -641,22 +673,14 @@ FileStream::~FileStream() {
close();
}
-Stream *FileStream::makeSubStream(Guint startA, GBool limitedA,
- Guint lengthA, Object *dictA) {
+Stream *FileStream::makeSubStream(GFileOffset startA, GBool limitedA,
+ GFileOffset 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
+ savePos = gftell(f);
+ gfseek(f, start, SEEK_SET);
saved = gTrue;
bufPtr = bufEnd = buf;
bufPos = start;
@@ -664,13 +688,7 @@ void FileStream::reset() {
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
+ gfseek(f, savePos, SEEK_SET);
saved = gFalse;
}
}
@@ -705,7 +723,7 @@ GBool FileStream::fillBuf() {
return gFalse;
}
if (limited && bufPos + fileStreamBufSize > start + length) {
- n = start + length - bufPos;
+ n = (int)(start + length - bufPos);
} else {
n = fileStreamBufSize;
}
@@ -717,41 +735,20 @@ GBool FileStream::fillBuf() {
return gTrue;
}
-void FileStream::setPos(Guint pos, int dir) {
- Guint size;
+void FileStream::setPos(GFileOffset pos, int dir) {
+ GFileOffset 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
+ gfseek(f, pos, SEEK_SET);
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;
-#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
+ gfseek(f, 0, SEEK_END);
+ size = gftell(f);
+ if (pos > size) {
+ pos = size;
+ }
+ gfseek(f, -pos, SEEK_END);
+ bufPos = gftell(f);
}
bufPtr = bufEnd = buf;
}
@@ -782,17 +779,24 @@ MemStream::~MemStream() {
}
}
-Stream *MemStream::makeSubStream(Guint startA, GBool limited,
- Guint lengthA, Object *dictA) {
+Stream *MemStream::makeSubStream(GFileOffset startA, GBool limited,
+ GFileOffset lengthA, Object *dictA) {
MemStream *subStr;
- Guint newLength;
+ Guint newStart, newLength;
- if (!limited || startA + lengthA > start + length) {
- newLength = start + length - startA;
+ if (startA < start) {
+ newStart = start;
+ } else if (startA > start + length) {
+ newStart = start + (int)length;
+ } else {
+ newStart = (int)startA;
+ }
+ if (!limited || newStart + lengthA > start + length) {
+ newLength = start + length - newStart;
} else {
- newLength = lengthA;
+ newLength = (Guint)lengthA;
}
- subStr = new MemStream(buf, startA, newLength, dictA);
+ subStr = new MemStream(buf, newStart, newLength, dictA);
return subStr;
}
@@ -819,13 +823,13 @@ int MemStream::getBlock(char *blk, int size) {
return n;
}
-void MemStream::setPos(Guint pos, int dir) {
+void MemStream::setPos(GFileOffset pos, int dir) {
Guint i;
if (dir >= 0) {
- i = pos;
+ i = (Guint)pos;
} else {
- i = start + length - pos;
+ i = (Guint)(start + length - pos);
}
if (i < start) {
i = start;
@@ -846,7 +850,7 @@ void MemStream::moveStart(int delta) {
//------------------------------------------------------------------------
EmbedStream::EmbedStream(Stream *strA, Object *dictA,
- GBool limitedA, Guint lengthA):
+ GBool limitedA, GFileOffset lengthA):
BaseStream(dictA) {
str = strA;
limited = limitedA;
@@ -856,8 +860,8 @@ EmbedStream::EmbedStream(Stream *strA, Object *dictA,
EmbedStream::~EmbedStream() {
}
-Stream *EmbedStream::makeSubStream(Guint start, GBool limitedA,
- Guint lengthA, Object *dictA) {
+Stream *EmbedStream::makeSubStream(GFileOffset start, GBool limitedA,
+ GFileOffset lengthA, Object *dictA) {
error(errInternal, -1, "Called makeSubStream() on EmbedStream");
return NULL;
}
@@ -887,11 +891,11 @@ int EmbedStream::getBlock(char *blk, int size) {
return str->getBlock(blk, size);
}
-void EmbedStream::setPos(Guint pos, int dir) {
+void EmbedStream::setPos(GFileOffset pos, int dir) {
error(errInternal, -1, "Called setPos() on EmbedStream");
}
-Guint EmbedStream::getStart() {
+GFileOffset EmbedStream::getStart() {
error(errInternal, -1, "Called getStart() on EmbedStream");
return 0;
}
@@ -1173,6 +1177,9 @@ int LZWStream::getBlock(char *blk, int size) {
void LZWStream::reset() {
str->reset();
+ if (pred) {
+ pred->reset();
+ }
eof = gFalse;
inputBits = 0;
clearTable();
@@ -2068,14 +2075,16 @@ GBool CCITTFaxStream::isBinary(GBool last) {
//------------------------------------------------------------------------
// 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
+#define dctSqrt2 5793 // sqrt(2)
+#define dctSqrt2Cos6 2217 // sqrt(2) * cos(6*pi/16)
+#define dctSqrt2Cos6PSin6 7568 // sqrt(2) * (cos(6*pi/16) + sin(6*pi/16))
+#define dctSqrt2Sin6MCos6 3135 // sqrt(2) * (sin(6*pi/16) - cos(6*pi/16))
+#define dctCos3 3406 // cos(3*pi/16)
+#define dctCos3PSin3 5681 // cos(3*pi/16) + sin(3*pi/16)
+#define dctSin3MCos3 -1130 // sin(3*pi/16) - cos(3*pi/16)
+#define dctCos1 4017 // cos(pi/16)
+#define dctCos1PSin1 4816 // cos(pi/16) + sin(pi/16)
+#define dctSin1MCos1 -3218 // sin(pi/16) - cos(pi/16)
// color conversion parameters (16.16 fixed point format)
#define dctCrToR 91881 // 1.4020
@@ -2083,10 +2092,47 @@ GBool CCITTFaxStream::isBinary(GBool last) {
#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;
+// The dctClip function clips signed integers to the [0,255] range.
+// To handle valid DCT inputs, this must support an input range of at
+// least [-256,511]. Invalid DCT inputs (e.g., from damaged PDF
+// files) can result in arbitrary values, so we want to mask those
+// out. We round the input range size up to a power of 2 (so we can
+// use a bit mask), which gives us an input range of [-384,639]. The
+// end result is:
+// input output
+// ---------- ------
+// <-384 X invalid inputs -> output is "don't care"
+// -384..-257 0 invalid inputs, clipped
+// -256..-1 0 valid inputs, need to be clipped
+// 0..255 0..255
+// 256..511 255 valid inputs, need to be clipped
+// 512..639 255 invalid inputs, clipped
+// >=512 X invalid inputs -> output is "don't care"
+
+#define dctClipOffset 384
+#define dctClipMask 1023
+static Guchar dctClipData[1024];
+
+static inline void dctClipInit() {
+ static int initDone = 0;
+ int i;
+ if (!initDone) {
+ for (i = -384; i < 0; ++i) {
+ dctClipData[dctClipOffset + i] = 0;
+ }
+ for (i = 0; i < 256; ++i) {
+ dctClipData[dctClipOffset + i] = i;
+ }
+ for (i = 256; i < 639; ++i) {
+ dctClipData[dctClipOffset + i] = 255;
+ }
+ initDone = 1;
+ }
+}
+
+static inline int dctClip(int x) {
+ return dctClipData[(dctClipOffset + x) & dctClipMask];
+}
// zig zag decode map
static int dctZigZag[64] = {
@@ -2109,7 +2155,7 @@ static int dctZigZag[64] = {
DCTStream::DCTStream(Stream *strA, GBool colorXformA):
FilterStream(strA) {
- int i, j;
+ int i;
colorXform = colorXformA;
progressive = interleaved = gFalse;
@@ -2117,23 +2163,15 @@ DCTStream::DCTStream(Stream *strA, GBool colorXformA):
mcuWidth = mcuHeight = 0;
numComps = 0;
comp = 0;
- x = y = dy = 0;
+ x = y = 0;
for (i = 0; i < 4; ++i) {
- for (j = 0; j < 32; ++j) {
- rowBuf[i][j] = NULL;
- }
frameBuf[i] = NULL;
}
+ rowBuf = NULL;
+ memset(dcHuffTables, 0, sizeof(dcHuffTables));
+ memset(acHuffTables, 0, sizeof(acHuffTables));
- 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;
- }
+ dctClipInit();
}
DCTStream::~DCTStream() {
@@ -2142,7 +2180,7 @@ DCTStream::~DCTStream() {
}
void DCTStream::reset() {
- int i, j;
+ int i;
str->reset();
@@ -2157,6 +2195,8 @@ void DCTStream::reset() {
restartInterval = 0;
if (!readHeader()) {
+ // force an EOF condition
+ progressive = gTrue;
y = height;
return;
}
@@ -2229,17 +2269,11 @@ void DCTStream::reset() {
// 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));
- }
- }
+ rowBuf = (Guchar *)gmallocn(numComps * mcuHeight, bufWidth);
+ rowBufPtr = rowBufEnd = rowBuf;
// initialize counters
- comp = 0;
- x = 0;
- y = 0;
- dy = mcuHeight;
+ y = -mcuHeight;
restartMarker = 0xd0;
restart();
@@ -2247,26 +2281,24 @@ void DCTStream::reset() {
}
void DCTStream::close() {
- int i, j;
+ int i;
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;
}
+ gfree(rowBuf);
+ rowBuf = NULL;
FilterStream::close();
}
int DCTStream::getChar() {
int c;
- if (y >= height) {
- return EOF;
- }
if (progressive || !interleaved) {
+ if (y >= height) {
+ return EOF;
+ }
c = frameBuf[comp][y * bufWidth + x];
if (++comp == numComps) {
comp = 0;
@@ -2276,48 +2308,38 @@ int DCTStream::getChar() {
}
}
} else {
- if (dy >= mcuHeight) {
+ if (rowBufPtr == rowBufEnd) {
+ if (y + mcuHeight >= height) {
+ return EOF;
+ }
+ y += 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();
- }
- }
}
+ c = *rowBufPtr++;
}
return c;
}
int DCTStream::lookChar() {
- if (y >= height) {
- return EOF;
- }
if (progressive || !interleaved) {
+ if (y >= height) {
+ return EOF;
+ }
return frameBuf[comp][y * bufWidth + x];
} else {
- if (dy >= mcuHeight) {
+ if (rowBufPtr == rowBufEnd) {
+ if (y + mcuHeight >= height) {
+ return EOF;
+ }
if (!readMCURow()) {
y = height;
return EOF;
}
- comp = 0;
- x = 0;
- dy = 0;
}
- return rowBuf[comp][dy][x];
+ return *rowBufPtr;
}
}
@@ -2375,38 +2397,49 @@ GBool DCTStream::readMCURow() {
}
transformDataUnit(quantTables[compInfo[cc].quantTable],
data1, data2);
- if (hSub == 1 && vSub == 1) {
+ if (hSub == 1 && vSub == 1 && x1+x2+8 <= width) {
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];
+ p1 = &rowBuf[((y2+y3) * width + (x1+x2)) * numComps + cc];
+ p1[0] = data2[i];
+ p1[ numComps] = data2[i+1];
+ p1[2*numComps] = data2[i+2];
+ p1[3*numComps] = data2[i+3];
+ p1[4*numComps] = data2[i+4];
+ p1[5*numComps] = data2[i+5];
+ p1[6*numComps] = data2[i+6];
+ p1[7*numComps] = data2[i+7];
}
- } else if (hSub == 2 && vSub == 2) {
+ } else if (hSub == 2 && vSub == 2 && x1+x2+16 <= width) {
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];
+ p1 = &rowBuf[((y2+y3) * width + (x1+x2)) * numComps + cc];
+ p2 = p1 + width * numComps;
+ p1[0] = p1[numComps] =
+ p2[0] = p2[numComps] = data2[i];
+ p1[2*numComps] = p1[3*numComps] =
+ p2[2*numComps] = p2[3*numComps] = data2[i+1];
+ p1[4*numComps] = p1[5*numComps] =
+ p2[4*numComps] = p2[5*numComps] = data2[i+2];
+ p1[6*numComps] = p1[7*numComps] =
+ p2[6*numComps] = p2[7*numComps] = data2[i+3];
+ p1[8*numComps] = p1[9*numComps] =
+ p2[8*numComps] = p2[9*numComps] = data2[i+4];
+ p1[10*numComps] = p1[11*numComps] =
+ p2[10*numComps] = p2[11*numComps] = data2[i+5];
+ p1[12*numComps] = p1[13*numComps] =
+ p2[12*numComps] = p2[13*numComps] = data2[i+6];
+ p1[14*numComps] = p1[15*numComps] =
+ p2[14*numComps] = p2[15*numComps] = data2[i+7];
}
} else {
+ p1 = &rowBuf[(y2 * width + (x1+x2)) * numComps + cc];
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];
+ for (y5 = 0; y5 < vSub; ++y5) {
+ for (x5 = 0; x5 < hSub && x1+x2+x4+x5 < width; ++x5) {
+ p1[((y4+y5) * width + (x4+x5)) * numComps] = data2[i];
+ }
+ }
++i;
}
}
@@ -2415,42 +2448,46 @@ GBool DCTStream::readMCURow() {
}
}
--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];
- }
- }
+ // color space conversion
+ if (colorXform) {
+ // convert YCbCr to RGB
+ if (numComps == 3) {
+ for (i = 0, p1 = rowBuf; i < width * mcuHeight; ++i, p1 += 3) {
+ pY = p1[0];
+ pCb = p1[1] - 128;
+ pCr = p1[2] - 128;
+ pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16;
+ p1[0] = dctClip(pR);
+ pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32768) >> 16;
+ p1[1] = dctClip(pG);
+ pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16;
+ p1[2] = dctClip(pB);
+ }
+ // convert YCbCrK to CMYK (K is passed through unchanged)
+ } else if (numComps == 4) {
+ for (i = 0, p1 = rowBuf; i < width * mcuHeight; ++i, p1 += 4) {
+ pY = p1[0];
+ pCb = p1[1] - 128;
+ pCr = p1[2] - 128;
+ pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16;
+ p1[0] = 255 - dctClip(pR);
+ pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32768) >> 16;
+ p1[1] = 255 - dctClip(pG);
+ pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16;
+ p1[2] = 255 - dctClip(pB);
}
}
}
+
+ rowBufPtr = rowBuf;
+ if (y + mcuHeight <= height) {
+ rowBufEnd = rowBuf + numComps * width * mcuHeight;
+ } else {
+ rowBufEnd = rowBuf + numComps * width * (height - y);
+ }
+
return gTrue;
}
@@ -2609,7 +2646,7 @@ GBool DCTStream::readDataUnit(DCTHuffTable *dcHuffTable,
return gTrue;
}
-// Read one data unit from a sequential JPEG stream.
+// Read one data unit from a progressive JPEG stream.
GBool DCTStream::readProgressiveDataUnit(DCTHuffTable *dcHuffTable,
DCTHuffTable *acHuffTable,
int *prevDC, int data[64]) {
@@ -2635,7 +2672,13 @@ GBool DCTStream::readProgressiveDataUnit(DCTHuffTable *dcHuffTable,
if ((bit = readBit()) == 9999) {
return gFalse;
}
- data[0] += bit << scanInfo.al;
+ if (bit) {
+ if (data[0] >= 0) {
+ data[0] += 1 << scanInfo.al;
+ } else {
+ data[0] -= 1 << scanInfo.al;
+ }
+ }
}
++i;
}
@@ -2652,7 +2695,11 @@ GBool DCTStream::readProgressiveDataUnit(DCTHuffTable *dcHuffTable,
return gFalse;
}
if (bit) {
- data[j] += 1 << scanInfo.al;
+ if (data[j] >= 0) {
+ data[j] += 1 << scanInfo.al;
+ } else {
+ data[j] -= 1 << scanInfo.al;
+ }
}
}
}
@@ -2678,7 +2725,11 @@ GBool DCTStream::readProgressiveDataUnit(DCTHuffTable *dcHuffTable,
return gFalse;
}
if (bit) {
- data[j] += 1 << scanInfo.al;
+ if (data[j] >= 0) {
+ data[j] += 1 << scanInfo.al;
+ } else {
+ data[j] -= 1 << scanInfo.al;
+ }
}
}
}
@@ -2701,7 +2752,11 @@ GBool DCTStream::readProgressiveDataUnit(DCTHuffTable *dcHuffTable,
return gFalse;
}
if (bit) {
- data[j] += 1 << scanInfo.al;
+ if (data[j] >= 0) {
+ data[j] += 1 << scanInfo.al;
+ } else {
+ data[j] -= 1 << scanInfo.al;
+ }
}
}
}
@@ -2723,7 +2778,11 @@ GBool DCTStream::readProgressiveDataUnit(DCTHuffTable *dcHuffTable,
return gFalse;
}
if (bit) {
- data[j] += 1 << scanInfo.al;
+ if (data[j] >= 0) {
+ data[j] += 1 << scanInfo.al;
+ } else {
+ data[j] -= 1 << scanInfo.al;
+ }
}
j = dctZigZag[i++];
}
@@ -2837,12 +2896,12 @@ void DCTStream::decodeImage() {
pCb = *p1 - 128;
pCr = *p2 - 128;
pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16;
- *p0++ = dctClip[dctClipOffset + pR];
+ *p0++ = dctClip(pR);
pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr +
32768) >> 16;
- *p1++ = dctClip[dctClipOffset + pG];
+ *p1++ = dctClip(pG);
pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16;
- *p2++ = dctClip[dctClipOffset + pB];
+ *p2++ = dctClip(pB);
}
}
// convert YCbCrK to CMYK (K is passed through unchanged)
@@ -2856,12 +2915,12 @@ void DCTStream::decodeImage() {
pCb = *p1 - 128;
pCr = *p2 - 128;
pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16;
- *p0++ = 255 - dctClip[dctClipOffset + pR];
+ *p0++ = 255 - dctClip(pR);
pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr +
32768) >> 16;
- *p1++ = 255 - dctClip[dctClipOffset + pG];
+ *p1++ = 255 - dctClip(pG);
pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16;
- *p2++ = 255 - dctClip[dctClipOffset + pB];
+ *p2++ = 255 - dctClip(pB);
}
}
}
@@ -2880,71 +2939,76 @@ void DCTStream::decodeImage() {
// paper.
void DCTStream::transformDataUnit(Gushort *quantTable,
int dataIn[64], Guchar dataOut[64]) {
- int v0, v1, v2, v3, v4, v5, v6, v7, t;
+ int v0, v1, v2, v3, v4, v5, v6, v7, t0, t1, t2;
int *p;
+ Gushort *q;
int i;
- // dequant
- for (i = 0; i < 64; ++i) {
- dataIn[i] *= quantTable[i];
- }
-
- // inverse DCT on rows
+ // dequant; inverse DCT on rows
for (i = 0; i < 64; i += 8) {
p = dataIn + i;
+ q = quantTable + 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;
+ t0 = p[0] * q[0];
+ p[0] = t0;
+ p[1] = t0;
+ p[2] = t0;
+ p[3] = t0;
+ p[4] = t0;
+ p[5] = t0;
+ p[6] = t0;
+ p[7] = t0;
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;
+ v0 = p[0] * q[0];
+ v1 = p[4] * q[4];
+ v2 = p[2] * q[2];
+ v3 = p[6] * q[6];
+ t0 = p[1] * q[1];
+ t1 = p[7] * q[7];
+ v4 = t0 - t1;
+ v7 = t0 + t1;
+ v5 = (dctSqrt2 * p[3] * q[3]) >> 12;
+ v6 = (dctSqrt2 * p[5] * q[5]) >> 12;
// 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;
+ t0 = v0 - v1;
+ v0 = v0 + v1;
+ v1 = t0;
+ t0 = dctSqrt2Cos6 * (v2 + v3);
+ t1 = dctSqrt2Cos6PSin6 * v3;
+ t2 = dctSqrt2Sin6MCos6 * v2;
+ v2 = (t0 - t1) >> 12;
+ v3 = (t0 + t2) >> 12;
+ t0 = v4 - v6;
+ v4 = v4 + v6;
+ v6 = t0;
+ t0 = v7 + v5;
+ v5 = v7 - v5;
+ v7 = t0;
// 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;
+ t0 = v0 - v3;
+ v0 = v0 + v3;
+ v3 = t0;
+ t0 = v1 - v2;
+ v1 = v1 + v2;
+ v2 = t0;
+ t0 = dctCos3 * (v4 + v7);
+ t1 = dctCos3PSin3 * v7;
+ t2 = dctSin3MCos3 * v4;
+ v4 = (t0 - t1) >> 12;
+ v7 = (t0 + t2) >> 12;
+ t0 = dctCos1 * (v5 + v6);
+ t1 = dctCos1PSin1 * v6;
+ t2 = dctSin1MCos1 * v5;
+ v5 = (t0 - t1) >> 12;
+ v6 = (t0 + t2) >> 12;
// stage 1
p[0] = v0 + v7;
@@ -2964,55 +3028,60 @@ void DCTStream::transformDataUnit(Gushort *quantTable,
// 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;
+ t0 = p[0*8];
+ p[1*8] = t0;
+ p[2*8] = t0;
+ p[3*8] = t0;
+ p[4*8] = t0;
+ p[5*8] = t0;
+ p[6*8] = t0;
+ p[7*8] = t0;
continue;
}
// stage 4
- v0 = (dctSqrt2 * p[0*8] + 2048) >> 12;
- v1 = (dctSqrt2 * p[4*8] + 2048) >> 12;
+ v0 = p[0*8];
+ v1 = p[4*8];
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];
+ v4 = p[1*8] - p[7*8];
+ v7 = p[1*8] + p[7*8];
+ v5 = (dctSqrt2 * p[3*8]) >> 12;
+ v6 = (dctSqrt2 * p[5*8]) >> 12;
// 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;
+ t0 = v0 - v1;
+ v0 = v0 + v1;
+ v1 = t0;
+ t0 = dctSqrt2Cos6 * (v2 + v3);
+ t1 = dctSqrt2Cos6PSin6 * v3;
+ t2 = dctSqrt2Sin6MCos6 * v2;
+ v2 = (t0 - t1) >> 12;
+ v3 = (t0 + t2) >> 12;
+ t0 = v4 - v6;
+ v4 = v4 + v6;
+ v6 = t0;
+ t0 = v7 + v5;
+ v5 = v7 - v5;
+ v7 = t0;
// 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;
+ t0 = v0 - v3;
+ v0 = v0 + v3;
+ v3 = t0;
+ t0 = v1 - v2;
+ v1 = v1 + v2;
+ v2 = t0;
+ t0 = dctCos3 * (v4 + v7);
+ t1 = dctCos3PSin3 * v7;
+ t2 = dctSin3MCos3 * v4;
+ v4 = (t0 - t1) >> 12;
+ v7 = (t0 + t2) >> 12;
+ t0 = dctCos1 * (v5 + v6);
+ t1 = dctCos1PSin1 * v6;
+ t2 = dctSin1MCos1 * v5;
+ v5 = (t0 - t1) >> 12;
+ v6 = (t0 + t2) >> 12;
// stage 1
p[0*8] = v0 + v7;
@@ -3027,7 +3096,7 @@ void DCTStream::transformDataUnit(Gushort *quantTable,
// convert to 8-bit integers
for (i = 0; i < 64; ++i) {
- dataOut[i] = dctClip[dctClipOffset + 128 + ((dataIn[i] + 8) >> 4)];
+ dataOut[i] = dctClip(128 + (dataIn[i] >> 3));
}
}
@@ -3103,7 +3172,6 @@ GBool DCTStream::readHeader() {
GBool doScan;
int n;
int c = 0;
- int i;
// read headers
doScan = gFalse;
@@ -3163,9 +3231,7 @@ GBool DCTStream::readHeader() {
// skip APPn / COM / etc.
if (c >= 0xe0) {
n = read16() - 2;
- for (i = 0; i < n; ++i) {
- str->getChar();
- }
+ str->discardChars(n);
} else {
error(errSyntaxError, getPos(), "Unknown DCT marker <{0:02x}>", c);
return gFalse;
@@ -3178,12 +3244,11 @@ GBool DCTStream::readHeader() {
}
GBool DCTStream::readBaselineSOF() {
- int length;
int prec;
int i;
int c;
- length = read16();
+ read16(); // length
prec = str->getChar();
height = read16();
width = read16();
@@ -3218,12 +3283,11 @@ GBool DCTStream::readBaselineSOF() {
}
GBool DCTStream::readProgressiveSOF() {
- int length;
int prec;
int i;
int c;
- length = read16();
+ read16(); // length
prec = str->getChar();
height = read16();
width = read16();
@@ -4194,6 +4258,9 @@ void FlateStream::reset() {
eof = gTrue;
str->reset();
+ if (pred) {
+ pred->reset();
+ }
// read header
//~ need to look at window size?
@@ -4274,10 +4341,10 @@ int FlateStream::getBlock(char *blk, int size) {
n = 0;
while (n < size) {
- if (endOfBlock && eof) {
- break;
- }
if (remain == 0) {
+ if (endOfBlock && eof) {
+ break;
+ }
readSome();
}
while (remain && n < size) {
@@ -4969,3 +5036,149 @@ GBool RunLengthEncoder::fillBuf() {
bufPtr = buf;
return gTrue;
}
+
+//------------------------------------------------------------------------
+// LZWEncoder
+//------------------------------------------------------------------------
+
+LZWEncoder::LZWEncoder(Stream *strA):
+ FilterStream(strA)
+{
+ inBufLen = 0;
+ outBufLen = 0;
+}
+
+LZWEncoder::~LZWEncoder() {
+ if (str->isEncoder()) {
+ delete str;
+ }
+}
+
+void LZWEncoder::reset() {
+ int i;
+
+ str->reset();
+
+ // initialize code table
+ for (i = 0; i < 256; ++i) {
+ table[i].byte = i;
+ table[i].next = NULL;
+ table[i].children = NULL;
+ }
+ nextSeq = 258;
+ codeLen = 9;
+
+ // initialize input buffer
+ inBufLen = str->getBlock((char *)inBuf, sizeof(inBuf));
+
+ // initialize output buffer with a clear-table code
+ outBuf = 256;
+ outBufLen = 9;
+ needEOD = gFalse;
+}
+
+int LZWEncoder::getChar() {
+ int ret;
+
+ if (inBufLen == 0 && !needEOD && outBufLen == 0) {
+ return EOF;
+ }
+ if (outBufLen < 8 && (inBufLen > 0 || needEOD)) {
+ fillBuf();
+ }
+ if (outBufLen >= 8) {
+ ret = (outBuf >> (outBufLen - 8)) & 0xff;
+ outBufLen -= 8;
+ } else {
+ ret = (outBuf << (8 - outBufLen)) & 0xff;
+ outBufLen = 0;
+ }
+ return ret;
+}
+
+int LZWEncoder::lookChar() {
+ if (inBufLen == 0 && !needEOD && outBufLen == 0) {
+ return EOF;
+ }
+ if (outBufLen < 8 && (inBufLen > 0 || needEOD)) {
+ fillBuf();
+ }
+ if (outBufLen >= 8) {
+ return (outBuf >> (outBufLen - 8)) & 0xff;
+ } else {
+ return (outBuf << (8 - outBufLen)) & 0xff;
+ }
+}
+
+// On input, outBufLen < 8.
+// This function generates, at most, 2 12-bit codes
+// --> outBufLen < 8 + 12 + 12 = 32
+void LZWEncoder::fillBuf() {
+ LZWEncoderNode *p0, *p1;
+ int seqLen, code, i;
+
+ if (needEOD) {
+ outBuf = (outBuf << codeLen) | 257;
+ outBufLen += codeLen;
+ needEOD = gFalse;
+ return;
+ }
+
+ // find longest matching sequence (if any)
+ p0 = table + inBuf[0];
+ seqLen = 1;
+ while (inBufLen > seqLen) {
+ for (p1 = p0->children; p1; p1 = p1->next) {
+ if (p1->byte == inBuf[seqLen]) {
+ break;
+ }
+ }
+ if (!p1) {
+ break;
+ }
+ p0 = p1;
+ ++seqLen;
+ }
+ code = (int)(p0 - table);
+
+ // generate an output code
+ outBuf = (outBuf << codeLen) | code;
+ outBufLen += codeLen;
+
+ // update the table
+ table[nextSeq].byte = seqLen < inBufLen ? inBuf[seqLen] : 0;
+ table[nextSeq].children = NULL;
+ if (table[code].children) {
+ table[nextSeq].next = table[code].children;
+ } else {
+ table[nextSeq].next = NULL;
+ }
+ table[code].children = table + nextSeq;
+ ++nextSeq;
+
+ // update the input buffer
+ memmove(inBuf, inBuf + seqLen, inBufLen - seqLen);
+ inBufLen -= seqLen;
+ inBufLen += str->getBlock((char *)inBuf + inBufLen,
+ sizeof(inBuf) - inBufLen);
+
+ // increment codeLen; generate clear-table code
+ if (nextSeq == (1 << codeLen)) {
+ ++codeLen;
+ if (codeLen == 13) {
+ outBuf = (outBuf << 12) | 256;
+ outBufLen += 12;
+ for (i = 0; i < 256; ++i) {
+ table[i].next = NULL;
+ table[i].children = NULL;
+ }
+ nextSeq = 258;
+ codeLen = 9;
+ }
+ }
+
+ // generate EOD next time
+ if (inBufLen == 0) {
+ needEOD = gTrue;
+ }
+}
diff --git a/xpdf/Stream.h b/xpdf/Stream.h
index 50710a6..c55d802 100644
--- a/xpdf/Stream.h
+++ b/xpdf/Stream.h
@@ -17,6 +17,7 @@
#include <stdio.h>
#include "gtypes.h"
+#include "gfile.h"
#include "Object.h"
class BaseStream;
@@ -97,13 +98,18 @@ public:
// Get next line from stream.
virtual char *getLine(char *buf, int size);
+ // Discard the next <n> bytes from stream. Returns the number of
+ // bytes discarded, which will be less than <n> only if EOF is
+ // reached.
+ virtual Guint discardChars(Guint n);
+
// Get current position in file.
- virtual int getPos() = 0;
+ virtual GFileOffset 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;
+ virtual void setPos(GFileOffset pos, int dir = 0) = 0;
// Get PostScript command for the filter(s).
virtual GString *getPSFilter(int psLevel, const char *indent);
@@ -133,11 +139,11 @@ public:
// Add filters to this stream according to the parameters in <dict>.
// Returns the new stream.
- Stream *addFilters(Object *dict);
+ Stream *addFilters(Object *dict, int recursion = 0);
private:
- Stream *makeFilter(char *name, Stream *str, Object *params);
+ Stream *makeFilter(char *name, Stream *str, Object *params, int recursion);
int ref; // reference count
};
@@ -153,9 +159,9 @@ 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 Stream *makeSubStream(GFileOffset start, GBool limited,
+ GFileOffset length, Object *dict) = 0;
+ virtual void setPos(GFileOffset pos, int dir = 0) = 0;
virtual GBool isBinary(GBool last = gTrue) { return last; }
virtual BaseStream *getBaseStream() { return this; }
virtual Stream *getUndecodedStream() { return this; }
@@ -163,7 +169,7 @@ public:
virtual GString *getFileName() { return NULL; }
// Get/set position of first byte of stream within the file.
- virtual Guint getStart() = 0;
+ virtual GFileOffset getStart() = 0;
virtual void moveStart(int delta) = 0;
private:
@@ -183,8 +189,8 @@ public:
FilterStream(Stream *strA);
virtual ~FilterStream();
virtual void close();
- virtual int getPos() { return str->getPos(); }
- virtual void setPos(Guint pos, int dir = 0);
+ virtual GFileOffset getPos() { return str->getPos(); }
+ virtual void setPos(GFileOffset pos, int dir = 0);
virtual BaseStream *getBaseStream() { return str->getBaseStream(); }
virtual Stream *getUndecodedStream() { return str->getUndecodedStream(); }
virtual Dict *getDict() { return str->getDict(); }
@@ -212,6 +218,9 @@ public:
// Reset the stream.
void reset();
+ // Close down the stream.
+ void close();
+
// 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);
@@ -252,6 +261,8 @@ public:
GBool isOk() { return ok; }
+ void reset();
+
int lookChar();
int getChar();
int getBlock(char *blk, int size);
@@ -282,11 +293,11 @@ private:
class FileStream: public BaseStream {
public:
- FileStream(FILE *fA, Guint startA, GBool limitedA,
- Guint lengthA, Object *dictA);
+ FileStream(FILE *fA, GFileOffset startA, GBool limitedA,
+ GFileOffset lengthA, Object *dictA);
virtual ~FileStream();
- virtual Stream *makeSubStream(Guint startA, GBool limitedA,
- Guint lengthA, Object *dictA);
+ virtual Stream *makeSubStream(GFileOffset startA, GBool limitedA,
+ GFileOffset lengthA, Object *dictA);
virtual StreamKind getKind() { return strFile; }
virtual void reset();
virtual void close();
@@ -295,9 +306,9 @@ public:
virtual int lookChar()
{ return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); }
virtual int getBlock(char *blk, int size);
- virtual int getPos() { return bufPos + (int)(bufPtr - buf); }
- virtual void setPos(Guint pos, int dir = 0);
- virtual Guint getStart() { return start; }
+ virtual GFileOffset getPos() { return bufPos + (int)(bufPtr - buf); }
+ virtual void setPos(GFileOffset pos, int dir = 0);
+ virtual GFileOffset getStart() { return start; }
virtual void moveStart(int delta);
private:
@@ -305,14 +316,14 @@ private:
GBool fillBuf();
FILE *f;
- Guint start;
+ GFileOffset start;
GBool limited;
- Guint length;
+ GFileOffset length;
char buf[fileStreamBufSize];
char *bufPtr;
char *bufEnd;
- Guint bufPos;
- int savePos;
+ GFileOffset bufPos;
+ GFileOffset savePos;
GBool saved;
};
@@ -325,8 +336,8 @@ public:
MemStream(char *bufA, Guint startA, Guint lengthA, Object *dictA);
virtual ~MemStream();
- virtual Stream *makeSubStream(Guint start, GBool limited,
- Guint lengthA, Object *dictA);
+ virtual Stream *makeSubStream(GFileOffset start, GBool limited,
+ GFileOffset lengthA, Object *dictA);
virtual StreamKind getKind() { return strWeird; }
virtual void reset();
virtual void close();
@@ -335,9 +346,9 @@ public:
virtual int lookChar()
{ return (bufPtr < bufEnd) ? (*bufPtr & 0xff) : EOF; }
virtual int getBlock(char *blk, int size);
- virtual int getPos() { return (int)(bufPtr - buf); }
- virtual void setPos(Guint pos, int dir = 0);
- virtual Guint getStart() { return start; }
+ virtual GFileOffset getPos() { return (GFileOffset)(bufPtr - buf); }
+ virtual void setPos(GFileOffset pos, int dir = 0);
+ virtual GFileOffset getStart() { return start; }
virtual void moveStart(int delta);
private:
@@ -363,25 +374,25 @@ private:
class EmbedStream: public BaseStream {
public:
- EmbedStream(Stream *strA, Object *dictA, GBool limitedA, Guint lengthA);
+ EmbedStream(Stream *strA, Object *dictA, GBool limitedA, GFileOffset lengthA);
virtual ~EmbedStream();
- virtual Stream *makeSubStream(Guint start, GBool limitedA,
- Guint lengthA, Object *dictA);
+ virtual Stream *makeSubStream(GFileOffset start, GBool limitedA,
+ GFileOffset lengthA, Object *dictA);
virtual StreamKind getKind() { return str->getKind(); }
virtual void reset() {}
virtual int getChar();
virtual int lookChar();
virtual int getBlock(char *blk, int size);
- virtual int getPos() { return str->getPos(); }
- virtual void setPos(Guint pos, int dir = 0);
- virtual Guint getStart();
+ virtual GFileOffset getPos() { return str->getPos(); }
+ virtual void setPos(GFileOffset pos, int dir = 0);
+ virtual GFileOffset getStart();
virtual void moveStart(int delta);
private:
Stream *str;
GBool limited;
- Guint length;
+ GFileOffset length;
};
//------------------------------------------------------------------------
@@ -623,9 +634,11 @@ private:
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)
+ Guchar *rowBuf;
+ Guchar *rowBufPtr; // current position within rowBuf
+ Guchar *rowBufEnd; // end of valid data in rowBuf
int *frameBuf[4]; // buffer for frame (progressive mode)
- int comp, x, y, dy; // current position within image/MCU
+ int comp, x, y; // 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
@@ -902,4 +915,42 @@ private:
GBool fillBuf();
};
+//------------------------------------------------------------------------
+// LZWEncoder
+//------------------------------------------------------------------------
+
+struct LZWEncoderNode {
+ int byte;
+ LZWEncoderNode *next; // next sibling
+ LZWEncoderNode *children; // first child
+};
+
+class LZWEncoder: public FilterStream {
+public:
+
+ LZWEncoder(Stream *strA);
+ virtual ~LZWEncoder();
+ virtual StreamKind getKind() { return strWeird; }
+ virtual void reset();
+ virtual int getChar();
+ virtual int lookChar();
+ virtual GString *getPSFilter(int psLevel, const char *indent)
+ { return NULL; }
+ virtual GBool isBinary(GBool last = gTrue) { return gTrue; }
+ virtual GBool isEncoder() { return gTrue; }
+
+private:
+
+ LZWEncoderNode table[4096];
+ int nextSeq;
+ int codeLen;
+ Guchar inBuf[4096];
+ int inBufLen;
+ int outBuf;
+ int outBufLen;
+ GBool needEOD;
+
+ void fillBuf();
+};
+
#endif
diff --git a/xpdf/TextOutputDev.cc b/xpdf/TextOutputDev.cc
index 971a3fe..be1fad2 100644
--- a/xpdf/TextOutputDev.cc
+++ b/xpdf/TextOutputDev.cc
@@ -2,7 +2,7 @@
//
// TextOutputDev.cc
//
-// Copyright 1997-2003 Glyph & Cog, LLC
+// Copyright 1997-2014 Glyph & Cog, LLC
//
//========================================================================
@@ -17,7 +17,7 @@
#include <stddef.h>
#include <math.h>
#include <ctype.h>
-#ifdef WIN32
+#ifdef _WIN32
#include <fcntl.h> // for O_BINARY
#include <io.h> // for setmode
#endif
@@ -33,107 +33,342 @@
#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
+// Size of bins used for horizontal and vertical profiles is
+// splitPrecisionMul * minFontSize.
+#define splitPrecisionMul 0.05
+
+// Minimum allowed split precision.
+#define minSplitPrecision 0.01
+
+// yMin and yMax (or xMin and xMax for rot=1,3) are adjusted by this
+// fraction of the text height, to allow for slightly overlapping
+// lines (or large ascent/descent values).
+#define ascentAdjustFactor 0
+#define descentAdjustFactor 0.35
+
+// Gaps larger than max{gap} - splitGapSlack * avgFontSize are
+// considered to be equivalent.
+#define splitGapSlack 0.2
+
+// The vertical gap threshold (minimum gap required to split
+// vertically) depends on the (approximate) number of lines in the
+// block:
+// threshold = (max + slope * nLines) * avgFontSize
+// with a min value of vertGapThresholdMin * avgFontSize.
+#define vertGapThresholdMin 0.8
+#define vertGapThresholdMax 3
+#define vertGapThresholdSlope -0.5
+
+// Vertical gap threshold for table mode.
+#define vertGapThresholdTableMin 0.2
+#define vertGapThresholdTableMax 0.5
+#define vertGapThresholdTableSlope -0.02
+
+// A large character has a font size larger than
+// largeCharThreshold * avgFontSize.
+#define largeCharThreshold 1.5
+
+// A block will be split vertically only if the resulting chunk
+// widths are greater than vertSplitChunkThreshold * avgFontSize.
+#define vertSplitChunkThreshold 2
-// Inter-character space width which will cause addChar to start a new
-// word.
-#define minWordBreakSpace 0.1
+// 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
-// Negative inter-character space width, i.e., overlap, which will
-// cause addChar to start a new word.
-#define minDupBreakOverlap 0.2
+// Inter-character spacing that varies by less than this multiple of
+// font size is assumed to be equivalent.
+#define uniformSpacing 0.07
-// Max distance between baselines of two lines within a block, as a
-// fraction of the font size.
-#define maxLineSpacingDelta 1.5
+// Typical word spacing, as a fraction of font size. This will be
+// added to the minimum inter-character spacing, to account for wide
+// character spacing.
+#define wordSpacing 0.1
-// 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
+// Minimum paragraph indent from left margin, as a fraction of font
+// size.
+#define minParagraphIndent 0.5
-// Max difference in font sizes inside a word.
-#define maxWordFontSizeDelta 0.05
+// If the space between two lines is greater than
+// paragraphSpacingThreshold * avgLineSpacing, start a new paragraph.
+#define paragraphSpacingThreshold 1.2
-// 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
+// If font size changes by at least this much (measured in points)
+// between lines, start a new paragraph.
+#define paragraphFontSizeDelta 1
-// Minimum inter-word spacing, as a fraction of the font size. (Only
-// used for raw ordering.)
-#define minWordSpacing 0.15
+// Spaces at the start of a line in physical layout mode are this wide
+// (as a multiple of font size).
+#define physLayoutSpaceWidth 0.33
-// Maximum inter-word spacing, as a fraction of the font size.
-#define maxWordSpacing 1.5
+// Table cells (TextColumns) are allowed to overlap by this much
+// in table layout mode (as a fraction of cell width or height).
+#define tableCellOverlapSlack 0.05
-// Maximum horizontal spacing which will allow a word to be pulled
-// into a block.
-#define minColSpacing1 0.3
+// Primary axis delta which will cause a line break in raw mode
+// (as a fraction of font size).
+#define rawModeLineDelta 0.5
-// Minimum spacing between columns, as a fraction of the font size.
-#define minColSpacing2 1.0
+// Secondary axis delta which will cause a word break in raw mode
+// (as a fraction of font size).
+#define rawModeWordSpacing 0.15
-// Maximum vertical spacing between blocks within a flow, as a
-// multiple of the font size.
-#define maxBlockSpacing 2.5
+// Secondary axis overlap which will cause a line break in raw mode
+// (as a fraction of font size).
+#define rawModeCharOverlap 0.2
-// Minimum spacing between characters within a word, as a fraction of
-// the font size.
-#define minCharSpacing -0.2
+// Max spacing (as a multiple of font size) allowed between the end of
+// a line and a clipped character to be included in that line.
+#define clippedTextMaxWordSpace 0.5
-// 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
+// Max width of underlines (in points).
+#define maxUnderlineWidth 3
-// 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
+// Max horizontal distance between edge of word and start of underline
+// (as a fraction of font size).
+#define underlineSlack 0.2
-// Upper limit on spacing between characters in a word.
-#define maxWideCharSpacing 0.4
+// Max vertical distance between baseline of word and start of
+// underline (as a fraction of font size).
+#define underlineBaselineSlack 0.2
-// 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 distance between edge of text and edge of link border (as a
+// fraction of font size).
+#define hyperlinkSlack 0.2
-// Max width of underlines (in points).
-#define maxUnderlineWidth 3
+//------------------------------------------------------------------------
+// TextChar
+//------------------------------------------------------------------------
-// Min distance between baseline and underline (in points).
-//~ this should be font-size-dependent
-#define minUnderlineGap -2
+class TextChar {
+public:
-// Max distance between baseline and underline (in points).
-//~ this should be font-size-dependent
-#define maxUnderlineGap 4
+ TextChar(Unicode cA, int charPosA, int charLenA,
+ double xMinA, double yMinA, double xMaxA, double yMaxA,
+ int rotA, GBool clippedA, GBool invisibleA,
+ TextFontInfo *fontA, double fontSizeA,
+ double colorRA, double colorGA, double colorBA);
+
+ static int cmpX(const void *p1, const void *p2);
+ static int cmpY(const void *p1, const void *p2);
+
+ Unicode c;
+ int charPos;
+ int charLen;
+ double xMin, yMin, xMax, yMax;
+ Guchar rot;
+ char clipped;
+ char invisible;
+ TextFontInfo *font;
+ double fontSize;
+ double colorR,
+ colorG,
+ colorB;
+};
-// Max horizontal distance between edge of word and start of underline
-// (in points).
-//~ this should be font-size-dependent
-#define underlineSlack 1
+TextChar::TextChar(Unicode cA, int charPosA, int charLenA,
+ double xMinA, double yMinA, double xMaxA, double yMaxA,
+ int rotA, GBool clippedA, GBool invisibleA,
+ TextFontInfo *fontA, double fontSizeA,
+ double colorRA, double colorGA, double colorBA) {
+ double t;
+
+ c = cA;
+ charPos = charPosA;
+ charLen = charLenA;
+ xMin = xMinA;
+ yMin = yMinA;
+ xMax = xMaxA;
+ yMax = yMaxA;
+ // this can happen with vertical writing mode, or with odd values
+ // for the char/word spacing parameters
+ if (xMin > xMax) {
+ t = xMin; xMin = xMax; xMax = t;
+ }
+ if (yMin > yMax) {
+ t = yMin; yMin = yMax; yMax = t;
+ }
+ rot = (Guchar)rotA;
+ clipped = (char)clippedA;
+ invisible = (char)invisibleA;
+ font = fontA;
+ fontSize = fontSizeA;
+ colorR = colorRA;
+ colorG = colorGA;
+ colorB = colorBA;
+}
+
+int TextChar::cmpX(const void *p1, const void *p2) {
+ const TextChar *ch1 = *(const TextChar **)p1;
+ const TextChar *ch2 = *(const TextChar **)p2;
+
+ if (ch1->xMin < ch2->xMin) {
+ return -1;
+ } else if (ch1->xMin > ch2->xMin) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+int TextChar::cmpY(const void *p1, const void *p2) {
+ const TextChar *ch1 = *(const TextChar **)p1;
+ const TextChar *ch2 = *(const TextChar **)p2;
+
+ if (ch1->yMin < ch2->yMin) {
+ return -1;
+ } else if (ch1->yMin > ch2->yMin) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+//------------------------------------------------------------------------
+// TextBlock
+//------------------------------------------------------------------------
+
+enum TextBlockType {
+ blkVertSplit,
+ blkHorizSplit,
+ blkLeaf
+};
+
+enum TextBlockTag {
+ blkTagMulticolumn,
+ blkTagColumn,
+ blkTagLine
+};
+
+class TextBlock {
+public:
+
+ TextBlock(TextBlockType typeA, int rotA);
+ ~TextBlock();
+ void addChild(TextBlock *child);
+ void addChild(TextChar *child);
+ void prependChild(TextChar *child);
+ void updateBounds(int childIdx);
+
+ TextBlockType type;
+ TextBlockTag tag;
+ int rot;
+ double xMin, yMin, xMax, yMax;
+ GBool smallSplit; // true for blkVertSplit/blkHorizSplit
+ // where the gap size is small
+ GList *children; // for blkLeaf, children are TextWord;
+ // for others, children are TextBlock
+};
+
+TextBlock::TextBlock(TextBlockType typeA, int rotA) {
+ type = typeA;
+ tag = blkTagMulticolumn;
+ rot = rotA;
+ xMin = yMin = xMax = yMax = 0;
+ smallSplit = gFalse;
+ children = new GList();
+}
+
+TextBlock::~TextBlock() {
+ if (type == blkLeaf) {
+ delete children;
+ } else {
+ deleteGList(children, TextBlock);
+ }
+}
-// Max distance between edge of text and edge of link border
-#define hyperlinkSlack 2
+void TextBlock::addChild(TextBlock *child) {
+ if (children->getLength() == 0) {
+ xMin = child->xMin;
+ yMin = child->yMin;
+ xMax = child->xMax;
+ yMax = child->yMax;
+ } else {
+ if (child->xMin < xMin) {
+ xMin = child->xMin;
+ }
+ if (child->yMin < yMin) {
+ yMin = child->yMin;
+ }
+ if (child->xMax > xMax) {
+ xMax = child->xMax;
+ }
+ if (child->yMax > yMax) {
+ yMax = child->yMax;
+ }
+ }
+ children->append(child);
+}
+
+void TextBlock::addChild(TextChar *child) {
+ if (children->getLength() == 0) {
+ xMin = child->xMin;
+ yMin = child->yMin;
+ xMax = child->xMax;
+ yMax = child->yMax;
+ } else {
+ if (child->xMin < xMin) {
+ xMin = child->xMin;
+ }
+ if (child->yMin < yMin) {
+ yMin = child->yMin;
+ }
+ if (child->xMax > xMax) {
+ xMax = child->xMax;
+ }
+ if (child->yMax > yMax) {
+ yMax = child->yMax;
+ }
+ }
+ children->append(child);
+}
+
+void TextBlock::prependChild(TextChar *child) {
+ if (children->getLength() == 0) {
+ xMin = child->xMin;
+ yMin = child->yMin;
+ xMax = child->xMax;
+ yMax = child->yMax;
+ } else {
+ if (child->xMin < xMin) {
+ xMin = child->xMin;
+ }
+ if (child->yMin < yMin) {
+ yMin = child->yMin;
+ }
+ if (child->xMax > xMax) {
+ xMax = child->xMax;
+ }
+ if (child->yMax > yMax) {
+ yMax = child->yMax;
+ }
+ }
+ children->insert(0, child);
+}
+
+void TextBlock::updateBounds(int childIdx) {
+ TextBlock *child;
+
+ child = (TextBlock *)children->get(childIdx);
+ if (child->xMin < xMin) {
+ xMin = child->xMin;
+ }
+ if (child->yMin < yMin) {
+ yMin = child->yMin;
+ }
+ if (child->xMax > xMax) {
+ xMax = child->xMax;
+ }
+ if (child->yMax > yMax) {
+ yMax = child->yMax;
+ }
+}
//------------------------------------------------------------------------
// TextUnderline
@@ -157,159 +392,202 @@ public:
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() {}
+ TextLink(double xMinA, double yMinA, double xMaxA, double yMaxA,
+ GString *uriA)
+ { xMin = xMinA; yMin = yMinA; xMax = xMaxA; yMax = yMaxA; uri = uriA; }
+ ~TextLink();
- int xMin, yMin, xMax, yMax;
- Link *link;
+ double xMin, yMin, xMax, yMax;
+ GString *uri;
};
+TextLink::~TextLink() {
+ if (uri) {
+ delete uri;
+ }
+}
+
+//------------------------------------------------------------------------
+// TextOutputControl
+//------------------------------------------------------------------------
+
+TextOutputControl::TextOutputControl() {
+ mode = textOutReadingOrder;
+ fixedPitch = 0;
+ fixedLineSpacing = 0;
+ html = gFalse;
+ clipText = gFalse;
+}
+
+
//------------------------------------------------------------------------
// TextFontInfo
//------------------------------------------------------------------------
TextFontInfo::TextFontInfo(GfxState *state) {
+ GfxFont *gfxFont;
+
gfxFont = state->getFont();
-#if TEXTOUT_WORD_LIST
+ if (gfxFont) {
+ fontID = *gfxFont->getID();
+ ascent = gfxFont->getAscent();
+ descent = gfxFont->getDescent();
+ // "odd" ascent/descent values cause trouble more often than not
+ // (in theory these could be legitimate values for oddly designed
+ // fonts -- but they are more often due to buggy PDF generators)
+ // (values that are too small are a different issue -- those seem
+ // to be more commonly legitimate)
+ if (ascent > 1) {
+ ascent = 0.75;
+ }
+ if (descent < -0.5) {
+ descent = -0.25;
+ }
+ } else {
+ fontID.num = -1;
+ fontID.gen = -1;
+ ascent = 0.75;
+ descent = -0.25;
+ }
fontName = (gfxFont && gfxFont->getName()) ? gfxFont->getName()->copy()
: (GString *)NULL;
flags = gfxFont ? gfxFont->getFlags() : 0;
-#endif
+ mWidth = 0;
+ if (gfxFont && !gfxFont->isCIDFont()) {
+ char *name;
+ int code;
+ for (code = 0; code < 256; ++code) {
+ if ((name = ((Gfx8BitFont *)gfxFont)->getCharName(code)) &&
+ name[0] == 'm' && name[1] == '\0') {
+ mWidth = ((Gfx8BitFont *)gfxFont)->getWidth(code);
+ break;
+ }
+ }
+ }
}
TextFontInfo::~TextFontInfo() {
-#if TEXTOUT_WORD_LIST
if (fontName) {
delete fontName;
}
-#endif
}
GBool TextFontInfo::matches(GfxState *state) {
- return state->getFont() == gfxFont;
+ Ref *id;
+
+ if (!state->getFont()) {
+ return gFalse;
+ }
+ id = state->getFont()->getID();
+ return id->num == fontID.num && id->gen == fontID.gen;
}
//------------------------------------------------------------------------
// TextWord
//------------------------------------------------------------------------
-TextWord::TextWord(GfxState *state, int rotA, double x0, double y0,
- TextFontInfo *fontA, double fontSizeA) {
- GfxFont *gfxFont;
- double x, y, ascent, descent;
- int wMode;
+// Build a TextWord object, using chars[start .. start+len-1].
+// (If rot >= 2, the chars list is in reverse order.)
+TextWord::TextWord(GList *chars, int start, int lenA,
+ int rotA, GBool spaceAfterA) {
+ TextChar *ch;
+ int i;
rot = rotA;
- font = fontA;
- fontSize = fontSizeA;
- state->transform(x0, y0, &x, &y);
- if ((gfxFont = font->gfxFont)) {
- ascent = gfxFont->getAscent() * fontSize;
- descent = gfxFont->getDescent() * fontSize;
- wMode = gfxFont->getWMode();
- } 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;
- wMode = 0;
- }
- if (wMode) { // vertical writing mode
- // NB: the rotation value has been incremented by 1 (in
- // TextPage::beginWord()) for vertical writing mode
- switch (rot) {
- case 0:
- yMin = y - fontSize;
- yMax = y;
- base = y;
- break;
- case 1:
- xMin = x;
- xMax = x + fontSize;
- base = x;
- break;
- case 2:
- yMin = y;
- yMax = y + fontSize;
- base = y;
- break;
- case 3:
- xMin = x - fontSize;
- xMax = x;
- base = x;
- break;
+ len = lenA;
+ text = (Unicode *)gmallocn(len, sizeof(Unicode));
+ edge = (double *)gmallocn(len + 1, sizeof(double));
+ charPos = (int *)gmallocn(len + 1, sizeof(int));
+ switch (rot) {
+ case 0:
+ default:
+ ch = (TextChar *)chars->get(start);
+ xMin = ch->xMin;
+ yMin = ch->yMin;
+ yMax = ch->yMax;
+ ch = (TextChar *)chars->get(start + len - 1);
+ xMax = ch->xMax;
+ break;
+ case 1:
+ ch = (TextChar *)chars->get(start);
+ xMin = ch->xMin;
+ xMax = ch->xMax;
+ yMin = ch->yMin;
+ ch = (TextChar *)chars->get(start + len - 1);
+ yMax = ch->yMax;
+ break;
+ case 2:
+ ch = (TextChar *)chars->get(start);
+ xMax = ch->xMax;
+ yMin = ch->yMin;
+ yMax = ch->yMax;
+ ch = (TextChar *)chars->get(start + len - 1);
+ xMin = ch->xMin;
+ break;
+ case 3:
+ ch = (TextChar *)chars->get(start);
+ xMin = ch->xMin;
+ xMax = ch->xMax;
+ yMax = ch->yMax;
+ ch = (TextChar *)chars->get(start + len - 1);
+ yMin = ch->yMin;
+ break;
+ }
+ for (i = 0; i < len; ++i) {
+ ch = (TextChar *)chars->get(rot >= 2 ? start + len - 1 - i : start + i);
+ text[i] = ch->c;
+ charPos[i] = ch->charPos;
+ if (i == len - 1) {
+ charPos[len] = ch->charPos + ch->charLen;
}
- } else { // horizontal writing mode
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;
+ default:
+ edge[i] = ch->xMin;
+ if (i == len - 1) {
+ edge[len] = ch->xMax;
}
- 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;
+ edge[i] = ch->yMin;
+ if (i == len - 1) {
+ edge[len] = ch->yMax;
}
- 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;
+ edge[i] = ch->xMax;
+ if (i == len - 1) {
+ edge[len] = ch->xMin;
}
- 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;
+ edge[i] = ch->yMax;
+ if (i == len - 1) {
+ edge[len] = ch->yMin;
}
- base = x;
break;
}
}
- text = NULL;
- edge = NULL;
- charPos = 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
-
+ ch = (TextChar *)chars->get(start);
+ font = ch->font;
+ fontSize = ch->fontSize;
+ spaceAfter = spaceAfterA;
underlined = gFalse;
link = NULL;
+ colorR = ch->colorR;
+ colorG = ch->colorG;
+ colorB = ch->colorB;
+ invisible = ch->invisible;
+}
+
+TextWord::TextWord(TextWord *word) {
+ *this = *word;
+ text = (Unicode *)gmallocn(len, sizeof(Unicode));
+ memcpy(text, word->text, len * sizeof(Unicode));
+ edge = (double *)gmallocn(len + 1, sizeof(double));
+ memcpy(edge, word->edge, (len + 1) * sizeof(double));
+ charPos = (int *)gmallocn(len + 1, sizeof(int));
+ memcpy(charPos, word->charPos, (len + 1) * sizeof(int));
}
TextWord::~TextWord() {
@@ -318,175 +596,65 @@ TextWord::~TextWord() {
gfree(charPos);
}
-void TextWord::addChar(GfxState *state, double x, double y,
- double dx, double dy, int charPosA, int charLen,
- Unicode u) {
- int wMode;
-
- if (len == size) {
- size += 16;
- text = (Unicode *)greallocn(text, size, sizeof(Unicode));
- edge = (double *)greallocn(edge, size + 1, sizeof(double));
- charPos = (int *)greallocn(charPos, size + 1, sizeof(int));
- }
- text[len] = u;
- charPos[len] = charPosA;
- charPos[len + 1] = charPosA + charLen;
- wMode = font->gfxFont ? font->gfxFont->getWMode() : 0;
- if (wMode) { // vertical writing mode
- // NB: the rotation value has been incremented by 1 (in
- // TextPage::beginWord()) for vertical writing mode
- switch (rot) {
- case 0:
- if (len == 0) {
- xMin = x - fontSize;
- }
- edge[len] = x - fontSize;
- xMax = edge[len+1] = x;
- break;
- case 1:
- if (len == 0) {
- yMin = y - fontSize;
- }
- edge[len] = y - fontSize;
- yMax = edge[len+1] = y;
- break;
- case 2:
- if (len == 0) {
- xMax = x + fontSize;
- }
- edge[len] = x + fontSize;
- xMin = edge[len+1] = x;
- break;
- case 3:
- if (len == 0) {
- yMax = y + fontSize;
- }
- edge[len] = y + fontSize;
- yMin = edge[len+1] = y;
- break;
- }
- } else { // horizontal writing mode
- 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;
- }
+// This is used to append a clipped character to a word.
+void TextWord::appendChar(TextChar *ch) {
+ if (ch->xMin < xMin) {
+ xMin = ch->xMin;
}
- ++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 (ch->xMax > xMax) {
+ xMax = ch->xMax;
}
- if (len + word->len > size) {
- size = len + word->len;
- text = (Unicode *)greallocn(text, size, sizeof(Unicode));
- edge = (double *)greallocn(edge, size + 1, sizeof(double));
- charPos = (int *)greallocn(charPos, size + 1, sizeof(int));
+ if (ch->yMin < yMin) {
+ yMin = ch->yMin;
}
- for (i = 0; i < word->len; ++i) {
- text[len + i] = word->text[i];
- edge[len + i] = word->edge[i];
- charPos[len + i] = word->charPos[i];
+ if (ch->yMax > yMax) {
+ yMax = ch->yMax;
}
- edge[len + word->len] = word->edge[word->len];
- charPos[len + word->len] = word->charPos[word->len];
- len += word->len;
-}
-
-inline int TextWord::primaryCmp(TextWord *word) {
- double cmp;
-
- cmp = 0; // make gcc happy
+ text = (Unicode *)greallocn(text, len + 1, sizeof(Unicode));
+ edge = (double *)greallocn(edge, len + 2, sizeof(double));
+ charPos = (int *)greallocn(charPos, len + 2, sizeof(int));
+ text[len] = ch->c;
+ charPos[len] = ch->charPos;
+ charPos[len+1] = ch->charPos + ch->charLen;
switch (rot) {
case 0:
- cmp = xMin - word->xMin;
+ default:
+ edge[len] = ch->xMin;
+ edge[len+1] = ch->xMax;
break;
case 1:
- cmp = yMin - word->yMin;
+ edge[len] = ch->yMin;
+ edge[len+1] = ch->yMax;
break;
case 2:
- cmp = word->xMax - xMax;
+ edge[len] = ch->xMax;
+ edge[len+1] = ch->xMin;
break;
case 3:
- cmp = word->yMax - yMax;
+ edge[len] = ch->yMax;
+ edge[len+1] = ch->yMin;
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;
+ ++len;
}
int TextWord::cmpYX(const void *p1, const void *p2) {
- TextWord *word1 = *(TextWord **)p1;
- TextWord *word2 = *(TextWord **)p2;
+ const TextWord *word1 = *(const TextWord **)p1;
+ const TextWord *word2 = *(const TextWord **)p2;
double cmp;
- cmp = word1->yMin - word2->yMin;
- if (cmp == 0) {
+ if ((cmp = word1->yMin - word2->yMin) == 0) {
cmp = word1->xMin - word2->xMin;
}
return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
}
-#if TEXTOUT_WORD_LIST
+int TextWord::cmpCharPos(const void *p1, const void *p2) {
+ const TextWord *word1 = *(const TextWord **)p1;
+ const TextWord *word2 = *(const TextWord **)p2;
+
+ return word1->charPos[0] - word2->charPos[0];
+}
GString *TextWord::getText() {
GString *s;
@@ -539,1285 +707,198 @@ void TextWord::getCharBBox(int charIdx, double *xMinA, double *yMinA,
}
}
-#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
+double TextWord::getBaseline() {
switch (rot) {
case 0:
- cmp = xMin - line->xMin;
- break;
+ default:
+ return yMax + fontSize * font->descent;
case 1:
- cmp = yMin - line->yMin;
- break;
+ return xMin - fontSize * font->descent;
case 2:
- cmp = line->xMax - xMax;
- break;
+ return yMin - fontSize * font->descent;
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 xMax + fontSize * font->descent;
}
- 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);
+GString *TextWord::getLinkURI() {
+ return link ? link->uri : (GString *)NULL;
}
-void TextLine::coalesce(UnicodeMap *uMap) {
- TextWord *word0, *word1;
- double space, delta, minSpace;
- GBool isUnicode;
- char buf[8];
- int i, j;
-
- if (words->next) {
+//------------------------------------------------------------------------
+// TextLine
+//------------------------------------------------------------------------
- // 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;
- }
- }
+TextLine::TextLine(GList *wordsA, double xMinA, double yMinA,
+ double xMaxA, double yMaxA, double fontSizeA) {
+ TextWord *word;
+ int i, j, k;
- // 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[0] == word0->charPos[word0->len]) {
- word0->merge(word1);
- word0->next = word1->next;
- delete word1;
- word1 = word0->next;
- } else {
- word0 = word1;
- word1 = word1->next;
- }
- }
- }
+ words = wordsA;
+ rot = 0;
+ xMin = xMinA;
+ yMin = yMinA;
+ xMax = xMaxA;
+ yMax = yMaxA;
+ fontSize = fontSizeA;
+ px = 0;
+ pw = 0;
- // build the line text
- isUnicode = uMap ? uMap->isUnicode() : gFalse;
+ // build the text
len = 0;
- for (word1 = words; word1; word1 = word1->next) {
- len += word1->len;
- if (word1->spaceAfter) {
+ for (i = 0; i < words->getLength(); ++i) {
+ word = (TextWord *)words->get(i);
+ len += word->len;
+ if (word->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;
+ j = 0;
+ for (i = 0; i < words->getLength(); ++i) {
+ word = (TextWord *)words->get(i);
+ if (i == 0) {
+ rot = word->rot;
}
- edge[i] = word1->edge[word1->len];
- if (word1->spaceAfter) {
- text[i] = (Unicode)0x0020;
- ++i;
+ for (k = 0; k < word->len; ++k) {
+ text[j] = word->text[k];
+ edge[j] = word->edge[k];
+ ++j;
}
- }
-
- // 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));
+ edge[j] = word->edge[word->len];
+ if (word->spaceAfter) {
+ text[j] = (Unicode)0x0020;
+ ++j;
+ edge[j] = edge[j - 1];
}
}
- col[len] = convertedLen;
-
- // check for hyphen at end of line
- //~ need to check for other chars used as hyphens
+ //~ need to check for other Unicode 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 + d4 * (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;
+TextLine::~TextLine() {
+ deleteGList(words, TextWord);
+ gfree(text);
+ gfree(edge);
}
-int TextLineFrag::cmpXYLineRot(const void *p1, const void *p2) {
- TextLineFrag *frag1 = (TextLineFrag *)p1;
- TextLineFrag *frag2 = (TextLineFrag *)p2;
- double cmp;
+double TextLine::getBaseline() {
+ TextWord *word0;
- cmp = 0; // make gcc happy
- switch (frag1->line->rot) {
+ word0 = (TextWord *)words->get(0);
+ switch (rot) {
case 0:
- if ((cmp = frag1->xMin - frag2->xMin) == 0) {
- cmp = frag1->yMin - frag2->yMin;
- }
- break;
+ default:
+ return yMax + fontSize * word0->font->descent;
case 1:
- if ((cmp = frag1->yMin - frag2->yMin) == 0) {
- cmp = frag2->xMax - frag1->xMax;
- }
- break;
+ return xMin - fontSize * word0->font->descent;
case 2:
- if ((cmp = frag2->xMax - frag1->xMax) == 0) {
- cmp = frag2->yMin - frag1->yMin;
- }
- break;
+ return yMin - fontSize * word0->font->descent;
case 3:
- if ((cmp = frag2->yMax - frag1->yMax) == 0) {
- cmp = frag1->xMax - frag2->xMax;
- }
- break;
+ return xMax + fontSize * word0->font->descent;
}
- 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
+// TextParagraph
//------------------------------------------------------------------------
-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() {
+TextParagraph::TextParagraph(GList *linesA) {
TextLine *line;
+ int i;
- 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, double fixedPitch) {
- 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, wordSpacing, delta, priDelta, secDelta;
- TextLine **lineArray;
- GBool found, overlap;
- 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;
- 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);
- wordSpacing = fixedPitch ? fixedPitch : maxWordSpacing * fontSize;
-
- // 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;
- overlap = gFalse;
- for (baseIdx = minBaseIdx;
- !overlap && 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);
- if (delta < minCharSpacing * fontSize) {
- overlap = gTrue;
- break;
- } else {
- if (delta < wordSpacing &&
- (!bestWord1 || word1->primaryCmp(bestWord1) < 0)) {
- bestWordBaseIdx = baseIdx;
- bestWord0 = word0;
- bestWord1 = word1;
- }
- break;
- }
- }
- }
- }
- if (overlap || !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;
- if (fixedPitch) {
- for (i = 0; i < nLines; ++i) {
- line0 = lineArray[i];
- col1 = 0; // make gcc happy
- switch (rot) {
- case 0:
- col1 = (int)((line0->xMin - xMin) / fixedPitch + 0.5);
- break;
- case 1:
- col1 = (int)((line0->yMin - yMin) / fixedPitch + 0.5);
- break;
- case 2:
- col1 = (int)((xMax - line0->xMax) / fixedPitch + 0.5);
- break;
- case 3:
- col1 = (int)((yMax - line0->yMax) / fixedPitch + 0.5);
- break;
- }
- for (k = 0; k <= line0->len; ++k) {
- line0->col[k] += col1;
- }
- if (line0->col[line0->len] > nColumns) {
- nColumns = line0->col[line0->len];
- }
- }
- } else {
- 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;
+ lines = linesA;
+ xMin = yMin = xMax = yMax = 0;
+ for (i = 0; i < lines->getLength(); ++i) {
+ line = (TextLine *)lines->get(i);
+ if (i == 0 || line->xMin < xMin) {
+ xMin = line->xMin;
}
- break;
- case 1:
- if ((cmp = blk1->yMin - blk2->yMin) == 0) {
- cmp = blk2->xMax - blk1->xMax;
+ if (i == 0 || line->yMin < yMin) {
+ yMin = line->yMin;
}
- break;
- case 2:
- if ((cmp = blk2->xMax - blk1->xMax) == 0) {
- cmp = blk2->yMin - blk1->yMin;
+ if (i == 0 || line->xMax > xMax) {
+ xMax = line->xMax;
}
- break;
- case 3:
- if ((cmp = blk2->yMax - blk1->yMax) == 0) {
- cmp = blk1->xMax - blk2->xMax;
+ if (i == 0 || line->yMax > yMax) {
+ yMax = line->yMax;
}
- 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;
+TextParagraph::~TextParagraph() {
+ deleteGList(lines, TextLine);
}
-int TextBlock::primaryCmp(TextBlock *blk) {
- double cmp;
+//------------------------------------------------------------------------
+// TextColumn
+//------------------------------------------------------------------------
- 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;
+TextColumn::TextColumn(GList *paragraphsA, double xMinA, double yMinA,
+ double xMaxA, double yMaxA) {
+ paragraphs = paragraphsA;
+ xMin = xMinA;
+ yMin = yMinA;
+ xMax = xMaxA;
+ yMax = yMaxA;
+ px = py = 0;
+ pw = ph = 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;
+TextColumn::~TextColumn() {
+ deleteGList(paragraphs, TextParagraph);
}
-GBool TextBlock::isBelow(TextBlock *blk) {
- GBool below;
+int TextColumn::cmpX(const void *p1, const void *p2) {
+ const TextColumn *col1 = *(const TextColumn **)p1;
+ const TextColumn *col2 = *(const TextColumn **)p2;
- 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;
+ if (col1->xMin < col2->xMin) {
+ return -1;
+ } else if (col1->xMin > col2->xMin) {
+ return 1;
+ } else {
+ return 0;
}
-
- 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;
-}
+int TextColumn::cmpY(const void *p1, const void *p2) {
+ const TextColumn *col1 = *(const TextColumn **)p1;
+ const TextColumn *col2 = *(const TextColumn **)p2;
-TextFlow::~TextFlow() {
- TextBlock *blk;
-
- while (blocks) {
- blk = blocks;
- blocks = blocks->next;
- delete blk;
- }
-}
-
-void TextFlow::addBlock(TextBlock *blk) {
- if (lastBlk) {
- lastBlk->next = blk;
+ if (col1->yMin < col2->yMin) {
+ return -1;
+ } else if (col1->yMin > col2->yMin) {
+ return 1;
} 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;
+ return 0;
}
}
-GBool TextFlow::blockFits(TextBlock *blk, TextBlock *prevBlk) {
- GBool fits;
+int TextColumn::cmpPX(const void *p1, const void *p2) {
+ const TextColumn *col1 = *(const TextColumn **)p1;
+ const TextColumn *col2 = *(const TextColumn **)p2;
- // 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;
+ if (col1->px < col2->px) {
+ return -1;
+ } else if (col1->px > col2->px) {
+ return 1;
+ } else {
+ return 0;
}
- 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(GList *wordsA) {
+ words = wordsA;
}
TextWordList::~TextWordList() {
- delete words;
+ deleteGList(words, TextWord);
}
int TextWordList::getLength() {
@@ -1831,54 +912,47 @@ TextWord *TextWordList::get(int idx) {
return (TextWord *)words->get(idx);
}
-#endif // TEXTOUT_WORD_LIST
-
//------------------------------------------------------------------------
// TextPage
//------------------------------------------------------------------------
-TextPage::TextPage(GBool rawOrderA) {
- int rot;
-
- rawOrder = rawOrderA;
- curWord = NULL;
+TextPage::TextPage(TextOutputControl *controlA) {
+ control = *controlA;
+ pageWidth = pageHeight = 0;
charPos = 0;
curFont = NULL;
curFontSize = 0;
- nest = 0;
+ curRot = 0;
nTinyChars = 0;
- lastCharOverlap = gFalse;
actualText = NULL;
actualTextLen = 0;
+ actualTextX0 = 0;
+ actualTextY0 = 0;
+ actualTextX1 = 0;
+ actualTextY1 = 0;
actualTextNBytes = 0;
- if (!rawOrder) {
- for (rot = 0; rot < 4; ++rot) {
- pools[rot] = new TextPool();
- }
- }
- flows = NULL;
- blocks = NULL;
- rawWords = NULL;
- rawLastWord = NULL;
+
+ chars = new GList();
fonts = new GList();
- lastFindXMin = lastFindYMin = 0;
- haveLastFind = gFalse;
+
underlines = new GList();
links = new GList();
+
+ findCols = NULL;
+ findLR = gTrue;
+ lastFindXMin = lastFindYMin = 0;
+ haveLastFind = gFalse;
}
TextPage::~TextPage() {
- int rot;
-
clear();
- if (!rawOrder) {
- for (rot = 0; rot < 4; ++rot) {
- delete pools[rot];
- }
- }
- delete fonts;
+ deleteGList(chars, TextChar);
+ deleteGList(fonts, TextFontInfo);
deleteGList(underlines, TextUnderline);
deleteGList(links, TextLink);
+ if (findCols) {
+ deleteGList(findCols, TextColumn);
+ }
}
void TextPage::startPage(GfxState *state) {
@@ -1891,64 +965,33 @@ void TextPage::startPage(GfxState *state) {
}
}
-void TextPage::endPage() {
- if (curWord) {
- endWord();
- }
-}
-
void TextPage::clear() {
- int rot;
- TextFlow *flow;
- TextWord *word;
-
- if (curWord) {
- delete curWord;
- curWord = NULL;
- }
- gfree(actualText);
- 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);
- deleteGList(underlines, TextUnderline);
- deleteGList(links, TextLink);
-
- curWord = NULL;
+ pageWidth = pageHeight = 0;
charPos = 0;
curFont = NULL;
curFontSize = 0;
- nest = 0;
+ curRot = 0;
nTinyChars = 0;
+ gfree(actualText);
actualText = NULL;
actualTextLen = 0;
actualTextNBytes = 0;
- if (!rawOrder) {
- for (rot = 0; rot < 4; ++rot) {
- pools[rot] = new TextPool();
- }
- }
- flows = NULL;
- blocks = NULL;
- rawWords = NULL;
- rawLastWord = NULL;
+ deleteGList(chars, TextChar);
+ chars = new GList();
+ deleteGList(fonts, TextFontInfo);
fonts = new GList();
+ deleteGList(underlines, TextUnderline);
underlines = new GList();
+ deleteGList(links, TextLink);
links = new GList();
+
+ if (findCols) {
+ deleteGList(findCols, TextColumn);
+ findCols = NULL;
+ }
+ findLR = gTrue;
+ lastFindXMin = lastFindYMin = 0;
+ haveLastFind = gFalse;
}
void TextPage::updateFont(GfxState *state) {
@@ -1957,6 +1000,7 @@ void TextPage::updateFont(GfxState *state) {
char *name;
int code, mCode, letterCode, anyCode;
double w;
+ double m[4], m2[4];
int i;
// get the font info object
@@ -2017,55 +1061,36 @@ void TextPage::updateFont(GfxState *state) {
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];
+ if (gfxFont && gfxFont->getType() == fontType3) {
+ fm = gfxFont->getFontMatrix();
+ m2[0] = fm[0] * m[0] + fm[1] * m[2];
+ m2[1] = fm[0] * m[1] + fm[1] * m[3];
+ m2[2] = fm[2] * m[0] + fm[3] * m[2];
+ m2[3] = fm[2] * m[1] + fm[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[0] > 0 || m[3] < 0) ? 0 : 2;
+ curRot = (m[0] > 0 || m[3] < 0) ? 0 : 2;
} else {
- rot = (m[2] > 0) ? 1 : 3;
+ curRot = (m[2] > 0) ? 1 : 3;
}
-
- // for vertical writing mode, the lines are effectively rotated 90
- // degrees
- if (state->getFont()->getWMode()) {
- rot = (rot + 1) & 3;
- }
-
- curWord = new TextWord(state, rot, x0, y0, 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;
+ double x1, y1, x2, y2, w1, h1, dx2, dy2, ascent, descent, sp;
+ double xMin, yMin, xMax, yMax;
+ double clipXMin, clipYMin, clipXMax, clipYMax;
+ GfxRGB rgb;
+ GBool clipped, rtl;
+ int i, j;
// if we're in an ActualText span, save the position info (the
// ActualText chars will be added by TextPage::endActualText()).
@@ -2109,90 +1134,93 @@ void TextPage::addChar(GfxState *state, double x, double y,
}
}
- // break words at space character
+ // skip space characters
if (uLen == 1 && u[0] == (Unicode)0x20) {
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 or 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;
+ // check for clipping
+ clipped = gFalse;
+ if (control.clipText) {
+ state->getClipBBox(&clipXMin, &clipYMin, &clipXMax, &clipYMax);
+ if (x1 + 0.1 * w1 < clipXMin || x1 + 0.9 * w1 > clipXMax ||
+ y1 + 0.1 * h1 < clipYMin || y1 + 0.9 * h1 > clipYMax) {
+ clipped = gTrue;
}
- 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 ||
- curFont != curWord->font ||
- curFontSize != curWord->fontSize) {
- endWord();
- }
- lastCharOverlap = overlap;
- } else {
- lastCharOverlap = gFalse;
}
- if (uLen != 0) {
- // start a new word if needed
- if (!curWord) {
- beginWord(state, x, y);
- }
+ // add the characters
+ if (uLen > 0) {
- // 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;
+ // handle right-to-left ligatures: if there are multiple Unicode
+ // characters, and they're all right-to-left, insert them in
+ // right-to-left order
+ if (uLen > 1) {
+ rtl = gTrue;
+ for (i = 0; i < uLen; ++i) {
+ if (!unicodeTypeR(u[i])) {
+ rtl = gFalse;
+ break;
+ }
+ }
+ } else {
+ rtl = gFalse;
}
- // add the characters to the current word
w1 /= uLen;
h1 /= uLen;
+ ascent = curFont->ascent * curFontSize;
+ descent = curFont->descent * curFontSize;
for (i = 0; i < uLen; ++i) {
- curWord->addChar(state, x1 + i*w1, y1 + i*h1, w1, h1,
- charPos, nBytes, u[i]);
+ x2 = x1 + i * w1;
+ y2 = y1 + i * h1;
+ switch (curRot) {
+ case 0:
+ default:
+ xMin = x2;
+ xMax = x2 + w1;
+ yMin = y2 - ascent;
+ yMax = y2 - descent;
+ break;
+ case 1:
+ xMin = x2 + descent;
+ xMax = x2 + ascent;
+ yMin = y2;
+ yMax = y2 + h1;
+ break;
+ case 2:
+ xMin = x2 + w1;
+ xMax = x2;
+ yMin = y2 + descent;
+ yMax = y2 + ascent;
+ break;
+ case 3:
+ xMin = x2 - ascent;
+ xMax = x2 - descent;
+ yMin = y2 + h1;
+ yMax = y2;
+ break;
+ }
+ if ((state->getRender() & 3) == 1) {
+ state->getStrokeRGB(&rgb);
+ } else {
+ state->getFillRGB(&rgb);
+ }
+ if (rtl) {
+ j = uLen - 1 - i;
+ } else {
+ j = i;
+ }
+ chars->append(new TextChar(u[j], charPos, nBytes, xMin, yMin, xMax, yMax,
+ curRot, clipped,
+ state->getRender() == 3,
+ curFont, curFontSize,
+ colToDbl(rgb.r), colToDbl(rgb.g),
+ colToDbl(rgb.b)));
}
}
+
charPos += nBytes;
}
@@ -2229,901 +1257,2381 @@ void TextPage::endActualText(GfxState *state) {
actualTextNBytes = gFalse;
}
-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;
+void TextPage::addUnderline(double x0, double y0, double x1, double y1) {
+ underlines->append(new TextUnderline(x0, y0, x1, y1));
+}
+
+void TextPage::addLink(double xMin, double yMin, double xMax, double yMax,
+ Link *link) {
+ GString *uri;
+
+ if (link && link->getAction() && link->getAction()->getKind() == actionURI) {
+ uri = ((LinkURI *)link->getAction())->getURI()->copy();
+ links->append(new TextLink(xMin, yMin, xMax, yMax, uri));
+ }
+}
+
+//------------------------------------------------------------------------
+// TextPage: output
+//------------------------------------------------------------------------
+
+void TextPage::write(void *outputStream, TextOutputFunc outputFunc) {
+ UnicodeMap *uMap;
+ char space[8], eol[16], eop[8];
+ int spaceLen, eolLen, eopLen;
+ GBool pageBreaks;
+
+ // 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();
- if (curWord) {
- addWord(curWord);
- curWord = NULL;
+ switch (control.mode) {
+ case textOutReadingOrder:
+ writeReadingOrder(outputStream, outputFunc, uMap, space, spaceLen,
+ eol, eolLen);
+ break;
+ case textOutPhysLayout:
+ case textOutTableLayout:
+ writePhysLayout(outputStream, outputFunc, uMap, space, spaceLen,
+ eol, eolLen);
+ break;
+ case textOutLinePrinter:
+ writeLinePrinter(outputStream, outputFunc, uMap, space, spaceLen,
+ eol, eolLen);
+ break;
+ case textOutRawOrder:
+ writeRaw(outputStream, outputFunc, uMap, space, spaceLen,
+ eol, eolLen);
+ break;
+ }
+
+ // end of page
+ if (pageBreaks) {
+ (*outputFunc)(outputStream, eop, eopLen);
}
+
+ uMap->decRefCnt();
}
-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;
+void TextPage::writeReadingOrder(void *outputStream,
+ TextOutputFunc outputFunc,
+ UnicodeMap *uMap,
+ char *space, int spaceLen,
+ char *eol, int eolLen) {
+ TextBlock *tree;
+ TextColumn *col;
+ TextParagraph *par;
+ TextLine *line;
+ GList *columns;
+ GBool primaryLR;
+ GString *s;
+ int colIdx, parIdx, lineIdx, rot, n;
+
+ rot = rotateChars(chars);
+ primaryLR = checkPrimaryLR(chars);
+ tree = splitChars(chars);
+#if 0 //~debug
+ dumpTree(tree);
+#endif
+ if (!tree) {
+ // no text
+ unrotateChars(chars, rot);
return;
}
+ columns = buildColumns(tree);
+ delete tree;
+ unrotateChars(chars, rot);
+ if (control.html) {
+ rotateUnderlinesAndLinks(rot);
+ generateUnderlinesAndLinks(columns);
+ }
+#if 0 //~debug
+ dumpColumns(columns);
+#endif
- if (rawOrder) {
- if (rawLastWord) {
- rawLastWord->next = word;
- } else {
- rawWords = word;
+ for (colIdx = 0; colIdx < columns->getLength(); ++colIdx) {
+ col = (TextColumn *)columns->get(colIdx);
+ for (parIdx = 0; parIdx < col->paragraphs->getLength(); ++parIdx) {
+ par = (TextParagraph *)col->paragraphs->get(parIdx);
+ for (lineIdx = 0; lineIdx < par->lines->getLength(); ++lineIdx) {
+ line = (TextLine *)par->lines->get(lineIdx);
+ n = line->len;
+ if (line->hyphenated && lineIdx + 1 < par->lines->getLength()) {
+ --n;
+ }
+ s = new GString();
+ encodeFragment(line->text, n, uMap, primaryLR, s);
+ if (lineIdx + 1 < par->lines->getLength() && !line->hyphenated) {
+ s->append(space, spaceLen);
+ }
+ (*outputFunc)(outputStream, s->getCString(), s->getLength());
+ delete s;
+ }
+ (*outputFunc)(outputStream, eol, eolLen);
}
- rawLastWord = word;
- } else {
- pools[word->rot]->addWord(word);
+ (*outputFunc)(outputStream, eol, eolLen);
}
-}
-void TextPage::addUnderline(double x0, double y0, double x1, double y1) {
- underlines->append(new TextUnderline(x0, y0, x1, y1));
+ deleteGList(columns, TextColumn);
}
-void TextPage::addLink(int xMin, int yMin, int xMax, int yMax, Link *link) {
- links->append(new TextLink(xMin, yMin, xMax, yMax, link));
+GList *TextPage::makeColumns() {
+ TextBlock *tree;
+ GList *columns;
+
+ tree = splitChars(chars);
+ if (!tree) {
+ // no text
+ return new GList();
+ }
+ columns = buildColumns(tree);
+ delete tree;
+ if (control.html) {
+ generateUnderlinesAndLinks(columns);
+ }
+ return columns;
}
-void TextPage::coalesce(GBool physLayout, double fixedPitch, GBool doHTML) {
- UnicodeMap *uMap;
- TextPool *pool;
- TextWord *word0, *word1, *word2;
+// This handles both physical layout and table layout modes.
+void TextPage::writePhysLayout(void *outputStream,
+ TextOutputFunc outputFunc,
+ UnicodeMap *uMap,
+ char *space, int spaceLen,
+ char *eol, int eolLen) {
+ TextBlock *tree;
+ GString **out;
+ int *outLen;
+ TextColumn *col;
+ TextParagraph *par;
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;
+ GList *columns;
+ GBool primaryLR;
+ int ph, colIdx, parIdx, lineIdx, rot, y, i;
+
+#if 0 //~debug
+ dumpChars(chars);
+#endif
+ rot = rotateChars(chars);
+ primaryLR = checkPrimaryLR(chars);
+ tree = splitChars(chars);
+#if 0 //~debug
+ dumpTree(tree);
+#endif
+ if (!tree) {
+ // no text
+ unrotateChars(chars, rot);
return;
}
+ columns = buildColumns(tree);
+ delete tree;
+ unrotateChars(chars, rot);
+ if (control.html) {
+ rotateUnderlinesAndLinks(rot);
+ generateUnderlinesAndLinks(columns);
+ }
+ ph = assignPhysLayoutPositions(columns);
+#if 0 //~debug
+ dumpColumns(columns);
+#endif
- uMap = globalParams->getTextEncoding();
- blkList = NULL;
- lastBlk = NULL;
- nBlocks = 0;
- primaryRot = 0;
-
-#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);
+ out = (GString **)gmallocn(ph, sizeof(GString *));
+ outLen = (int *)gmallocn(ph, sizeof(int));
+ for (i = 0; i < ph; ++i) {
+ out[i] = NULL;
+ outLen[i] = 0;
+ }
+
+ columns->sort(&TextColumn::cmpPX);
+ for (colIdx = 0; colIdx < columns->getLength(); ++colIdx) {
+ col = (TextColumn *)columns->get(colIdx);
+ y = col->py;
+ for (parIdx = 0;
+ parIdx < col->paragraphs->getLength() && y < ph;
+ ++parIdx) {
+ par = (TextParagraph *)col->paragraphs->get(parIdx);
+ for (lineIdx = 0;
+ lineIdx < par->lines->getLength() && y < ph;
+ ++lineIdx) {
+ line = (TextLine *)par->lines->get(lineIdx);
+ if (!out[y]) {
+ out[y] = new GString();
}
- printf("'\n");
+ while (outLen[y] < col->px + line->px) {
+ out[y]->append(space, spaceLen);
+ ++outLen[y];
+ }
+ encodeFragment(line->text, line->len, uMap, primaryLR, out[y]);
+ outLen[y] += line->pw;
+ ++y;
+ }
+ if (parIdx + 1 < col->paragraphs->getLength()) {
+ ++y;
}
}
}
- 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);
+ for (i = 0; i < ph; ++i) {
+ if (out[i]) {
+ (*outputFunc)(outputStream, out[i]->getCString(), out[i]->getLength());
+ delete out[i];
+ }
+ (*outputFunc)(outputStream, eol, eolLen);
}
-#endif
- if (doHTML) {
+ gfree(out);
+ gfree(outLen);
- //----- 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;
- }
- }
+ deleteGList(columns, TextColumn);
+}
+
+void TextPage::writeLinePrinter(void *outputStream,
+ TextOutputFunc outputFunc,
+ UnicodeMap *uMap,
+ char *space, int spaceLen,
+ char *eol, int eolLen) {
+ TextChar *ch, *ch2;
+ GList *line;
+ GString *s;
+ char buf[8];
+ double pitch, lineSpacing, delta;
+ double yMin0, yShift, xMin0, xShift;
+ double y, x;
+ int rot, n, i, j, k;
+
+ rot = rotateChars(chars);
+ chars->sort(&TextChar::cmpX);
+ removeDuplicates(chars, 0);
+ chars->sort(&TextChar::cmpY);
+
+ // get character pitch
+ if (control.fixedPitch > 0) {
+ pitch = control.fixedPitch;
+ } else {
+ // compute (approximate) character pitch
+ pitch = pageWidth;
+ for (i = 0; i < chars->getLength(); ++i) {
+ ch = (TextChar *)chars->get(i);
+ for (j = i + 1; j < chars->getLength(); ++j) {
+ ch2 = (TextChar *)chars->get(j);
+ if (ch2->yMin + ascentAdjustFactor * (ch2->yMax - ch2->yMin) <
+ ch->yMax - descentAdjustFactor * (ch->yMax - ch->yMin) &&
+ ch->yMin + ascentAdjustFactor * (ch->yMax - ch->yMin) <
+ ch2->yMax - descentAdjustFactor * (ch2->yMax - ch2->yMin)) {
+ delta = fabs(ch2->xMin - ch->xMin);
+ if (delta > 0 && delta < pitch) {
+ pitch = delta;
}
}
+ }
+ }
+ }
- // 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;
- }
- }
- }
+ // get line spacing
+ if (control.fixedLineSpacing > 0) {
+ lineSpacing = control.fixedLineSpacing;
+ } else {
+ // compute (approximate) line spacing
+ lineSpacing = pageHeight;
+ i = 0;
+ while (i < chars->getLength()) {
+ ch = (TextChar *)chars->get(i);
+ // look for the first char that does not (substantially)
+ // vertically overlap this one
+ delta = 0;
+ for (++i; delta == 0 && i < chars->getLength(); ++i) {
+ ch2 = (TextChar *)chars->get(i);
+ if (ch2->yMin + ascentAdjustFactor * (ch2->yMax - ch2->yMin) >
+ ch->yMax - descentAdjustFactor * (ch->yMax - ch->yMin)) {
+ delta = ch2->yMin - ch->yMin;
}
+ }
+ if (delta > 0 && delta < lineSpacing) {
+ lineSpacing = delta;
+ }
+ }
+ }
+
+ // shift the grid to avoid problems with floating point accuracy --
+ // for fixed line spacing, this avoids problems with
+ // dropping/inserting blank lines
+ if (chars->getLength()) {
+ yMin0 = ((TextChar *)chars->get(0))->yMin;
+ yShift = yMin0 - (int)(yMin0 / lineSpacing + 0.5) * lineSpacing
+ - 0.5 * lineSpacing;
+ } else {
+ yShift = 0;
+ }
+
+ // for each line...
+ i = 0;
+ j = chars->getLength() - 1;
+ for (y = yShift; y < pageHeight; y += lineSpacing) {
+
+ // get the characters in this line
+ line = new GList;
+ while (i < chars->getLength() &&
+ ((TextChar *)chars->get(i))->yMin < y + lineSpacing) {
+ line->append(chars->get(i++));
+ }
+ line->sort(&TextChar::cmpX);
+
+ // shift the grid to avoid problems with floating point accuracy
+ // -- for fixed char spacing, this avoids problems with
+ // dropping/inserting spaces
+ if (line->getLength()) {
+ xMin0 = ((TextChar *)line->get(0))->xMin;
+ xShift = xMin0 - (int)(xMin0 / pitch + 0.5) * pitch - 0.5 * pitch;
+ } else {
+ xShift = 0;
+ }
+
+ // write the line
+ s = new GString();
+ x = xShift;
+ k = 0;
+ while (k < line->getLength()) {
+ ch = (TextChar *)line->get(k);
+ if (ch->xMin < x + pitch) {
+ n = uMap->mapUnicode(ch->c, buf, sizeof(buf));
+ s->append(buf, n);
+ ++k;
} 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;
- }
- }
+ s->append(space, spaceLen);
+ n = spaceLen;
+ }
+ x += (uMap->isUnicode() ? 1 : n) * pitch;
+ }
+ s->append(eol, eolLen);
+ (*outputFunc)(outputStream, s->getCString(), s->getLength());
+ delete s;
+ delete line;
+ }
+
+ unrotateChars(chars, rot);
+}
+
+void TextPage::writeRaw(void *outputStream,
+ TextOutputFunc outputFunc,
+ UnicodeMap *uMap,
+ char *space, int spaceLen,
+ char *eol, int eolLen) {
+ TextChar *ch, *ch2;
+ GString *s;
+ char buf[8];
+ int n, i;
+
+ s = new GString();
+
+ for (i = 0; i < chars->getLength(); ++i) {
+
+ // process one char
+ ch = (TextChar *)chars->get(i);
+ n = uMap->mapUnicode(ch->c, buf, sizeof(buf));
+ s->append(buf, n);
+
+ // check for space or eol
+ if (i+1 < chars->getLength()) {
+ ch2 = (TextChar *)chars->get(i+1);
+ if (ch2->rot != ch->rot) {
+ s->append(eol, eolLen);
+ } else {
+ switch (ch->rot) {
+ case 0:
+ default:
+ if (fabs(ch2->yMin - ch->yMin) > rawModeLineDelta * ch->fontSize ||
+ ch2->xMin - ch->xMax < -rawModeCharOverlap * ch->fontSize) {
+ s->append(eol, eolLen);
+ } else if (ch2->xMin - ch->xMax >
+ rawModeWordSpacing * ch->fontSize) {
+ s->append(space, spaceLen);
+ }
+ break;
+ case 1:
+ if (fabs(ch->xMax - ch2->xMax) > rawModeLineDelta * ch->fontSize ||
+ ch2->yMin - ch->yMax < -rawModeCharOverlap * ch->fontSize) {
+ s->append(eol, eolLen);
+ } else if (ch2->yMin - ch->yMax >
+ rawModeWordSpacing * ch->fontSize) {
+ s->append(space, spaceLen);
+ }
+ break;
+ case 2:
+ if (fabs(ch->yMax - ch2->yMax) > rawModeLineDelta * ch->fontSize ||
+ ch->xMin - ch2->xMax < -rawModeCharOverlap * ch->fontSize) {
+ s->append(eol, eolLen);
+ } else if (ch->xMin - ch2->xMax >
+ rawModeWordSpacing * ch->fontSize) {
+ s->append(space, spaceLen);
+ }
+ break;
+ case 3:
+ if (fabs(ch2->xMin - ch->xMin) > rawModeLineDelta * ch->fontSize ||
+ ch->yMin - ch2->yMax < -rawModeCharOverlap * ch->fontSize) {
+ s->append(eol, eolLen);
+ } else if (ch->yMin - ch2->yMax >
+ rawModeWordSpacing * ch->fontSize) {
+ s->append(space, spaceLen);
}
+ break;
}
+ }
+ } else {
+ s->append(eol, eolLen);
+ }
- // 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;
- }
- }
+ if (s->getLength() > 1000) {
+ (*outputFunc)(outputStream, s->getCString(), s->getLength());
+ s->clear();
+ }
+ }
+
+ if (s->getLength() > 0) {
+ (*outputFunc)(outputStream, s->getCString(), s->getLength());
+ }
+ delete s;
+}
+
+void TextPage::encodeFragment(Unicode *text, int len, UnicodeMap *uMap,
+ GBool primaryLR, GString *s) {
+ char lre[8], rle[8], popdf[8], buf[8];
+ int lreLen, rleLen, popdfLen, n;
+ int i, j, k;
+
+ 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);
+ }
+ i = j;
+ // output a right-to-left section
+ for (j = i;
+ j < len && !(unicodeTypeL(text[j]) || unicodeTypeNum(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);
+ }
+ s->append(popdf, popdfLen);
+ i = j;
+ }
+ }
+
+ } else {
+
+ // Note: This code treats numeric characters (European and
+ // Arabic/Indic) as left-to-right, which isn't strictly correct
+ // (incurs extra LRE/POPDF pairs), but does produce correct
+ // visual formatting.
+ s->append(rle, rleLen);
+ i = len - 1;
+ while (i >= 0) {
+ // output a right-to-left section
+ for (j = i;
+ j >= 0 && !(unicodeTypeL(text[j]) || unicodeTypeNum(text[j]));
+ --j) ;
+ for (k = i; k > j; --k) {
+ n = uMap->mapUnicode(text[k], buf, sizeof(buf));
+ s->append(buf, n);
+ }
+ 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);
}
+ 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);
+ }
+ }
+}
+
+//------------------------------------------------------------------------
+// TextPage: layout analysis
+//------------------------------------------------------------------------
+
+// Determine primary (most common) rotation value. Rotate all chars
+// to that primary rotation.
+int TextPage::rotateChars(GList *charsA) {
+ TextChar *ch;
+ int nChars[4];
+ double xMin, yMin, xMax, yMax, t;
+ int rot, i;
+
+ // determine primary rotation
+ nChars[0] = nChars[1] = nChars[2] = nChars[3] = 0;
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ ++nChars[ch->rot];
+ }
+ rot = 0;
+ for (i = 1; i < 4; ++i) {
+ if (nChars[i] > nChars[rot]) {
+ rot = i;
+ }
+ }
+
+ // rotate
+ switch (rot) {
+ case 0:
+ default:
+ break;
+ case 1:
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ xMin = ch->yMin;
+ xMax = ch->yMax;
+ yMin = pageWidth - ch->xMax;
+ yMax = pageWidth - ch->xMin;
+ ch->xMin = xMin;
+ ch->xMax = xMax;
+ ch->yMin = yMin;
+ ch->yMax = yMax;
+ ch->rot = (ch->rot + 3) & 3;
+ }
+ t = pageWidth;
+ pageWidth = pageHeight;
+ pageHeight = t;
+ break;
+ case 2:
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ xMin = pageWidth - ch->xMax;
+ xMax = pageWidth - ch->xMin;
+ yMin = pageHeight - ch->yMax;
+ yMax = pageHeight - ch->yMin;
+ ch->xMin = xMin;
+ ch->xMax = xMax;
+ ch->yMin = yMin;
+ ch->yMax = yMax;
+ ch->rot = (ch->rot + 2) & 3;
}
+ break;
+ case 3:
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ xMin = pageHeight - ch->yMax;
+ xMax = pageHeight - ch->yMin;
+ yMin = ch->xMin;
+ yMax = ch->xMax;
+ ch->xMin = xMin;
+ ch->xMax = xMax;
+ ch->yMin = yMin;
+ ch->yMax = yMax;
+ ch->rot = (ch->rot + 1) & 3;
+ }
+ t = pageWidth;
+ pageWidth = pageHeight;
+ pageHeight = t;
+ break;
+ }
+
+ return rot;
+}
+
+// Rotate the TextUnderlines and TextLinks to match the transform
+// performed by rotateChars().
+void TextPage::rotateUnderlinesAndLinks(int rot) {
+ TextUnderline *underline;
+ TextLink *link;
+ double xMin, yMin, xMax, yMax;
+ int i;
- //----- handle links
+ switch (rot) {
+ case 0:
+ default:
+ break;
+ case 1:
+ for (i = 0; i < underlines->getLength(); ++i) {
+ underline = (TextUnderline *)underlines->get(i);
+ xMin = underline->y0;
+ xMax = underline->y1;
+ yMin = pageWidth - underline->x1;
+ yMax = pageWidth - underline->x0;
+ underline->x0 = xMin;
+ underline->x1 = xMax;
+ underline->y0 = yMin;
+ underline->y1 = yMax;
+ underline->horiz = !underline->horiz;
+ }
for (i = 0; i < links->getLength(); ++i) {
link = (TextLink *)links->get(i);
+ xMin = link->yMin;
+ xMax = link->yMax;
+ yMin = pageWidth - link->xMax;
+ yMax = pageWidth - link->xMin;
+ link->xMin = xMin;
+ link->xMax = xMax;
+ link->yMin = yMin;
+ link->yMax = yMax;
+ }
+ break;
+ case 2:
+ for (i = 0; i < underlines->getLength(); ++i) {
+ underline = (TextUnderline *)underlines->get(i);
+ xMin = pageWidth - underline->x1;
+ xMax = pageWidth - underline->x0;
+ yMin = pageHeight - underline->y1;
+ yMax = pageHeight - underline->y0;
+ underline->x0 = xMin;
+ underline->x1 = xMax;
+ underline->y0 = yMin;
+ underline->y1 = yMax;
+ }
+ for (i = 0; i < links->getLength(); ++i) {
+ link = (TextLink *)links->get(i);
+ xMin = pageWidth - link->xMax;
+ xMax = pageWidth - link->xMin;
+ yMin = pageHeight - link->yMax;
+ yMax = pageHeight - link->yMin;
+ link->xMin = xMin;
+ link->xMax = xMax;
+ link->yMin = yMin;
+ link->yMax = yMax;
+ }
+ break;
+ case 3:
+ for (i = 0; i < underlines->getLength(); ++i) {
+ underline = (TextUnderline *)underlines->get(i);
+ xMin = pageHeight - underline->y1;
+ xMax = pageHeight - underline->y0;
+ yMin = underline->x0;
+ yMax = underline->x1;
+ underline->x0 = xMin;
+ underline->x1 = xMax;
+ underline->y0 = yMin;
+ underline->y1 = yMax;
+ underline->horiz = !underline->horiz;
+ }
+ for (i = 0; i < links->getLength(); ++i) {
+ link = (TextLink *)links->get(i);
+ xMin = pageHeight - link->yMax;
+ xMax = pageHeight - link->yMin;
+ yMin = link->xMin;
+ yMax = link->xMax;
+ link->xMin = xMin;
+ link->xMax = xMax;
+ link->yMin = yMin;
+ link->yMax = yMax;
+ }
+ break;
+ }
+}
- // 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;
- }
+// Undo the coordinate transform performed by rotateChars().
+void TextPage::unrotateChars(GList *charsA, int rot) {
+ TextChar *ch;
+ double xMin, yMin, xMax, yMax, t;
+ int i;
+
+ switch (rot) {
+ case 0:
+ default:
+ // no transform
+ break;
+ case 1:
+ t = pageWidth;
+ pageWidth = pageHeight;
+ pageHeight = t;
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ xMin = pageWidth - ch->yMax;
+ xMax = pageWidth - ch->yMin;
+ yMin = ch->xMin;
+ yMax = ch->xMax;
+ ch->xMin = xMin;
+ ch->xMax = xMax;
+ ch->yMin = yMin;
+ ch->yMax = yMax;
+ ch->rot = (ch->rot + 1) & 3;
+ }
+ break;
+ case 2:
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ xMin = pageWidth - ch->xMax;
+ xMax = pageWidth - ch->xMin;
+ yMin = pageHeight - ch->yMax;
+ yMax = pageHeight - ch->yMin;
+ ch->xMin = xMin;
+ ch->xMax = xMax;
+ ch->yMin = yMin;
+ ch->yMax = yMax;
+ ch->rot = (ch->rot + 2) & 3;
+ }
+ break;
+ case 3:
+ t = pageWidth;
+ pageWidth = pageHeight;
+ pageHeight = t;
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ xMin = ch->yMin;
+ xMax = ch->yMax;
+ yMin = pageHeight - ch->xMax;
+ yMax = pageHeight - ch->xMin;
+ ch->xMin = xMin;
+ ch->xMax = xMax;
+ ch->yMin = yMin;
+ ch->yMax = yMax;
+ ch->rot = (ch->rot + 3) & 3;
+ }
+ break;
+ }
+}
+
+// Undo the coordinate transform performed by rotateChars().
+void TextPage::unrotateColumns(GList *columns, int rot) {
+ TextColumn *col;
+ TextParagraph *par;
+ TextLine *line;
+ TextWord *word;
+ double xMin, yMin, xMax, yMax, t;
+ int colIdx, parIdx, lineIdx, wordIdx, i;
+
+ switch (rot) {
+ case 0:
+ default:
+ // no transform
+ break;
+ case 1:
+ t = pageWidth;
+ pageWidth = pageHeight;
+ pageHeight = t;
+ for (colIdx = 0; colIdx < columns->getLength(); ++colIdx) {
+ col = (TextColumn *)columns->get(colIdx);
+ xMin = pageWidth - col->yMax;
+ xMax = pageWidth - col->yMin;
+ yMin = col->xMin;
+ yMax = col->xMax;
+ col->xMin = xMin;
+ col->xMax = xMax;
+ col->yMin = yMin;
+ col->yMax = yMax;
+ for (parIdx = 0;
+ parIdx < col->paragraphs->getLength();
+ ++parIdx) {
+ par = (TextParagraph *)col->paragraphs->get(parIdx);
+ xMin = pageWidth - par->yMax;
+ xMax = pageWidth - par->yMin;
+ yMin = par->xMin;
+ yMax = par->xMax;
+ par->xMin = xMin;
+ par->xMax = xMax;
+ par->yMin = yMin;
+ par->yMax = yMax;
+ for (lineIdx = 0;
+ lineIdx < par->lines->getLength();
+ ++lineIdx) {
+ line = (TextLine *)par->lines->get(lineIdx);
+ xMin = pageWidth - line->yMax;
+ xMax = pageWidth - line->yMin;
+ yMin = line->xMin;
+ yMax = line->xMax;
+ line->xMin = xMin;
+ line->xMax = xMax;
+ line->yMin = yMin;
+ line->yMax = yMax;
+ line->rot = (line->rot + 1) & 3;
+ for (wordIdx = 0; wordIdx < line->words->getLength(); ++wordIdx) {
+ word = (TextWord *)line->words->get(wordIdx);
+ xMin = pageWidth - word->yMax;
+ xMax = pageWidth - word->yMin;
+ yMin = word->xMin;
+ yMax = word->xMax;
+ word->xMin = xMin;
+ word->xMax = xMax;
+ word->yMin = yMin;
+ word->yMax = yMax;
+ word->rot = (word->rot + 1) & 3;
}
}
}
-
- // 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;
+ }
+ break;
+ case 2:
+ for (colIdx = 0; colIdx < columns->getLength(); ++colIdx) {
+ col = (TextColumn *)columns->get(colIdx);
+ xMin = pageWidth - col->xMax;
+ xMax = pageWidth - col->xMin;
+ yMin = pageHeight - col->yMax;
+ yMax = pageHeight - col->yMin;
+ col->xMin = xMin;
+ col->xMax = xMax;
+ col->yMin = yMin;
+ col->yMax = yMax;
+ for (parIdx = 0;
+ parIdx < col->paragraphs->getLength();
+ ++parIdx) {
+ par = (TextParagraph *)col->paragraphs->get(parIdx);
+ xMin = pageWidth - par->xMax;
+ xMax = pageWidth - par->xMin;
+ yMin = pageHeight - par->yMax;
+ yMax = pageHeight - par->yMin;
+ par->xMin = xMin;
+ par->xMax = xMax;
+ par->yMin = yMin;
+ par->yMax = yMax;
+ for (lineIdx = 0;
+ lineIdx < par->lines->getLength();
+ ++lineIdx) {
+ line = (TextLine *)par->lines->get(lineIdx);
+ xMin = pageWidth - line->xMax;
+ xMax = pageWidth - line->xMin;
+ yMin = pageHeight - line->yMax;
+ yMax = pageHeight - line->yMin;
+ line->xMin = xMin;
+ line->xMax = xMax;
+ line->yMin = yMin;
+ line->yMax = yMax;
+ line->rot = (line->rot + 2) & 3;
+ for (i = 0; i <= line->len; ++i) {
+ line->edge[i] = pageWidth - line->edge[i];
+ }
+ for (wordIdx = 0; wordIdx < line->words->getLength(); ++wordIdx) {
+ word = (TextWord *)line->words->get(wordIdx);
+ xMin = pageWidth - word->xMax;
+ xMax = pageWidth - word->xMin;
+ yMin = pageHeight - word->yMax;
+ yMax = pageHeight - word->yMin;
+ word->xMin = xMin;
+ word->xMax = xMax;
+ word->yMin = yMin;
+ word->yMax = yMax;
+ word->rot = (word->rot + 2) & 3;
+ for (i = 0; i <= word->len; ++i) {
+ word->edge[i] = pageWidth - word->edge[i];
}
}
}
}
-
- // 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;
+ }
+ break;
+ case 3:
+ t = pageWidth;
+ pageWidth = pageHeight;
+ pageHeight = t;
+ for (colIdx = 0; colIdx < columns->getLength(); ++colIdx) {
+ col = (TextColumn *)columns->get(colIdx);
+ xMin = col->yMin;
+ xMax = col->yMax;
+ yMin = pageHeight - col->xMax;
+ yMax = pageHeight - col->xMin;
+ col->xMin = xMin;
+ col->xMax = xMax;
+ col->yMin = yMin;
+ col->yMax = yMax;
+ for (parIdx = 0;
+ parIdx < col->paragraphs->getLength();
+ ++parIdx) {
+ par = (TextParagraph *)col->paragraphs->get(parIdx);
+ xMin = par->yMin;
+ xMax = par->yMax;
+ yMin = pageHeight - par->xMax;
+ yMax = pageHeight - par->xMin;
+ par->xMin = xMin;
+ par->xMax = xMax;
+ par->yMin = yMin;
+ par->yMax = yMax;
+ for (lineIdx = 0;
+ lineIdx < par->lines->getLength();
+ ++lineIdx) {
+ line = (TextLine *)par->lines->get(lineIdx);
+ xMin = line->yMin;
+ xMax = line->yMax;
+ yMin = pageHeight - line->xMax;
+ yMax = pageHeight - line->xMin;
+ line->xMin = xMin;
+ line->xMax = xMax;
+ line->yMin = yMin;
+ line->yMax = yMax;
+ line->rot = (line->rot + 3) & 3;
+ for (i = 0; i <= line->len; ++i) {
+ line->edge[i] = pageHeight - line->edge[i];
+ }
+ for (wordIdx = 0; wordIdx < line->words->getLength(); ++wordIdx) {
+ word = (TextWord *)line->words->get(wordIdx);
+ xMin = word->yMin;
+ xMax = word->yMax;
+ yMin = pageHeight - word->xMax;
+ yMax = pageHeight - word->xMin;
+ word->xMin = xMin;
+ word->xMax = xMax;
+ word->yMin = yMin;
+ word->yMax = yMax;
+ word->rot = (word->rot + 3) & 3;
+ for (i = 0; i <= word->len; ++i) {
+ word->edge[i] = pageHeight - word->edge[i];
}
}
}
}
+ }
+ break;
+ }
+}
- // 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;
- }
- }
- }
+void TextPage::unrotateWords(GList *words, int rot) {
+ TextWord *word;
+ double xMin, yMin, xMax, yMax;
+ int i, j;
+
+ switch (rot) {
+ case 0:
+ default:
+ // no transform
+ break;
+ case 1:
+ for (i = 0; i < words->getLength(); ++i) {
+ word = (TextWord *)words->get(i);
+ xMin = pageWidth - word->yMax;
+ xMax = pageWidth - word->yMin;
+ yMin = word->xMin;
+ yMax = word->xMax;
+ word->xMin = xMin;
+ word->xMax = xMax;
+ word->yMin = yMin;
+ word->yMax = yMax;
+ word->rot = (word->rot + 1) & 3;
+ }
+ break;
+ case 2:
+ for (i = 0; i < words->getLength(); ++i) {
+ word = (TextWord *)words->get(i);
+ xMin = pageWidth - word->xMax;
+ xMax = pageWidth - word->xMin;
+ yMin = pageHeight - word->yMax;
+ yMax = pageHeight - word->yMin;
+ word->xMin = xMin;
+ word->xMax = xMax;
+ word->yMin = yMin;
+ word->yMax = yMax;
+ word->rot = (word->rot + 2) & 3;
+ for (j = 0; j <= word->len; ++j) {
+ word->edge[j] = pageWidth - word->edge[j];
}
}
+ break;
+ case 3:
+ for (i = 0; i < words->getLength(); ++i) {
+ word = (TextWord *)words->get(i);
+ xMin = word->yMin;
+ xMax = word->yMax;
+ yMin = pageHeight - word->xMax;
+ yMax = pageHeight - word->xMin;
+ word->xMin = xMin;
+ word->xMax = xMax;
+ word->yMin = yMin;
+ word->yMax = yMax;
+ word->rot = (word->rot + 3) & 3;
+ for (j = 0; j <= word->len; ++j) {
+ word->edge[j] = pageHeight - word->edge[j];
+ }
+ }
+ break;
}
+}
- //----- assemble the blocks
+// Determine the primary text direction (LR vs RL). Returns true for
+// LR, false for RL.
+GBool TextPage::checkPrimaryLR(GList *charsA) {
+ TextChar *ch;
+ int i, lrCount;
- //~ add an outer loop for writing mode (vertical text)
+ lrCount = 0;
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ if (unicodeTypeL(ch->c)) {
+ ++lrCount;
+ } else if (unicodeTypeR(ch->c)) {
+ --lrCount;
+ }
+ }
+ return lrCount >= 0;
+}
- // 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;
- }
+// Remove duplicate characters. The list of chars has been sorted --
+// by x for rot=0,2; by y for rot=1,3.
+void TextPage::removeDuplicates(GList *charsA, int rot) {
+ TextChar *ch, *ch2;
+ double xDelta, yDelta;
+ int i, j;
- // 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 (rot & 1) {
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ xDelta = dupMaxSecDelta * ch->fontSize;
+ yDelta = dupMaxPriDelta * ch->fontSize;
+ j = i + 1;
+ while (j < charsA->getLength()) {
+ ch2 = (TextChar *)charsA->get(j);
+ if (ch2->yMin - ch->yMin >= yDelta) {
+ break;
}
- if (pool->getPool(baseIdx)->primaryCmp(pool->getPool(startBaseIdx))
- < 0) {
- startBaseIdx = baseIdx;
+ if (ch2->c == ch->c &&
+ fabs(ch2->xMin - ch->xMin) < xDelta &&
+ fabs(ch2->xMax - ch->xMax) < xDelta &&
+ fabs(ch2->yMax - ch->yMax) < yDelta) {
+ charsA->del(j);
+ } else {
+ ++j;
}
}
-
- // 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;
- }
- }
+ }
+ } else {
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ xDelta = dupMaxPriDelta * ch->fontSize;
+ yDelta = dupMaxSecDelta * ch->fontSize;
+ j = i + 1;
+ while (j < charsA->getLength()) {
+ ch2 = (TextChar *)charsA->get(j);
+ if (ch2->xMin - ch->xMin >= xDelta) {
+ break;
}
- 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;
- }
- }
+ if (ch2->c == ch->c &&
+ fabs(ch2->xMax - ch->xMax) < xDelta &&
+ fabs(ch2->yMin - ch->yMin) < yDelta &&
+ fabs(ch2->yMax - ch->yMax) < yDelta) {
+ charsA->del(j);
+ } else {
+ ++j;
}
+ }
+ }
+ }
+}
- // only check for outlying words (the next two chunks of code)
- // if we didn't find anything else
- if (found) {
- continue;
- }
+// Split the characters into trees of TextBlocks, one tree for each
+// rotation. Merge into a single tree (with the primary rotation).
+TextBlock *TextPage::splitChars(GList *charsA) {
+ TextBlock *tree[4];
+ TextBlock *blk;
+ GList *chars2, *clippedChars;
+ TextChar *ch;
+ int rot, i;
- // 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;
- }
- }
+ // split: build a tree of TextBlocks for each rotation
+ clippedChars = new GList();
+ for (rot = 0; rot < 4; ++rot) {
+ chars2 = new GList();
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ if (ch->rot == rot) {
+ chars2->append(ch);
+ }
+ }
+ tree[rot] = NULL;
+ if (chars2->getLength() > 0) {
+ chars2->sort((rot & 1) ? &TextChar::cmpY : &TextChar::cmpX);
+ removeDuplicates(chars2, rot);
+ if (control.clipText) {
+ i = 0;
+ while (i < chars2->getLength()) {
+ ch = (TextChar *)chars2->get(i);
+ if (ch->clipped) {
+ ch = (TextChar *)chars2->del(i);
+ clippedChars->append(ch);
+ } else {
+ ++i;
}
}
+ }
+ if (chars2->getLength() > 0) {
+ tree[rot] = split(chars2, rot);
+ }
+ }
+ delete chars2;
+ }
- // 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 the page contains no (unclipped) text, just leave an empty
+ // column list
+ if (!tree[0]) {
+ delete clippedChars;
+ return NULL;
+ }
+
+ // if the main tree is not a multicolumn node, insert one so that
+ // rotated text has somewhere to go
+ if (tree[0]->tag != blkTagMulticolumn) {
+ blk = new TextBlock(blkHorizSplit, 0);
+ blk->addChild(tree[0]);
+ blk->tag = blkTagMulticolumn;
+ tree[0] = blk;
+ }
+
+ // merge non-primary-rotation text into the primary-rotation tree
+ for (rot = 1; rot < 4; ++rot) {
+ if (tree[rot]) {
+ insertIntoTree(tree[rot], tree[0]);
+ tree[rot] = NULL;
+ }
+ }
+
+ if (clippedChars->getLength()) {
+ insertClippedChars(clippedChars, tree[0]);
+ }
+ delete clippedChars;
+
+#if 0 //~debug
+ dumpTree(tree[0]);
+#endif
+
+ return tree[0];
+}
+
+// Generate a tree of TextBlocks, marked as columns, lines, and words.
+TextBlock *TextPage::split(GList *charsA, int rot) {
+ TextBlock *blk;
+ GList *chars2, *chars3;
+ int *horizProfile, *vertProfile;
+ double xMin, yMin, xMax, yMax;
+ int xMinI, yMinI, xMaxI, yMaxI;
+ int xMinI2, yMinI2, xMaxI2, yMaxI2;
+ TextChar *ch;
+ double minFontSize, avgFontSize, splitPrecision;
+ double nLines, vertGapThreshold, ascentAdjust, descentAdjust, minChunk;
+ int horizGapSize, vertGapSize;
+ double horizGapSize2, vertGapSize2;
+ int minHorizChunkWidth, minVertChunkWidth, nHorizGaps, nVertGaps;
+ double largeCharSize;
+ int nLargeChars;
+ GBool doHorizSplit, doVertSplit, smallSplit;
+ int i, x, y, prev, start;
+
+ //----- compute bbox, min font size, average font size, and
+ // split precision for this block
+
+ xMin = yMin = xMax = yMax = 0; // make gcc happy
+ minFontSize = avgFontSize = 0;
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ if (i == 0 || ch->xMin < xMin) {
+ xMin = ch->xMin;
+ }
+ if (i == 0 || ch->yMin < yMin) {
+ yMin = ch->yMin;
+ }
+ if (i == 0 || ch->xMax > xMax) {
+ xMax = ch->xMax;
+ }
+ if (i == 0 || ch->yMax > yMax) {
+ yMax = ch->yMax;
+ }
+ avgFontSize += ch->fontSize;
+ if (i == 0 || ch->fontSize < minFontSize) {
+ minFontSize = ch->fontSize;
+ }
+ }
+ avgFontSize /= charsA->getLength();
+ splitPrecision = splitPrecisionMul * minFontSize;
+ if (splitPrecision < minSplitPrecision) {
+ splitPrecision = minSplitPrecision;
+ }
+
+ //----- compute the horizontal and vertical profiles
+
+ // add some slack to the array bounds to avoid floating point
+ // precision problems
+ xMinI = (int)floor(xMin / splitPrecision) - 1;
+ yMinI = (int)floor(yMin / splitPrecision) - 1;
+ xMaxI = (int)floor(xMax / splitPrecision) + 1;
+ yMaxI = (int)floor(yMax / splitPrecision) + 1;
+ horizProfile = (int *)gmallocn(yMaxI - yMinI + 1, sizeof(int));
+ vertProfile = (int *)gmallocn(xMaxI - xMinI + 1, sizeof(int));
+ memset(horizProfile, 0, (yMaxI - yMinI + 1) * sizeof(int));
+ memset(vertProfile, 0, (xMaxI - xMinI + 1) * sizeof(int));
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ // yMinI2 and yMaxI2 are adjusted to allow for slightly overlapping lines
+ switch (rot) {
+ case 0:
+ default:
+ xMinI2 = (int)floor(ch->xMin / splitPrecision);
+ xMaxI2 = (int)floor(ch->xMax / splitPrecision);
+ ascentAdjust = ascentAdjustFactor * (ch->yMax - ch->yMin);
+ yMinI2 = (int)floor((ch->yMin + ascentAdjust) / splitPrecision);
+ descentAdjust = descentAdjustFactor * (ch->yMax - ch->yMin);
+ yMaxI2 = (int)floor((ch->yMax - descentAdjust) / splitPrecision);
+ break;
+ case 1:
+ descentAdjust = descentAdjustFactor * (ch->xMax - ch->xMin);
+ xMinI2 = (int)floor((ch->xMin + descentAdjust) / splitPrecision);
+ ascentAdjust = ascentAdjustFactor * (ch->xMax - ch->xMin);
+ xMaxI2 = (int)floor((ch->xMax - ascentAdjust) / splitPrecision);
+ yMinI2 = (int)floor(ch->yMin / splitPrecision);
+ yMaxI2 = (int)floor(ch->yMax / splitPrecision);
+ break;
+ case 2:
+ xMinI2 = (int)floor(ch->xMin / splitPrecision);
+ xMaxI2 = (int)floor(ch->xMax / splitPrecision);
+ descentAdjust = descentAdjustFactor * (ch->yMax - ch->yMin);
+ yMinI2 = (int)floor((ch->yMin + descentAdjust) / splitPrecision);
+ ascentAdjust = ascentAdjustFactor * (ch->yMax - ch->yMin);
+ yMaxI2 = (int)floor((ch->yMax - ascentAdjust) / splitPrecision);
+ break;
+ case 3:
+ ascentAdjust = ascentAdjustFactor * (ch->xMax - ch->xMin);
+ xMinI2 = (int)floor((ch->xMin + ascentAdjust) / splitPrecision);
+ descentAdjust = descentAdjustFactor * (ch->xMax - ch->xMin);
+ xMaxI2 = (int)floor((ch->xMax - descentAdjust) / splitPrecision);
+ yMinI2 = (int)floor(ch->yMin / splitPrecision);
+ yMaxI2 = (int)floor(ch->yMax / splitPrecision);
+ break;
+ }
+ for (y = yMinI2; y <= yMaxI2; ++y) {
+ ++horizProfile[y - yMinI];
+ }
+ for (x = xMinI2; x <= xMaxI2; ++x) {
+ ++vertProfile[x - xMinI];
+ }
+ }
+
+ //----- find the largest gaps in the horizontal and vertical profiles
+
+ horizGapSize = 0;
+ for (start = yMinI; start < yMaxI && !horizProfile[start - yMinI]; ++start) ;
+ for (y = start; y < yMaxI; ++y) {
+ if (horizProfile[y - yMinI] && !horizProfile[y + 1 - yMinI]) {
+ start = y;
+ } else if (!horizProfile[y - yMinI] && horizProfile[y + 1 - yMinI]) {
+ if (y - start > horizGapSize) {
+ horizGapSize = y - start;
+ }
+ }
+ }
+ vertGapSize = 0;
+ for (start = xMinI; start < xMaxI && !vertProfile[start - xMinI]; ++start) ;
+ for (x = start; x < xMaxI; ++x) {
+ if (vertProfile[x - xMinI] && !vertProfile[x + 1 - xMinI]) {
+ start = x;
+ } else if (!vertProfile[x - xMinI] && vertProfile[x + 1 - xMinI]) {
+ if (x - start > vertGapSize) {
+ vertGapSize = x - start;
+ }
+ }
+ }
+ horizGapSize2 = horizGapSize - splitGapSlack * avgFontSize / splitPrecision;
+ if (horizGapSize2 < 0.99) {
+ horizGapSize2 = 0.99;
+ }
+ vertGapSize2 = vertGapSize - splitGapSlack * avgFontSize / splitPrecision;
+ if (vertGapSize2 < 0.99) {
+ vertGapSize2 = 0.99;
+ }
+
+ //----- count horiz/vert gaps equivalent to largest gaps
+
+ minHorizChunkWidth = yMaxI - yMinI;
+ nHorizGaps = 0;
+ for (start = yMinI; start < yMaxI && !horizProfile[start - yMinI]; ++start) ;
+ prev = start - 1;
+ for (y = start; y < yMaxI; ++y) {
+ if (horizProfile[y - yMinI] && !horizProfile[y + 1 - yMinI]) {
+ start = y;
+ } else if (!horizProfile[y - yMinI] && horizProfile[y + 1 - yMinI]) {
+ if (y - start > horizGapSize2) {
+ ++nHorizGaps;
+ if (start - prev < minHorizChunkWidth) {
+ minHorizChunkWidth = start - prev;
}
- 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;
- }
- }
- }
+ prev = y;
+ }
+ }
+ }
+ minVertChunkWidth = xMaxI - xMinI;
+ nVertGaps = 0;
+ for (start = xMinI; start < xMaxI && !vertProfile[start - xMinI]; ++start) ;
+ prev = start - 1;
+ for (x = start; x < xMaxI; ++x) {
+ if (vertProfile[x - xMinI] && !vertProfile[x + 1 - xMinI]) {
+ start = x;
+ } else if (!vertProfile[x - xMinI] && vertProfile[x + 1 - xMinI]) {
+ if (x - start > vertGapSize2) {
+ ++nVertGaps;
+ if (start - prev < minVertChunkWidth) {
+ minVertChunkWidth = start - prev;
}
+ prev = x;
+ }
+ }
+ }
- } while (found);
+ //----- compute splitting parameters
- //~ need to compute the primary writing mode (horiz/vert) in
- //~ addition to primary rotation
+ // approximation of number of lines in block
+ if (fabs(avgFontSize) < 0.001) {
+ nLines = 1;
+ } else if (rot & 1) {
+ nLines = (xMax - xMin) / avgFontSize;
+ } else {
+ nLines = (yMax - yMin) / avgFontSize;
+ }
+
+ // compute the minimum allowed vertical gap size
+ // (this is a horizontal gap threshold for rot=1,3
+ if (control.mode == textOutTableLayout) {
+ vertGapThreshold = vertGapThresholdTableMax
+ + vertGapThresholdTableSlope * nLines;
+ if (vertGapThreshold < vertGapThresholdTableMin) {
+ vertGapThreshold = vertGapThresholdTableMin;
+ }
+ } else {
+ vertGapThreshold = vertGapThresholdMax + vertGapThresholdSlope * nLines;
+ if (vertGapThreshold < vertGapThresholdMin) {
+ vertGapThreshold = vertGapThresholdMin;
+ }
+ }
+ vertGapThreshold = vertGapThreshold * avgFontSize / splitPrecision;
+
+ // compute the minimum allowed chunk width
+ if (control.mode == textOutTableLayout) {
+ minChunk = 0;
+ } else {
+ minChunk = vertSplitChunkThreshold * avgFontSize / splitPrecision;
+ }
+
+ // look for large chars
+ // -- this kludge (multiply by 256, convert to int, divide by 256.0)
+ // prevents floating point stability issues on x86 with gcc, where
+ // largeCharSize could otherwise have slightly different values
+ // here and where it's used below to do the large char partition
+ // (because it gets truncated from 80 to 64 bits when spilled)
+ largeCharSize = (int)(largeCharThreshold * avgFontSize * 256) / 256.0;
+ nLargeChars = 0;
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ if (ch->fontSize > largeCharSize) {
+ ++nLargeChars;
+ }
+ }
+
+ // figure out which type of split to do
+ doHorizSplit = doVertSplit = gFalse;
+ smallSplit = gFalse;
+ if (rot & 1) {
+ if (nHorizGaps > 0 &&
+ (horizGapSize > vertGapSize || control.mode == textOutTableLayout) &&
+ horizGapSize > vertGapThreshold &&
+ minHorizChunkWidth > minChunk) {
+ doHorizSplit = gTrue;
+ } else if (nVertGaps > 0) {
+ doVertSplit = gTrue;
+ } else if (nLargeChars == 0 && nHorizGaps > 0) {
+ doHorizSplit = gTrue;
+ smallSplit = gTrue;
+ }
+ } else {
+ if (nVertGaps > 0 &&
+ (vertGapSize > horizGapSize || control.mode == textOutTableLayout) &&
+ vertGapSize > vertGapThreshold &&
+ minVertChunkWidth > minChunk) {
+ doVertSplit = gTrue;
+ } else if (nHorizGaps > 0) {
+ doHorizSplit = gTrue;
+ } else if (nLargeChars == 0 && nVertGaps > 0) {
+ doVertSplit = gTrue;
+ smallSplit = gTrue;
+ }
+ }
- // coalesce the block, and add it to the list
- blk->coalesce(uMap, fixedPitch);
- if (lastBlk) {
- lastBlk->next = blk;
+ //----- split the block
+
+ //~ this could use "other content" (vector graphics, rotated text) --
+ //~ presence of other content in a gap means we should definitely split
+
+ // split vertically
+ if (doVertSplit) {
+ blk = new TextBlock(blkVertSplit, rot);
+ blk->smallSplit = smallSplit;
+ for (start = xMinI; start < xMaxI && !vertProfile[start - xMinI]; ++start) ;
+ prev = start - 1;
+ for (x = start; x < xMaxI; ++x) {
+ if (vertProfile[x - xMinI] && !vertProfile[x + 1 - xMinI]) {
+ start = x;
+ } else if (!vertProfile[x - xMinI] && vertProfile[x + 1 - xMinI]) {
+ if (x - start > vertGapSize2) {
+ chars2 = getChars(charsA, (prev + 0.5) * splitPrecision, yMin - 1,
+ (start + 1.5) * splitPrecision, yMax + 1);
+ blk->addChild(split(chars2, rot));
+ delete chars2;
+ prev = x;
+ }
+ }
+ }
+ chars2 = getChars(charsA, (prev + 0.5) * splitPrecision, yMin - 1,
+ xMax + 1, yMax + 1);
+ blk->addChild(split(chars2, rot));
+ delete chars2;
+
+ // split horizontally
+ } else if (doHorizSplit) {
+ blk = new TextBlock(blkHorizSplit, rot);
+ blk->smallSplit = smallSplit;
+ for (start = yMinI;
+ start < yMaxI && !horizProfile[start - yMinI];
+ ++start) ;
+ prev = start - 1;
+ for (y = start; y < yMaxI; ++y) {
+ if (horizProfile[y - yMinI] && !horizProfile[y + 1 - yMinI]) {
+ start = y;
+ } else if (!horizProfile[y - yMinI] && horizProfile[y + 1 - yMinI]) {
+ if (y - start > horizGapSize2) {
+ chars2 = getChars(charsA, xMin - 1, (prev + 0.5) * splitPrecision,
+ xMax + 1, (start + 1.5) * splitPrecision);
+ blk->addChild(split(chars2, rot));
+ delete chars2;
+ prev = y;
+ }
+ }
+ }
+ chars2 = getChars(charsA, xMin - 1, (prev + 0.5) * splitPrecision,
+ xMax + 1, yMax + 1);
+ blk->addChild(split(chars2, rot));
+ delete chars2;
+
+ // split into larger and smaller chars
+ } else if (nLargeChars > 0) {
+ chars2 = new GList();
+ chars3 = new GList();
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ if (ch->fontSize > largeCharSize) {
+ chars2->append(ch);
} else {
- blkList = blk;
+ chars3->append(ch);
}
- lastBlk = blk;
- count[rot] += blk->charCount;
- ++nBlocks;
}
+ blk = split(chars3, rot);
+ insertLargeChars(chars2, blk);
+ delete chars2;
+ delete chars3;
- if (count[rot] > count[primaryRot]) {
- primaryRot = rot;
+ // create a leaf node
+ } else {
+ blk = new TextBlock(blkLeaf, rot);
+ for (i = 0; i < charsA->getLength(); ++i) {
+ blk->addChild((TextChar *)charsA->get(i));
}
}
-#if 0 // for debugging
- printf("*** rotation ***\n");
- for (rot = 0; rot < 4; ++rot) {
- printf(" %d: %6d\n", rot, count[rot]);
+ gfree(horizProfile);
+ gfree(vertProfile);
+
+ tagBlock(blk);
+
+ return blk;
+}
+
+// Return the subset of chars inside a rectangle.
+GList *TextPage::getChars(GList *charsA, double xMin, double yMin,
+ double xMax, double yMax) {
+ GList *ret;
+ TextChar *ch;
+ double x, y;
+ int i;
+
+ ret = new GList();
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ // because of {ascent,descent}AdjustFactor, the y coords (or x
+ // coords for rot 1,3) for the gaps will be a little bit tight --
+ // so we use the center of the character here
+ x = 0.5 * (ch->xMin + ch->xMax);
+ y = 0.5 * (ch->yMin + ch->yMax);
+ if (x > xMin && x < xMax && y > yMin && y < yMax) {
+ ret->append(ch);
+ }
}
- printf(" primary rot = %d\n", primaryRot);
- printf("\n");
-#endif
+ return ret;
+}
-#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);
+// Decide whether this block is a line, column, or multiple columns:
+// - all leaf nodes are lines
+// - horiz split nodes whose children are lines or columns are columns
+// - other horiz split nodes are multiple columns
+// - vert split nodes, with small gaps, whose children are lines are lines
+// - other vert split nodes are multiple columns
+// (for rot=1,3: the horiz and vert splits are swapped)
+// In table layout mode:
+// - all leaf nodes are lines
+// - vert split nodes, with small gaps, whose children are lines are lines
+// - everything else is multiple columns
+void TextPage::tagBlock(TextBlock *blk) {
+ TextBlock *child;
+ int i;
+
+ if (control.mode == textOutTableLayout) {
+ if (blk->type == blkLeaf) {
+ blk->tag = blkTagLine;
+ } else if (blk->type == ((blk->rot & 1) ? blkHorizSplit : blkVertSplit) &&
+ blk->smallSplit) {
+ blk->tag = blkTagLine;
+ for (i = 0; i < blk->children->getLength(); ++i) {
+ child = (TextBlock *)blk->children->get(i);
+ if (child->tag != blkTagLine) {
+ blk->tag = blkTagMulticolumn;
+ break;
}
- printf("'\n");
}
+ } else {
+ blk->tag = blkTagMulticolumn;
}
+ return;
}
- 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;
+ if (blk->type == blkLeaf) {
+ blk->tag = blkTagLine;
+
+ } else {
+ if (blk->type == ((blk->rot & 1) ? blkVertSplit : blkHorizSplit)) {
+ blk->tag = blkTagColumn;
+ for (i = 0; i < blk->children->getLength(); ++i) {
+ child = (TextBlock *)blk->children->get(i);
+ if (child->tag != blkTagColumn && child->tag != blkTagLine) {
+ blk->tag = blkTagMulticolumn;
+ break;
+ }
+ }
+ } else {
+ if (blk->smallSplit) {
+ blk->tag = blkTagLine;
+ for (i = 0; i < blk->children->getLength(); ++i) {
+ child = (TextBlock *)blk->children->get(i);
+ if (child->tag != blkTagLine) {
+ blk->tag = blkTagMulticolumn;
+ break;
}
}
+ } else {
+ blk->tag = blkTagMulticolumn;
}
}
}
- primaryLR = lrCount >= 0;
+}
-#if 0 // for debugging
- printf("*** direction ***\n");
- printf("lrCount = %d\n", lrCount);
- printf("primaryLR = %d\n", primaryLR);
-#endif
+// Insert a list of large characters into a tree.
+void TextPage::insertLargeChars(GList *largeChars, TextBlock *blk) {
+ TextChar *ch, *ch2;
+ GBool singleLine;
+ double xLimit, yLimit, minOverlap;
+ int i;
- //----- column assignment
+ //~ this currently works only for characters in the primary rotation
+
+ // check to see if the large chars are a single line, in the
+ // upper-left corner of blk (this is just a rough estimate)
+ xLimit = blk->xMin + 0.5 * (blk->xMin + blk->xMax);
+ yLimit = blk->yMin + 0.5 * (blk->yMin + blk->yMax);
+ singleLine = gTrue;
+ // note: largeChars are already sorted by x
+ for (i = 0; i < largeChars->getLength(); ++i) {
+ ch2 = (TextChar *)largeChars->get(i);
+ if (ch2->xMax > xLimit || ch2->yMax > yLimit) {
+ singleLine = gFalse;
+ break;
+ }
+ if (i > 0) {
+ ch = (TextChar *)largeChars->get(i-1);
+ minOverlap = 0.5 * (ch->fontSize < ch2->fontSize ? ch->fontSize
+ : ch2->fontSize);
+ if (ch->yMax - ch2->yMin < minOverlap ||
+ ch2->yMax - ch->yMin < minOverlap) {
+ singleLine = gFalse;
+ break;
+ }
+ }
+ }
- if (physLayout && fixedPitch) {
+ if (singleLine) {
+ // if the large chars are a single line, prepend them to the first
+ // leaf node in blk
+ insertLargeCharsInFirstLeaf(largeChars, blk);
+ } else {
+ // if the large chars are not a single line, prepend each one to
+ // the appropriate leaf node -- this handles cases like bullets
+ // drawn in a large font, on the left edge of a column
+ for (i = largeChars->getLength() - 1; i >= 0; --i) {
+ ch = (TextChar *)largeChars->get(i);
+ insertLargeCharInLeaf(ch, blk);
+ }
+ }
+}
- blocks = (TextBlock **)gmallocn(nBlocks, sizeof(TextBlock *));
- for (blk = blkList, i = 0; blk; blk = blk->next, ++i) {
- blocks[i] = blk;
- col1 = 0; // make gcc happy
- switch (primaryRot) {
- case 0:
- col1 = (int)(blk->xMin / fixedPitch + 0.5);
- break;
- case 1:
- col1 = (int)(blk->yMin / fixedPitch + 0.5);
- break;
- case 2:
- col1 = (int)((pageWidth - blk->xMax) / fixedPitch + 0.5);
- break;
- case 3:
- col1 = (int)((pageHeight - blk->yMax) / fixedPitch + 0.5);
+// Find the first leaf (in depth-first order) in blk, and prepend a
+// list of large chars.
+void TextPage::insertLargeCharsInFirstLeaf(GList *largeChars, TextBlock *blk) {
+ TextChar *ch;
+ int i;
+
+ if (blk->type == blkLeaf) {
+ for (i = largeChars->getLength() - 1; i >= 0; --i) {
+ ch = (TextChar *)largeChars->get(i);
+ blk->prependChild(ch);
+ }
+ } else {
+ insertLargeCharsInFirstLeaf(largeChars, (TextBlock *)blk->children->get(0));
+ blk->updateBounds(0);
+ }
+}
+
+// Find the leaf in <blk> where large char <ch> belongs, and prepend
+// it.
+void TextPage::insertLargeCharInLeaf(TextChar *ch, TextBlock *blk) {
+ TextBlock *child;
+ double y;
+ int i;
+
+ //~ this currently works only for characters in the primary rotation
+
+ //~ this currently just looks down the left edge of blk
+ //~ -- it could be extended to do more
+
+ // estimate the baseline of ch
+ y = ch->yMin + 0.75 * (ch->yMax - ch->yMin);
+
+ if (blk->type == blkLeaf) {
+ blk->prependChild(ch);
+ } else if (blk->type == blkHorizSplit) {
+ for (i = 0; i < blk->children->getLength(); ++i) {
+ child = (TextBlock *)blk->children->get(i);
+ if (y < child->yMax || i == blk->children->getLength() - 1) {
+ insertLargeCharInLeaf(ch, child);
+ blk->updateBounds(i);
break;
}
- blk->col = col1;
- for (line = blk->lines; line; line = line->next) {
- for (j = 0; j <= line->len; ++j) {
- line->col[j] += col1;
+ }
+ } else {
+ insertLargeCharInLeaf(ch, (TextBlock *)blk->children->get(0));
+ blk->updateBounds(0);
+ }
+}
+
+// Merge blk (rot != 0) into primaryTree (rot == 0).
+void TextPage::insertIntoTree(TextBlock *blk, TextBlock *primaryTree) {
+ TextBlock *child;
+
+ // we insert a whole column at a time - so call insertIntoTree
+ // recursively until we get to a column (or line)
+
+ if (blk->tag == blkTagMulticolumn) {
+ while (blk->children->getLength()) {
+ child = (TextBlock *)blk->children->del(0);
+ insertIntoTree(child, primaryTree);
+ }
+ delete blk;
+ } else {
+ insertColumnIntoTree(blk, primaryTree);
+ }
+}
+
+// Insert a column (as an atomic subtree) into tree.
+// Requirement: tree is not a leaf node.
+void TextPage::insertColumnIntoTree(TextBlock *column, TextBlock *tree) {
+ TextBlock *child;
+ int i;
+
+ for (i = 0; i < tree->children->getLength(); ++i) {
+ child = (TextBlock *)tree->children->get(i);
+ if (child->tag == blkTagMulticolumn &&
+ column->xMin >= child->xMin &&
+ column->yMin >= child->yMin &&
+ column->xMax <= child->xMax &&
+ column->yMax <= child->yMax) {
+ insertColumnIntoTree(column, child);
+ tree->tag = blkTagMulticolumn;
+ return;
+ }
+ }
+
+ if (tree->type == blkVertSplit) {
+ if (tree->rot == 1 || tree->rot == 2) {
+ for (i = 0; i < tree->children->getLength(); ++i) {
+ child = (TextBlock *)tree->children->get(i);
+ if (column->xMax > 0.5 * (child->xMin + child->xMax)) {
+ break;
+ }
+ }
+ } else {
+ for (i = 0; i < tree->children->getLength(); ++i) {
+ child = (TextBlock *)tree->children->get(i);
+ if (column->xMin < 0.5 * (child->xMin + child->xMax)) {
+ break;
}
}
}
+ } else if (tree->type == blkHorizSplit) {
+ if (tree->rot >= 2) {
+ for (i = 0; i < tree->children->getLength(); ++i) {
+ child = (TextBlock *)tree->children->get(i);
+ if (column->yMax > 0.5 * (child->yMin + child->yMax)) {
+ break;
+ }
+ }
+ } else {
+ for (i = 0; i < tree->children->getLength(); ++i) {
+ child = (TextBlock *)tree->children->get(i);
+ if (column->yMin < 0.5 * (child->yMin + child->yMax)) {
+ break;
+ }
+ }
+ }
+ } else {
+ // this should never happen
+ return;
+ }
+ tree->children->insert(i, column);
+ tree->tag = blkTagMulticolumn;
+}
+// Insert clipped characters back into the TextBlock tree.
+void TextPage::insertClippedChars(GList *clippedChars, TextBlock *tree) {
+ TextChar *ch, *ch2;
+ TextBlock *leaf;
+ double y;
+ int i;
+
+ //~ this currently works only for characters in the primary rotation
+
+ clippedChars->sort(TextChar::cmpX);
+ while (clippedChars->getLength()) {
+ ch = (TextChar *)clippedChars->del(0);
+ if (ch->rot != 0) {
+ continue;
+ }
+ if (!(leaf = findClippedCharLeaf(ch, tree))) {
+ continue;
+ }
+ leaf->addChild(ch);
+ i = 0;
+ while (i < clippedChars->getLength()) {
+ ch2 = (TextChar *)clippedChars->get(i);
+ if (ch2->xMin > ch->xMax + clippedTextMaxWordSpace * ch->fontSize) {
+ break;
+ }
+ y = 0.5 * (ch2->yMin + ch2->yMax);
+ if (y > leaf->yMin && y < leaf->yMax) {
+ ch2 = (TextChar *)clippedChars->del(i);
+ leaf->addChild(ch2);
+ ch = ch2;
+ } else {
+ ++i;
+ }
+ }
+ }
+}
+
+// Find the leaf in <tree> to which clipped char <ch> can be appended.
+// Returns NULL if there is no appropriate append point.
+TextBlock *TextPage::findClippedCharLeaf(TextChar *ch, TextBlock *tree) {
+ TextBlock *ret, *child;
+ double y;
+ int i;
+
+ //~ this currently works only for characters in the primary rotation
+
+ y = 0.5 * (ch->yMin + ch->yMax);
+ if (tree->type == blkLeaf) {
+ if (tree->rot == 0) {
+ if (y > tree->yMin && y < tree->yMax &&
+ ch->xMin <= tree->xMax + clippedTextMaxWordSpace * ch->fontSize) {
+ return tree;
+ }
+ }
} else {
+ for (i = 0; i < tree->children->getLength(); ++i) {
+ child = (TextBlock *)tree->children->get(i);
+ if ((ret = findClippedCharLeaf(ch, child))) {
+ return ret;
+ }
+ }
+ }
+ return NULL;
+}
+
+// Convert the tree of TextBlocks into a list of TextColumns.
+GList *TextPage::buildColumns(TextBlock *tree) {
+ GList *columns;
+
+ columns = new GList();
+ buildColumns2(tree, columns);
+ return columns;
+}
+
+void TextPage::buildColumns2(TextBlock *blk, GList *columns) {
+ TextColumn *col;
+ int i;
- // 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;
+ switch (blk->tag) {
+ case blkTagLine:
+ case blkTagColumn:
+ col = buildColumn(blk);
+ columns->append(col);
+ break;
+ case blkTagMulticolumn:
+ for (i = 0; i < blk->children->getLength(); ++i) {
+ buildColumns2((TextBlock *)blk->children->get(i), columns);
}
- qsort(blocks, nBlocks, sizeof(TextBlock *), &TextBlock::cmpXYPrimaryRot);
+ break;
+ }
+}
- // 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);
+TextColumn *TextPage::buildColumn(TextBlock *blk) {
+ GList *lines, *parLines;
+ GList *paragraphs;
+ TextLine *line0, *line1;
+ double spaceThresh, indent0, indent1, fontSize0, fontSize1;
+ int i;
+
+ lines = new GList();
+ buildLines(blk, lines);
+
+ spaceThresh = paragraphSpacingThreshold * getAverageLineSpacing(lines);
+
+ //~ could look for bulleted lists here: look for the case where
+ //~ all out-dented lines start with the same char
+
+ // build the paragraphs
+ paragraphs = new GList();
+ i = 0;
+ while (i < lines->getLength()) {
+
+ // get the first line of the paragraph
+ parLines = new GList();
+ line0 = (TextLine *)lines->get(i);
+ parLines->append(line0);
+ ++i;
+
+ if (i < lines->getLength()) {
+ line1 = (TextLine *)lines->get(i);
+ indent0 = getLineIndent(line0, blk);
+ indent1 = getLineIndent(line1, blk);
+ fontSize0 = line0->fontSize;
+ fontSize1 = line1->fontSize;
+
+ // inverted indent
+ if (indent1 - indent0 > minParagraphIndent * fontSize0 &&
+ fabs(fontSize0 - fontSize1) <= paragraphFontSizeDelta &&
+ getLineSpacing(line0, line1) <= spaceThresh) {
+ parLines->append(line1);
+ indent0 = indent1;
+ for (++i; i < lines->getLength(); ++i) {
+ line1 = (TextLine *)lines->get(i);
+ indent1 = getLineIndent(line1, blk);
+ fontSize1 = line1->fontSize;
+ if (indent0 - indent1 > minParagraphIndent * fontSize0) {
+ break;
}
- 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);
+ if (fabs(fontSize0 - fontSize1) > paragraphFontSizeDelta) {
+ break;
}
- 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);
+ if (getLineSpacing((TextLine *)lines->get(i - 1), line1)
+ > spaceThresh) {
+ break;
}
- 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);
+ parLines->append(line1);
+ }
+
+ // drop cap
+ } else if (fontSize0 > largeCharThreshold * fontSize1 &&
+ indent1 - indent0 > minParagraphIndent * fontSize1 &&
+ getLineSpacing(line0, line1) < 0) {
+ parLines->append(line1);
+ fontSize0 = fontSize1;
+ for (++i; i < lines->getLength(); ++i) {
+ line1 = (TextLine *)lines->get(i);
+ indent1 = getLineIndent(line1, blk);
+ if (indent1 - indent0 <= minParagraphIndent * fontSize0) {
+ break;
}
- break;
+ if (getLineSpacing((TextLine *)lines->get(i - 1), line1)
+ > spaceThresh) {
+ break;
+ }
+ parLines->append(line1);
}
- if (col2 > col1) {
- col1 = col2;
+ for (; i < lines->getLength(); ++i) {
+ line1 = (TextLine *)lines->get(i);
+ indent1 = getLineIndent(line1, blk);
+ fontSize1 = line1->fontSize;
+ if (indent1 - indent0 > minParagraphIndent * fontSize0) {
+ break;
+ }
+ if (fabs(fontSize0 - fontSize1) > paragraphFontSizeDelta) {
+ break;
+ }
+ if (getLineSpacing((TextLine *)lines->get(i - 1), line1)
+ > spaceThresh) {
+ break;
+ }
+ parLines->append(line1);
}
- }
- blk0->col = col1;
- for (line = blk0->lines; line; line = line->next) {
- for (j = 0; j <= line->len; ++j) {
- line->col[j] += col1;
+
+ // regular indent or no indent
+ } else if (fabs(fontSize0 - fontSize1) <= paragraphFontSizeDelta &&
+ getLineSpacing(line0, line1) <= spaceThresh) {
+ parLines->append(line1);
+ indent0 = indent1;
+ for (++i; i < lines->getLength(); ++i) {
+ line1 = (TextLine *)lines->get(i);
+ indent1 = getLineIndent(line1, blk);
+ fontSize1 = line1->fontSize;
+ if (indent1 - indent0 > minParagraphIndent * fontSize0) {
+ break;
+ }
+ if (fabs(fontSize0 - fontSize1) > paragraphFontSizeDelta) {
+ break;
+ }
+ if (getLineSpacing((TextLine *)lines->get(i - 1), line1)
+ > spaceThresh) {
+ break;
+ }
+ parLines->append(line1);
}
}
}
+ paragraphs->append(new TextParagraph(parLines));
}
-#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: col[0]=%d\n", line->col[0]);
- 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");
- }
+ delete lines;
+
+ return new TextColumn(paragraphs, blk->xMin, blk->yMin,
+ blk->xMax, blk->yMax);
+}
+
+double TextPage::getLineIndent(TextLine *line, TextBlock *blk) {
+ double indent;
+
+ switch (line->rot) {
+ case 0:
+ default: indent = line->xMin - blk->xMin; break;
+ case 1: indent = line->yMin - blk->yMin; break;
+ case 2: indent = blk->xMax - line->xMax; break;
+ case 3: indent = blk->yMax - line->yMax; break;
+ }
+ return indent;
+}
+
+// Compute average line spacing in column.
+double TextPage::getAverageLineSpacing(GList *lines) {
+ double avg, sp;
+ int n, i;
+
+ avg = 0;
+ n = 0;
+ for (i = 1; i < lines->getLength(); ++i) {
+ sp = getLineSpacing((TextLine *)lines->get(i - 1),
+ (TextLine *)lines->get(i));
+ if (sp > 0) {
+ avg += sp;
+ ++n;
}
}
- printf("\n");
-#endif
+ if (n > 0) {
+ avg /= n;
+ }
+ return avg;
+}
+
+// Compute the space between two lines.
+double TextPage::getLineSpacing(TextLine *line0, TextLine *line1) {
+ double sp;
+
+ switch (line0->rot) {
+ case 0:
+ default: sp = line1->yMin - line0->yMax; break;
+ case 1: sp = line0->xMin - line1->xMax; break;
+ case 2: sp = line0->yMin - line1->yMin; break;
+ case 3: sp = line1->xMin - line1->xMax; break;
+ }
+ return sp;
+}
+
+void TextPage::buildLines(TextBlock *blk, GList *lines) {
+ TextLine *line;
+ int i;
+
+ switch (blk->tag) {
+ case blkTagLine:
+ line = buildLine(blk);
+ if (blk->rot == 1 || blk->rot == 2) {
+ lines->insert(0, line);
+ } else {
+ lines->append(line);
+ }
+ break;
+ case blkTagColumn:
+ case blkTagMulticolumn: // multicolumn should never happen here
+ for (i = 0; i < blk->children->getLength(); ++i) {
+ buildLines((TextBlock *)blk->children->get(i), lines);
+ }
+ break;
+ }
+}
+
+TextLine *TextPage::buildLine(TextBlock *blk) {
+ GList *charsA;
+ GList *words;
+ TextChar *ch, *ch2;
+ TextWord *word;
+ double wordSp, lineFontSize, sp;
+ GBool spaceAfter, spaceAfter2;
+ int i, j;
- //----- reading order sort
+ charsA = new GList();
+ getLineChars(blk, charsA);
- // sort blocks into yx order (in preparation for reading order sort)
- qsort(blocks, nBlocks, sizeof(TextBlock *), &TextBlock::cmpYXPrimaryRot);
+ wordSp = computeWordSpacingThreshold(charsA, blk->rot);
- // 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);
+ words = new GList();
+ lineFontSize = 0;
+ spaceAfter = gFalse;
+ i = 0;
+ while (i < charsA->getLength()) {
+ sp = wordSp - 1;
+ for (j = i+1; j < charsA->getLength(); ++j) {
+ ch = (TextChar *)charsA->get(j-1);
+ ch2 = (TextChar *)charsA->get(j);
+ sp = (blk->rot & 1) ? (ch2->yMin - ch->yMax) : (ch2->xMin - ch->xMax);
+ if (sp > wordSp ||
+ ch->font != ch2->font ||
+ fabs(ch->fontSize - ch2->fontSize) > 0.01 ||
+ (control.mode == textOutRawOrder &&
+ ch2->charPos != ch->charPos + ch->charLen)) {
+ break;
}
+ sp = wordSp - 1;
+ }
+ spaceAfter2 = spaceAfter;
+ spaceAfter = sp > wordSp;
+ word = new TextWord(charsA, i, j - i, blk->rot,
+ (blk->rot >= 2) ? spaceAfter2 : spaceAfter);
+ i = j;
+ if (blk->rot >= 2) {
+ words->insert(0, word);
+ } else {
+ words->append(word);
+ }
+ if (i == 0 || word->fontSize > lineFontSize) {
+ lineFontSize = word->fontSize;
}
}
-#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");
+ delete charsA;
+
+ return new TextLine(words, blk->xMin, blk->yMin, blk->xMax, blk->yMax,
+ lineFontSize);
+}
+
+void TextPage::getLineChars(TextBlock *blk, GList *charsA) {
+ int i;
+
+ if (blk->type == blkLeaf) {
+ charsA->append(blk->children);
+ } else {
+ for (i = 0; i < blk->children->getLength(); ++i) {
+ getLineChars((TextBlock *)blk->children->get(i), charsA);
+ }
+ }
+}
+
+// Compute the inter-word spacing threshold for a line of chars.
+// Spaces greater than this threshold will be considered inter-word
+// spaces.
+double TextPage::computeWordSpacingThreshold(GList *charsA, int rot) {
+ TextChar *ch, *ch2;
+ double avgFontSize, minSp, maxSp, sp;
+ int i;
+
+ avgFontSize = 0;
+ minSp = maxSp = 0;
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ avgFontSize += ch->fontSize;
+ if (i < charsA->getLength() - 1) {
+ ch2 = (TextChar *)charsA->get(i+1);
+ sp = (rot & 1) ? (ch2->yMin - ch->yMax) : (ch2->xMin - ch->xMax);
+ if (i == 0 || sp < minSp) {
+ minSp = sp;
+ }
+ if (sp > maxSp) {
+ maxSp = sp;
}
}
}
- printf("\n");
-#endif
+ avgFontSize /= charsA->getLength();
+ if (minSp < 0) {
+ minSp = 0;
+ }
- // 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 spacing is completely uniform, assume it's a single word
+ // (technically it could be either "ABC" or "A B C", but it's
+ // essentially impossible to tell)
+ if (maxSp - minSp < uniformSpacing * avgFontSize) {
+ return maxSp + 1;
+
+ // if there is some variation in spacing, but it's small, assume
+ // there are some inter-word spaces
+ } else if (maxSp - minSp < wordSpacing * avgFontSize) {
+ return 0.5 * (minSp + maxSp);
+
+ // otherwise, assume a reasonable threshold for inter-word spacing
+ // (we can't use something like 0.5*(minSp+maxSp) here because there
+ // can be outliers at the high end)
+ } else {
+ return minSp + wordSpacing * avgFontSize;
+ }
+}
+
+int TextPage::assignPhysLayoutPositions(GList *columns) {
+ assignLinePhysPositions(columns);
+ return assignColumnPhysPositions(columns);
+}
+
+// Assign a physical x coordinate for each TextLine (relative to the
+// containing TextColumn). This also computes TextColumn width and
+// height.
+void TextPage::assignLinePhysPositions(GList *columns) {
+ TextColumn *col;
+ TextParagraph *par;
+ TextLine *line;
+ UnicodeMap *uMap;
+ int colIdx, parIdx, lineIdx;
+
+ if (!(uMap = globalParams->getTextEncoding())) {
+ return;
+ }
+
+ for (colIdx = 0; colIdx < columns->getLength(); ++colIdx) {
+ col = (TextColumn *)columns->get(colIdx);
+ col->pw = col->ph = 0;
+ for (parIdx = 0; parIdx < col->paragraphs->getLength(); ++parIdx) {
+ par = (TextParagraph *)col->paragraphs->get(parIdx);
+ for (lineIdx = 0; lineIdx < par->lines->getLength(); ++lineIdx) {
+ line = (TextLine *)par->lines->get(lineIdx);
+ computeLinePhysWidth(line, uMap);
+ if (control.fixedPitch > 0) {
+ line->px = (int)((line->xMin - col->xMin) / control.fixedPitch);
+ } else if (fabs(line->fontSize) < 0.001) {
+ line->px = 0;
+ } else {
+ line->px = (int)((line->xMin - col->xMin) /
+ (physLayoutSpaceWidth * line->fontSize));
}
- if (blk1->primaryCmp(blk) < 0) {
- i = j;
- blk = blk1;
+ if (line->px + line->pw > col->pw) {
+ col->pw = line->px + line->pw;
}
}
+ col->ph += par->lines->getLength();
}
- blkArray[i] = NULL;
- --nBlocksLeft;
- blk->next = NULL;
+ col->ph += col->paragraphs->getLength() - 1;
+ }
- // create a new flow, starting with the upper-left-most block
- flow = new TextFlow(this, blk);
- if (lastFlow) {
- lastFlow->next = flow;
+ uMap->decRefCnt();
+}
+
+void TextPage::computeLinePhysWidth(TextLine *line, UnicodeMap *uMap) {
+ char buf[8];
+ int n, i;
+
+ if (uMap->isUnicode()) {
+ line->pw = line->len;
+ } else {
+ line->pw = 0;
+ for (i = 0; i < line->len; ++i) {
+ n = uMap->mapUnicode(line->text[i], buf, sizeof(buf));
+ line->pw += n;
+ }
+ }
+}
+
+// Assign physical x and y coordinates for each TextColumn. Returns
+// the text height (max physical y + 1).
+int TextPage::assignColumnPhysPositions(GList *columns) {
+ TextColumn *col, *col2;
+ double slack, xOverlap, yOverlap;
+ int ph, i, j;
+
+ if (control.mode == textOutTableLayout) {
+ slack = tableCellOverlapSlack;
+ } else {
+ slack = 0;
+ }
+
+ // assign x positions
+ columns->sort(&TextColumn::cmpX);
+ for (i = 0; i < columns->getLength(); ++i) {
+ col = (TextColumn *)columns->get(i);
+ if (control.fixedPitch) {
+ col->px = (int)(col->xMin / control.fixedPitch);
} 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;
+ col->px = 0;
+ for (j = 0; j < i; ++j) {
+ col2 = (TextColumn *)columns->get(j);
+ xOverlap = col2->xMax - col->xMin;
+ if (xOverlap < slack * (col2->xMax - col2->xMin)) {
+ if (col2->px + col2->pw + 2 > col->px) {
+ col->px = col2->px + col2->pw + 2;
}
- if (blk1->isBelow(blkStack) &&
- (!blk || blk1->primaryCmp(blk) < 0)) {
- i = j;
- blk = blk1;
+ } else {
+ yOverlap = (col->yMax < col2->yMax ? col->yMax : col2->yMax) -
+ (col->yMin > col2->yMin ? col->yMin : col2->yMin);
+ if (yOverlap > 0 && xOverlap < yOverlap) {
+ if (col2->px + col2->pw > col->px) {
+ col->px = col2->px + col2->pw;
+ }
+ } else {
+ if (col2->px > col->px) {
+ col->px = col2->px;
+ }
}
}
}
+ }
+ }
- // 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
+ // assign y positions
+ ph = 0;
+ columns->sort(&TextColumn::cmpY);
+ for (i = 0; i < columns->getLength(); ++i) {
+ col = (TextColumn *)columns->get(i);
+ col->py = 0;
+ for (j = 0; j < i; ++j) {
+ col2 = (TextColumn *)columns->get(j);
+ yOverlap = col2->yMax - col->yMin;
+ if (yOverlap < slack * (col2->yMax - col2->yMin)) {
+ if (col2->py + col2->ph + 1 > col->py) {
+ col->py = col2->py + col2->ph + 1;
+ }
} else {
- blkStack = blkStack->stackNext;
+ xOverlap = (col->xMax < col2->xMax ? col->xMax : col2->xMax) -
+ (col->xMin > col2->xMin ? col->xMin : col2->xMin);
+ if (xOverlap > 0 && yOverlap < xOverlap) {
+ if (col2->py + col2->ph > col->py) {
+ col->py = col2->py + col2->ph;
+ }
+ } else {
+ if (col2->py > col->py) {
+ col->py = col2->py;
+ }
+ }
}
}
+ if (col->py + col->ph > ph) {
+ ph = col->py + col->ph;
+ }
}
- 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);
+
+ return ph;
+}
+
+void TextPage::generateUnderlinesAndLinks(GList *columns) {
+ TextColumn *col;
+ TextParagraph *par;
+ TextLine *line;
+ TextWord *word;
+ TextUnderline *underline;
+ TextLink *link;
+ double base, uSlack, ubSlack, hSlack;
+ int colIdx, parIdx, lineIdx, wordIdx, i;
+
+ for (colIdx = 0; colIdx < columns->getLength(); ++colIdx) {
+ col = (TextColumn *)columns->get(colIdx);
+ for (parIdx = 0; parIdx < col->paragraphs->getLength(); ++parIdx) {
+ par = (TextParagraph *)col->paragraphs->get(parIdx);
+ for (lineIdx = 0; lineIdx < par->lines->getLength(); ++lineIdx) {
+ line = (TextLine *)par->lines->get(lineIdx);
+ for (wordIdx = 0; wordIdx < line->words->getLength(); ++wordIdx) {
+ word = (TextWord *)line->words->get(wordIdx);
+ base = word->getBaseline();
+ uSlack = underlineSlack * word->fontSize;
+ ubSlack = underlineBaselineSlack * word->fontSize;
+ hSlack = hyperlinkSlack * word->fontSize;
+
+ //----- handle underlining
+ for (i = 0; i < underlines->getLength(); ++i) {
+ underline = (TextUnderline *)underlines->get(i);
+ if (underline->horiz) {
+ if (word->rot == 0 || word->rot == 2) {
+ if (fabs(underline->y0 - base) < ubSlack &&
+ underline->x0 < word->xMin + uSlack &&
+ word->xMax - uSlack < underline->x1) {
+ word->underlined = gTrue;
+ }
+ }
+ } else {
+ if (word->rot == 1 || word->rot == 3) {
+ if (fabs(underline->x0 - base) < ubSlack &&
+ underline->y0 < word->yMin + uSlack &&
+ word->yMax - uSlack < underline->y1) {
+ word->underlined = gTrue;
+ }
+ }
+ }
+ }
+
+ //----- handle links
+ for (i = 0; i < links->getLength(); ++i) {
+ link = (TextLink *)links->get(i);
+ if (link->xMin < word->xMin + hSlack &&
+ word->xMax - hSlack < link->xMax &&
+ link->yMin < word->yMin + hSlack &&
+ word->yMax - hSlack < link->yMax) {
+ word->link = link;
+ }
}
- printf("'\n");
}
}
}
}
- printf("\n");
-#endif
-
- if (uMap) {
- uMap->decRefCnt();
- }
}
+//------------------------------------------------------------------------
+// TextPage: access
+//------------------------------------------------------------------------
+
GBool TextPage::findText(Unicode *s, int len,
GBool startAtTop, GBool stopAtBottom,
GBool startAtLast, GBool stopAtLast,
@@ -3131,20 +3639,31 @@ GBool TextPage::findText(Unicode *s, int len,
GBool wholeWord,
double *xMin, double *yMin,
double *xMax, double *yMax) {
- TextBlock *blk;
+ TextBlock *tree;
+ TextColumn *column;
+ TextParagraph *par;
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;
+ int txtSize, m, rot, colIdx, parIdx, lineIdx, i, j, k;
- //~ needs to handle right-to-left text
+ //~ need to handle right-to-left text
- if (rawOrder) {
- return gFalse;
+ if (!findCols) {
+ rot = rotateChars(chars);
+ if ((tree = splitChars(chars))) {
+ findCols = buildColumns(tree);
+ delete tree;
+ } else {
+ // no text
+ findCols = new GList();
+ }
+ unrotateChars(chars, rot);
+ unrotateColumns(findCols, rot);
}
// convert the search string to uppercase
@@ -3180,139 +3699,152 @@ GBool TextPage::findText(Unicode *s, int len,
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];
+ for (colIdx = backward ? findCols->getLength() - 1 : 0;
+ backward ? colIdx >= 0 : colIdx < findCols->getLength();
+ colIdx += backward ? -1 : 1) {
+ column = (TextColumn *)findCols->get(colIdx);
- // check: is the block above the top limit?
- // (this only works if the page's primary rotation is zero --
- // otherwise the blocks won't be sorted in the useful order)
- if (!startAtTop && primaryRot == 0 &&
- (backward ? blk->yMin > yStart : blk->yMax < yStart)) {
+ // check: is the column above the top limit?
+ if (!startAtTop && (backward ? column->yMin > yStart
+ : column->yMax < yStart)) {
continue;
}
- // check: is the block below the bottom limit?
- // (this only works if the page's primary rotation is zero --
- // otherwise the blocks won't be sorted in the useful order)
- if (!stopAtBottom && primaryRot == 0 &&
- (backward ? blk->yMax < yStop : blk->yMin > yStop)) {
- break;
+ // check: is the column below the bottom limit?
+ if (!stopAtBottom && (backward ? column->yMax < yStop
+ : column->yMin > yStop)) {
+ continue;
}
- for (line = blk->lines; line; line = line->next) {
+ for (parIdx = backward ? column->paragraphs->getLength() - 1 : 0;
+ backward ? parIdx >= 0 : parIdx < column->paragraphs->getLength();
+ parIdx += backward ? -1 : 1) {
+ par = (TextParagraph *)column->paragraphs->get(parIdx);
- // check: is the line above the top limit?
- // (this only works if the page's primary rotation is zero --
- // otherwise the lines won't be sorted in the useful order)
- if (!startAtTop && primaryRot == 0 &&
- (backward ? line->yMin > yStart : line->yMin < yStart)) {
+ // check: is the paragraph above the top limit?
+ if (!startAtTop && (backward ? par->yMin > yStart
+ : par->yMax < yStart)) {
continue;
}
- // check: is the line below the bottom limit?
- // (this only works if the page's primary rotation is zero --
- // otherwise the lines won't be sorted in the useful order)
- if (!stopAtBottom && primaryRot == 0 &&
- (backward ? line->yMin < yStop : line->yMin > yStop)) {
+ // check: is the paragraph below the bottom limit?
+ if (!stopAtBottom && (backward ? par->yMax < yStop
+ : par->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 (lineIdx = backward ? par->lines->getLength() - 1 : 0;
+ backward ? lineIdx >= 0 : lineIdx < par->lines->getLength();
+ lineIdx += backward ? -1 : 1) {
+ line = (TextLine *)par->lines->get(lineIdx);
+
+ // check: is the line above the top limit?
+ if (!startAtTop && (backward ? line->yMin > yStart
+ : line->yMax < yStart)) {
+ continue;
}
- for (k = 0; k < m; ++k) {
- txt[k] = unicodeToUpper(line->text[k]);
+
+ // check: is the line below the bottom limit?
+ if (!stopAtBottom && (backward ? line->yMax < yStop
+ : line->yMin > yStop)) {
+ continue;
}
- } 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) {
- if (!wholeWord ||
- ((j == 0 || !unicodeTypeAlphaNum(txt[j - 1])) &&
- (j + len == m || !unicodeTypeAlphaNum(txt[j + len])))) {
-
- // compare the strings
- for (k = 0; k < len; ++k) {
- if (p[k] != s2[k]) {
- break;
- }
+ // 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;
+ }
- // 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;
+ // search each position in this line
+ j = backward ? m - len : 0;
+ p = txt + j;
+ while (backward ? j >= 0 : j <= m - len) {
+ if (!wholeWord ||
+ ((j == 0 || !unicodeTypeWord(txt[j - 1])) &&
+ (j + len == m || !unicodeTypeWord(txt[j + len])))) {
+
+ // compare the strings
+ for (k = 0; k < len; ++k) {
+ if (p[k] != s2[k]) {
+ 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;
- }
+
+ // 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;
}
- } 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) {
+ 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 (backward) {
+ --j;
+ --p;
+ } else {
+ ++j;
+ ++p;
+ }
}
}
}
@@ -3339,32 +3871,27 @@ GBool TextPage::findText(Unicode *s, int len,
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;
- }
+ GList *chars2;
+ GString **out;
+ int *outLen;
+ TextColumn *col;
+ TextParagraph *par;
+ TextLine *line;
+ TextChar *ch;
+ GBool primaryLR;
+ TextBlock *tree;
+ GList *columns;
+ GString *ret;
+ double xx, yy;
+ int rot, colIdx, parIdx, lineIdx, ph, y, i;
// get the output encoding
if (!(uMap = globalParams->getTextEncoding())) {
- return s;
+ return NULL;
}
- isUnicode = uMap->isUnicode();
spaceLen = uMap->mapUnicode(0x20, space, sizeof(space));
eolLen = 0; // make gcc happy
switch (globalParams->getTextEOL()) {
@@ -3380,655 +3907,276 @@ GString *TextPage::getText(double xMin, double yMin,
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;
- }
- }
- }
+ // get all chars in the rectangle
+ // (i.e., all chars whose center lies inside the rectangle)
+ chars2 = new GList();
+ for (i = 0; i < chars->getLength(); ++i) {
+ ch = (TextChar *)chars->get(i);
+ xx = 0.5 * (ch->xMin + ch->xMax);
+ yy = 0.5 * (ch->yMin + ch->yMax);
+ if (xx > xMin && xx < xMax && yy > yMin && yy < yMax) {
+ chars2->append(ch);
}
}
+#if 0 //~debug
+ dumpChars(chars2);
+#endif
- // 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;
+ rot = rotateChars(chars2);
+ primaryLR = checkPrimaryLR(chars2);
+ tree = splitChars(chars2);
+ if (!tree) {
+ unrotateChars(chars2, rot);
+ delete chars2;
+ return new GString();
+ }
+#if 0 //~debug
+ dumpTree(tree);
+#endif
+ columns = buildColumns(tree);
+ delete tree;
+ ph = assignPhysLayoutPositions(columns);
+#if 0 //~debug
+ dumpColumns(columns);
+#endif
+ unrotateChars(chars2, rot);
+ delete chars2;
+
+ out = (GString **)gmallocn(ph, sizeof(GString *));
+ outLen = (int *)gmallocn(ph, sizeof(int));
+ for (i = 0; i < ph; ++i) {
+ out[i] = NULL;
+ outLen[i] = 0;
+ }
+
+ columns->sort(&TextColumn::cmpPX);
+ for (colIdx = 0; colIdx < columns->getLength(); ++colIdx) {
+ col = (TextColumn *)columns->get(colIdx);
+ y = col->py;
+ for (parIdx = 0;
+ parIdx < col->paragraphs->getLength() && y < ph;
+ ++parIdx) {
+ par = (TextParagraph *)col->paragraphs->get(parIdx);
+ for (lineIdx = 0;
+ lineIdx < par->lines->getLength() && y < ph;
+ ++lineIdx) {
+ line = (TextLine *)par->lines->get(lineIdx);
+ if (!out[y]) {
+ out[y] = new GString();
+ }
+ while (outLen[y] < col->px + line->px) {
+ out[y]->append(space, spaceLen);
+ ++outLen[y];
+ }
+ encodeFragment(line->text, line->len, uMap, primaryLR, out[y]);
+ outLen[y] += line->pw;
+ ++y;
}
-
- // column alignment
- for (; col < frag->col; ++col) {
- s->append(space, spaceLen);
+ if (parIdx + 1 < col->paragraphs->getLength()) {
+ ++y;
}
-
- // get the fragment text
- col += dumpFragment(frag->line->text + frag->start, frag->len, uMap, s);
}
+ }
- if (multiLine) {
- s->append(eol, eolLen);
+ ret = new GString();
+ for (i = 0; i < ph; ++i) {
+ if (out[i]) {
+ ret->append(out[i]);
+ delete out[i];
+ }
+ if (ph > 1) {
+ ret->append(eol, eolLen);
}
}
- gfree(frags);
+ gfree(out);
+ gfree(outLen);
+ deleteGList(columns, TextColumn);
uMap->decRefCnt();
- return s;
+ return ret;
}
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;
+ TextChar *ch;
+ double xMin2, yMin2, xMax2, yMax2;
GBool first;
- int i, j0, j1;
-
- if (rawOrder) {
- return gFalse;
- }
+ int i;
//~ this doesn't correctly handle ranges split across multiple lines
//~ (the highlighted region is the bounding box of all the parts of
//~ the range)
+
+ xMin2 = yMin2 = xMax2 = yMax2 = 0;
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->len] &&
- pos + length > word->charPos[0]) {
- for (j0 = 0;
- j0 < word->len && pos >= word->charPos[j0 + 1];
- ++j0) ;
- for (j1 = word->len - 1;
- j1 > j0 && pos + length <= word->charPos[j1];
- --j1) ;
- 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;
- }
+ for (i = 0; i < chars->getLength(); ++i) {
+ ch = (TextChar *)chars->get(i);
+ if (ch->charPos >= pos && ch->charPos < pos + length) {
+ if (first || ch->xMin < xMin2) {
+ xMin2 = ch->xMin;
+ }
+ if (first || ch->yMin < yMin2) {
+ yMin2 = ch->yMin;
+ }
+ if (first || ch->xMax > xMax2) {
+ xMax2 = ch->xMax;
}
+ if (first || ch->yMax > yMax2) {
+ yMax2 = ch->yMax;
+ }
+ first = gFalse;
}
}
- if (!first) {
- *xMin = xMin0;
- *xMax = xMax0;
- *yMin = yMin0;
- *yMax = yMax0;
- return gTrue;
+ if (first) {
+ return gFalse;
}
- return gFalse;
+ *xMin = xMin2;
+ *yMin = yMin2;
+ *xMax = xMax2;
+ *yMax = yMax2;
+ return gTrue;
}
-void TextPage::dump(void *outputStream, TextOutputFunc outputFunc,
- GBool physLayout) {
- UnicodeMap *uMap;
- TextFlow *flow;
- TextBlock *blk;
+TextWordList *TextPage::makeWordList() {
+ TextBlock *tree;
+ GList *columns;
+ TextColumn *col;
+ TextParagraph *par;
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;
+ GList *words;
+ int rot, colIdx, parIdx, lineIdx, wordIdx;
- // get the output encoding
- if (!(uMap = globalParams->getTextEncoding())) {
- return;
+ rot = rotateChars(chars);
+ tree = splitChars(chars);
+ if (!tree) {
+ // no text
+ unrotateChars(chars, rot);
+ return new TextWordList(new GList());
}
- 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;
+ columns = buildColumns(tree);
+ delete tree;
+ unrotateChars(chars, rot);
+ if (control.html) {
+ rotateUnderlinesAndLinks(rot);
+ generateUnderlinesAndLinks(columns);
}
- 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 &&
- word->next->xMin >
- word->xMax - minDupBreakOverlap * word->fontSize) {
- if (word->next->xMin > word->xMax + minWordSpacing * word->fontSize) {
- (*outputFunc)(outputStream, space, spaceLen);
+ words = new GList();
+ for (colIdx = 0; colIdx < columns->getLength(); ++colIdx) {
+ col = (TextColumn *)columns->get(colIdx);
+ for (parIdx = 0; parIdx < col->paragraphs->getLength(); ++parIdx) {
+ par = (TextParagraph *)col->paragraphs->get(parIdx);
+ for (lineIdx = 0; lineIdx < par->lines->getLength(); ++lineIdx) {
+ line = (TextLine *)par->lines->get(lineIdx);
+ for (wordIdx = 0; wordIdx < line->words->getLength(); ++wordIdx) {
+ word = (TextWord *)line->words->get(wordIdx);
+ words->append(word->copy());
}
- } 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
+ switch (control.mode) {
+ case textOutReadingOrder:
+ // already in reading order
+ break;
+ case textOutPhysLayout:
+ case textOutTableLayout:
+ case textOutLinePrinter:
+ words->sort(&TextWord::cmpYX);
+ break;
+ case textOutRawOrder:
+ words->sort(&TextWord::cmpCharPos);
+ break;
+ }
- // generate output
- col = 0;
- for (i = 0; i < nFrags; ++i) {
- frag = &frags[i];
+ // this has to be done after sorting with cmpYX
+ unrotateColumns(columns, rot);
+ unrotateWords(words, rot);
- // column alignment
- for (; col < frag->col; ++col) {
- (*outputFunc)(outputStream, space, spaceLen);
- }
+ deleteGList(columns, TextColumn);
- // 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;
- }
- }
+ return new TextWordList(words);
+}
- gfree(frags);
+//------------------------------------------------------------------------
+// TextPage: debug
+//------------------------------------------------------------------------
- // 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);
- }
- }
+#if 0 //~debug
- // end of page
- if (pageBreaks) {
- (*outputFunc)(outputStream, eop, eopLen);
- }
+void TextPage::dumpChars(GList *charsA) {
+ TextChar *ch;
+ int i;
- uMap->decRefCnt();
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ printf("char: U+%04x '%c' xMin=%g yMin=%g xMax=%g yMax=%g fontSize=%g rot=%d\n",
+ ch->c, ch->c & 0xff, ch->xMin, ch->yMin, ch->xMax, ch->yMax,
+ ch->fontSize, ch->rot);
+ }
}
-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;
- }
+void TextPage::dumpTree(TextBlock *tree, int indent) {
+ TextChar *ch;
+ int i;
- // 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;
- }
+ printf("%*sblock: type=%s tag=%s small=%d rot=%d xMin=%g yMin=%g xMax=%g yMax=%g\n",
+ indent, "",
+ tree->type == blkLeaf ? "leaf" :
+ tree->type == blkHorizSplit ? "horiz" : "vert",
+ tree->tag == blkTagMulticolumn ? "multicolumn" :
+ tree->tag == blkTagColumn ? "column" : "line",
+ tree->smallSplit,
+ tree->rot, tree->xMin, tree->yMin, tree->xMax, tree->yMax);
+ if (tree->type == blkLeaf) {
+ for (i = 0; i < tree->children->getLength(); ++i) {
+ ch = (TextChar *)tree->children->get(i);
+ printf("%*schar: '%c' xMin=%g yMin=%g xMax=%g yMax=%g font=%d.%d\n",
+ indent + 2, "", ch->c & 0xff,
+ ch->xMin, ch->yMin, ch->xMax, ch->yMax,
+ ch->font->fontID.num, ch->font->fontID.gen);
}
- for (i = 0; i < nFrags; ++i) {
- frags[i].col -= col1;
+ } else {
+ for (i = 0; i < tree->children->getLength(); ++i) {
+ dumpTree((TextBlock *)tree->children->get(i), indent + 2);
}
}
}
-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]) || unicodeTypeNum(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 {
-
- // Note: This code treats numeric characters (European and
- // Arabic/Indic) as left-to-right, which isn't strictly correct
- // (incurs extra LRE/POPDF pairs), but does produce correct
- // visual formatting.
- s->append(rle, rleLen);
- i = len - 1;
- while (i >= 0) {
- // output a right-to-left section
- for (j = i;
- j >= 0 && !(unicodeTypeL(text[j]) || unicodeTypeNum(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;
+void TextPage::dumpColumns(GList *columns) {
+ TextColumn *col;
+ TextParagraph *par;
+ TextLine *line;
+ int colIdx, parIdx, lineIdx, i;
+
+ for (colIdx = 0; colIdx < columns->getLength(); ++colIdx) {
+ col = (TextColumn *)columns->get(colIdx);
+ printf("column: xMin=%g yMin=%g xMax=%g yMax=%g px=%d py=%d pw=%d ph=%d\n",
+ col->xMin, col->yMin, col->xMax, col->yMax,
+ col->px, col->py, col->pw, col->ph);
+ for (parIdx = 0; parIdx < col->paragraphs->getLength(); ++parIdx) {
+ par = (TextParagraph *)col->paragraphs->get(parIdx);
+ printf(" paragraph:\n");
+ for (lineIdx = 0; lineIdx < par->lines->getLength(); ++lineIdx) {
+ line = (TextLine *)par->lines->get(lineIdx);
+ printf(" line: xMin=%g yMin=%g xMax=%g yMax=%g px=%d pw=%d rot=%d\n",
+ line->xMin, line->yMin, line->xMax, line->yMax,
+ line->px, line->pw, line->rot);
+ printf(" ");
+ for (i = 0; i < line->len; ++i) {
+ printf("%c", line->text[i] & 0xff);
}
+ printf("\n");
}
- 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
+#endif //~debug
//------------------------------------------------------------------------
// TextOutputDev
@@ -4038,14 +4186,10 @@ static void outputToFile(void *stream, const char *text, int len) {
fwrite(text, 1, len, (FILE *)stream);
}
-TextOutputDev::TextOutputDev(char *fileName, GBool physLayoutA,
- double fixedPitchA, GBool rawOrderA,
+TextOutputDev::TextOutputDev(char *fileName, TextOutputControl *controlA,
GBool append) {
text = NULL;
- physLayout = physLayoutA;
- fixedPitch = physLayout ? fixedPitchA : 0;
- rawOrder = rawOrderA;
- doHTML = gFalse;
+ control = *controlA;
ok = gTrue;
// open file
@@ -4070,28 +4214,21 @@ TextOutputDev::TextOutputDev(char *fileName, GBool physLayoutA,
}
// set up text object
- text = new TextPage(rawOrderA);
+ text = new TextPage(&control);
}
TextOutputDev::TextOutputDev(TextOutputFunc func, void *stream,
- GBool physLayoutA, double fixedPitchA,
- GBool rawOrderA) {
+ TextOutputControl *controlA) {
outputFunc = func;
outputStream = stream;
needClose = gFalse;
- physLayout = physLayoutA;
- fixedPitch = physLayout ? fixedPitchA : 0;
- rawOrder = rawOrderA;
- doHTML = gFalse;
- text = new TextPage(rawOrderA);
+ control = *controlA;
+ text = new TextPage(&control);
ok = gTrue;
}
TextOutputDev::~TextOutputDev() {
if (needClose) {
-#ifdef MACOS
- ICS_MapRefNumAndAssign((short)((FILE *)outputStream)->handle);
-#endif
fclose((FILE *)outputStream);
}
if (text) {
@@ -4104,10 +4241,8 @@ void TextOutputDev::startPage(int pageNum, GfxState *state) {
}
void TextOutputDev::endPage() {
- text->endPage();
- text->coalesce(physLayout, fixedPitch, doHTML);
if (outputStream) {
- text->dump(outputStream, outputFunc, physLayout);
+ text->write(outputStream, outputFunc);
}
}
@@ -4129,7 +4264,7 @@ 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 - originX, y - originY, dx, dy, c, nBytes, u, uLen);
+ text->addChar(state, x, y, dx, dy, c, nBytes, u, uLen);
}
void TextOutputDev::incCharCount(int nChars) {
@@ -4149,7 +4284,7 @@ void TextOutputDev::stroke(GfxState *state) {
GfxSubpath *subpath;
double x[2], y[2];
- if (!doHTML) {
+ if (!control.html) {
return;
}
path = state->getPath();
@@ -4176,7 +4311,7 @@ void TextOutputDev::fill(GfxState *state) {
double rx0, ry0, rx1, ry1, t;
int i;
- if (!doHTML) {
+ if (!control.html) {
return;
}
path = state->getPath();
@@ -4238,7 +4373,7 @@ void TextOutputDev::fill(GfxState *state) {
}
void TextOutputDev::eoFill(GfxState *state) {
- if (!doHTML) {
+ if (!control.html) {
return;
}
fill(state);
@@ -4248,7 +4383,7 @@ void TextOutputDev::processLink(Link *link) {
double x1, y1, x2, y2;
int xMin, yMin, xMax, yMax, x, y;
- if (!doHTML) {
+ if (!control.html) {
return;
}
link->getRect(&x1, &y1, &x2, &y2);
@@ -4315,16 +4450,14 @@ GBool TextOutputDev::findCharRange(int pos, int length,
return text->findCharRange(pos, length, xMin, yMin, xMax, yMax);
}
-#if TEXTOUT_WORD_LIST
TextWordList *TextOutputDev::makeWordList() {
- return text->makeWordList(physLayout);
+ return text->makeWordList();
}
-#endif
TextPage *TextOutputDev::takeText() {
TextPage *ret;
ret = text;
- text = new TextPage(rawOrder);
+ text = new TextPage(&control);
return ret;
}
diff --git a/xpdf/TextOutputDev.h b/xpdf/TextOutputDev.h
index e3bb26c..4399029 100644
--- a/xpdf/TextOutputDev.h
+++ b/xpdf/TextOutputDev.h
@@ -2,7 +2,7 @@
//
// TextOutputDev.h
//
-// Copyright 1997-2003 Glyph & Cog, LLC
+// Copyright 1997-2012 Glyph & Cog, LLC
//
//========================================================================
@@ -20,20 +20,12 @@
#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 TextChar;
+class TextLink;
class TextPage;
//------------------------------------------------------------------------
@@ -41,6 +33,37 @@ class TextPage;
typedef void (*TextOutputFunc)(void *stream, const char *text, int len);
//------------------------------------------------------------------------
+// TextOutputControl
+//------------------------------------------------------------------------
+
+enum TextOutputMode {
+ textOutReadingOrder, // format into reading order
+ textOutPhysLayout, // maintain original physical layout
+ textOutTableLayout, // similar to PhysLayout, but optimized
+ // for tables
+ textOutLinePrinter, // strict fixed-pitch/height layout
+ textOutRawOrder // keep text in content stream order
+};
+
+class TextOutputControl {
+public:
+
+ TextOutputControl();
+ ~TextOutputControl() {}
+
+ TextOutputMode mode; // formatting mode
+ double fixedPitch; // if this is non-zero, assume fixed-pitch
+ // characters with this width
+ // (only relevant for PhysLayout, Table,
+ // and LinePrinter modes)
+ double fixedLineSpacing; // fixed line spacing (only relevant for
+ // LinePrinter mode)
+ GBool html; // enable extra processing for HTML
+ GBool clipText; // separate clipped text and add it back
+ // in after forming columns
+};
+
+//------------------------------------------------------------------------
// TextFontInfo
//------------------------------------------------------------------------
@@ -52,7 +75,6 @@ public:
GBool matches(GfxState *state);
-#if TEXTOUT_WORD_LIST
// Get the font name (which may be NULL).
GString *getFontName() { return fontName; }
@@ -62,18 +84,21 @@ public:
GBool isSymbolic() { return flags & fontSymbolic; }
GBool isItalic() { return flags & fontItalic; }
GBool isBold() { return flags & fontBold; }
-#endif
+
+ // Get the width of the 'm' character, if available.
+ double getMWidth() { return mWidth; }
private:
- GfxFont *gfxFont;
-#if TEXTOUT_WORD_LIST
+ Ref fontID;
GString *fontName;
int flags;
-#endif
+ double mWidth;
+ double ascent, descent;
- friend class TextWord;
+ friend class TextLine;
friend class TextPage;
+ friend class TextWord;
};
//------------------------------------------------------------------------
@@ -83,44 +108,21 @@ private:
class TextWord {
public:
- // Constructor.
- TextWord(GfxState *state, int rotA, double x0, double y0,
- TextFontInfo *fontA, double fontSize);
-
- // Destructor.
+ TextWord(GList *chars, int start, int lenA,
+ int rotA, GBool spaceAfterA);
~TextWord();
-
- // Add a character to the word.
- void addChar(GfxState *state, double x, double y,
- double dx, double dy, int charPosA, int charLen,
- 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);
+ TextWord *copy() { return new TextWord(this); }
// 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; }
+ GBool isInvisible() { return invisible; }
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,
@@ -130,76 +132,43 @@ public:
int getCharPos() { return charPos[0]; }
int getCharLen() { return charPos[len] - charPos[0]; }
GBool getSpaceAfter() { return spaceAfter; }
-#endif
-
+ double getBaseline();
GBool isUnderlined() { return underlined; }
- Link *getLink() { return link; }
+ GString *getLinkURI();
private:
+ TextWord(TextWord *word);
+ void appendChar(TextChar *ch);
+ static int cmpYX(const void *p1, const void *p2);
+ static int cmpCharPos(const void *p1, const void *p2);
+
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 *charPos; // character position (within content stream)
// of each char (plus one extra entry for
// the last char)
- int len; // length of text/edge/charPos arrays
- int size; // size of text/edge/charPos arrays
+ double *edge; // "near" edge x or y coord of each char
+ // (plus one extra entry for the last char)
+ int len; // number of characters
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
+ GBool underlined;
+ TextLink *link;
+
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
+ GBool invisible; // set for invisible text (render mode 3)
friend class TextBlock;
+ friend class TextLine;
friend class TextPage;
};
@@ -210,168 +179,92 @@ private:
class TextLine {
public:
- TextLine(TextBlock *blkA, int rotA, double baseA);
+ TextLine(GList *wordsA, double xMinA, double yMinA,
+ double xMaxA, double yMaxA, double fontSizeA);
~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; }
+ double getXMin() { return xMin; }
+ double getYMin() { return yMin; }
+ double getBaseline();
+ int getRotation() { return rot; }
+ GList *getWords() { return words; }
private:
- TextBlock *blk; // parent block
- int rot; // text rotation
+ GList *words; // [TextWord]
+ 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
- TextWord *words; // words in this line
- TextWord *lastWord; // last word in this line
+ double fontSize; // main (max) font size for 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
+ int px; // x offset (in characters, relative to
+ // containing column) in physical layout mode
+ int pw; // line width (in characters) in physical
+ // layout mode
- friend class TextLineFrag;
- friend class TextBlock;
- friend class TextFlow;
- friend class TextWordList;
friend class TextPage;
+ friend class TextParagraph;
};
//------------------------------------------------------------------------
-// TextBlock
+// TextParagraph
//------------------------------------------------------------------------
-class TextBlock {
+class TextParagraph {
public:
- TextBlock(TextPage *pageA, int rotA);
- ~TextBlock();
-
- void addWord(TextWord *word);
-
- void coalesce(UnicodeMap *uMap, double fixedPitch);
-
- // 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);
+ TextParagraph(GList *linesA);
+ ~TextParagraph();
- 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; }
+ // Get the list of TextLine objects.
+ GList *getLines() { return lines; }
private:
- TextPage *page; // the parent page
- int rot; // text rotation
+ GList *lines; // [TextLine]
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
+// TextColumn
//------------------------------------------------------------------------
-class TextFlow {
+class TextColumn {
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);
+ TextColumn(GList *paragraphsA, double xMinA, double yMinA,
+ double xMaxA, double yMaxA);
+ ~TextColumn();
- // 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; }
+ // Get the list of TextParagraph objects.
+ GList *getParagraphs() { return paragraphs; }
private:
- TextPage *page; // the parent page
+ static int cmpX(const void *p1, const void *p2);
+ static int cmpY(const void *p1, const void *p2);
+ static int cmpPX(const void *p1, const void *p2);
+
+ GList *paragraphs; // [TextParagraph]
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;
+ int px, py; // x, y position (in characters) in physical
+ // layout mode
+ int pw, ph; // column width, height (in characters) in
+ // physical layout mode
- friend class TextWordList;
friend class TextPage;
};
-#if TEXTOUT_WORD_LIST
-
//------------------------------------------------------------------------
// TextWordList
//------------------------------------------------------------------------
@@ -379,11 +272,7 @@ private:
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(GList *wordsA);
~TextWordList();
@@ -398,8 +287,6 @@ private:
GList *words; // [TextWord]
};
-#endif // TEXTOUT_WORD_LIST
-
//------------------------------------------------------------------------
// TextPage
//------------------------------------------------------------------------
@@ -407,52 +294,11 @@ private:
class TextPage {
public:
- // Constructor.
- TextPage(GBool rawOrderA);
-
- // Destructor.
+ TextPage(TextOutputControl *controlA);
~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);
-
- // Add <nChars> invisible characters.
- void incCharCount(int nChars);
-
- // Begin/end an "ActualText" span, where the char indexes are
- // supplied by a marked content operator rather than the text
- // drawing operators.
- void beginActualText(GfxState *state, Unicode *u, int uLen);
- void endActualText(GfxState *state);
-
- // 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, double fixedPitch, GBool doHTML);
+ // Write contents of page to a stream.
+ void write(void *outputStream, TextOutputFunc outputFunc);
// Find a string. If <startAtTop> is true, starts looking at the
// top of the page; else if <startAtLast> is true, starts looking
@@ -480,39 +326,106 @@ public:
double *xMin, double *yMin,
double *xMax, double *yMax);
- // Dump contents of page to a file.
- void dump(void *outputStream, TextOutputFunc outputFunc,
- GBool physLayout);
+ // Create and return a list of TextColumn objects.
+ GList *makeColumns();
- // Get the head of the linked list of TextFlows.
- TextFlow *getFlows() { return flows; }
+ // Get the list of all TextFontInfo objects used on this page.
+ GList *getFonts() { return fonts; }
-#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
+ // Build a flat word list, in the specified ordering.
+ TextWordList *makeWordList();
private:
+ void startPage(GfxState *state);
void clear();
- void assignColumns(TextLineFrag *frags, int nFrags, int rot);
- int dumpFragment(Unicode *text, int len, UnicodeMap *uMap, GString *s);
+ void updateFont(GfxState *state);
+ void addChar(GfxState *state, double x, double y,
+ double dx, double dy,
+ CharCode c, int nBytes, Unicode *u, int uLen);
+ void incCharCount(int nChars);
+ void beginActualText(GfxState *state, Unicode *u, int uLen);
+ void endActualText(GfxState *state);
+ void addUnderline(double x0, double y0, double x1, double y1);
+ void addLink(double xMin, double yMin, double xMax, double yMax,
+ Link *link);
+
+ // output
+ void writeReadingOrder(void *outputStream,
+ TextOutputFunc outputFunc,
+ UnicodeMap *uMap,
+ char *space, int spaceLen,
+ char *eol, int eolLen);
+ void writePhysLayout(void *outputStream,
+ TextOutputFunc outputFunc,
+ UnicodeMap *uMap,
+ char *space, int spaceLen,
+ char *eol, int eolLen);
+ void writeLinePrinter(void *outputStream,
+ TextOutputFunc outputFunc,
+ UnicodeMap *uMap,
+ char *space, int spaceLen,
+ char *eol, int eolLen);
+ void writeRaw(void *outputStream,
+ TextOutputFunc outputFunc,
+ UnicodeMap *uMap,
+ char *space, int spaceLen,
+ char *eol, int eolLen);
+ void encodeFragment(Unicode *text, int len, UnicodeMap *uMap,
+ GBool primaryLR, GString *s);
+
+ // analysis
+ int rotateChars(GList *charsA);
+ void rotateUnderlinesAndLinks(int rot);
+ void unrotateChars(GList *charsA, int rot);
+ void unrotateColumns(GList *columns, int rot);
+ void unrotateWords(GList *words, int rot);
+ GBool checkPrimaryLR(GList *charsA);
+ void removeDuplicates(GList *charsA, int rot);
+ TextBlock *splitChars(GList *charsA);
+ TextBlock *split(GList *charsA, int rot);
+ GList *getChars(GList *charsA, double xMin, double yMin,
+ double xMax, double yMax);
+ void tagBlock(TextBlock *blk);
+ void insertLargeChars(GList *largeChars, TextBlock *blk);
+ void insertLargeCharsInFirstLeaf(GList *largeChars, TextBlock *blk);
+ void insertLargeCharInLeaf(TextChar *ch, TextBlock *blk);
+ void insertIntoTree(TextBlock *subtree, TextBlock *primaryTree);
+ void insertColumnIntoTree(TextBlock *column, TextBlock *tree);
+ void insertClippedChars(GList *clippedChars, TextBlock *tree);
+ TextBlock *findClippedCharLeaf(TextChar *ch, TextBlock *tree);
+ GList *buildColumns(TextBlock *tree);
+ void buildColumns2(TextBlock *blk, GList *columns);
+ TextColumn *buildColumn(TextBlock *tree);
+ double getLineIndent(TextLine *line, TextBlock *blk);
+ double getAverageLineSpacing(GList *lines);
+ double getLineSpacing(TextLine *line0, TextLine *line1);
+ void buildLines(TextBlock *blk, GList *lines);
+ TextLine *buildLine(TextBlock *blk);
+ void getLineChars(TextBlock *blk, GList *charsA);
+ double computeWordSpacingThreshold(GList *charsA, int rot);
+ int assignPhysLayoutPositions(GList *columns);
+ void assignLinePhysPositions(GList *columns);
+ void computeLinePhysWidth(TextLine *line, UnicodeMap *uMap);
+ int assignColumnPhysPositions(GList *columns);
+ void generateUnderlinesAndLinks(GList *columns);
+
+ // debug
+#if 0 //~debug
+ void dumpChars(GList *charsA);
+ void dumpTree(TextBlock *tree, int indent = 0);
+ void dumpColumns(GList *columns);
+#endif
- GBool rawOrder; // keep text in content stream order
+ TextOutputControl control; // formatting parameters
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 curRot; // current rotation
int nTinyChars; // number of "tiny" chars seen so far
- GBool lastCharOverlap; // set if the last added char overlapped the
- // previous char
Unicode *actualText; // current "ActualText" span
int actualTextLen;
double actualTextX0,
@@ -521,32 +434,22 @@ private:
actualTextY1;
int actualTextNBytes;
- 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 *chars; // [TextChar]
GList *fonts; // all font info objects used on this
// page [TextFontInfo]
+ GList *underlines; // [TextUnderline]
+ GList *links; // [TextLink]
+
+ GList *findCols; // text used by the findText function
+ // [TextColumn]
+ GBool findLR; // primary text direction, used by the
+ // findText function
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;
+ friend class TextOutputDev;
};
//------------------------------------------------------------------------
@@ -561,8 +464,7 @@ public:
// <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,
- double fixedPitchA, GBool rawOrderA,
+ TextOutputDev(char *fileName, TextOutputControl *controlA,
GBool append);
// Create a TextOutputDev which will write to a generic stream. If
@@ -570,8 +472,7 @@ public:
// is maintained. If <rawOrder> is true, the text is kept in
// content stream order.
TextOutputDev(TextOutputFunc func, void *stream,
- GBool physLayoutA, double fixedPitchA,
- GBool rawOrderA);
+ TextOutputControl *controlA);
// Destructor.
virtual ~TextOutputDev();
@@ -660,20 +561,18 @@ public:
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; }
+ void enableHTMLExtras(GBool html) { control.html = html; }
private:
@@ -682,13 +581,7 @@ private:
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
- double fixedPitch; // if physLayout is true and this is non-zero,
- // assume fixed-pitch characters with this
- // width
- GBool rawOrder; // keep text in content stream order
- GBool doHTML; // extra processing for HTML conversion
+ TextOutputControl control; // formatting parameters
GBool ok; // set up ok?
};
diff --git a/xpdf/TextString.cc b/xpdf/TextString.cc
new file mode 100644
index 0000000..5bc9de2
--- /dev/null
+++ b/xpdf/TextString.cc
@@ -0,0 +1,164 @@
+//========================================================================
+//
+// TextString.cc
+//
+// Copyright 2011-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <string.h>
+#include "gmem.h"
+#include "GString.h"
+#include "PDFDocEncoding.h"
+#include "TextString.h"
+
+//------------------------------------------------------------------------
+
+TextString::TextString() {
+ u = NULL;
+ len = size = 0;
+}
+
+TextString::TextString(GString *s) {
+ u = NULL;
+ len = size = 0;
+ append(s);
+}
+
+TextString::TextString(TextString *s) {
+ len = size = s->len;
+ if (len) {
+ u = (Unicode *)gmallocn(size, sizeof(Unicode));
+ memcpy(u, s->u, len * sizeof(Unicode));
+ } else {
+ u = NULL;
+ }
+}
+
+TextString::~TextString() {
+ gfree(u);
+}
+
+TextString *TextString::append(Unicode c) {
+ expand(1);
+ u[len] = c;
+ ++len;
+ return this;
+}
+
+TextString *TextString::append(GString *s) {
+ int n, i;
+
+ if ((s->getChar(0) & 0xff) == 0xfe &&
+ (s->getChar(1) & 0xff) == 0xff) {
+ n = (s->getLength() - 2) / 2;
+ expand(n);
+ for (i = 0; i < n; ++i) {
+ u[len + i] = ((s->getChar(2 + 2*i) & 0xff) << 8) |
+ (s->getChar(3 + 2*i) & 0xff);
+ }
+ len += n;
+ } else {
+ n = s->getLength();
+ expand(n);
+ for (i = 0; i < n; ++i) {
+ u[len + i] = pdfDocEncoding[s->getChar(i) & 0xff];
+ }
+ len += n;
+ }
+ return this;
+}
+
+TextString *TextString::insert(int idx, Unicode c) {
+ if (idx >= 0 && idx <= len) {
+ expand(1);
+ if (idx < len) {
+ memmove(u + idx + 1, u + idx, (len - idx) * sizeof(Unicode));
+ }
+ u[idx] = c;
+ ++len;
+ }
+ return this;
+}
+
+TextString *TextString::insert(int idx, GString *s) {
+ int n, i;
+
+ if (idx >= 0 && idx <= len) {
+ if ((s->getChar(0) & 0xff) == 0xfe &&
+ (s->getChar(1) & 0xff) == 0xff) {
+ n = (s->getLength() - 2) / 2;
+ expand(n);
+ if (idx < len) {
+ memmove(u + idx + n, u + idx, (len - idx) * sizeof(Unicode));
+ }
+ for (i = 0; i < n; ++i) {
+ u[idx + i] = ((s->getChar(2 + 2*i) & 0xff) << 8) |
+ (s->getChar(3 + 2*i) & 0xff);
+ }
+ len += n;
+ } else {
+ n = s->getLength();
+ expand(n);
+ if (idx < len) {
+ memmove(u + idx + n, u + idx, (len - idx) * sizeof(Unicode));
+ }
+ for (i = 0; i < n; ++i) {
+ u[idx + i] = pdfDocEncoding[s->getChar(i) & 0xff];
+ }
+ len += n;
+ }
+ }
+ return this;
+}
+
+void TextString::expand(int delta) {
+ int newLen;
+
+ newLen = len + delta;
+ if (delta > INT_MAX - len) {
+ // trigger an out-of-memory error
+ size = -1;
+ } else if (newLen <= size) {
+ return;
+ } else if (size > 0 && size <= INT_MAX / 2 && size*2 >= newLen) {
+ size *= 2;
+ } else {
+ size = newLen;
+ }
+ u = (Unicode *)greallocn(u, size, sizeof(Unicode));
+}
+
+GString *TextString::toPDFTextString() {
+ GString *s;
+ GBool useUnicode;
+ int i;
+
+ useUnicode = gFalse;
+ for (i = 0; i < len; ++i) {
+ if (u[i] >= 0x80) {
+ useUnicode = gTrue;
+ break;
+ }
+ }
+ s = new GString();
+ if (useUnicode) {
+ s->append((char)0xfe);
+ s->append((char)0xff);
+ for (i = 0; i < len; ++i) {
+ s->append((char)(u[i] >> 8));
+ s->append((char)u[i]);
+ }
+ } else {
+ for (i = 0; i < len; ++i) {
+ s->append((char)u[i]);
+ }
+ }
+ return s;
+}
diff --git a/xpdf/TextString.h b/xpdf/TextString.h
new file mode 100644
index 0000000..fac15e9
--- /dev/null
+++ b/xpdf/TextString.h
@@ -0,0 +1,66 @@
+//========================================================================
+//
+// TextString.h
+//
+// Copyright 2011-2013 Glyph & Cog, LLC
+//
+// Represents a PDF "text string", which can either be a UTF-16BE
+// string (with a leading byte order marker), or an 8-bit string in
+// PDFDocEncoding.
+//
+//========================================================================
+
+#ifndef TEXTSTRING_H
+#define TEXTSTRING_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "CharTypes.h"
+
+class GString;
+
+//------------------------------------------------------------------------
+
+class TextString {
+public:
+
+ // Create an empty TextString.
+ TextString();
+
+ // Create a TextString from a PDF text string.
+ TextString(GString *s);
+
+ // Copy a TextString.
+ TextString(TextString *s);
+
+ ~TextString();
+
+ // Append a Unicode character or PDF text string to this TextString.
+ TextString *append(Unicode c);
+ TextString *append(GString *s);
+
+ // Insert a Unicode character or PDF text string in this TextString.
+ TextString *insert(int idx, Unicode c);
+ TextString *insert(int idx, GString *s);
+
+ // Get the Unicode characters in the TextString.
+ int getLength() { return len; }
+ Unicode *getUnicode() { return u; }
+
+ // Create a PDF text string from a TextString.
+ GString *toPDFTextString();
+
+private:
+
+ void expand(int delta);
+
+ Unicode *u; // NB: not null-terminated
+ int len;
+ int size;
+};
+
+#endif
diff --git a/xpdf/UnicodeTypeTable.cc b/xpdf/UnicodeTypeTable.cc
index edd2970..34e68dc 100644
--- a/xpdf/UnicodeTypeTable.cc
+++ b/xpdf/UnicodeTypeTable.cc
@@ -2,7 +2,7 @@
//
// UnicodeTypeTable.cc
//
-// Copyright 2004 Glyph & Cog, LLC
+// Copyright 2004-2013 Glyph & Cog, LLC
//
//========================================================================
@@ -20,21 +20,21 @@ struct UnicodeCaseTableVector {
};
static UnicodeMapTableEntry typeTable[256] = {
- { "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN###NNNNN################NNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN#N####NNNNLNNNNN####NLNNN#LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLL", 'X' },
+ { "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN...NNNNN.....##########.NNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN.N....NNNNLNNNNN..##NLNNN#LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLL", 'X' },
{ NULL, 'L' },
{ "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLNNNNNNNNNNNNNNLLNNNNNNNNNNNNNNLLLLLNNNNNNNNNLNNNNNNNNNNNNNNNNN", 'X' },
{ "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLNNNNNNNNNNNLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLL", 'X' },
{ "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
{ "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNRNRNNRNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR", 'X' },
- { "RRRR#########RNNNNNNNNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNNNNNNNNNNNNN####################RRRNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNNNNNNRNNNNNNNRRNNNNNNNRR##########RRRRRR", 'X' },
+ { "RRRR.........RNNNNNNNNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNNNNNNNNNNNNN#################.##RRRNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNNNNNNRNNNNNNNRRNNNNNNNRR##########RRRRRR", 'X' },
{ "RRRRRRRRRRRRRRNNRNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNNNNNNNNNNNNNNNNNNNNNNNNNNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNNNNNNNNNNRNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },
{ NULL, 'N' },
- { "NNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNNNNNLLLLNLLLNNNNLLLLLLLLLLLLLNNLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNLLLLLLLLNLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLL##LLLLLLLNNNNN", 'X' },
- { "NNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNNNNNLLLLNLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLL##NNNNNNNNNNNNNN", 'X' },
- { "NNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLNLNNNLLLLLLLLLNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNN#NLLLLL", 'X' },
+ { "NNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNNNNNLLLLNLLLNNNNLLLLLLLLLLLLLNNLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNLLLLLLLLNLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLL..LLLLLLLNNNNN", 'X' },
+ { "NNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNNNNNLLLLNLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLL..NNNNNNNNNNNNNN", 'X' },
+ { "NNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLNLNNNLLLLLLLLLNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNN.NLLLLL", 'X' },
{ "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNLLLLNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
{ "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNLLLLLLLNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
- { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLNNNNNNN#####LLLLLLLNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLNNNNNNNNNLLLLLLLLLLNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
+ { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLNNNNNNN.....LLLLLLLNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLNNNNNNNNNLLLLLLLLLLNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
{ "LLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLNLNLNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNLNNNNNLNNLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
{ "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNLNNNNNNLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
{ NULL, 'L' },
@@ -43,7 +43,7 @@ static UnicodeMapTableEntry typeTable[256] = {
{ NULL, 'L' },
{ NULL, 'L' },
{ "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
- { "LLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNLLLLLLLLNLLNNNNNNNNNNNLLLLLLL#LNLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNN", 'X' },
+ { "LLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNLLLLLLLLNLLNNNNNNNNNNNLLLLLLL.LNLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNN", 'X' },
{ "NNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
{ "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLNNNNNLLLLLLNLLLLLLNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },
{ NULL, 'L' },
@@ -52,9 +52,9 @@ static UnicodeMapTableEntry typeTable[256] = {
{ NULL, 'L' },
{ NULL, 'L' },
{ "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLNNNLLLLLLLLLLLNNNLLLLLLLLLLLLNNNNLLLLLLLLLLLLLNNNLLLLLLLLLLLLLNNN", 'X' },
- { "NNNNNNNNNNNNNNLRNNNNNNNNNNNNNNNNNNNNNNNNNNLRNLRN#####NNNNNNNNNNNNNNN#NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN#L##########NNNL############NNN###################################NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },
- { "NNLNNNNLNNLLLLLLLLLLNLNNNLLLLLNNNNNNLNLNLNLLLL#LLLNLLLLLLLNNLLLLNNNNNLLLLLNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },
- { "NNNNNNNNNNNNNNNNNN##NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },
+ { "NNNNNNNNNNNNNNLRNNNNNNNNNNNNNNNNNNNNNNNNNNLRNLRN.....NNNNNNNNNNNNNNN.NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN#L########..NNNL##########..NNN...................................NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },
+ { "NNLNNNNLNNLLLLLLLLLLNLNNNLLLLLNNNNNNLNLNLNLLLL.LLLNLLLLLLLNNLLLLNNNNNLLLLLNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },
+ { "NNNNNNNNNNNNNNNNNN..NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },
{ "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },
{ "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN####################LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNN", 'X' },
{ NULL, 'N' },
@@ -271,11 +271,11 @@ static UnicodeMapTableEntry typeTable[256] = {
{ NULL, 'L' },
{ NULL, 'L' },
{ NULL, 'L' },
- { "LLLLLLLLLLLLLLLLLLLLLLLLRRRRRRNRRRRRRRRRR#RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR", 'X' },
+ { "LLLLLLLLLLLLLLLLLLLLLLLLRRRRRRNRRRRRRRRRR.RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR", 'X' },
{ NULL, 'R' },
{ "RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNN", 'X' },
- { "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN#N#NN#NNNNNNNNN#NN##NNNNN##NRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNN", 'X' },
- { "NNN###NNNNN################NNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#####NNN##NNNNNNNNNNNNNNNNNNNNNNNLL", 'X' }
+ { "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN.N.NN.NNNNNNNNN.NN..NNNNN..NRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNN", 'X' },
+ { "NNN...NNNNN.....##########.NNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL.....NNN..NNNNNNNNNNNNNNNNNNNNNNNLL", 'X' }
};
static UnicodeCaseTableVector caseTable00 = {{
@@ -935,13 +935,23 @@ GBool unicodeTypeR(Unicode c) {
}
GBool unicodeTypeNum(Unicode c) {
- return getType(c) == '#';
+ char t;
+
+ t = getType(c);
+ return t == '#' || t == '.';
}
GBool unicodeTypeAlphaNum(Unicode c) {
char t;
t = getType(c);
+ return t == 'L' || t == 'R' || t == '#' || t == '.';
+}
+
+GBool unicodeTypeWord(Unicode c) {
+ char t;
+
+ t = getType(c);
return t == 'L' || t == 'R' || t == '#';
}
diff --git a/xpdf/UnicodeTypeTable.h b/xpdf/UnicodeTypeTable.h
index 879363d..3230d92 100644
--- a/xpdf/UnicodeTypeTable.h
+++ b/xpdf/UnicodeTypeTable.h
@@ -2,7 +2,7 @@
//
// UnicodeTypeTable.h
//
-// Copyright 2003 Glyph & Cog, LLC
+// Copyright 2003-2013 Glyph & Cog, LLC
//
//========================================================================
@@ -19,6 +19,8 @@ extern GBool unicodeTypeNum(Unicode c);
extern GBool unicodeTypeAlphaNum(Unicode c);
+extern GBool unicodeTypeWord(Unicode c);
+
extern Unicode unicodeToUpper(Unicode c);
#endif
diff --git a/xpdf/XFAForm.cc b/xpdf/XFAForm.cc
new file mode 100644
index 0000000..fd420a9
--- /dev/null
+++ b/xpdf/XFAForm.cc
@@ -0,0 +1,1458 @@
+//========================================================================
+//
+// XFAForm.cc
+//
+// Copyright 2012 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include "GString.h"
+#include "GList.h"
+#include "GHash.h"
+#include "Error.h"
+#include "Object.h"
+#include "PDFDoc.h"
+#include "Gfx.h"
+#include "GfxFont.h"
+#include "Zoox.h"
+#include "XFAForm.h"
+
+#ifdef _WIN32
+# define strcasecmp stricmp
+# define strncasecmp strnicmp
+#endif
+
+//------------------------------------------------------------------------
+
+// 5 bars + 5 spaces -- each can be wide (1) or narrow (0)
+// (there are always exactly 3 wide elements;
+// the last space is always narrow)
+static Guchar code3Of9Data[128][10] = {
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 0x00
+ { 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, 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, 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, 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, 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 }, // 0x10
+ { 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, 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, 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, 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, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 1, 0, 0, 0, 1, 0, 0, 0 }, // ' ' = 0x20
+ { 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, 1, 0, 1, 0, 1, 0, 0, 0, 0 }, // '$' = 0x24
+ { 0, 0, 0, 1, 0, 1, 0, 1, 0, 0 }, // '%' = 0x25
+ { 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, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 1, 0, 1, 0, 0, 0 }, // '*' = 0x2a
+ { 0, 1, 0, 0, 0, 1, 0, 1, 0, 0 }, // '+' = 0x2b
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 1, 0, 1, 0 }, // '-' = 0x2d
+ { 1, 1, 0, 0, 0, 0, 1, 0, 0, 0 }, // '.' = 0x2e
+ { 0, 1, 0, 1, 0, 0, 0, 1, 0, 0 }, // '/' = 0x2f
+ { 0, 0, 0, 1, 1, 0, 1, 0, 0, 0 }, // '0' = 0x30
+ { 1, 0, 0, 1, 0, 0, 0, 0, 1, 0 }, // '1'
+ { 0, 0, 1, 1, 0, 0, 0, 0, 1, 0 }, // '2'
+ { 1, 0, 1, 1, 0, 0, 0, 0, 0, 0 }, // '3'
+ { 0, 0, 0, 1, 1, 0, 0, 0, 1, 0 }, // '4'
+ { 1, 0, 0, 1, 1, 0, 0, 0, 0, 0 }, // '5'
+ { 0, 0, 1, 1, 1, 0, 0, 0, 0, 0 }, // '6'
+ { 0, 0, 0, 1, 0, 0, 1, 0, 1, 0 }, // '7'
+ { 1, 0, 0, 1, 0, 0, 1, 0, 0, 0 }, // '8'
+ { 0, 0, 1, 1, 0, 0, 1, 0, 0, 0 }, // '9'
+ { 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, 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, 0, 0, 0, 0, 0, 0 }, // 0x40
+ { 1, 0, 0, 0, 0, 1, 0, 0, 1, 0 }, // 'A' = 0x41
+ { 0, 0, 1, 0, 0, 1, 0, 0, 1, 0 }, // 'B'
+ { 1, 0, 1, 0, 0, 1, 0, 0, 0, 0 }, // 'C'
+ { 0, 0, 0, 0, 1, 1, 0, 0, 1, 0 }, // 'D'
+ { 1, 0, 0, 0, 1, 1, 0, 0, 0, 0 }, // 'E'
+ { 0, 0, 1, 0, 1, 1, 0, 0, 0, 0 }, // 'F'
+ { 0, 0, 0, 0, 0, 1, 1, 0, 1, 0 }, // 'G'
+ { 1, 0, 0, 0, 0, 1, 1, 0, 0, 0 }, // 'H'
+ { 0, 0, 1, 0, 0, 1, 1, 0, 0, 0 }, // 'I'
+ { 0, 0, 0, 0, 1, 1, 1, 0, 0, 0 }, // 'J'
+ { 1, 0, 0, 0, 0, 0, 0, 1, 1, 0 }, // 'K'
+ { 0, 0, 1, 0, 0, 0, 0, 1, 1, 0 }, // 'L'
+ { 1, 0, 1, 0, 0, 0, 0, 1, 0, 0 }, // 'M'
+ { 0, 0, 0, 0, 1, 0, 0, 1, 1, 0 }, // 'N'
+ { 1, 0, 0, 0, 1, 0, 0, 1, 0, 0 }, // 'O'
+ { 0, 0, 1, 0, 1, 0, 0, 1, 0, 0 }, // 'P' = 0x50
+ { 0, 0, 0, 0, 0, 0, 1, 1, 1, 0 }, // 'Q'
+ { 1, 0, 0, 0, 0, 0, 1, 1, 0, 0 }, // 'R'
+ { 0, 0, 1, 0, 0, 0, 1, 1, 0, 0 }, // 'S'
+ { 0, 0, 0, 0, 1, 0, 1, 1, 0, 0 }, // 'T'
+ { 1, 1, 0, 0, 0, 0, 0, 0, 1, 0 }, // 'U'
+ { 0, 1, 1, 0, 0, 0, 0, 0, 1, 0 }, // 'V'
+ { 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 }, // 'W'
+ { 0, 1, 0, 0, 1, 0, 0, 0, 1, 0 }, // 'X'
+ { 1, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, // 'Y'
+ { 0, 1, 1, 0, 1, 0, 0, 0, 0, 0 }, // 'Z'
+ { 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, 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 }, // 0x60
+ { 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, 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, 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, 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, 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 }, // 0x70
+ { 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, 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, 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, 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, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
+};
+
+//------------------------------------------------------------------------
+// XFAForm
+//------------------------------------------------------------------------
+
+XFAForm *XFAForm::load(PDFDoc *docA, Object *acroFormObj, Object *xfaObj) {
+ XFAForm *xfaForm;
+ ZxDoc *xmlA;
+ ZxElement *tmpl;
+ Object catDict, resourceDictA, obj1;
+ GString *data;
+ GBool fullXFAA;
+ GString *name;
+ char buf[4096];
+ int n, i;
+
+ docA->getXRef()->getCatalog(&catDict);
+ catDict.dictLookup("NeedsRendering", &obj1);
+ fullXFAA = obj1.isBool() && obj1.getBool();
+ obj1.free();
+ catDict.free();
+
+ if (xfaObj->isStream()) {
+ data = new GString();
+ xfaObj->streamReset();
+ while ((n = xfaObj->getStream()->getBlock(buf, sizeof(buf))) > 0) {
+ data->append(buf, n);
+ }
+ } else if (xfaObj->isArray()) {
+ data = new GString();
+ for (i = 1; i < xfaObj->arrayGetLength(); i += 2) {
+ if (!xfaObj->arrayGet(i, &obj1)->isStream()) {
+ error(errSyntaxError, -1, "XFA array element is wrong type");
+ obj1.free();
+ delete data;
+ return NULL;
+ }
+ obj1.streamReset();
+ while ((n = obj1.getStream()->getBlock(buf, sizeof(buf))) > 0) {
+ data->append(buf, n);
+ }
+ obj1.free();
+ }
+ } else {
+ error(errSyntaxError, -1, "XFA object is wrong type");
+ return NULL;
+ }
+
+ xmlA = ZxDoc::loadMem(data->getCString(), data->getLength());
+ delete data;
+ if (!xmlA) {
+ error(errSyntaxError, -1, "Invalid XML in XFA form");
+ return NULL;
+ }
+
+ if (acroFormObj->isDict()) {
+ acroFormObj->dictLookup("DR", &resourceDictA);
+ }
+
+ xfaForm = new XFAForm(docA, xmlA, &resourceDictA, fullXFAA);
+
+ resourceDictA.free();
+
+ if (xfaForm->xml->getRoot()) {
+ if ((tmpl = xfaForm->xml->getRoot()->findFirstChildElement("template"))) {
+ name = new GString("form");
+ xfaForm->curPageNum = 1;
+ xfaForm->curXOffset = xfaForm->curYOffset = 0;
+ xfaForm->scanFields(tmpl, name, name);
+ delete name;
+ }
+ }
+
+ return xfaForm;
+}
+
+XFAForm::XFAForm(PDFDoc *docA, ZxDoc *xmlA, Object *resourceDictA,
+ GBool fullXFAA): Form(docA) {
+ xml = xmlA;
+ fields = new GList();
+ resourceDictA->copy(&resourceDict);
+ fullXFA = fullXFAA;
+}
+
+XFAForm::~XFAForm() {
+ delete xml;
+ deleteGList(fields, XFAFormField);
+ resourceDict.free();
+}
+
+void XFAForm::scanFields(ZxElement *elem, GString *name, GString *dataName) {
+ ZxAttr *attr;
+ ZxNode *child;
+ ZxElement *bindElem;
+ GHash *names1, *names2;
+ GString *childName, *fullName, *fullDataName;
+ int i;
+
+ //~ need to handle subform
+
+ //~ need to handle exclGroup
+ //~ - fields in an exclGroup may/must(?) not have names
+ //~ - each field has an items element with the the value when that
+ //~ field is selected
+
+ if (elem->isElement("field")) {
+ fields->append(new XFAFormField(this, elem, name->copy(),
+ dataName->copy(), curPageNum,
+ curXOffset, curYOffset));
+ } else if (elem->isElement("breakBefore")) {
+ if ((attr = elem->findAttr("targetType")) &&
+ !attr->getValue()->cmp("pageArea") &&
+ (attr = elem->findAttr("startNew")) &&
+ !attr->getValue()->cmp("1")) {
+ ++curPageNum;
+ }
+ } else if (elem->isElement("break")) {
+ if ((attr = elem->findAttr("before")) &&
+ !attr->getValue()->cmp("pageArea") &&
+ (attr = elem->findAttr("startNew")) &&
+ !attr->getValue()->cmp("1")) {
+ ++curPageNum;
+ }
+ } else if (elem->isElement("contentArea")) {
+ curXOffset = XFAFormField::getMeasurement(elem->findAttr("x"), 0);
+ curYOffset = XFAFormField::getMeasurement(elem->findAttr("y"), 0);
+ } else {
+ names1 = new GHash();
+ for (child = elem->getFirstChild(); child; child = child->getNextChild()) {
+ if (child->isElement() &&
+ (attr = ((ZxElement *)child)->findAttr("name"))) {
+ childName = attr->getValue();
+ names1->replace(childName, names1->lookupInt(childName) + 1);
+ }
+ }
+ names2 = new GHash();
+ for (child = elem->getFirstChild(); child; child = child->getNextChild()) {
+ if (child->isElement()) {
+ if (!((bindElem = child->findFirstChildElement("bind")) &&
+ (attr = bindElem->findAttr("match")) &&
+ !attr->getValue()->cmp("none")) &&
+ (attr = ((ZxElement *)child)->findAttr("name"))) {
+ childName = attr->getValue();
+ if (names1->lookupInt(childName) > 1) {
+ i = names2->lookupInt(childName);
+ fullName = GString::format("{0:t}.{1:t}[{2:d}]",
+ name, childName, i);
+ fullDataName = GString::format("{0:t}.{1:t}[{2:d}]",
+ dataName, childName, i);
+ names2->replace(childName, i + 1);
+ } else {
+ fullName = GString::format("{0:t}.{1:t}", name, childName);
+ fullDataName = GString::format("{0:t}.{1:t}", dataName, childName);
+ }
+ } else {
+ fullName = name->copy();
+ fullDataName = dataName->copy();
+ }
+ scanFields((ZxElement *)child, fullName, fullDataName);
+ delete fullName;
+ delete fullDataName;
+ }
+ }
+ delete names1;
+ delete names2;
+ }
+}
+
+void XFAForm::draw(int pageNum, Gfx *gfx, GBool printing) {
+ GfxFontDict *fontDict;
+ Object obj1;
+ int i;
+
+ // build the font dictionary
+ if (resourceDict.isDict() &&
+ resourceDict.dictLookup("Font", &obj1)->isDict()) {
+ fontDict = new GfxFontDict(doc->getXRef(), NULL, obj1.getDict());
+ } else {
+ fontDict = NULL;
+ }
+ obj1.free();
+
+ for (i = 0; i < fields->getLength(); ++i) {
+ ((XFAFormField *)fields->get(i))->draw(pageNum, gfx, printing, fontDict);
+ }
+
+ delete fontDict;
+}
+
+int XFAForm::getNumFields() {
+ return fields->getLength();
+}
+
+FormField *XFAForm::getField(int idx) {
+ return (XFAFormField *)fields->get(idx);
+}
+
+//------------------------------------------------------------------------
+// XFAFormField
+//------------------------------------------------------------------------
+
+XFAFormField::XFAFormField(XFAForm *xfaFormA, ZxElement *xmlA, GString *nameA,
+ GString *dataNameA, int pageNumA,
+ double xOffsetA, double yOffsetA) {
+ xfaForm = xfaFormA;
+ xml = xmlA;
+ name = nameA;
+ dataName = dataNameA;
+ pageNum = pageNumA;
+ xOffset = xOffsetA;
+ yOffset = yOffsetA;
+}
+
+XFAFormField::~XFAFormField() {
+ delete name;
+ delete dataName;
+}
+
+const char *XFAFormField::getType() {
+ ZxElement *uiElem;
+ ZxNode *node;
+
+ if ((uiElem = xml->findFirstChildElement("ui"))) {
+ for (node = uiElem->getFirstChild(); node; node = node->getNextChild()) {
+ if (node->isElement("textEdit")) {
+ return "Text";
+ } else if (node->isElement("barcode")) {
+ return "BarCode";
+ }
+ //~ other field types go here
+ }
+ }
+ return NULL;
+}
+
+Unicode *XFAFormField::getName(int *length) {
+ //~ assumes name is UTF-8
+ return utf8ToUnicode(name, length);
+}
+
+Unicode *XFAFormField::getValue(int *length) {
+ ZxElement *uiElem;
+ ZxNode *node;
+ GString *s;
+
+ //~ assumes value is UTF-8
+ s = NULL;
+ if ((uiElem = xml->findFirstChildElement("ui"))) {
+ for (node = uiElem->getFirstChild(); node; node = node->getNextChild()) {
+ if (node->isElement("textEdit")) {
+ s = getFieldValue("text");
+ } else if (node->isElement("barcode")) {
+ s = getFieldValue("text");
+ }
+ //~ other field types go here
+ }
+ }
+ if (!s) {
+ return NULL;
+ }
+ return utf8ToUnicode(s, length);
+}
+
+Unicode *XFAFormField::utf8ToUnicode(GString *s, int *length) {
+ Unicode *u;
+ int n, size, c0, c1, c2, c3, c4, c5, i;
+
+ n = size = 0;
+ u = NULL;
+ i = 0;
+ while (i < s->getLength()) {
+ if (n == size) {
+ size = size ? size * 2 : 16;
+ u = (Unicode *)greallocn(u, size, sizeof(Unicode));
+ }
+ c0 = s->getChar(i++) & 0xff;
+ if (c0 <= 0x7f) {
+ u[n++] = c0;
+ } else if (c0 <= 0xdf && i < n) {
+ c1 = s->getChar(i++) & 0xff;
+ u[n++] = ((c0 & 0x1f) << 6) | (c1 & 0x3f);
+ } else if (c0 <= 0xef && i+1 < n) {
+ c1 = s->getChar(i++) & 0xff;
+ c2 = s->getChar(i++) & 0xff;
+ u[n++] = ((c0 & 0x0f) << 12) | ((c1 & 0x3f) << 6) | (c2 & 0x3f);
+ } else if (c0 <= 0xf7 && i+2 < n) {
+ c1 = s->getChar(i++) & 0xff;
+ c2 = s->getChar(i++) & 0xff;
+ c3 = s->getChar(i++) & 0xff;
+ u[n++] = ((c0 & 0x07) << 18) | ((c1 & 0x3f) << 12) | ((c2 & 0x3f) << 6)
+ | (c3 & 0x3f);
+ } else if (c0 <= 0xfb && i+3 < n) {
+ c1 = s->getChar(i++) & 0xff;
+ c2 = s->getChar(i++) & 0xff;
+ c3 = s->getChar(i++) & 0xff;
+ c4 = s->getChar(i++) & 0xff;
+ u[n++] = ((c0 & 0x03) << 24) | ((c1 & 0x3f) << 18) | ((c2 & 0x3f) << 12)
+ | ((c3 & 0x3f) << 6) | (c4 & 0x3f);
+ } else if (c0 <= 0xfd && i+4 < n) {
+ c1 = s->getChar(i++) & 0xff;
+ c2 = s->getChar(i++) & 0xff;
+ c3 = s->getChar(i++) & 0xff;
+ c4 = s->getChar(i++) & 0xff;
+ c5 = s->getChar(i++) & 0xff;
+ u[n++] = ((c0 & 0x01) << 30) | ((c1 & 0x3f) << 24) | ((c2 & 0x3f) << 18)
+ | ((c3 & 0x3f) << 12) | ((c4 & 0x3f) << 6) | (c5 & 0x3f);
+ } else {
+ u[n++] = '?';
+ }
+ }
+ *length = n;
+ return u;
+}
+
+void XFAFormField::draw(int pageNumA, Gfx *gfx, GBool printing,
+ GfxFontDict *fontDict) {
+ Page *page;
+ PDFRectangle *pageRect;
+ ZxElement *uiElem;
+ ZxNode *node;
+ ZxAttr *attr;
+ GString *appearBuf;
+ MemStream *appearStream;
+ Object appearDict, appearance, obj1, obj2;
+ double mat[6];
+ double x, y, w, h, x2, y2, w2, h2, x3, y3, w3, h3;
+ double anchorX, anchorY;
+ int pageRot, rot, rot3;
+
+ if (pageNumA != pageNum) {
+ return;
+ }
+
+ page = xfaForm->doc->getCatalog()->getPage(pageNum);
+ pageRect = page->getMediaBox();
+ pageRot = page->getRotate();
+
+ anchorX = 0;
+ anchorY = 0;
+ if ((attr = xml->findAttr("anchorType"))) {
+ if (!attr->getValue()->cmp("topLeft")) {
+ anchorX = 0;
+ anchorY = 0;
+ } else if (!attr->getValue()->cmp("topCenter")) {
+ anchorX = 0.5;
+ anchorY = 0;
+ } else if (!attr->getValue()->cmp("topRight")) {
+ anchorX = 1;
+ anchorY = 0;
+ } else if (!attr->getValue()->cmp("middleLeft")) {
+ anchorX = 0;
+ anchorY = 0.5;
+ } else if (!attr->getValue()->cmp("middleCenter")) {
+ anchorX = 0.5;
+ anchorY = 0.5;
+ } else if (!attr->getValue()->cmp("middleRight")) {
+ anchorX = 1;
+ anchorY = 0.5;
+ } else if (!attr->getValue()->cmp("bottomLeft")) {
+ anchorX = 0;
+ anchorY = 1;
+ } else if (!attr->getValue()->cmp("bottomCenter")) {
+ anchorX = 0.5;
+ anchorY = 1;
+ } else if (!attr->getValue()->cmp("bottomRight")) {
+ anchorX = 1;
+ anchorY = 1;
+ }
+ }
+ x = getMeasurement(xml->findAttr("x"), 0) + xOffset;
+ y = getMeasurement(xml->findAttr("y"), 0) + yOffset;
+ w = getMeasurement(xml->findAttr("w"), 0);
+ h = getMeasurement(xml->findAttr("h"), 0);
+ if ((attr = xml->findAttr("rotate"))) {
+ rot = atoi(attr->getValue()->getCString());
+ if ((rot %= 360) < 0) {
+ rot += 360;
+ }
+ } else {
+ rot = 0;
+ }
+
+ // get annot rect (UL corner, width, height) in XFA coords
+ // notes:
+ // - XFA coordinates are top-left origin, after page rotation
+ // - XFA coordinates are dependent on choice of anchor point
+ // and field rotation
+ switch (rot) {
+ case 0:
+ default:
+ x2 = x - anchorX * w;
+ y2 = y - anchorY * h;
+ w2 = w;
+ h2 = h;
+ break;
+ case 90:
+ x2 = x - anchorY * h;
+ y2 = y - (1 - anchorX) * w;
+ w2 = h;
+ h2 = w;
+ break;
+ case 180:
+ x2 = x - (1 - anchorX) * w;
+ y2 = y - (1 - anchorY) * h;
+ w2 = w;
+ h2 = h;
+ break;
+ case 270:
+ x2 = x - (1 - anchorY) * h;
+ y2 = y - anchorX * w;
+ w2 = h;
+ h2 = w;
+ break;
+ }
+
+ // convert annot rect to PDF coords (LL corner, width, height),
+ // taking page rotation into account
+ switch (pageRot) {
+ case 0:
+ default:
+ x3 = pageRect->x1 + x2;
+ y3 = pageRect->y2 - (y2 + h2);
+ w3 = w2;
+ h3 = h2;
+ break;
+ case 90:
+ x3 = pageRect->x1 + y2;
+ y3 = pageRect->y1 + x2;
+ w3 = h2;
+ h3 = w2;
+ break;
+ case 180:
+ x3 = pageRect->x2 - (x2 + w2);
+ y3 = pageRect->y1 + y2;
+ w3 = w2;
+ h3 = h2;
+ break;
+ case 270:
+ x3 = pageRect->x2 - (y2 + h2);
+ y3 = pageRect->y1 + (x2 + w2);
+ w3 = h2;
+ h3 = w2;
+ break;
+ }
+ rot3 = (rot + pageRot) % 360;
+
+ // generate transform matrix
+ switch (rot3) {
+ case 0:
+ default:
+ mat[0] = 1; mat[1] = 0;
+ mat[2] = 0; mat[3] = 1;
+ mat[4] = 0; mat[5] = 0;
+ break;
+ case 90:
+ mat[0] = 0; mat[1] = 1;
+ mat[2] = -1; mat[3] = 0;
+ mat[4] = h; mat[5] = 0;
+ break;
+ case 180:
+ mat[0] = -1; mat[1] = 0;
+ mat[2] = 0; mat[3] = -1;
+ mat[4] = w; mat[5] = h;
+ break;
+ case 270:
+ mat[0] = 0; mat[1] = -1;
+ mat[2] = 1; mat[3] = 0;
+ mat[4] = 0; mat[5] = w;
+ break;
+ }
+
+ // get the appearance stream data
+ appearBuf = new GString();
+#if 0 //~ for debugging
+ appearBuf->appendf("q 1 1 0 rg 0 0 {0:.4f} {1:.4f} re f Q\n", w, h);
+#endif
+ if ((uiElem = xml->findFirstChildElement("ui"))) {
+ for (node = uiElem->getFirstChild(); node; node = node->getNextChild()) {
+ if (node->isElement("textEdit")) {
+ drawTextEdit(fontDict, w, h, rot3, appearBuf);
+ break;
+ } else if (node->isElement("barcode")) {
+ drawBarCode(fontDict, w, h, rot3, appearBuf);
+ break;
+ }
+ //~ other field types go here
+ }
+ }
+
+ // create the appearance stream
+ appearDict.initDict(xfaForm->doc->getXRef());
+ appearDict.dictAdd(copyString("Length"),
+ obj1.initInt(appearBuf->getLength()));
+ appearDict.dictAdd(copyString("Subtype"), obj1.initName("Form"));
+ obj1.initArray(xfaForm->doc->getXRef());
+ obj1.arrayAdd(obj2.initReal(0));
+ obj1.arrayAdd(obj2.initReal(0));
+ obj1.arrayAdd(obj2.initReal(w));
+ obj1.arrayAdd(obj2.initReal(h));
+ appearDict.dictAdd(copyString("BBox"), &obj1);
+ obj1.initArray(xfaForm->doc->getXRef());
+ obj1.arrayAdd(obj2.initReal(mat[0]));
+ obj1.arrayAdd(obj2.initReal(mat[1]));
+ obj1.arrayAdd(obj2.initReal(mat[2]));
+ obj1.arrayAdd(obj2.initReal(mat[3]));
+ obj1.arrayAdd(obj2.initReal(mat[4]));
+ obj1.arrayAdd(obj2.initReal(mat[5]));
+ appearDict.dictAdd(copyString("Matrix"), &obj1);
+ if (xfaForm->resourceDict.isDict()) {
+ appearDict.dictAdd(copyString("Resources"),
+ xfaForm->resourceDict.copy(&obj1));
+ }
+ appearStream = new MemStream(appearBuf->getCString(), 0,
+ appearBuf->getLength(), &appearDict);
+ appearance.initStream(appearStream);
+ gfx->drawAnnot(&appearance, NULL, x3, y3, x3 + w3, y3 + h3);
+ appearance.free();
+ delete appearBuf;
+}
+
+void XFAFormField::drawTextEdit(GfxFontDict *fontDict,
+ double w, double h, int rot,
+ GString *appearBuf) {
+ ZxElement *valueElem, *textElem, *uiElem, *textEditElem, *combElem;
+ ZxElement *fontElem, *paraElem;
+ ZxAttr *attr;
+ GString *value, *fontName;
+ double fontSize;
+ int maxChars, combCells;
+ GBool multiLine, bold, italic;
+ XFAHorizAlign hAlign;
+ XFAVertAlign vAlign;
+
+ if (!(value = getFieldValue("text"))) {
+ return;
+ }
+
+ maxChars = 0;
+ if ((valueElem = xml->findFirstChildElement("value")) &&
+ (textElem = valueElem->findFirstChildElement("text")) &&
+ (attr = textElem->findAttr("maxChars"))) {
+ maxChars = atoi(attr->getValue()->getCString());
+ }
+
+ multiLine = gFalse;
+ combCells = 0;
+ if ((uiElem = xml->findFirstChildElement("ui")) &&
+ (textEditElem = uiElem->findFirstChildElement("textEdit"))) {
+ if ((attr = textEditElem->findAttr("multiLine")) &&
+ !attr->getValue()->cmp("1")) {
+ multiLine = gTrue;
+ }
+ if ((combElem = textEditElem->findFirstChildElement("comb"))) {
+ if ((attr = combElem->findAttr("numberOfCells"))) {
+ combCells = atoi(attr->getValue()->getCString());
+ } else {
+ combCells = maxChars;
+ }
+ }
+ }
+
+ fontName = NULL;
+ fontSize = 10;
+ bold = gFalse;
+ italic = gFalse;
+ if ((fontElem = xml->findFirstChildElement("font"))) {
+ if ((attr = fontElem->findAttr("typeface"))) {
+ fontName = attr->getValue()->copy();
+ }
+ if ((attr = fontElem->findAttr("weight"))) {
+ if (!attr->getValue()->cmp("bold")) {
+ bold = gTrue;
+ }
+ }
+ if ((attr = fontElem->findAttr("posture"))) {
+ if (!attr->getValue()->cmp("italic")) {
+ italic = gTrue;
+ }
+ }
+ if ((attr = fontElem->findAttr("size"))) {
+ fontSize = getMeasurement(attr, fontSize);
+ }
+ }
+ if (!fontName) {
+ fontName = new GString("Courier");
+ }
+
+ hAlign = xfaHAlignLeft;
+ vAlign = xfaVAlignTop;
+ if ((paraElem = xml->findFirstChildElement("para"))) {
+ if ((attr = paraElem->findAttr("hAlign"))) {
+ if (!attr->getValue()->cmp("left")) {
+ hAlign = xfaHAlignLeft;
+ } else if (!attr->getValue()->cmp("center")) {
+ hAlign = xfaHAlignCenter;
+ } else if (!attr->getValue()->cmp("right")) {
+ hAlign = xfaHAlignRight;
+ }
+ //~ other hAlign values (justify, justifyAll, radix) are
+ //~ currently unsupported
+ }
+ if ((attr = paraElem->findAttr("vAlign"))) {
+ if (!attr->getValue()->cmp("top")) {
+ vAlign = xfaVAlignTop;
+ } else if (!attr->getValue()->cmp("bottom")) {
+ vAlign = xfaVAlignBottom;
+ } else if (!attr->getValue()->cmp("middle")) {
+ vAlign = xfaVAlignMiddle;
+ }
+ }
+ }
+
+ drawText(value, multiLine, combCells,
+ fontName, bold, italic, fontSize,
+ hAlign, vAlign, 0, 0, w, h, gFalse, fontDict, appearBuf);
+ delete fontName;
+}
+
+void XFAFormField::drawBarCode(GfxFontDict *fontDict,
+ double w, double h, int rot,
+ GString *appearBuf) {
+ ZxElement *uiElem, *barcodeElem, *fontElem;
+ ZxAttr *attr;
+ GString *value, *value2, *barcodeType, *textLocation, *fontName, *s1, *s2;
+ XFAVertAlign textAlign;
+ double wideNarrowRatio, fontSize;
+ double yText, wText, yBarcode, hBarcode, wNarrow, xx;
+ GBool doText;
+ int dataLength;
+ GBool bold, italic;
+ char *p;
+ int i, j, c;
+
+ //--- get field value
+ if (!(value = getFieldValue("text"))) {
+ return;
+ }
+
+ //--- get field attributes
+ barcodeType = NULL;
+ wideNarrowRatio = 3;
+ dataLength = 0;
+ textLocation = NULL;
+ if ((uiElem = xml->findFirstChildElement("ui")) &&
+ (barcodeElem = uiElem->findFirstChildElement("barcode"))) {
+ if ((attr = barcodeElem->findAttr("type"))) {
+ barcodeType = attr->getValue();
+ }
+ if ((attr = barcodeElem->findAttr("wideNarrowRatio"))) {
+ s1 = attr->getValue();
+ if ((p = strchr(s1->getCString(), ':'))) {
+ s2 = new GString(s1, 0, p - s1->getCString());
+ wideNarrowRatio = atof(p + 1);
+ if (wideNarrowRatio == 0) {
+ wideNarrowRatio = 1;
+ }
+ wideNarrowRatio = atof(s2->getCString()) / wideNarrowRatio;
+ delete s2;
+ } else {
+ wideNarrowRatio = atof(s1->getCString());
+ }
+ }
+ if ((attr = barcodeElem->findAttr("dataLength"))) {
+ dataLength = atoi(attr->getValue()->getCString());
+ }
+ if ((attr = barcodeElem->findAttr("textLocation"))) {
+ textLocation = attr->getValue();
+ }
+ }
+ if (!barcodeType) {
+ error(errSyntaxError, -1, "Missing 'type' attribute in XFA barcode field");
+ return;
+ }
+ if (!dataLength) {
+ error(errSyntaxError, -1,
+ "Missing 'dataLength' attribute in XFA barcode field");
+ return;
+ }
+
+ //--- get font
+ fontName = NULL;
+ fontSize = 0.2 * h;
+ bold = gFalse;
+ italic = gFalse;
+ if ((fontElem = xml->findFirstChildElement("font"))) {
+ if ((attr = fontElem->findAttr("typeface"))) {
+ fontName = attr->getValue()->copy();
+ }
+ if ((attr = fontElem->findAttr("weight"))) {
+ if (!attr->getValue()->cmp("bold")) {
+ bold = gTrue;
+ }
+ }
+ if ((attr = fontElem->findAttr("posture"))) {
+ if (!attr->getValue()->cmp("italic")) {
+ italic = gTrue;
+ }
+ }
+ if ((attr = fontElem->findAttr("size"))) {
+ fontSize = getMeasurement(attr, fontSize);
+ }
+ }
+ if (!fontName) {
+ fontName = new GString("Courier");
+ }
+
+ //--- compute the embedded text type position
+ doText = gTrue;
+ yText = yBarcode = hBarcode = 0;
+ if (textLocation && !textLocation->cmp("above")) {
+ textAlign = xfaVAlignTop;
+ yText = h;
+ yBarcode = 0;
+ hBarcode = h - fontSize;
+ } else if (textLocation && !textLocation->cmp("belowEmbedded")) {
+ textAlign = xfaVAlignBottom;
+ yText = 0;
+ yBarcode = 0;
+ hBarcode = h;
+ } else if (textLocation && !textLocation->cmp("aboveEmbedded")) {
+ textAlign = xfaVAlignTop;
+ yText = h;
+ yBarcode = 0;
+ hBarcode = h;
+ } else if (textLocation && !textLocation->cmp("none")) {
+ textAlign = xfaVAlignBottom; // make gcc happy
+ doText = gFalse;
+ } else { // default is "below"
+ textAlign = xfaVAlignBottom;
+ yText = 0;
+ yBarcode = fontSize;
+ hBarcode = h - fontSize;
+ }
+ wText = w;
+
+ //--- remove extraneous start/stop chars
+ //~ this may depend on barcode type
+ value2 = value->copy();
+ if (value2->getLength() >= 1 && value2->getChar(0) == '*') {
+ value2->del(0);
+ }
+ if (value2->getLength() >= 1 &&
+ value2->getChar(value2->getLength() - 1) == '*') {
+ value2->del(value2->getLength() - 1);
+ }
+
+ //--- draw the bar code
+ if (!barcodeType->cmp("code3Of9")) {
+ appearBuf->append("0 g\n");
+ wNarrow = w / ((7 + 3 * wideNarrowRatio) * (dataLength + 2));
+ xx = 0;
+ for (i = -1; i <= value2->getLength(); ++i) {
+ if (i < 0 || i >= value2->getLength()) {
+ c = '*';
+ } else {
+ c = value2->getChar(i) & 0x7f;
+ }
+ for (j = 0; j < 10; j += 2) {
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} re f\n",
+ xx, yBarcode,
+ (code3Of9Data[c][j] ? wideNarrowRatio : 1) * wNarrow,
+ hBarcode);
+ xx += ((code3Of9Data[c][j] ? wideNarrowRatio : 1) +
+ (code3Of9Data[c][j+1] ? wideNarrowRatio : 1)) * wNarrow;
+ }
+ }
+ // center the text on the drawn barcode (not the max length barcode)
+ wText = (value2->getLength() + 2) * (7 + 3 * wideNarrowRatio) * wNarrow;
+ } else {
+ error(errSyntaxError, -1,
+ "Unimplemented barcode type in XFA barcode field");
+ }
+ //~ add other barcode types here
+
+ //--- draw the embedded text
+ if (doText) {
+ appearBuf->append("0 g\n");
+ drawText(value2, gFalse, 0,
+ fontName, bold, italic, fontSize,
+ xfaHAlignCenter, textAlign, 0, yText, wText, h, gTrue,
+ fontDict, appearBuf);
+ }
+ delete fontName;
+ delete value2;
+}
+
+Object *XFAFormField::getResources(Object *res) {
+ return xfaForm->resourceDict.copy(res);
+}
+
+double XFAFormField::getMeasurement(ZxAttr *attr, double defaultVal) {
+ GString *s;
+ double val, mul;
+ GBool neg;
+ int i;
+
+ if (!attr) {
+ return defaultVal;
+ }
+ s = attr->getValue();
+ i = 0;
+ neg = gFalse;
+ if (i < s->getLength() && s->getChar(i) == '+') {
+ ++i;
+ } else if (i < s->getLength() && s->getChar(i) == '-') {
+ neg = gTrue;
+ ++i;
+ }
+ val = 0;
+ while (i < s->getLength() && s->getChar(i) >= '0' && s->getChar(i) <= '9') {
+ val = val * 10 + s->getChar(i) - '0';
+ ++i;
+ }
+ if (i < s->getLength() && s->getChar(i) == '.') {
+ ++i;
+ mul = 0.1;
+ while (i < s->getLength() && s->getChar(i) >= '0' && s->getChar(i) <= '9') {
+ val += mul * (s->getChar(i) - '0');
+ mul *= 0.1;
+ ++i;
+ }
+ }
+ if (neg) {
+ val = -val;
+ }
+ if (i+1 < s->getLength()) {
+ if (s->getChar(i) == 'i' && s->getChar(i+1) == 'n') {
+ val *= 72;
+ } else if (s->getChar(i) == 'p' && s->getChar(i+1) == 't') {
+ // no change
+ } else if (s->getChar(i) == 'c' && s->getChar(i+1) == 'm') {
+ val *= 72 / 2.54;
+ } else if (s->getChar(i) == 'm' && s->getChar(i+1) == 'm') {
+ val *= 72 / 25.4;
+ } else {
+ // default to inches
+ val *= 72;
+ }
+ } else {
+ // default to inches
+ val *= 72;
+ }
+ return val;
+}
+
+GString *XFAFormField::getFieldValue(const char *valueChildType) {
+ ZxElement *valueElem, *datasets, *data, *elem;
+ char *p;
+
+ // check the <value> element within the field
+ if ((valueElem = xml->findFirstChildElement("value")) &&
+ (elem = valueElem->findFirstChildElement(valueChildType))) {
+ if (elem->getFirstChild() &&
+ elem->getFirstChild()->isCharData() &&
+ ((ZxCharData *)elem->getFirstChild())->getData()->getLength() > 0) {
+ return ((ZxCharData *)elem->getFirstChild())->getData();
+ }
+ }
+
+ // check the <datasets> packet
+ if (!xfaForm->xml->getRoot() ||
+ !(datasets =
+ xfaForm->xml->getRoot()->findFirstChildElement("xfa:datasets")) ||
+ !(data = datasets->findFirstChildElement("xfa:data"))) {
+ return NULL;
+ }
+ p = name->getCString();
+ if (!strncmp(p, "form.", 5)) {
+ p += 5;
+ } else {
+ return NULL;
+ }
+ elem = findFieldData(data, p);
+ if (elem &&
+ elem->getFirstChild() &&
+ elem->getFirstChild()->isCharData() &&
+ ((ZxCharData *)elem->getFirstChild())->getData()->getLength() > 0) {
+ return ((ZxCharData *)elem->getFirstChild())->getData();
+ }
+
+ return NULL;
+}
+
+ZxElement *XFAFormField::findFieldData(ZxElement *elem, char *partName) {
+ ZxNode *node;
+ GString *nodeName;
+ int curIdx, idx, n;
+
+ curIdx = 0;
+ for (node = elem->getFirstChild(); node; node = node->getNextChild()) {
+ if (node->isElement()) {
+ nodeName = ((ZxElement *)node)->getType();
+ n = nodeName->getLength();
+ if (!strncmp(partName, nodeName->getCString(), n)) {
+ if (partName[n] == '[') {
+ idx = atoi(partName + n + 1);
+ if (idx == curIdx) {
+ for (++n; partName[n] && partName[n-1] != ']'; ++n) ;
+ } else {
+ ++curIdx;
+ continue;
+ }
+ }
+ if (!partName[n]) {
+ return (ZxElement *)node;
+ } else if (partName[n] == '.') {
+ return findFieldData((ZxElement *)node, partName + n + 1);
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+void XFAFormField::transform(int rot, double w, double h,
+ double *wNew, double *hNew, GString *appearBuf) {
+ switch (rot) {
+ case 0:
+ default:
+ appearBuf->appendf("1 0 0 1 0 {0:.4f} cm\n", -h);
+ break;
+ case 90:
+ appearBuf->appendf("0 1 -1 0 {0:.4f} 0 cm\n", w);
+ *wNew = h;
+ *hNew = w;
+ break;
+ case 180:
+ appearBuf->appendf("-1 0 0 -1 {0:.4f} {1:.4f} cm\n", w, h);
+ *wNew = w;
+ *hNew = h;
+ break;
+ case 270:
+ appearBuf->appendf("0 -1 1 0 0 {0:.4f} cm\n", h);
+ *wNew = h;
+ *hNew = w;
+ break;
+ }
+}
+
+void XFAFormField::drawText(GString *text, GBool multiLine, int combCells,
+ GString *fontName, GBool bold,
+ GBool italic, double fontSize,
+ XFAHorizAlign hAlign, XFAVertAlign vAlign,
+ double x, double y, double w, double h,
+ GBool whiteBackground,
+ GfxFontDict *fontDict, GString *appearBuf) {
+ GfxFont *font;
+ GString *s;
+ double xx, yy, tw, charWidth, lineHeight;
+ double rectX, rectY, rectW, rectH;
+ int line, i, j, k, c, rectI;
+
+ //~ deal with Unicode text (is it UTF-8?)
+
+ // find the font
+ if (!(font = findFont(fontDict, fontName, bold, italic))) {
+ error(errSyntaxError, -1, "Couldn't find a font for '{0:t}', {1:s}, {2:s} used in XFA field",
+ fontName, bold ? "bold" : "non-bold",
+ italic ? "italic" : "non-italic");
+ return;
+ }
+
+ // setup
+ rectW = rectH = 0;
+ rectI = appearBuf->getLength();
+ appearBuf->append("BT\n");
+ appearBuf->appendf("/{0:t} {1:.2f} Tf\n", font->getTag(), fontSize);
+
+ // multi-line text
+ if (multiLine) {
+
+ // figure out how many lines will fit
+ lineHeight = 1.2 * fontSize;
+
+ // write a series of lines of text
+ line = 0;
+ i = 0;
+ while (i < text->getLength()) {
+
+ getNextLine(text, i, font, fontSize, w, &j, &tw, &k);
+ if (tw > rectW) {
+ rectW = tw;
+ }
+
+ // compute text start position
+ switch (hAlign) {
+ case xfaHAlignLeft:
+ default:
+ xx = x;
+ break;
+ case xfaHAlignCenter:
+ xx = x + 0.5 * (w - tw);
+ break;
+ case xfaHAlignRight:
+ xx = x + w - tw;
+ break;
+ }
+ yy = y + h - fontSize * font->getAscent() - line * lineHeight;
+
+ // draw the line
+ appearBuf->appendf("1 0 0 1 {0:.4f} {1:.4f} Tm\n", xx, yy);
+ 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;
+ ++line;
+ }
+ rectH = line * lineHeight;
+ rectY = y + h - rectH;
+
+ // comb formatting
+ } else if (combCells > 0) {
+
+ // compute comb spacing
+ tw = w / combCells;
+
+ // compute text start position
+ switch (hAlign) {
+ case xfaHAlignLeft:
+ default:
+ xx = x;
+ break;
+ case xfaHAlignCenter:
+ xx = x + (int)(0.5 * (combCells - text->getLength())) * tw;
+ break;
+ case xfaHAlignRight:
+ xx = x + w - text->getLength() * tw;
+ break;
+ }
+ rectW = text->getLength() * tw;
+ switch (vAlign) {
+ case xfaVAlignTop:
+ default:
+ yy = y + h - fontSize * font->getAscent();
+ break;
+ case xfaVAlignMiddle:
+ yy = y + 0.5 * (h - fontSize * (font->getAscent() +
+ font->getDescent()));
+ break;
+ case xfaVAlignBottom:
+ yy = y - fontSize * font->getDescent();
+ break;
+ }
+ rectY = yy + fontSize * font->getDescent();
+ rectH = fontSize * (font->getAscent() - font->getDescent());
+
+ // write the text string
+ for (i = 0; i < text->getLength(); ++i) {
+ c = text->getChar(i) & 0xff;
+ if (!font->isCIDFont()) {
+ charWidth = fontSize * ((Gfx8BitFont *)font)->getWidth(c);
+ appearBuf->appendf("1 0 0 1 {0:.4f} {1:.4f} Tm\n",
+ xx + i * tw + 0.5 * (tw - charWidth), yy);
+ } else {
+ appearBuf->appendf("1 0 0 1 {0:.4f} {1:.4f} Tm\n",
+ xx + i * tw, yy);
+ }
+ appearBuf->append('(');
+ if (c == '(' || c == ')' || c == '\\') {
+ appearBuf->append('\\');
+ appearBuf->append(c);
+ } else if (c < 0x20 || c >= 0x80) {
+ appearBuf->appendf("{0:.4f} 0 Td\n", w);
+ } else {
+ appearBuf->append(c);
+ }
+ appearBuf->append(") Tj\n");
+ }
+
+ // regular (non-comb) formatting
+ } else {
+
+ // compute string width
+ if (!font->isCIDFont()) {
+ tw = 0;
+ for (i = 0; i < text->getLength(); ++i) {
+ tw += ((Gfx8BitFont *)font)->getWidth(text->getChar(i));
+ }
+ } else {
+ // otherwise, make a crude estimate
+ tw = text->getLength() * 0.5;
+ }
+ tw *= fontSize;
+ rectW = tw;
+
+ // compute text start position
+ switch (hAlign) {
+ case xfaHAlignLeft:
+ default:
+ xx = x;
+ break;
+ case xfaHAlignCenter:
+ xx = x + 0.5 * (w - tw);
+ break;
+ case xfaHAlignRight:
+ xx = x + w - tw;
+ break;
+ }
+ switch (vAlign) {
+ case xfaVAlignTop:
+ default:
+ yy = y + h - fontSize * font->getAscent();
+ break;
+ case xfaVAlignMiddle:
+ yy = y + 0.5 * (h - fontSize * (font->getAscent() +
+ font->getDescent()));
+ break;
+ case xfaVAlignBottom:
+ yy = y - fontSize * font->getDescent();
+ break;
+ }
+ rectY = yy + fontSize * font->getDescent();
+ rectH = fontSize * (font->getAscent() - font->getDescent());
+ appearBuf->appendf("{0:.4f} {1:.4f} Td\n", xx, yy);
+
+ // 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");
+
+ // draw a white rectangle behind the text
+ if (whiteBackground) {
+ switch (hAlign) {
+ case xfaHAlignLeft:
+ default:
+ rectX = x;
+ break;
+ case xfaHAlignCenter:
+ rectX = x + 0.5 * (w - rectW);
+ break;
+ case xfaHAlignRight:
+ rectX = x + w - rectW;
+ break;
+ }
+ rectX -= 0.25 * fontSize;
+ rectW += 0.5 * fontSize;
+ s = GString::format("q 1 g {0:.4f} {1:.4f} {2:.4f} {3:.4f} re f Q\n",
+ rectX, rectY, rectW, rectH);
+ appearBuf->insert(rectI, s);
+ delete s;
+ }
+}
+
+// Searches <fontDict> for a font matching(<fontName>, <bold>,
+// <italic>).
+GfxFont *XFAFormField::findFont(GfxFontDict *fontDict, GString *fontName,
+ GBool bold, GBool italic) {
+ GString *reqName, *testName;
+ GfxFont *font;
+ GBool foundName, foundBold, foundItalic;
+ char *p;
+ char c;
+ int i, j;
+
+ if (!fontDict) {
+ return NULL;
+ }
+
+ reqName = new GString();
+ for (i = 0; i < fontName->getLength(); ++i) {
+ c = fontName->getChar(i);
+ if (c != ' ') {
+ reqName->append(c);
+ }
+ }
+
+ for (i = 0; i < fontDict->getNumFonts(); ++i) {
+ font = fontDict->getFont(i);
+ if (!font || !font->getName()) {
+ continue;
+ }
+ testName = new GString();
+ for (j = 0; j < font->getName()->getLength(); ++j) {
+ c = font->getName()->getChar(j);
+ if (c != ' ') {
+ testName->append(c);
+ }
+ }
+ foundName = foundBold = foundItalic = gFalse;
+ for (p = testName->getCString(); *p; ++p) {
+ if (!strncasecmp(p, reqName->getCString(), reqName->getLength())) {
+ foundName = gTrue;
+ }
+ if (!strncasecmp(p, "bold", 4)) {
+ foundBold = gTrue;
+ }
+ if (!strncasecmp(p, "italic", 6) || !strncasecmp(p, "oblique", 7)) {
+ foundItalic = gTrue;
+ }
+ }
+ delete testName;
+ if (foundName && foundBold == bold && foundItalic == italic) {
+ delete reqName;
+ return font;
+ }
+ }
+
+ delete reqName;
+ return NULL;
+}
+
+// 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 XFAFormField::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;
+}
diff --git a/xpdf/XFAForm.h b/xpdf/XFAForm.h
new file mode 100644
index 0000000..95bf14c
--- /dev/null
+++ b/xpdf/XFAForm.h
@@ -0,0 +1,126 @@
+//========================================================================
+//
+// XFAForm.h
+//
+// Copyright 2012 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef XFAFORM_H
+#define XFAFORM_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "Form.h"
+
+class ZxDoc;
+class ZxElement;
+class ZxAttr;
+
+//------------------------------------------------------------------------
+
+enum XFAHorizAlign {
+ xfaHAlignLeft,
+ xfaHAlignCenter,
+ xfaHAlignRight
+};
+
+enum XFAVertAlign {
+ xfaVAlignTop,
+ xfaVAlignBottom,
+ xfaVAlignMiddle
+};
+
+//------------------------------------------------------------------------
+
+class XFAForm: public Form {
+public:
+
+ static XFAForm *load(PDFDoc *docA, Object *acroFormObj, Object *xfaObj);
+
+ virtual ~XFAForm();
+
+ virtual const char *getType() { return "XFA"; }
+
+ virtual void draw(int pageNum, Gfx *gfx, GBool printing);
+
+ virtual int getNumFields();
+ virtual FormField *getField(int idx);
+
+private:
+
+ XFAForm(PDFDoc *docA, ZxDoc *xmlA, Object *resourceDictA, GBool fullXFAA);
+ void scanFields(ZxElement *elem, GString *name, GString *dataName);
+
+ ZxDoc *xml;
+ GList *fields; // [XFAFormField]
+ Object resourceDict;
+ GBool fullXFA; // true for "Full XFA", false for
+ // "XFA Foreground"
+ int curPageNum; // current page number - used by scanFields()
+ double curXOffset, // current x,y offset - used by scanFields()
+ curYOffset;
+
+ friend class XFAFormField;
+};
+
+//------------------------------------------------------------------------
+
+class XFAFormField: public FormField {
+public:
+
+ XFAFormField(XFAForm *xfaFormA, ZxElement *xmlA, GString *nameA,
+ GString *dataNameA, int pageNumA,
+ double xOffsetA, double yOffsetA);
+
+ virtual ~XFAFormField();
+
+ virtual const char *getType();
+ virtual Unicode *getName(int *length);
+ virtual Unicode *getValue(int *length);
+
+ virtual Object *getResources(Object *res);
+
+private:
+
+ Unicode *utf8ToUnicode(GString *s, int *length);
+ void draw(int pageNumA, Gfx *gfx, GBool printing, GfxFontDict *fontDict);
+ void drawTextEdit(GfxFontDict *fontDict,
+ double w, double h, int rot,
+ GString *appearBuf);
+ void drawBarCode(GfxFontDict *fontDict,
+ double w, double h, int rot,
+ GString *appearBuf);
+ static double getMeasurement(ZxAttr *attr, double defaultVal);
+ GString *getFieldValue(const char *valueChildType);
+ ZxElement *findFieldData(ZxElement *elem, char *partName);
+ void transform(int rot, double w, double h,
+ double *wNew, double *hNew, GString *appearBuf);
+ void drawText(GString *text, GBool multiLine, int combCells,
+ GString *fontName, GBool bold,
+ GBool italic, double fontSize,
+ XFAHorizAlign hAlign, XFAVertAlign vAlign,
+ double x, double y, double w, double h,
+ GBool whiteBackground,
+ GfxFontDict *fontDict, GString *appearBuf);
+ GfxFont *findFont(GfxFontDict *fontDict, GString *fontName,
+ GBool bold, GBool italic);
+ void getNextLine(GString *text, int start,
+ GfxFont *font, double fontSize, double wMax,
+ int *end, double *width, int *next);
+
+ XFAForm *xfaForm;
+ ZxElement *xml;
+ GString *name;
+ GString *dataName;
+ int pageNum;
+ double xOffset, yOffset;
+
+ friend class XFAForm;
+};
+
+#endif
diff --git a/xpdf/XPDFCore.cc b/xpdf/XPDFCore.cc
index b98bc37..9a3725f 100644
--- a/xpdf/XPDFCore.cc
+++ b/xpdf/XPDFCore.cc
@@ -493,7 +493,7 @@ void XPDFCore::doAction(LinkAction *action) {
}
s = ((LinkGoToR *)action)->getFileName()->getCString();
//~ translate path name for VMS (deal with '/')
- if (isAbsolutePath(s)) {
+ if (isAbsolutePath(s) || !doc->getFileName()) {
fileName = new GString(s);
} else {
fileName = appendToPath(grabPath(doc->getFileName()->getCString()), s);
@@ -531,7 +531,7 @@ void XPDFCore::doAction(LinkAction *action) {
if (!strcmp(s + fileName->getLength() - 4, ".pdf") ||
!strcmp(s + fileName->getLength() - 4, ".PDF")) {
//~ translate path name for VMS (deal with '/')
- if (isAbsolutePath(s)) {
+ if (isAbsolutePath(s) || !doc->getFileName()) {
fileName = fileName->copy();
} else {
fileName = appendToPath(grabPath(doc->getFileName()->getCString()), s);
@@ -640,7 +640,8 @@ void XPDFCore::doAction(LinkAction *action) {
if (movieAnnot.dictLookup("Movie", &obj1)->isDict()) {
if (obj1.dictLookup("F", &obj2)) {
if ((fileName = LinkAction::getFileSpecName(&obj2))) {
- if (!isAbsolutePath(fileName->getCString())) {
+ if (!isAbsolutePath(fileName->getCString()) &&
+ doc->getFileName()) {
fileName2 = appendToPath(
grabPath(doc->getFileName()->getCString()),
fileName->getCString());
@@ -658,6 +659,13 @@ void XPDFCore::doAction(LinkAction *action) {
movieAnnot.free();
break;
+ // unsupported action types
+ case actionJavaScript:
+ case actionSubmitForm:
+ case actionHide:
+ error(errSyntaxError, -1, "Unsupported link action type");
+ break;
+
// unknown action type
case actionUnknown:
error(errSyntaxError, -1, "Unknown link action type: '{0:t}'",
@@ -1124,6 +1132,9 @@ void XPDFCore::inputCbk(Widget widget, XtPointer ptr, XtPointer callData) {
case actionMovie:
s = "[movie]";
break;
+ case actionJavaScript:
+ case actionSubmitForm:
+ case actionHide:
case actionUnknown:
s = "[unknown link]";
break;
@@ -1356,6 +1367,8 @@ void XPDFCore::redrawRect(PDFCoreTile *tileA, int xSrc, int ySrc,
XFillRectangle(display, drawAreaWin, drawAreaGC,
xDest, yDest, width, height);
}
+
+ XFlush(display);
}
void XPDFCore::updateScrollbars() {
diff --git a/xpdf/XPDFViewer.cc b/xpdf/XPDFViewer.cc
index 2de349d..4292a0e 100644
--- a/xpdf/XPDFViewer.cc
+++ b/xpdf/XPDFViewer.cc
@@ -165,6 +165,7 @@ XPDFViewerCmd XPDFViewer::cmdTab[] = {
{ "about", 0, gFalse, gFalse, &XPDFViewer::cmdAbout },
{ "closeOutline", 0, gFalse, gFalse, &XPDFViewer::cmdCloseOutline },
{ "closeWindow", 0, gFalse, gFalse, &XPDFViewer::cmdCloseWindow },
+ { "closeWindowOrQuit", 0, gFalse, gFalse, &XPDFViewer::cmdCloseWindowOrQuit },
{ "continuousMode", 0, gFalse, gFalse, &XPDFViewer::cmdContinuousMode },
{ "endPan", 0, gTrue, gTrue, &XPDFViewer::cmdEndPan },
{ "endSelection", 0, gTrue, gTrue, &XPDFViewer::cmdEndSelection },
@@ -384,7 +385,9 @@ void XPDFViewer::open(GString *fileName, int pageA, GString *destName) {
int pg;
double z;
- if (!core->getDoc() || fileName->cmp(core->getDoc()->getFileName())) {
+ if (!core->getDoc() ||
+ !core->getDoc()->getFileName() ||
+ fileName->cmp(core->getDoc()->getFileName())) {
if (!loadFile(fileName, NULL, NULL)) {
return;
}
@@ -445,7 +448,7 @@ GBool XPDFViewer::loadFile(GString *fileName, GString *ownerPassword,
void XPDFViewer::reloadFile() {
int pg;
- if (!core->getDoc()) {
+ if (!core->getDoc() || !core->getDoc()->getFileName()) {
return;
}
pg = core->getPageNum();
@@ -808,6 +811,11 @@ void XPDFViewer::cmdCloseWindow(GString *args[], int nArgs,
app->close(this, gFalse);
}
+void XPDFViewer::cmdCloseWindowOrQuit(GString *args[], int nArgs,
+ XEvent *event) {
+ app->close(this, gTrue);
+}
+
void XPDFViewer::cmdContinuousMode(GString *args[], int nArgs,
XEvent *event) {
Widget btn;
@@ -1803,7 +1811,7 @@ void XPDFViewer::initToolbar(Widget parent) {
menuPane = XmCreatePulldownMenu(toolBar, "zoomMenuPane", args, n);
for (i = 0; i < nZoomMenuItems; ++i) {
n = 0;
- s = XmStringCreateLocalized(zoomMenuInfo[i].label);
+ s = XmStringCreateLocalized((char *)zoomMenuInfo[i].label);
XtSetArg(args[n], XmNlabelString, s); ++n;
XtSetArg(args[n], XmNuserData, (XtPointer)i); ++n;
sprintf(buf, "zoom%d", i);
@@ -3422,17 +3430,18 @@ void XPDFViewer::setupPrintDialog() {
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();
+ if ((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;
}
- psFileName2->append(".ps");
- XmTextFieldSetString(printFileText, psFileName2->getCString());
- delete psFileName2;
}
if (psFileName && psFileName->getChar(0) == '|') {
XmToggleButtonSetState(printWithCmdBtn, True, False);
diff --git a/xpdf/XPDFViewer.h b/xpdf/XPDFViewer.h
index 8a345e8..8b03e7f 100644
--- a/xpdf/XPDFViewer.h
+++ b/xpdf/XPDFViewer.h
@@ -103,6 +103,7 @@ private:
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 cmdCloseWindowOrQuit(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);
diff --git a/xpdf/XRef.cc b/xpdf/XRef.cc
index 83322c3..71540a9 100644
--- a/xpdf/XRef.cc
+++ b/xpdf/XRef.cc
@@ -18,6 +18,7 @@
#include <ctype.h>
#include <limits.h>
#include "gmem.h"
+#include "gfile.h"
#include "Object.h"
#include "Stream.h"
#include "Lexer.h"
@@ -43,6 +44,84 @@
#define defPermFlags 0xfffc
//------------------------------------------------------------------------
+// XRefPosSet
+//------------------------------------------------------------------------
+
+class XRefPosSet {
+public:
+
+ XRefPosSet();
+ ~XRefPosSet();
+ void add(GFileOffset pos);
+ GBool check(GFileOffset pos);
+
+private:
+
+ int find(GFileOffset pos);
+
+ GFileOffset *tab;
+ int size;
+ int len;
+};
+
+XRefPosSet::XRefPosSet() {
+ size = 16;
+ len = 0;
+ tab = (GFileOffset *)gmallocn(size, sizeof(GFileOffset));
+}
+
+XRefPosSet::~XRefPosSet() {
+ gfree(tab);
+}
+
+void XRefPosSet::add(GFileOffset pos) {
+ int i;
+
+ i = find(pos);
+ if (i < len && tab[i] == pos) {
+ return;
+ }
+ if (len == size) {
+ if (size > INT_MAX / 2) {
+ gMemError("Integer overflow in XRefPosSet::add()");
+ }
+ size *= 2;
+ tab = (GFileOffset *)greallocn(tab, size, sizeof(GFileOffset));
+ }
+ if (i < len) {
+ memmove(&tab[i + 1], &tab[i], (len - i) * sizeof(GFileOffset));
+ }
+ tab[i] = pos;
+ ++len;
+}
+
+GBool XRefPosSet::check(GFileOffset pos) {
+ int i;
+
+ i = find(pos);
+ return i < len && tab[i] == pos;
+}
+
+int XRefPosSet::find(GFileOffset pos) {
+ int a, b, m;
+
+ a = - 1;
+ b = len;
+ // invariant: tab[a] < pos < tab[b]
+ while (b - a > 1) {
+ m = (a + b) / 2;
+ if (tab[m] < pos) {
+ a = m;
+ } else if (tab[m] > pos) {
+ b = m;
+ } else {
+ return m;
+ }
+ }
+ return b;
+}
+
+//------------------------------------------------------------------------
// ObjectStream
//------------------------------------------------------------------------
@@ -134,7 +213,7 @@ ObjectStream::ObjectStream(XRef *xref, int objStrNumA) {
obj2.free();
delete parser;
gfree(offsets);
- goto err1;
+ goto err2;
}
objNums[i] = obj1.getInt();
offsets[i] = obj2.getInt();
@@ -144,7 +223,7 @@ ObjectStream::ObjectStream(XRef *xref, int objStrNumA) {
(i > 0 && offsets[i] < offsets[i-1])) {
delete parser;
gfree(offsets);
- goto err1;
+ goto err2;
}
}
while (str->getChar() != EOF) ;
@@ -153,8 +232,8 @@ ObjectStream::ObjectStream(XRef *xref, int objStrNumA) {
// 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();
+ if (i < offsets[0]) {
+ objStr.getStream()->discardChars(offsets[0] - i);
}
// parse the objects
@@ -175,6 +254,8 @@ ObjectStream::ObjectStream(XRef *xref, int objStrNumA) {
gfree(offsets);
ok = gTrue;
+ err2:
+ objStr.streamClose();
err1:
objStr.free();
}
@@ -203,8 +284,10 @@ Object *ObjectStream::getObject(int objIdx, int objNum, Object *obj) {
//------------------------------------------------------------------------
XRef::XRef(BaseStream *strA, GBool repair) {
- Guint pos;
+ GFileOffset pos;
Object obj;
+ XRefPosSet *posSet;
+ int i;
ok = gTrue;
errCode = errNone;
@@ -213,12 +296,18 @@ XRef::XRef(BaseStream *strA, GBool repair) {
entries = NULL;
streamEnds = NULL;
streamEndsLen = 0;
- objStr = NULL;
+ for (i = 0; i < objStrCacheSize; ++i) {
+ objStrs[i] = NULL;
+ }
encrypted = gFalse;
permFlags = defPermFlags;
ownerPasswordOk = gFalse;
+ for (i = 0; i < xrefCacheSize; ++i) {
+ cache[i].num = -1;
+ }
+
str = strA;
start = str->getStart();
@@ -241,7 +330,9 @@ XRef::XRef(BaseStream *strA, GBool repair) {
}
// read the xref table
- while (readXRef(&pos)) ;
+ posSet = new XRefPosSet();
+ while (readXRef(&pos, posSet)) ;
+ delete posSet;
if (!ok) {
errCode = errDamaged;
return;
@@ -268,30 +359,34 @@ XRef::XRef(BaseStream *strA, GBool repair) {
}
XRef::~XRef() {
+ int i;
+
+ for (i = 0; i < xrefCacheSize; ++i) {
+ if (cache[i].num >= 0) {
+ cache[i].obj.free();
+ }
+ }
gfree(entries);
trailerDict.free();
if (streamEnds) {
gfree(streamEnds);
}
- if (objStr) {
- delete objStr;
+ for (i = 0; i < objStrCacheSize; ++i) {
+ if (objStrs[i]) {
+ delete objStrs[i];
+ }
}
}
// Read the 'startxref' position.
-Guint XRef::getStartXref() {
+GFileOffset XRef::getStartXref() {
char buf[xrefSearchSize+1];
char *p;
- int c, n, i;
+ int 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;
- }
+ n = str->getBlock(buf, xrefSearchSize);
buf[n] = '\0';
// find startxref
@@ -304,86 +399,123 @@ Guint XRef::getStartXref() {
return 0;
}
for (p = &buf[i+9]; isspace(*p & 0xff); ++p) ;
- lastXRefPos = strToUnsigned(p);
+ lastXRefPos = strToFileOffset(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) {
+GBool XRef::readXRef(GFileOffset *pos, XRefPosSet *posSet) {
Parser *parser;
Object obj;
GBool more;
+ char buf[100];
+ int n, i;
- // 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, gTrue);
+ // the xref data should either be "xref ..." (for an xref table) or
+ // "nn gg obj << ... >> stream ..." (for an xref stream); possibly
+ // preceded by whitespace
+ str->setPos(start + *pos);
+ n = str->getBlock(buf, 100);
+ for (i = 0; i < n && Lexer::isSpace(buf[i]); ++i) ;
// parse an old-style xref table
- if (obj.isCmd("xref")) {
- obj.free();
- more = readXRefTable(parser, pos);
+ if (i + 4 < n &&
+ buf[i] == 'x' && buf[i+1] == 'r' && buf[i+2] == 'e' && buf[i+3] == 'f' &&
+ Lexer::isSpace(buf[i+4])) {
+ more = readXRefTable(pos, i + 5, posSet);
// parse an xref stream
- } else if (obj.isInt()) {
+ } else if (i < n && buf[i] >= '0' && buf[i] <= '9') {
+ obj.initNull();
+ parser = new Parser(NULL,
+ new Lexer(NULL,
+ str->makeSubStream(start + *pos, gFalse, 0, &obj)),
+ gTrue);
+ if (!parser->getObj(&obj, gTrue)->isInt()) {
+ goto err2;
+ }
obj.free();
if (!parser->getObj(&obj, gTrue)->isInt()) {
- goto err1;
+ goto err2;
}
obj.free();
if (!parser->getObj(&obj, gTrue)->isCmd("obj")) {
- goto err1;
+ goto err2;
}
obj.free();
if (!parser->getObj(&obj)->isStream()) {
- goto err1;
+ goto err2;
}
more = readXRefStream(obj.getStream(), pos);
obj.free();
+ delete parser;
} else {
goto err1;
}
- delete parser;
return more;
- err1:
+ err2:
obj.free();
delete parser;
+ err1:
ok = gFalse;
return gFalse;
}
-GBool XRef::readXRefTable(Parser *parser, Guint *pos) {
+GBool XRef::readXRefTable(GFileOffset *pos, int offset, XRefPosSet *posSet) {
XRefEntry entry;
- GBool more;
+ Parser *parser;
Object obj, obj2;
- Guint pos2;
- int first, n, newSize, i;
+ char buf[6];
+ GFileOffset off, pos2;
+ GBool more;
+ int first, n, newSize, gen, i, c;
+
+ if (posSet->check(*pos)) {
+ error(errSyntaxWarning, -1, "Infinite loop in xref table");
+ return gFalse;
+ }
+ posSet->add(*pos);
+
+ str->setPos(start + *pos + offset);
while (1) {
- parser->getObj(&obj, gTrue);
- if (obj.isCmd("trailer")) {
- obj.free();
+ do {
+ c = str->getChar();
+ } while (Lexer::isSpace(c));
+ if (c == 't') {
+ if (str->getBlock(buf, 6) != 6 || memcmp(buf, "railer", 6)) {
+ goto err1;
+ }
break;
}
- if (!obj.isInt()) {
+ if (c < '0' || c > '9') {
goto err1;
}
- first = obj.getInt();
- obj.free();
- if (!parser->getObj(&obj, gTrue)->isInt()) {
+ first = 0;
+ do {
+ first = (first * 10) + (c - '0');
+ c = str->getChar();
+ } while (c >= '0' && c <= '9');
+ if (!Lexer::isSpace(c)) {
goto err1;
}
- n = obj.getInt();
- obj.free();
- if (first < 0 || n < 0 || first + n < 0) {
+ do {
+ c = str->getChar();
+ } while (Lexer::isSpace(c));
+ n = 0;
+ do {
+ n = (n * 10) + (c - '0');
+ c = str->getChar();
+ } while (c >= '0' && c <= '9');
+ if (!Lexer::isSpace(c)) {
+ goto err1;
+ }
+ if (first < 0 || n < 0 || first > INT_MAX - n) {
goto err1;
}
if (first + n > size) {
@@ -395,32 +527,51 @@ GBool XRef::readXRefTable(Parser *parser, Guint *pos) {
}
entries = (XRefEntry *)greallocn(entries, newSize, sizeof(XRefEntry));
for (i = size; i < newSize; ++i) {
- entries[i].offset = 0xffffffff;
+ entries[i].offset = (GFileOffset)-1;
entries[i].type = xrefEntryFree;
}
size = newSize;
}
for (i = first; i < first + n; ++i) {
- if (!parser->getObj(&obj, gTrue)->isInt()) {
+ do {
+ c = str->getChar();
+ } while (Lexer::isSpace(c));
+ off = 0;
+ do {
+ off = (off * 10) + (c - '0');
+ c = str->getChar();
+ } while (c >= '0' && c <= '9');
+ if (!Lexer::isSpace(c)) {
goto err1;
}
- entry.offset = (Guint)obj.getInt();
- obj.free();
- if (!parser->getObj(&obj, gTrue)->isInt()) {
+ entry.offset = off;
+ do {
+ c = str->getChar();
+ } while (Lexer::isSpace(c));
+ gen = 0;
+ do {
+ gen = (gen * 10) + (c - '0');
+ c = str->getChar();
+ } while (c >= '0' && c <= '9');
+ if (!Lexer::isSpace(c)) {
goto err1;
}
- entry.gen = obj.getInt();
- obj.free();
- parser->getObj(&obj, gTrue);
- if (obj.isCmd("n")) {
+ entry.gen = gen;
+ do {
+ c = str->getChar();
+ } while (Lexer::isSpace(c));
+ if (c == 'n') {
entry.type = xrefEntryUncompressed;
- } else if (obj.isCmd("f")) {
+ } else if (c == 'f') {
entry.type = xrefEntryFree;
} else {
goto err1;
}
- obj.free();
- if (entries[i].offset == 0xffffffff) {
+ c = str->getChar();
+ if (!Lexer::isSpace(c)) {
+ goto err1;
+ }
+ if (entries[i].offset == (GFileOffset)-1) {
entries[i] = entry;
// PDF files of patents from the IBM Intellectual Property
// Network have a bug: the xref table claims to start at 1
@@ -430,7 +581,7 @@ GBool XRef::readXRefTable(Parser *parser, Guint *pos) {
entries[1].type == xrefEntryFree) {
i = first = 0;
entries[0] = entries[1];
- entries[1].offset = 0xffffffff;
+ entries[1].offset = (GFileOffset)-1;
}
if (i > last) {
last = i;
@@ -440,32 +591,29 @@ GBool XRef::readXRefTable(Parser *parser, Guint *pos) {
}
// read the trailer dictionary
- if (!parser->getObj(&obj)->isDict()) {
+ obj.initNull();
+ parser = new Parser(NULL,
+ new Lexer(NULL,
+ str->makeSubStream(str->getPos(), gFalse, 0, &obj)),
+ gTrue);
+ parser->getObj(&obj);
+ delete parser;
+ if (!obj.isDict()) {
+ obj.free();
goto err1;
}
// get the 'Prev' pointer
+ //~ this can be a 64-bit int (?)
obj.getDict()->lookupNF("Prev", &obj2);
if (obj2.isInt()) {
- pos2 = (Guint)obj2.getInt();
- if (pos2 != *pos) {
- *pos = pos2;
- more = gTrue;
- } else {
- error(errSyntaxWarning, -1, "Infinite loop in xref table");
- more = gFalse;
- }
+ *pos = (GFileOffset)(Guint)obj2.getInt();
+ more = gTrue;
} else if (obj2.isRef()) {
// certain buggy PDF generators generate "/Prev NNN 0 R" instead
// of "/Prev NNN"
- pos2 = (Guint)obj2.getRefNum();
- if (pos2 != *pos) {
- *pos = pos2;
- more = gTrue;
- } else {
- error(errSyntaxWarning, -1, "Infinite loop in xref table");
- more = gFalse;
- }
+ *pos = (GFileOffset)(Guint)obj2.getRefNum();
+ more = gTrue;
} else {
more = gFalse;
}
@@ -477,9 +625,10 @@ GBool XRef::readXRefTable(Parser *parser, Guint *pos) {
}
// check for an 'XRefStm' key
+ //~ this can be a 64-bit int (?)
if (obj.getDict()->lookup("XRefStm", &obj2)->isInt()) {
- pos2 = (Guint)obj2.getInt();
- readXRef(&pos2);
+ pos2 = (GFileOffset)(Guint)obj2.getInt();
+ readXRef(&pos2, posSet);
if (!ok) {
obj2.free();
goto err1;
@@ -491,12 +640,11 @@ GBool XRef::readXRefTable(Parser *parser, Guint *pos) {
return more;
err1:
- obj.free();
ok = gFalse;
return gFalse;
}
-GBool XRef::readXRefStream(Stream *xrefStr, Guint *pos) {
+GBool XRef::readXRefStream(Stream *xrefStr, GFileOffset *pos) {
Dict *dict;
int w[3];
GBool more;
@@ -516,7 +664,7 @@ GBool XRef::readXRefStream(Stream *xrefStr, Guint *pos) {
if (newSize > size) {
entries = (XRefEntry *)greallocn(entries, newSize, sizeof(XRefEntry));
for (i = size; i < newSize; ++i) {
- entries[i].offset = 0xffffffff;
+ entries[i].offset = (GFileOffset)-1;
entries[i].type = xrefEntryFree;
}
size = newSize;
@@ -533,11 +681,13 @@ GBool XRef::readXRefStream(Stream *xrefStr, Guint *pos) {
}
w[i] = obj2.getInt();
obj2.free();
- if (w[i] < 0 || w[i] > 4) {
- goto err1;
- }
}
obj.free();
+ if (w[0] < 0 || w[0] > 4 ||
+ w[1] < 0 || w[1] > (int)sizeof(GFileOffset) ||
+ w[2] < 0 || w[2] > 4) {
+ goto err0;
+ }
xrefStr->reset();
dict->lookupNF("Index", &idx);
@@ -569,9 +719,10 @@ GBool XRef::readXRefStream(Stream *xrefStr, Guint *pos) {
}
idx.free();
+ //~ this can be a 64-bit int (?)
dict->lookupNF("Prev", &obj);
if (obj.isInt()) {
- *pos = (Guint)obj.getInt();
+ *pos = (GFileOffset)(Guint)obj.getInt();
more = gTrue;
} else {
more = gFalse;
@@ -591,7 +742,7 @@ GBool XRef::readXRefStream(Stream *xrefStr, Guint *pos) {
}
GBool XRef::readXRefStreamSection(Stream *xrefStr, int *w, int first, int n) {
- Guint offset;
+ GFileOffset offset;
int type, gen, c, newSize, i, j;
if (first + n < 0) {
@@ -606,7 +757,7 @@ GBool XRef::readXRefStreamSection(Stream *xrefStr, int *w, int first, int n) {
}
entries = (XRefEntry *)greallocn(entries, newSize, sizeof(XRefEntry));
for (i = size; i < newSize; ++i) {
- entries[i].offset = 0xffffffff;
+ entries[i].offset = (GFileOffset)-1;
entries[i].type = xrefEntryFree;
}
size = newSize;
@@ -634,7 +785,7 @@ GBool XRef::readXRefStreamSection(Stream *xrefStr, int *w, int first, int n) {
}
gen = (gen << 8) + c;
}
- if (entries[i].offset == 0xffffffff) {
+ if (entries[i].offset == (GFileOffset)-1) {
switch (type) {
case 0:
entries[i].offset = offset;
@@ -668,7 +819,7 @@ GBool XRef::constructXRef() {
Parser *parser;
Object newTrailerDict, obj;
char buf[256];
- Guint pos;
+ GFileOffset pos;
int num, gen;
int newSize;
int streamEndsSize;
@@ -748,7 +899,7 @@ GBool XRef::constructXRef() {
entries = (XRefEntry *)
greallocn(entries, newSize, sizeof(XRefEntry));
for (i = size; i < newSize; ++i) {
- entries[i].offset = 0xffffffff;
+ entries[i].offset = (GFileOffset)-1;
entries[i].type = xrefEntryFree;
}
size = newSize;
@@ -771,15 +922,16 @@ GBool XRef::constructXRef() {
} else if (!strncmp(p, "endstream", 9)) {
if (streamEndsLen == streamEndsSize) {
streamEndsSize += 64;
- streamEnds = (Guint *)greallocn(streamEnds,
- streamEndsSize, sizeof(Guint));
+ streamEnds = (GFileOffset *)greallocn(streamEnds, streamEndsSize,
+ sizeof(GFileOffset));
}
streamEnds[streamEndsLen++] = pos;
}
}
- if (gotRoot)
+ if (gotRoot) {
return gTrue;
+ }
error(errSyntaxError, -1, "Couldn't find trailer dictionary");
return gFalse;
@@ -824,13 +976,31 @@ GBool XRef::okToAddNotes(GBool ignoreOwnerPW) {
Object *XRef::fetch(int num, int gen, Object *obj, int recursion) {
XRefEntry *e;
Parser *parser;
+ ObjectStream *objStr;
Object obj1, obj2, obj3;
+ XRefCacheEntry tmp;
+ int i, j;
// check for bogus ref - this can happen in corrupted PDF files
if (num < 0 || num >= size) {
goto err;
}
+ // check the cache
+ if (cache[0].num == num && cache[0].gen == gen) {
+ return cache[0].obj.copy(obj);
+ }
+ for (i = 1; i < xrefCacheSize; ++i) {
+ if (cache[i].num == num && cache[i].gen == gen) {
+ tmp = cache[i];
+ for (j = i; j > 0; --j) {
+ cache[j] = cache[j - 1];
+ }
+ cache[0] = tmp;
+ return cache[0].obj.copy(obj);
+ }
+ }
+
e = &entries[num];
switch (e->type) {
@@ -869,21 +1039,13 @@ Object *XRef::fetch(int num, int gen, Object *obj, int recursion) {
goto err;
}
#endif
- if (e->offset >= (Guint)size ||
+ if (e->offset >= (GFileOffset)size ||
entries[e->offset].type != xrefEntryUncompressed) {
error(errSyntaxError, -1, "Invalid object stream");
goto err;
}
- if (!objStr || objStr->getObjStrNum() != (int)e->offset) {
- if (objStr) {
- delete objStr;
- }
- objStr = new ObjectStream(this, e->offset);
- if (!objStr->isOk()) {
- delete objStr;
- objStr = NULL;
- goto err;
- }
+ if (!(objStr = getObjectStream((int)e->offset))) {
+ goto err;
}
objStr->getObject(e->gen, num, obj);
break;
@@ -892,12 +1054,61 @@ Object *XRef::fetch(int num, int gen, Object *obj, int recursion) {
goto err;
}
+ // put the new object in the cache, throwing away the oldest object
+ // currently in the cache
+ if (cache[xrefCacheSize - 1].num >= 0) {
+ cache[xrefCacheSize - 1].obj.free();
+ }
+ for (i = xrefCacheSize - 1; i > 0; --i) {
+ cache[i] = cache[i - 1];
+ }
+ cache[0].num = num;
+ cache[0].gen = gen;
+ obj->copy(&cache[0].obj);
+
return obj;
err:
return obj->initNull();
}
+ObjectStream *XRef::getObjectStream(int objStrNum) {
+ ObjectStream *objStr;
+ int i, j;
+
+ // check the MRU entry in the cache
+ if (objStrs[0] && objStrs[0]->getObjStrNum() == objStrNum) {
+ return objStrs[0];
+ }
+
+ // check the rest of the cache
+ for (i = 1; i < objStrCacheSize; ++i) {
+ if (objStrs[i] && objStrs[i]->getObjStrNum() == objStrNum) {
+ objStr = objStrs[i];
+ for (j = i; j > 0; --j) {
+ objStrs[j] = objStrs[j - 1];
+ }
+ objStrs[0] = objStr;
+ return objStr;
+ }
+ }
+
+ // load a new ObjectStream
+ objStr = new ObjectStream(this, objStrNum);
+ if (!objStr->isOk()) {
+ delete objStr;
+ return NULL;
+ }
+ if (objStrs[objStrCacheSize - 1]) {
+ delete objStrs[objStrCacheSize - 1];
+ }
+ for (j = objStrCacheSize - 1; j > 0; --j) {
+ objStrs[j] = objStrs[j - 1];
+ }
+ objStrs[0] = objStr;
+ return objStr;
+}
+
Object *XRef::getDocInfo(Object *obj) {
return trailerDict.dictLookup("Info", obj);
}
@@ -907,7 +1118,7 @@ Object *XRef::getDocInfoNF(Object *obj) {
return trailerDict.dictLookupNF("Info", obj);
}
-GBool XRef::getStreamEnd(Guint streamStart, Guint *streamEnd) {
+GBool XRef::getStreamEnd(GFileOffset streamStart, GFileOffset *streamEnd) {
int a, b, m;
if (streamEndsLen == 0 ||
@@ -930,14 +1141,14 @@ GBool XRef::getStreamEnd(Guint streamStart, Guint *streamEnd) {
return gTrue;
}
-Guint XRef::strToUnsigned(char *s) {
- Guint x, d;
+GFileOffset XRef::strToFileOffset(char *s) {
+ GFileOffset x, d;
char *p;
x = 0;
for (p = s; *p && isdigit(*p & 0xff); ++p) {
d = *p - '0';
- if (x > (UINT_MAX - d) / 10) {
+ if (x > (GFILEOFFSET_MAX - d) / 10) {
break;
}
x = 10 * x + d;
diff --git a/xpdf/XRef.h b/xpdf/XRef.h
index f2c4ac3..0b74dcb 100644
--- a/xpdf/XRef.h
+++ b/xpdf/XRef.h
@@ -16,12 +16,14 @@
#endif
#include "gtypes.h"
+#include "gfile.h"
#include "Object.h"
class Dict;
class Stream;
class Parser;
class ObjectStream;
+class XRefPosSet;
//------------------------------------------------------------------------
// XRef
@@ -34,11 +36,21 @@ enum XRefEntryType {
};
struct XRefEntry {
- Guint offset;
+ GFileOffset offset;
int gen;
XRefEntryType type;
};
+struct XRefCacheEntry {
+ int num;
+ int gen;
+ Object obj;
+};
+
+#define xrefCacheSize 16
+
+#define objStrCacheSize 4
+
class XRef {
public:
@@ -83,7 +95,7 @@ public:
int getNumObjects() { return last + 1; }
// Return the offset of the last xref table.
- Guint getLastXRefPos() { return lastXRefPos; }
+ GFileOffset getLastXRefPos() { return lastXRefPos; }
// Return the catalog object reference.
int getRootNum() { return rootNum; }
@@ -91,7 +103,7 @@ public:
// 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);
+ GBool getStreamEnd(GFileOffset streamStart, GFileOffset *streamEnd);
// Direct access.
int getSize() { return size; }
@@ -101,7 +113,7 @@ public:
private:
BaseStream *str; // input stream
- Guint start; // offset in file (to allow for garbage
+ GFileOffset start; // offset in file (to allow for garbage
// at beginning of file)
XRefEntry *entries; // xref entries
int size; // size of <entries> array
@@ -110,11 +122,12 @@ private:
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
+ GFileOffset lastXRefPos; // offset of last xref table
+ GFileOffset *streamEnds; // 'endstream' positions - only used in
// damaged files
int streamEndsLen; // number of valid entries in streamEnds
- ObjectStream *objStr; // cached object stream
+ ObjectStream * // cached object streams
+ objStrs[objStrCacheSize];
GBool encrypted; // true if file is encrypted
int permFlags; // permission bits
GBool ownerPasswordOk; // true if owner password is correct
@@ -122,14 +135,17 @@ private:
int keyLength; // length of key, in bytes
int encVersion; // encryption version
CryptAlgorithm encAlgorithm; // encryption algorithm
+ XRefCacheEntry // cache of recently accessed objects
+ cache[xrefCacheSize];
- Guint getStartXref();
- GBool readXRef(Guint *pos);
- GBool readXRefTable(Parser *parser, Guint *pos);
+ GFileOffset getStartXref();
+ GBool readXRef(GFileOffset *pos, XRefPosSet *posSet);
+ GBool readXRefTable(GFileOffset *pos, int offset, XRefPosSet *posSet);
GBool readXRefStreamSection(Stream *xrefStr, int *w, int first, int n);
- GBool readXRefStream(Stream *xrefStr, Guint *pos);
+ GBool readXRefStream(Stream *xrefStr, GFileOffset *pos);
GBool constructXRef();
- Guint strToUnsigned(char *s);
+ ObjectStream *getObjectStream(int objStrNum);
+ GFileOffset strToFileOffset(char *s);
};
#endif
diff --git a/xpdf/XpdfPluginAPI.cc b/xpdf/XpdfPluginAPI.cc
index 4c51537..d513bec 100644
--- a/xpdf/XpdfPluginAPI.cc
+++ b/xpdf/XpdfPluginAPI.cc
@@ -14,7 +14,7 @@
#include "GlobalParams.h"
#include "Object.h"
#include "PDFDoc.h"
-#ifdef WIN32
+#ifdef _WIN32
#include "WinPDFCore.h"
#else
#include "XPDFCore.h"
diff --git a/xpdf/Zoox.cc b/xpdf/Zoox.cc
new file mode 100644
index 0000000..9524267
--- /dev/null
+++ b/xpdf/Zoox.cc
@@ -0,0 +1,839 @@
+//========================================================================
+//
+// Zoox.cc
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "gmem.h"
+#include "GString.h"
+#include "GList.h"
+#include "GHash.h"
+#include "Zoox.h"
+
+//~ all of this code assumes the encoding is UTF-8 or ASCII or something
+//~ similar (ISO-8859-*)
+
+//------------------------------------------------------------------------
+
+static char nameStartChar[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 10
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, // 30
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 40
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, // 50
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 60
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 70
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 90
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // a0
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // b0
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // c0
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // d0
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // e0
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // f0
+};
+
+static char nameChar[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 10
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, // 20
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 30
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 40
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, // 50
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 60
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 70
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 90
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // a0
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // b0
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // c0
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // d0
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // e0
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // f0
+};
+
+//------------------------------------------------------------------------
+
+ZxNode::ZxNode() {
+ next = NULL;
+ parent = NULL;
+ firstChild = lastChild = NULL;
+}
+
+ZxNode::~ZxNode() {
+ ZxNode *child;
+
+ while (firstChild) {
+ child = firstChild;
+ firstChild = firstChild->next;
+ delete child;
+ }
+}
+
+ZxElement *ZxNode::findFirstElement(const char *type) {
+ ZxNode *child;
+ ZxElement *result;
+
+ if (isElement(type)) {
+ return (ZxElement *)this;
+ }
+ for (child = firstChild; child; child = child->next) {
+ if ((result = child->findFirstElement(type))) {
+ return result;
+ }
+ }
+ return NULL;
+}
+
+ZxElement *ZxNode::findFirstChildElement(const char *type) {
+ ZxNode *child;
+
+ for (child = firstChild; child; child = child->next) {
+ if (child->isElement(type)) {
+ return (ZxElement *)child;
+ }
+ }
+ return NULL;
+}
+
+GList *ZxNode::findAllElements(const char *type) {
+ GList *results;
+
+ results = new GList();
+ findAllElements(type, results);
+ return results;
+}
+
+void ZxNode::findAllElements(const char *type, GList *results) {
+ ZxNode *child;
+
+ if (isElement(type)) {
+ results->append(this);
+ }
+ for (child = firstChild; child; child = child->next) {
+ child->findAllElements(type, results);
+ }
+}
+
+GList *ZxNode::findAllChildElements(const char *type) {
+ GList *results;
+ ZxNode *child;
+
+ results = new GList();
+ for (child = firstChild; child; child = child->next) {
+ if (child->isElement(type)) {
+ results->append(child);
+ }
+ }
+ return results;
+}
+
+void ZxNode::addChild(ZxNode *child) {
+ if (lastChild) {
+ lastChild->next = child;
+ lastChild = child;
+ } else {
+ firstChild = lastChild = child;
+ }
+ child->parent = this;
+ child->next = NULL;
+}
+
+//------------------------------------------------------------------------
+
+ZxDoc::ZxDoc() {
+ xmlDecl = NULL;
+ docTypeDecl = NULL;
+ root = NULL;
+}
+
+ZxDoc *ZxDoc::loadMem(const char *data, Guint dataLen) {
+ ZxDoc *doc;
+
+ doc = new ZxDoc();
+ if (!doc->parse(data, dataLen)) {
+ delete doc;
+ return NULL;
+ }
+ return doc;
+}
+
+ZxDoc *ZxDoc::loadFile(const char *fileName) {
+ ZxDoc *doc;
+ FILE *f;
+ char *data;
+ Guint dataLen;
+
+ if (!(f = fopen(fileName, "rb"))) {
+ return NULL;
+ }
+ fseek(f, 0, SEEK_END);
+ dataLen = (Guint)ftell(f);
+ if (!dataLen) {
+ fclose(f);
+ return NULL;
+ }
+ fseek(f, 0, SEEK_SET);
+ data = (char *)gmalloc(dataLen);
+ if (fread(data, 1, dataLen, f) != dataLen) {
+ fclose(f);
+ gfree(data);
+ return NULL;
+ }
+ fclose(f);
+ doc = loadMem(data, dataLen);
+ gfree(data);
+ return doc;
+}
+
+ZxDoc::~ZxDoc() {
+}
+
+void ZxDoc::addChild(ZxNode *node) {
+ if (node->isXMLDecl() && !xmlDecl) {
+ xmlDecl = (ZxXMLDecl *)node;
+ } else if (node->isDocTypeDecl() && !docTypeDecl) {
+ docTypeDecl = (ZxDocTypeDecl *)node;
+ } else if (node->isElement() && !root) {
+ root = (ZxElement *)node;
+ }
+ ZxNode::addChild(node);
+}
+
+bool ZxDoc::parse(const char *data, Guint dataLen) {
+ parsePtr = data;
+ parseEnd = data + dataLen;
+
+ parseSpace();
+ parseXMLDecl(this);
+ parseMisc(this);
+ parseDocTypeDecl(this);
+ parseMisc(this);
+ if (match("<")) {
+ parseElement(this);
+ }
+ parseMisc(this);
+ return root != NULL;
+}
+
+void ZxDoc::parseXMLDecl(ZxNode *par) {
+ GString *version, *encoding, *s;
+ bool standalone;
+
+ if (!match("<?xml")) {
+ return;
+ }
+ parsePtr += 5;
+ parseSpace();
+
+ // version
+ version = NULL;
+ if (match("version")) {
+ parsePtr += 7;
+ parseSpace();
+ if (match("=")) {
+ ++parsePtr;
+ parseSpace();
+ version = parseQuotedString();
+ }
+ }
+ if (!version) {
+ version = new GString("1.0");
+ }
+ parseSpace();
+
+ // encoding
+ encoding = NULL;
+ if (match("encoding")) {
+ parsePtr += 8;
+ parseSpace();
+ if (match("=")) {
+ ++parsePtr;
+ parseSpace();
+ encoding = parseQuotedString();
+ }
+ }
+ parseSpace();
+
+ // standalone
+ standalone = false;
+ if (match("standalone")) {
+ parsePtr += 10;
+ parseSpace();
+ if (match("=")) {
+ ++parsePtr;
+ parseSpace();
+ s = parseQuotedString();
+ standalone = !s->cmp("yes");
+ delete s;
+ }
+ }
+ parseSpace();
+
+ if (match("?>")) {
+ parsePtr += 2;
+ }
+
+ par->addChild(new ZxXMLDecl(version, encoding, standalone));
+}
+
+//~ this just skips everything after the name
+void ZxDoc::parseDocTypeDecl(ZxNode *par) {
+ GString *name;
+ int state;
+ char c, quote;
+
+ if (!match("<!DOCTYPE")) {
+ return;
+ }
+ parsePtr += 9;
+ parseSpace();
+
+ name = parseName();
+ parseSpace();
+
+ state = 0;
+ quote = '\0';
+ while (parsePtr < parseEnd && state < 4) {
+ c = *parsePtr++;
+ switch (state) {
+ case 0: // not in square brackets; not in quotes
+ if (c == '>') {
+ state = 4;
+ } else if (c == '"' || c == '\'') {
+ state = 1;
+ } else if (c == '[') {
+ state = 2;
+ }
+ break;
+ case 1: // not in square brackets; in quotes
+ if (c == quote) {
+ state = 0;
+ }
+ break;
+ case 2: // in square brackets; not in quotes
+ if (c == ']') {
+ state = 0;
+ } else if (c == '"' || c == '\'') {
+ state = 3;
+ }
+ break;
+ case 3: // in square brackets; in quotes
+ if (c == quote) {
+ state = 2;
+ }
+ break;
+ }
+ }
+
+ par->addChild(new ZxDocTypeDecl(name));
+}
+
+// assumes match("<")
+void ZxDoc::parseElement(ZxNode *par) {
+ GString *type;
+ ZxElement *elem;
+ ZxAttr *attr;
+
+ ++parsePtr;
+ type = parseName();
+ elem = new ZxElement(type);
+ parseSpace();
+ while ((attr = parseAttr())) {
+ elem->addAttr(attr);
+ parseSpace();
+ }
+ if (match("/>")) {
+ parsePtr += 2;
+ } else if (match(">")) {
+ ++parsePtr;
+ parseContent(elem);
+ }
+ par->addChild(elem);
+}
+
+ZxAttr *ZxDoc::parseAttr() {
+ GString *name, *value;
+ const char *start;
+ char quote, c;
+ int x, n;
+
+ name = parseName();
+ parseSpace();
+ if (!match("=")) {
+ delete name;
+ return NULL;
+ }
+ ++parsePtr;
+ parseSpace();
+ if (!(parsePtr < parseEnd && (*parsePtr == '"' || *parsePtr == '\''))) {
+ delete name;
+ return NULL;
+ }
+ quote = *parsePtr++;
+ value = new GString();
+ while (parsePtr < parseEnd && *parsePtr != quote) {
+ if (*parsePtr == '&') {
+ ++parsePtr;
+ if (parsePtr < parseEnd && *parsePtr == '#') {
+ ++parsePtr;
+ if (parsePtr < parseEnd && *parsePtr == 'x') {
+ ++parsePtr;
+ x = 0;
+ while (parsePtr < parseEnd) {
+ c = *parsePtr;
+ if (c >= '0' && c <= '9') {
+ x = (x << 4) + (c - '0');
+ } else if (c >= 'a' && c <= 'f') {
+ x = (x << 4) + (10 + c - 'a');
+ } else if (c >= 'A' && c <= 'F') {
+ x = (x << 4) + (10 + c - 'A');
+ } else {
+ break;
+ }
+ ++parsePtr;
+ }
+ if (parsePtr < parseEnd && *parsePtr == ';') {
+ ++parsePtr;
+ }
+ appendUTF8(value, x);
+ } else {
+ x = 0;
+ while (parsePtr < parseEnd) {
+ c = *parsePtr;
+ if (c >= '0' && c <= '9') {
+ x = x * 10 + (c - '0');
+ } else {
+ break;
+ }
+ ++parsePtr;
+ }
+ if (parsePtr < parseEnd && *parsePtr == ';') {
+ ++parsePtr;
+ }
+ appendUTF8(value, x);
+ }
+ } else {
+ start = parsePtr;
+ for (++parsePtr;
+ parsePtr < parseEnd && *parsePtr != ';' &&
+ *parsePtr != quote && *parsePtr != '&';
+ ++parsePtr) ;
+ n = (int)(parsePtr - start);
+ if (parsePtr < parseEnd && *parsePtr == ';') {
+ ++parsePtr;
+ }
+ if (n == 2 && !strncmp(start, "lt", 2)) {
+ value->append('<');
+ } else if (n == 2 && !strncmp(start, "gt", 2)) {
+ value->append('>');
+ } else if (n == 3 && !strncmp(start, "amp", 3)) {
+ value->append('&');
+ } else if (n == 4 && !strncmp(start, "apos", 4)) {
+ value->append('\'');
+ } else if (n == 4 && !strncmp(start, "quot", 4)) {
+ value->append('"');
+ } else {
+ value->append(start - 1, (int)(parsePtr - start) + 1);
+ }
+ }
+ } else {
+ start = parsePtr;
+ for (++parsePtr;
+ parsePtr < parseEnd && *parsePtr != quote && *parsePtr != '&';
+ ++parsePtr) ;
+ value->append(start, (int)(parsePtr - start));
+ }
+ }
+ if (parsePtr < parseEnd && *parsePtr == quote) {
+ ++parsePtr;
+ }
+ return new ZxAttr(name, value);
+}
+
+// this consumes the end tag
+void ZxDoc::parseContent(ZxElement *par) {
+ GString *endType;
+
+ endType = (new GString("</"))->append(par->getType());
+
+ while (parsePtr < parseEnd) {
+ if (match(endType->getCString())) {
+ parsePtr += endType->getLength();
+ parseSpace();
+ if (match(">")) {
+ ++parsePtr;
+ }
+ break;
+ } else if (match("<?")) {
+ parsePI(par);
+ } else if (match("<![CDATA[")) {
+ parseCDSect(par);
+ } else if (match("<!--")) {
+ parseComment(par);
+ } else if (match("<")) {
+ parseElement(par);
+ } else {
+ parseCharData(par);
+ }
+ }
+
+ delete endType;
+}
+
+void ZxDoc::parseCharData(ZxElement *par) {
+ GString *data;
+ const char *start;
+ char c;
+ int x, n;
+
+ data = new GString();
+ while (parsePtr < parseEnd && *parsePtr != '<') {
+ if (*parsePtr == '&') {
+ ++parsePtr;
+ if (parsePtr < parseEnd && *parsePtr == '#') {
+ ++parsePtr;
+ if (parsePtr < parseEnd && *parsePtr == 'x') {
+ ++parsePtr;
+ x = 0;
+ while (parsePtr < parseEnd) {
+ c = *parsePtr;
+ if (c >= '0' && c <= '9') {
+ x = (x << 4) + (c - '0');
+ } else if (c >= 'a' && c <= 'f') {
+ x = (x << 4) + (10 + c - 'a');
+ } else if (c >= 'A' && c <= 'F') {
+ x = (x << 4) + (10 + c - 'A');
+ } else {
+ break;
+ }
+ ++parsePtr;
+ }
+ if (parsePtr < parseEnd && *parsePtr == ';') {
+ ++parsePtr;
+ }
+ appendUTF8(data, x);
+ } else {
+ x = 0;
+ while (parsePtr < parseEnd) {
+ c = *parsePtr;
+ if (c >= '0' && c <= '9') {
+ x = x * 10 + (c - '0');
+ } else {
+ break;
+ }
+ ++parsePtr;
+ }
+ if (parsePtr < parseEnd && *parsePtr == ';') {
+ ++parsePtr;
+ }
+ appendUTF8(data, x);
+ }
+ } else {
+ start = parsePtr;
+ for (++parsePtr;
+ parsePtr < parseEnd && *parsePtr != ';' &&
+ *parsePtr != '<' && *parsePtr != '&';
+ ++parsePtr) ;
+ n = (int)(parsePtr - start);
+ if (parsePtr < parseEnd && *parsePtr == ';') {
+ ++parsePtr;
+ }
+ if (n == 2 && !strncmp(start, "lt", 2)) {
+ data->append('<');
+ } else if (n == 2 && !strncmp(start, "gt", 2)) {
+ data->append('>');
+ } else if (n == 3 && !strncmp(start, "amp", 3)) {
+ data->append('&');
+ } else if (n == 4 && !strncmp(start, "apos", 4)) {
+ data->append('\'');
+ } else if (n == 4 && !strncmp(start, "quot", 4)) {
+ data->append('"');
+ } else {
+ data->append(start - 1, (int)(parsePtr - start) + 1);
+ }
+ }
+ } else {
+ start = parsePtr;
+ for (++parsePtr;
+ parsePtr < parseEnd && *parsePtr != '<' && *parsePtr != '&';
+ ++parsePtr) ;
+ data->append(start, (int)(parsePtr - start));
+ }
+ }
+ par->addChild(new ZxCharData(data, true));
+}
+
+void ZxDoc::appendUTF8(GString *s, int c) {
+ if (c <= 0x7f) {
+ s->append((char)c);
+ } else if (c <= 0x7ff) {
+ s->append((char)(0xc0 + (c >> 6)));
+ s->append((char)(0x80 + (c & 0x3f)));
+ } else if (c <= 0xffff) {
+ s->append((char)(0xe0 + (c >> 12)));
+ s->append((char)(0x80 + ((c >> 6) & 0x3f)));
+ s->append((char)(0x80 + (c & 0x3f)));
+ } else if (c <= 0x1fffff) {
+ s->append((char)(0xf0 + (c >> 18)));
+ s->append((char)(0x80 + ((c >> 12) & 0x3f)));
+ s->append((char)(0x80 + ((c >> 6) & 0x3f)));
+ s->append((char)(0x80 + (c & 0x3f)));
+ } else if (c <= 0x3ffffff) {
+ s->append((char)(0xf8 + (c >> 24)));
+ s->append((char)(0x80 + ((c >> 18) & 0x3f)));
+ s->append((char)(0x80 + ((c >> 12) & 0x3f)));
+ s->append((char)(0x80 + ((c >> 6) & 0x3f)));
+ s->append((char)(0x80 + (c & 0x3f)));
+ } else if (c <= 0x7fffffff) {
+ s->append((char)(0xfc + (c >> 30)));
+ s->append((char)(0x80 + ((c >> 24) & 0x3f)));
+ s->append((char)(0x80 + ((c >> 18) & 0x3f)));
+ s->append((char)(0x80 + ((c >> 12) & 0x3f)));
+ s->append((char)(0x80 + ((c >> 6) & 0x3f)));
+ s->append((char)(0x80 + (c & 0x3f)));
+ }
+}
+
+// assumes match("<![CDATA[")
+void ZxDoc::parseCDSect(ZxNode *par) {
+ const char *start;
+
+ parsePtr += 9;
+ start = parsePtr;
+ while (parsePtr < parseEnd - 3) {
+ if (!strncmp(parsePtr, "]]>", 3)) {
+ par->addChild(new ZxCharData(new GString(start, (int)(parsePtr - start)),
+ false));
+ parsePtr += 3;
+ return;
+ }
+ ++parsePtr;
+ }
+ parsePtr = parseEnd;
+ par->addChild(new ZxCharData(new GString(start, (int)(parsePtr - start)),
+ false));
+}
+
+void ZxDoc::parseMisc(ZxNode *par) {
+ while (1) {
+ if (match("<!--")) {
+ parseComment(par);
+ } else if (match("<?")) {
+ parsePI(par);
+ } else if (parsePtr < parseEnd && (*parsePtr == '\x20' ||
+ *parsePtr == '\x09' ||
+ *parsePtr == '\x0d' ||
+ *parsePtr == '\x0a')) {
+ ++parsePtr;
+ } else {
+ break;
+ }
+ }
+}
+
+// assumes match("<!--")
+void ZxDoc::parseComment(ZxNode *par) {
+ const char *start;
+
+ parsePtr += 4;
+ start = parsePtr;
+ while (parsePtr <= parseEnd - 3) {
+ if (!strncmp(parsePtr, "-->", 3)) {
+ par->addChild(new ZxComment(new GString(start, (int)(parsePtr - start))));
+ parsePtr += 3;
+ return;
+ }
+ ++parsePtr;
+ }
+ parsePtr = parseEnd;
+}
+
+// assumes match("<?")
+void ZxDoc::parsePI(ZxNode *par) {
+ GString *target;
+ const char *start;
+
+ parsePtr += 2;
+ target = parseName();
+ parseSpace();
+ start = parsePtr;
+ while (parsePtr <= parseEnd - 2) {
+ if (!strncmp(parsePtr, "?>", 2)) {
+ par->addChild(new ZxPI(target, new GString(start,
+ (int)(parsePtr - start))));
+ parsePtr += 2;
+ return;
+ }
+ ++parsePtr;
+ }
+ parsePtr = parseEnd;
+ par->addChild(new ZxPI(target, new GString(start, (int)(parsePtr - start))));
+}
+
+//~ this accepts all chars >= 0x80
+//~ this doesn't check for properly-formed UTF-8
+GString *ZxDoc::parseName() {
+ GString *name;
+
+ name = new GString();
+ if (parsePtr < parseEnd && nameStartChar[*parsePtr & 0xff]) {
+ name->append(*parsePtr++);
+ while (parsePtr < parseEnd && nameChar[*parsePtr & 0xff]) {
+ name->append(*parsePtr++);
+ }
+ }
+ return name;
+}
+
+GString *ZxDoc::parseQuotedString() {
+ GString *s;
+ const char *start;
+ char quote;
+
+ if (parsePtr < parseEnd && (*parsePtr == '"' || *parsePtr == '\'')) {
+ quote = *parsePtr++;
+ start = parsePtr;
+ while (parsePtr < parseEnd && *parsePtr != quote) {
+ ++parsePtr;
+ }
+ s = new GString(start, (int)(parsePtr - start));
+ if (parsePtr < parseEnd && *parsePtr == quote) {
+ ++parsePtr;
+ }
+ } else {
+ s = new GString();
+ }
+ return s;
+}
+
+void ZxDoc::parseSpace() {
+ while (parsePtr < parseEnd && (*parsePtr == '\x20' ||
+ *parsePtr == '\x09' ||
+ *parsePtr == '\x0d' ||
+ *parsePtr == '\x0a')) {
+ ++parsePtr;
+ }
+}
+
+bool ZxDoc::match(const char *s) {
+ int n;
+
+ n = (int)strlen(s);
+ return parseEnd - parsePtr >= n && !strncmp(parsePtr, s, n);
+}
+
+//------------------------------------------------------------------------
+
+ZxXMLDecl::ZxXMLDecl(GString *versionA, GString *encodingA, bool standaloneA) {
+ version = versionA;
+ encoding = encodingA;
+ standalone = standaloneA;
+}
+
+ZxXMLDecl::~ZxXMLDecl() {
+ delete version;
+ if (encoding) {
+ delete encoding;
+ }
+}
+
+//------------------------------------------------------------------------
+
+ZxDocTypeDecl::ZxDocTypeDecl(GString *nameA) {
+ name = nameA;
+}
+
+ZxDocTypeDecl::~ZxDocTypeDecl() {
+ delete name;
+}
+
+//------------------------------------------------------------------------
+
+ZxComment::ZxComment(GString *textA) {
+ text = textA;
+}
+
+ZxComment::~ZxComment() {
+ delete text;
+}
+
+//------------------------------------------------------------------------
+
+ZxPI::ZxPI(GString *targetA, GString *textA) {
+ target = targetA;
+ text = textA;
+}
+
+ZxPI::~ZxPI() {
+ delete target;
+ delete text;
+}
+
+//------------------------------------------------------------------------
+
+ZxElement::ZxElement(GString *typeA) {
+ type = typeA;
+ attrs = new GHash();
+ firstAttr = lastAttr = NULL;
+}
+
+ZxElement::~ZxElement() {
+ delete type;
+ deleteGHash(attrs, ZxAttr);
+}
+
+bool ZxElement::isElement(const char *typeA) {
+ return !type->cmp(typeA);
+}
+
+ZxAttr *ZxElement::findAttr(const char *attrName) {
+ return (ZxAttr *)attrs->lookup(attrName);
+}
+
+void ZxElement::addAttr(ZxAttr *attr) {
+ attrs->add(attr->getName(), attr);
+ if (lastAttr) {
+ lastAttr->next = attr;
+ lastAttr= attr;
+ } else {
+ firstAttr = lastAttr = attr;
+ }
+ attr->parent = this;
+ attr->next = NULL;
+}
+
+//------------------------------------------------------------------------
+
+ZxAttr::ZxAttr(GString *nameA, GString *valueA) {
+ name = nameA;
+ value = valueA;
+ parent = NULL;
+ next = NULL;
+}
+
+ZxAttr::~ZxAttr() {
+ delete name;
+ delete value;
+}
+
+//------------------------------------------------------------------------
+
+ZxCharData::ZxCharData(GString *dataA, bool parsedA) {
+ data = dataA;
+ parsed = parsedA;
+}
+
+ZxCharData::~ZxCharData() {
+ delete data;
+}
diff --git a/xpdf/Zoox.h b/xpdf/Zoox.h
new file mode 100644
index 0000000..091f72e
--- /dev/null
+++ b/xpdf/Zoox.h
@@ -0,0 +1,241 @@
+//========================================================================
+//
+// Zoox.h
+//
+//========================================================================
+
+#ifndef ZOOX_H
+#define ZOOX_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+
+class GString;
+class GList;
+class GHash;
+
+class ZxAttr;
+class ZxDocTypeDecl;
+class ZxElement;
+class ZxXMLDecl;
+
+//------------------------------------------------------------------------
+
+class ZxNode {
+public:
+
+ ZxNode();
+ virtual ~ZxNode();
+
+ virtual bool isDoc() { return false; }
+ virtual bool isXMLDecl() { return false; }
+ virtual bool isDocTypeDecl() { return false; }
+ virtual bool isComment() { return false; }
+ virtual bool isPI() { return false; }
+ virtual bool isElement() { return false; }
+ virtual bool isElement(const char *type) { return false; }
+ virtual bool isCharData() { return false; }
+ virtual ZxNode *getFirstChild() { return firstChild; }
+ virtual ZxNode *getNextChild() { return next; }
+ ZxElement *findFirstElement(const char *type);
+ ZxElement *findFirstChildElement(const char *type);
+ GList *findAllElements(const char *type);
+ GList *findAllChildElements(const char *type);
+ virtual void addChild(ZxNode *child);
+
+protected:
+
+ void findAllElements(const char *type, GList *results);
+
+ ZxNode *next;
+ ZxNode *parent;
+ ZxNode *firstChild,
+ *lastChild;
+};
+
+//------------------------------------------------------------------------
+
+class ZxDoc: public ZxNode {
+public:
+
+ ZxDoc();
+
+ // Parse from memory. Returns NULL on error.
+ static ZxDoc *loadMem(const char *data, Guint dataLen);
+
+ // Parse from disk. Returns NULL on error.
+ static ZxDoc *loadFile(const char *fileName);
+
+ virtual ~ZxDoc();
+
+ virtual bool isDoc() { return true; }
+ ZxXMLDecl *getXMLDecl() { return xmlDecl; }
+ ZxDocTypeDecl *getDocTypeDecl() { return docTypeDecl; }
+ ZxElement *getRoot() { return root; }
+ virtual void addChild(ZxNode *node);
+
+private:
+
+ bool parse(const char *data, Guint dataLen);
+ void parseXMLDecl(ZxNode *par);
+ void parseDocTypeDecl(ZxNode *par);
+ void parseElement(ZxNode *par);
+ ZxAttr *parseAttr();
+ void parseContent(ZxElement *par);
+ void parseCharData(ZxElement *par);
+ void appendUTF8(GString *s, int c);
+ void parseCDSect(ZxNode *par);
+ void parseMisc(ZxNode *par);
+ void parseComment(ZxNode *par);
+ void parsePI(ZxNode *par);
+ GString *parseName();
+ GString *parseQuotedString();
+ void parseSpace();
+ bool match(const char *s);
+
+ ZxXMLDecl *xmlDecl; // may be NULL
+ ZxDocTypeDecl *docTypeDecl; // may be NULL
+ ZxElement *root; // may be NULL
+
+ const char *parsePtr;
+ const char *parseEnd;
+};
+
+//------------------------------------------------------------------------
+
+class ZxXMLDecl: public ZxNode {
+public:
+
+ ZxXMLDecl(GString *versionA, GString *encodingA, bool standaloneA);
+ virtual ~ZxXMLDecl();
+
+ virtual bool isXMLDecl() { return true; }
+ GString *getVersion() { return version; }
+ GString *getEncoding() { return encoding; }
+ bool getStandalone() { return standalone; }
+
+private:
+
+ GString *version;
+ GString *encoding; // may be NULL
+ bool standalone;
+};
+
+//------------------------------------------------------------------------
+
+class ZxDocTypeDecl: public ZxNode {
+public:
+
+ ZxDocTypeDecl(GString *nameA);
+ virtual ~ZxDocTypeDecl();
+
+ virtual bool isDocTypeDecl() { return true; }
+ GString *getName() { return name; }
+
+private:
+
+ GString *name;
+};
+
+//------------------------------------------------------------------------
+
+class ZxComment: public ZxNode {
+public:
+
+ ZxComment(GString *textA);
+ virtual ~ZxComment();
+
+ virtual bool isComment() { return true; }
+ GString *getText() { return text; }
+
+private:
+
+ GString *text;
+};
+
+//------------------------------------------------------------------------
+
+class ZxPI: public ZxNode {
+public:
+
+ ZxPI(GString *targetA, GString *textA);
+ virtual ~ZxPI();
+
+ virtual bool isPI() { return true; }
+ GString *getTarget() { return target; }
+ GString *getText() { return text; }
+
+private:
+
+ GString *target;
+ GString *text;
+};
+
+//------------------------------------------------------------------------
+
+class ZxElement: public ZxNode {
+public:
+
+ ZxElement(GString *typeA);
+ virtual ~ZxElement();
+
+ virtual bool isElement() { return true; }
+ virtual bool isElement(const char *typeA);
+ GString *getType() { return type; }
+ ZxAttr *findAttr(const char *attrName);
+ ZxAttr *getFirstAttr() { return firstAttr; }
+ void addAttr(ZxAttr *attr);
+
+private:
+
+ GString *type;
+ GHash *attrs; // [ZxAttr]
+ ZxAttr *firstAttr, *lastAttr;
+};
+
+//------------------------------------------------------------------------
+
+class ZxAttr {
+public:
+
+ ZxAttr(GString *nameA, GString *valueA);
+ ~ZxAttr();
+
+ GString *getName() { return name; }
+ GString *getValue() { return value; }
+ ZxAttr *getNextAttr() { return next; }
+
+private:
+
+ GString *name;
+ GString *value;
+ ZxElement *parent;
+ ZxAttr *next;
+
+ friend class ZxElement;
+};
+
+//------------------------------------------------------------------------
+
+class ZxCharData: public ZxNode {
+public:
+
+ ZxCharData(GString *dataA, bool parsedA);
+ virtual ~ZxCharData();
+
+ virtual bool isCharData() { return true; }
+ GString *getData() { return data; }
+ bool isParsed() { return parsed; }
+
+private:
+
+ GString *data; // in UTF-8 format
+ bool parsed;
+};
+
+#endif
diff --git a/xpdf/config.h b/xpdf/config.h
index 998d345..c2b8779 100644
--- a/xpdf/config.h
+++ b/xpdf/config.h
@@ -2,7 +2,7 @@
//
// config.h
//
-// Copyright 1996-2011 Glyph & Cog, LLC
+// Copyright 1996-2014 Glyph & Cog, LLC
//
//========================================================================
@@ -14,13 +14,13 @@
//------------------------------------------------------------------------
// xpdf version
-#define xpdfVersion "3.03"
-#define xpdfVersionNum 3.03
+#define xpdfVersion "3.04"
+#define xpdfVersionNum 3.04
#define xpdfMajorVersion 3
-#define xpdfMinorVersion 3
+#define xpdfMinorVersion 4
#define xpdfUpdateVersion 0
#define xpdfMajorVersionStr "3"
-#define xpdfMinorVersionStr "3"
+#define xpdfMinorVersionStr "4"
#define xpdfUpdateVersionStr "0"
// supported PDF version
@@ -28,11 +28,11 @@
#define supportedPDFVersionNum 1.7
// copyright notice
-#define xpdfCopyright "Copyright 1996-2011 Glyph & Cog, LLC"
+#define xpdfCopyright "Copyright 1996-2014 Glyph & Cog, LLC"
// Windows resource file stuff
-#define winxpdfVersion "WinXpdf 3.03"
-#define xpdfCopyrightAmp "Copyright 1996-2011 Glyph && Cog, LLC"
+#define winxpdfVersion "WinXpdf 3.04"
+#define xpdfCopyrightAmp "Copyright 1996-2014 Glyph && Cog, LLC"
//------------------------------------------------------------------------
// paper size
@@ -52,7 +52,7 @@
//------------------------------------------------------------------------
// user config file name, relative to the user's home directory
-#if defined(VMS) || defined(WIN32)
+#if defined(VMS) || defined(_WIN32)
#define xpdfUserConfigFile "xpdfrc"
#else
#define xpdfUserConfigFile ".xpdfrc"
@@ -83,7 +83,7 @@
#define pclose _pclose
#endif
-#if defined(VMS) || defined(VMCMS) || defined(DOS) || defined(OS2) || defined(__EMX__) || defined(WIN32) || defined(__DJGPP__) || defined(MACOS)
+#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"
diff --git a/xpdf/pdfdetach.cc b/xpdf/pdfdetach.cc
index 4be3a48..6d29ff3 100644
--- a/xpdf/pdfdetach.cc
+++ b/xpdf/pdfdetach.cc
@@ -147,7 +147,7 @@ int main(int argc, char *argv[]) {
} else if (saveAll) {
for (i = 0; i < nFiles; ++i) {
if (savePath[0]) {
- n = strlen(savePath);
+ n = (int)strlen(savePath);
if (n > (int)sizeof(path) - 2) {
n = sizeof(path) - 2;
}
diff --git a/xpdf/pdffonts.cc b/xpdf/pdffonts.cc
index 80403a1..e1a0b43 100644
--- a/xpdf/pdffonts.cc
+++ b/xpdf/pdffonts.cc
@@ -22,6 +22,7 @@
#include "Dict.h"
#include "GfxFont.h"
#include "Annot.h"
+#include "Form.h"
#include "PDFDoc.h"
#include "config.h"
@@ -41,11 +42,14 @@ static const char *fontTypeNames[] = {
"CID TrueType (OT)"
};
+static void scanFonts(Object *obj, PDFDoc *doc);
static void scanFonts(Dict *resDict, PDFDoc *doc);
static void scanFont(GfxFont *font, PDFDoc *doc);
static int firstPage = 1;
static int lastPage = 0;
+static GBool showFontLoc = gFalse;
+static GBool showFontLocPS = gFalse;
static char ownerPassword[33] = "\001";
static char userPassword[33] = "\001";
static char cfgFileName[256] = "";
@@ -57,6 +61,10 @@ static ArgDesc argDesc[] = {
"first page to examine"},
{"-l", argInt, &lastPage, 0,
"last page to examine"},
+ {"-loc", argFlag, &showFontLoc, 0,
+ "print extended info on font location"},
+ {"-locPS", argFlag, &showFontLocPS, 0,
+ "print extended info on font location for PostScript conversion"},
{"-opw", argString, ownerPassword, sizeof(ownerPassword),
"owner password (for encrypted files)"},
{"-upw", argString, userPassword, sizeof(userPassword),
@@ -80,6 +88,10 @@ static Ref *fonts;
static int fontsLen;
static int fontsSize;
+static Ref *seenObjs;
+static int seenObjsLen;
+static int seenObjsSize;
+
int main(int argc, char *argv[]) {
PDFDoc *doc;
GString *fileName;
@@ -88,8 +100,9 @@ int main(int argc, char *argv[]) {
Page *page;
Dict *resDict;
Annots *annots;
+ Form *form;
Object obj1, obj2;
- int pg, i;
+ int pg, i, j;
int exitCode;
exitCode = 99;
@@ -108,6 +121,7 @@ int main(int argc, char *argv[]) {
// read config file
globalParams = new GlobalParams(cfgFileName);
+ globalParams->setupBaseFonts(NULL);
// open PDF file
if (ownerPassword[0] != '\001') {
@@ -141,10 +155,17 @@ int main(int argc, char *argv[]) {
}
// scan the fonts
- printf("name type emb sub uni object ID\n");
- printf("------------------------------------ ----------------- --- --- --- ---------\n");
+ if (showFontLoc || showFontLocPS) {
+ printf("name type emb sub uni object ID location\n");
+ printf("------------------------------------ ----------------- --- --- --- --------- --------\n");
+ } else {
+ printf("name type emb sub uni object ID\n");
+ printf("------------------------------------ ----------------- --- --- --- ---------\n");
+ }
fonts = NULL;
fontsLen = fontsSize = 0;
+ seenObjs = NULL;
+ seenObjsLen = seenObjsSize = 0;
for (pg = firstPage; pg <= lastPage; ++pg) {
page = doc->getCatalog()->getPage(pg);
if ((resDict = page->getResourceDict())) {
@@ -154,21 +175,35 @@ int main(int argc, char *argv[]) {
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);
- }
+ obj1.streamGetDict()->lookupNF("Resources", &obj2);
+ scanFonts(&obj2, doc);
obj2.free();
}
obj1.free();
}
delete annots;
}
+ if ((form = doc->getCatalog()->getForm())) {
+ for (i = 0; i < form->getNumFields(); ++i) {
+ form->getField(i)->getResources(&obj1);
+ if (obj1.isArray()) {
+ for (j = 0; j < obj1.arrayGetLength(); ++j) {
+ obj1.arrayGetNF(j, &obj2);
+ scanFonts(&obj2, doc);
+ obj2.free();
+ }
+ } else if (obj1.isDict()) {
+ scanFonts(obj1.getDict(), doc);
+ }
+ obj1.free();
+ }
+ }
exitCode = 0;
// clean up
gfree(fonts);
+ gfree(seenObjs);
err1:
delete doc;
delete globalParams;
@@ -181,8 +216,37 @@ int main(int argc, char *argv[]) {
return exitCode;
}
+static void scanFonts(Object *obj, PDFDoc *doc) {
+ Object obj2;
+ int i;
+
+ if (obj->isRef()) {
+ for (i = 0; i < seenObjsLen; ++i) {
+ if (obj->getRefNum() == seenObjs[i].num &&
+ obj->getRefGen() == seenObjs[i].gen) {
+ return;
+ }
+ }
+ if (seenObjsLen == seenObjsSize) {
+ if (seenObjsSize <= INT_MAX - 32) {
+ seenObjsSize += 32;
+ } else {
+ // let greallocn throw an exception
+ seenObjsSize = -1;
+ }
+ seenObjs = (Ref *)greallocn(seenObjs, seenObjsSize, sizeof(Ref));
+ }
+ seenObjs[seenObjsLen++] = obj->getRef();
+ }
+ if (obj->fetch(doc->getXRef(), &obj2)->isDict()) {
+ scanFonts(obj2.getDict(), doc);
+ }
+ obj2.free();
+}
+
static void scanFonts(Dict *resDict, PDFDoc *doc) {
- Object obj1, obj2, xObjDict, xObj, resObj;
+ Object obj1, obj2, xObjDict, xObj;
+ Object patternDict, pattern, gsDict, gs, smask, smaskGroup, resObj;
Ref r;
GfxFontDict *gfxFontDict;
GfxFont *font;
@@ -211,23 +275,58 @@ static void scanFonts(Dict *resDict, PDFDoc *doc) {
}
obj1.free();
- // recursively scan any resource dictionaries in objects in this
+ // recursively scan any resource dictionaries in XObjects 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);
- }
+ xObj.streamGetDict()->lookupNF("Resources", &resObj);
+ scanFonts(&resObj, doc);
resObj.free();
}
xObj.free();
}
}
xObjDict.free();
+
+ // recursively scan any resource dictionaries in Patterns in this
+ // resource dictionary
+ resDict->lookup("Pattern", &patternDict);
+ if (patternDict.isDict()) {
+ for (i = 0; i < patternDict.dictGetLength(); ++i) {
+ patternDict.dictGetVal(i, &pattern);
+ if (pattern.isStream()) {
+ pattern.streamGetDict()->lookupNF("Resources", &resObj);
+ scanFonts(&resObj, doc);
+ resObj.free();
+ }
+ pattern.free();
+ }
+ }
+ patternDict.free();
+
+ // recursively scan any resource dictionaries in ExtGStates in this
+ // resource dictionary
+ resDict->lookup("ExtGState", &gsDict);
+ if (gsDict.isDict()) {
+ for (i = 0; i < gsDict.dictGetLength(); ++i) {
+ if (gsDict.dictGetVal(i, &gs)->isDict()) {
+ if (gs.dictLookup("SMask", &smask)->isDict()) {
+ if (smask.dictLookup("G", &smaskGroup)->isStream()) {
+ smaskGroup.streamGetDict()->lookupNF("Resources", &resObj);
+ scanFonts(&resObj, doc);
+ resObj.free();
+ }
+ smaskGroup.free();
+ }
+ smask.free();
+ }
+ gs.free();
+ }
+ }
+ gsDict.free();
}
static void scanFont(GfxFont *font, PDFDoc *doc) {
@@ -235,6 +334,7 @@ static void scanFont(GfxFont *font, PDFDoc *doc) {
Object fontObj, toUnicodeObj;
GString *name;
GBool emb, subset, hasToUnicode;
+ GfxFontLoc *loc;
int i;
fontRef = *font->getID();
@@ -284,10 +384,38 @@ static void scanFont(GfxFont *font, PDFDoc *doc) {
subset ? "yes" : "no",
hasToUnicode ? "yes" : "no");
if (fontRef.gen >= 100000) {
- printf(" [none]\n");
+ printf(" [none]");
} else {
- printf(" %6d %2d\n", fontRef.num, fontRef.gen);
+ printf(" %6d %2d", fontRef.num, fontRef.gen);
+ }
+ if (showFontLoc || showFontLocPS) {
+ if (font->getType() == fontType3) {
+ printf(" embedded");
+ } else {
+ loc = font->locateFont(doc->getXRef(), showFontLocPS);
+ if (loc) {
+ if (loc->locType == gfxFontLocEmbedded) {
+ printf(" embedded");
+ } else if (loc->locType == gfxFontLocExternal) {
+ if (loc->path) {
+ printf(" external: %s", loc->path->getCString());
+ } else {
+ printf(" unavailable");
+ }
+ } else if (loc->locType == gfxFontLocResident) {
+ if (loc->path) {
+ printf(" resident: %s", loc->path->getCString());
+ } else {
+ printf(" unavailable");
+ }
+ }
+ } else {
+ printf(" unknown");
+ }
+ delete loc;
+ }
}
+ printf("\n");
// add this font to the list
if (fontsLen == fontsSize) {
diff --git a/xpdf/pdfinfo.cc b/xpdf/pdfinfo.cc
index ad482ac..9c0493d 100644
--- a/xpdf/pdfinfo.cc
+++ b/xpdf/pdfinfo.cc
@@ -2,7 +2,7 @@
//
// pdfinfo.cc
//
-// Copyright 1998-2003 Glyph & Cog, LLC
+// Copyright 1998-2013 Glyph & Cog, LLC
//
//========================================================================
@@ -16,6 +16,7 @@
#include "parseargs.h"
#include "GString.h"
#include "gmem.h"
+#include "gfile.h"
#include "GlobalParams.h"
#include "Object.h"
#include "Stream.h"
@@ -27,7 +28,7 @@
#include "PDFDoc.h"
#include "CharTypes.h"
#include "UnicodeMap.h"
-#include "PDFDocEncoding.h"
+#include "TextString.h"
#include "Error.h"
#include "config.h"
@@ -239,6 +240,7 @@ int main(int argc, char *argv[]) {
wISO /= sqrt(2.0);
}
}
+ printf(" (rotated %d degrees)", doc->getPageRotate(pg));
printf("\n");
}
@@ -275,16 +277,8 @@ int main(int argc, char *argv[]) {
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
+ gfseek(f, 0, SEEK_END);
+ printf("File size: %u bytes\n", (Guint)gftell(f));
fclose(f);
}
@@ -322,36 +316,21 @@ int main(int argc, char *argv[]) {
static void printInfoString(Dict *infoDict, const char *key, const char *text,
UnicodeMap *uMap) {
Object obj;
- GString *s1;
- GBool isUnicode;
- Unicode u;
+ TextString *s;
+ 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));
+ s = new TextString(obj.getString());
+ u = s->getUnicode();
+ for (i = 0; i < s->getLength(); ++i) {
+ n = uMap->mapUnicode(u[i], buf, sizeof(buf));
fwrite(buf, 1, n, stdout);
}
fputc('\n', stdout);
+ delete s;
}
obj.free();
}
diff --git a/xpdf/pdftohtml.cc b/xpdf/pdftohtml.cc
new file mode 100644
index 0000000..f1fe691
--- /dev/null
+++ b/xpdf/pdftohtml.cc
@@ -0,0 +1,246 @@
+//========================================================================
+//
+// pdftohtml.cc
+//
+// Copyright 2005 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "parseargs.h"
+#include "gmem.h"
+#include "gfile.h"
+#include "GString.h"
+#include "GlobalParams.h"
+#include "PDFDoc.h"
+#include "HTMLGen.h"
+#include "Error.h"
+#include "ErrorCodes.h"
+#include "config.h"
+
+//------------------------------------------------------------------------
+
+static GBool createIndex(char *htmlDir);
+
+//------------------------------------------------------------------------
+
+static int firstPage = 1;
+static int lastPage = 0;
+static int resolution = 150;
+static GBool skipInvisible = 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"},
+ {"-r", argInt, &resolution, 0,
+ "resolution, in DPI (default is 150)"},
+ {"-skipinvisible", argFlag, &skipInvisible, 0,
+ "do not draw invisible text"},
+ {"-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}
+};
+
+//------------------------------------------------------------------------
+
+static int writeToFile(void *file, const char *data, int size) {
+ return (int)fwrite(data, 1, size, (FILE *)file);
+}
+
+int main(int argc, char *argv[]) {
+ PDFDoc *doc;
+ GString *fileName;
+ char *htmlDir;
+ GString *ownerPW, *userPW;
+ HTMLGen *htmlGen;
+ GString *htmlFileName, *pngFileName, *pngURL;
+ FILE *htmlFile, *pngFile;
+ int pg, err, exitCode;
+ GBool ok;
+
+ exitCode = 99;
+
+ // parse args
+ ok = parseArgs(argDesc, &argc, argv);
+ if (!ok || argc != 3 || printVersion || printHelp) {
+ fprintf(stderr, "pdftohtml version %s\n", xpdfVersion);
+ fprintf(stderr, "%s\n", xpdfCopyright);
+ if (!printVersion) {
+ printUsage("pdftohtml", "<PDF-file> <html-dir>", argDesc);
+ }
+ goto err0;
+ }
+ fileName = new GString(argv[1]);
+ htmlDir = argv[2];
+
+ // read config file
+ globalParams = new GlobalParams(cfgFileName);
+ if (quiet) {
+ globalParams->setErrQuiet(quiet);
+ }
+ globalParams->setupBaseFonts(NULL);
+ globalParams->setTextEncoding("UTF-8");
+
+ // 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(errNotAllowed, -1,
+ "Copying of text 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();
+ }
+
+ // create HTML directory
+ if (!createDir(htmlDir, 0755)) {
+ error(errIO, -1, "Couldn't create HTML output directory '{0:s}'",
+ htmlDir);
+ exitCode = 2;
+ goto err1;
+ }
+
+ // set up the HTMLGen object
+ htmlGen = new HTMLGen(resolution);
+ if (!htmlGen->isOk()) {
+ exitCode = 99;
+ goto err1;
+ }
+ htmlGen->setDrawInvisibleText(!skipInvisible);
+ htmlGen->startDoc(doc);
+
+ // convert the pages
+ for (pg = firstPage; pg <= lastPage; ++pg) {
+ htmlFileName = GString::format("{0:s}/page{1:d}.html", htmlDir, pg);
+ pngFileName = GString::format("{0:s}/page{1:d}.png", htmlDir, pg);
+ if (!(htmlFile = fopen(htmlFileName->getCString(), "wb"))) {
+ error(errIO, -1, "Couldn't open HTML file '{0:t}'", htmlFileName);
+ delete htmlFileName;
+ delete pngFileName;
+ goto err2;
+ }
+ if (!(pngFile = fopen(pngFileName->getCString(), "wb"))) {
+ error(errIO, -1, "Couldn't open PNG file '{0:t}'", pngFileName);
+ fclose(htmlFile);
+ delete htmlFileName;
+ delete pngFileName;
+ goto err2;
+ }
+ pngURL = GString::format("page{0:d}.png", pg);
+ err = htmlGen->convertPage(pg, pngURL->getCString(),
+ &writeToFile, htmlFile,
+ &writeToFile, pngFile);
+ delete pngURL;
+ fclose(htmlFile);
+ fclose(pngFile);
+ delete htmlFileName;
+ delete pngFileName;
+ if (err != errNone) {
+ error(errIO, -1, "Error converting page {0:d}", pg);
+ exitCode = 2;
+ goto err2;
+ }
+ }
+
+ // create the master index
+ if (!createIndex(htmlDir)) {
+ exitCode = 2;
+ goto err2;
+ }
+
+ exitCode = 0;
+
+ // clean up
+ err2:
+ delete htmlGen;
+ err1:
+ delete doc;
+ delete globalParams;
+ err0:
+
+ // check for memory leaks
+ Object::memCheck(stderr);
+ gMemReport(stderr);
+
+ return exitCode;
+}
+
+static GBool createIndex(char *htmlDir) {
+ GString *htmlFileName;
+ FILE *html;
+ int pg;
+
+ htmlFileName = GString::format("{0:s}/index.html", htmlDir);
+ html = fopen(htmlFileName->getCString(), "w");
+ delete htmlFileName;
+ if (!html) {
+ error(errIO, -1, "Couldn't open HTML file '{0:t}'", htmlFileName);
+ return gFalse;
+ }
+
+ fprintf(html, "<html>\n");
+ fprintf(html, "<body>\n");
+ for (pg = firstPage; pg <= lastPage; ++pg) {
+ fprintf(html, "<a href=\"page%d.html\">page %d</a><br>\n", pg, pg);
+ }
+ fprintf(html, "</body>\n");
+ fprintf(html, "</html>\n");
+
+ fclose(html);
+
+ return gTrue;
+}
diff --git a/xpdf/pdftopng.cc b/xpdf/pdftopng.cc
new file mode 100644
index 0000000..bb91eba
--- /dev/null
+++ b/xpdf/pdftopng.cc
@@ -0,0 +1,289 @@
+//========================================================================
+//
+// pdftopng.cc
+//
+// Copyright 2009 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <png.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 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_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}
+};
+
+static void setupPNG(png_structp *png, png_infop *pngInfo, FILE *f,
+ int bitDepth, int colorType,
+ SplashBitmap *bitmap);
+static void writePNGData(png_structp png, SplashBitmap *bitmap);
+static void finishPNG(png_structp *png, png_infop *pngInfo);
+
+int main(int argc, char *argv[]) {
+ PDFDoc *doc;
+ GString *fileName;
+ char *pngRoot;
+ GString *pngFile;
+ GString *ownerPW, *userPW;
+ SplashColor paperColor;
+ SplashOutputDev *splashOut;
+ GBool ok;
+ int exitCode;
+ int pg;
+ png_structp png;
+ png_infop pngInfo;
+ FILE *f;
+
+ exitCode = 99;
+
+ // parse args
+ ok = parseArgs(argDesc, &argc, argv);
+ if (mono && gray) {
+ ok = gFalse;
+ }
+ if (!ok || argc != 3 || printVersion || printHelp) {
+ fprintf(stderr, "pdftopng version %s\n", xpdfVersion);
+ fprintf(stderr, "%s\n", xpdfCopyright);
+ if (!printVersion) {
+ printUsage("pdftopng", "<PDF-file> <PNG-root>", argDesc);
+ }
+ goto err0;
+ }
+ fileName = new GString(argv[1]);
+ pngRoot = argv[2];
+
+ // read config file
+ globalParams = new GlobalParams(cfgFileName);
+ globalParams->setupBaseFonts(NULL);
+ 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 PNG 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);
+ if (mono) {
+ if (!strcmp(pngRoot, "-")) {
+ f = stdout;
+ } else {
+ pngFile = GString::format("{0:s}-{1:06d}.png", pngRoot, pg);
+ if (!(f = fopen(pngFile->getCString(), "wb"))) {
+ exit(2);
+ }
+ delete pngFile;
+ }
+ setupPNG(&png, &pngInfo, f,
+ 1, PNG_COLOR_TYPE_GRAY, splashOut->getBitmap());
+ writePNGData(png, splashOut->getBitmap());
+ finishPNG(&png, &pngInfo);
+ fclose(f);
+ } else if (gray) {
+ if (!strcmp(pngRoot, "-")) {
+ f = stdout;
+ } else {
+ pngFile = GString::format("{0:s}-{1:06d}.png", pngRoot, pg);
+ if (!(f = fopen(pngFile->getCString(), "wb"))) {
+ exit(2);
+ }
+ delete pngFile;
+ }
+ setupPNG(&png, &pngInfo, f,
+ 8, PNG_COLOR_TYPE_GRAY, splashOut->getBitmap());
+ writePNGData(png, splashOut->getBitmap());
+ finishPNG(&png, &pngInfo);
+ fclose(f);
+ } else { // RGB
+ if (!strcmp(pngRoot, "-")) {
+ f = stdout;
+ } else {
+ pngFile = GString::format("{0:s}-{1:06d}.png", pngRoot, pg);
+ if (!(f = fopen(pngFile->getCString(), "wb"))) {
+ exit(2);
+ }
+ delete pngFile;
+ }
+ setupPNG(&png, &pngInfo, f,
+ 8, PNG_COLOR_TYPE_RGB, splashOut->getBitmap());
+ writePNGData(png, splashOut->getBitmap());
+ finishPNG(&png, &pngInfo);
+ fclose(f);
+ }
+ }
+ delete splashOut;
+
+ exitCode = 0;
+
+ // clean up
+ err1:
+ delete doc;
+ delete globalParams;
+ err0:
+
+ // check for memory leaks
+ Object::memCheck(stderr);
+ gMemReport(stderr);
+
+ return exitCode;
+}
+
+static void setupPNG(png_structp *png, png_infop *pngInfo, FILE *f,
+ int bitDepth, int colorType,
+ SplashBitmap *bitmap) {
+ if (!(*png = png_create_write_struct(PNG_LIBPNG_VER_STRING,
+ NULL, NULL, NULL)) ||
+ !(*pngInfo = png_create_info_struct(*png))) {
+ exit(2);
+ }
+ if (setjmp(png_jmpbuf(*png))) {
+ exit(2);
+ }
+ png_init_io(*png, f);
+ png_set_IHDR(*png, *pngInfo, bitmap->getWidth(), bitmap->getHeight(),
+ bitDepth, colorType, PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+ png_write_info(*png, *pngInfo);
+}
+
+static void writePNGData(png_structp png, SplashBitmap *bitmap) {
+ Guchar *p;
+ int y;
+
+ if (setjmp(png_jmpbuf(png))) {
+ exit(2);
+ }
+ p = bitmap->getDataPtr();
+ for (y = 0; y < bitmap->getHeight(); ++y) {
+ png_write_row(png, (png_bytep)p);
+ p += bitmap->getRowSize();
+ }
+}
+
+
+
+static void finishPNG(png_structp *png, png_infop *pngInfo) {
+ if (setjmp(png_jmpbuf(*png))) {
+ exit(2);
+ }
+ png_write_end(*png, *pngInfo);
+ png_destroy_write_struct(png, pngInfo);
+}
diff --git a/xpdf/pdftoppm.cc b/xpdf/pdftoppm.cc
index cfc7236..17c0bbf 100644
--- a/xpdf/pdftoppm.cc
+++ b/xpdf/pdftoppm.cc
@@ -8,6 +8,14 @@
#include <aconf.h>
#include <stdio.h>
+#ifdef _WIN32
+# include <io.h>
+# include <fcntl.h>
+#endif
+#ifdef DEBUG_FP_LINUX
+# include <fenv.h>
+# include <fpu_control.h>
+#endif
#include "parseargs.h"
#include "gmem.h"
#include "GString.h"
@@ -24,7 +32,6 @@ 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] = "";
@@ -46,10 +53,6 @@ static ArgDesc argDesc[] = {
"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"},
@@ -91,6 +94,21 @@ int main(int argc, char *argv[]) {
int exitCode;
int pg;
+#ifdef DEBUG_FP_LINUX
+ // enable exceptions on floating point div-by-zero
+ feenableexcept(FE_DIVBYZERO);
+ // force 64-bit rounding: this avoids changes in output when minor
+ // code changes result in spills of x87 registers; it also avoids
+ // differences in output with valgrind's 64-bit floating point
+ // emulation (yes, this is a kludge; but it's pretty much
+ // unavoidable given the x87 instruction set; see gcc bug 323 for
+ // more info)
+ fpu_control_t cw;
+ _FPU_GETCW(cw);
+ cw = (cw & ~_FPU_EXTENDED) | _FPU_DOUBLE;
+ _FPU_SETCW(cw);
+#endif
+
exitCode = 99;
// parse args
@@ -112,11 +130,6 @@ int main(int argc, char *argv[]) {
// 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");
@@ -182,6 +195,9 @@ int main(int argc, char *argv[]) {
doc->displayPage(splashOut, pg, resolution, resolution, 0,
gFalse, gTrue, gFalse);
if (!strcmp(ppmRoot, "-")) {
+#ifdef _WIN32
+ _setmode(_fileno(stdout), _O_BINARY);
+#endif
splashOut->getBitmap()->writePNMFile(stdout);
} else {
ppmFile = GString::format("{0:s}-{1:06d}.{2:s}",
diff --git a/xpdf/pdftops.cc b/xpdf/pdftops.cc
index eae441f..a6f478d 100644
--- a/xpdf/pdftops.cc
+++ b/xpdf/pdftops.cc
@@ -11,6 +11,10 @@
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
+#ifdef DEBUG_FP_LINUX
+# include <fenv.h>
+# include <fpu_control.h>
+#endif
#include "parseargs.h"
#include "GString.h"
#include "gmem.h"
@@ -147,6 +151,21 @@ int main(int argc, char *argv[]) {
char *p;
int exitCode;
+#ifdef DEBUG_FP_LINUX
+ // enable exceptions on floating point div-by-zero
+ feenableexcept(FE_DIVBYZERO);
+ // force 64-bit rounding: this avoids changes in output when minor
+ // code changes result in spills of x87 registers; it also avoids
+ // differences in output with valgrind's 64-bit floating point
+ // emulation (yes, this is a kludge; but it's pretty much
+ // unavoidable given the x87 instruction set; see gcc bug 323 for
+ // more info)
+ fpu_control_t cw;
+ _FPU_GETCW(cw);
+ cw = (cw & ~_FPU_EXTENDED) | _FPU_DOUBLE;
+ _FPU_SETCW(cw);
+#endif
+
exitCode = 99;
// parse args
@@ -216,6 +235,9 @@ int main(int argc, char *argv[]) {
if (noCrop) {
globalParams->setPSCrop(gFalse);
}
+ if (pageCrop) {
+ globalParams->setPSUseCropBoxAsPage(gTrue);
+ }
if (expand) {
globalParams->setPSExpandSmaller(gTrue);
}
@@ -318,15 +340,18 @@ int main(int argc, char *argv[]) {
firstPage, lastPage, mode);
if (psOut->isOk()) {
doc->displayPages(psOut, firstPage, lastPage, 72, 72,
- 0, !pageCrop, globalParams->getPSCrop(), gTrue);
+ 0, !globalParams->getPSUseCropBoxAsPage(),
+ globalParams->getPSCrop(), gTrue);
} else {
delete psOut;
exitCode = 2;
goto err2;
}
- delete psOut;
-
exitCode = 0;
+ if (!psOut->checkIO()) {
+ exitCode = 2;
+ }
+ delete psOut;
// clean up
err2:
diff --git a/xpdf/pdftotext.cc b/xpdf/pdftotext.cc
index 5296ac4..758413e 100644
--- a/xpdf/pdftotext.cc
+++ b/xpdf/pdftotext.cc
@@ -2,7 +2,7 @@
//
// pdftotext.cc
//
-// Copyright 1997-2003 Glyph & Cog, LLC
+// Copyright 1997-2013 Glyph & Cog, LLC
//
//========================================================================
@@ -11,6 +11,10 @@
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
+#ifdef DEBUG_FP_LINUX
+# include <fenv.h>
+# include <fpu_control.h>
+#endif
#include "parseargs.h"
#include "GString.h"
#include "gmem.h"
@@ -26,21 +30,19 @@
#include "TextOutputDev.h"
#include "CharTypes.h"
#include "UnicodeMap.h"
+#include "TextString.h"
#include "Error.h"
#include "config.h"
-static void printInfoString(FILE *f, Dict *infoDict, const char *key,
- const char *text1, const char *text2,
- UnicodeMap *uMap);
-static void printInfoDate(FILE *f, Dict *infoDict, const char *key,
- const char *fmt);
-
static int firstPage = 1;
static int lastPage = 0;
static GBool physLayout = gFalse;
-static double fixedPitch = 0;
+static GBool tableLayout = gFalse;
+static GBool linePrinter = gFalse;
static GBool rawOrder = gFalse;
-static GBool htmlMeta = gFalse;
+static double fixedPitch = 0;
+static double fixedLineSpacing = 0;
+static GBool clipText = gFalse;
static char textEncName[128] = "";
static char textEOL[16] = "";
static GBool noPageBreaks = gFalse;
@@ -58,12 +60,18 @@ static ArgDesc argDesc[] = {
"last page to convert"},
{"-layout", argFlag, &physLayout, 0,
"maintain original physical layout"},
- {"-fixed", argFP, &fixedPitch, 0,
- "assume fixed-pitch (or tabular) text"},
+ {"-table", argFlag, &tableLayout, 0,
+ "similar to -layout, but optimized for tables"},
+ {"-lineprinter", argFlag, &linePrinter, 0,
+ "use strict fixed-pitch/height layout"},
{"-raw", argFlag, &rawOrder, 0,
"keep strings in content stream order"},
- {"-htmlmeta", argFlag, &htmlMeta, 0,
- "generate a simple HTML file, including the meta information"},
+ {"-fixed", argFP, &fixedPitch, 0,
+ "assume fixed-pitch (or tabular) text"},
+ {"-linespacing", argFP, &fixedLineSpacing, 0,
+ "fixed line spacing for LinePrinter mode"},
+ {"-clip", argFlag, &clipText, 0,
+ "separate clipped text"},
{"-enc", argString, textEncName, sizeof(textEncName),
"output text encoding name"},
{"-eol", argString, textEOL, sizeof(textEOL),
@@ -96,14 +104,29 @@ int main(int argc, char *argv[]) {
GString *fileName;
GString *textFileName;
GString *ownerPW, *userPW;
+ TextOutputControl textOutControl;
TextOutputDev *textOut;
- FILE *f;
UnicodeMap *uMap;
Object info;
GBool ok;
char *p;
int exitCode;
+#ifdef DEBUG_FP_LINUX
+ // enable exceptions on floating point div-by-zero
+ feenableexcept(FE_DIVBYZERO);
+ // force 64-bit rounding: this avoids changes in output when minor
+ // code changes result in spills of x87 registers; it also avoids
+ // differences in output with valgrind's 64-bit floating point
+ // emulation (yes, this is a kludge; but it's pretty much
+ // unavoidable given the x87 instruction set; see gcc bug 323 for
+ // more info)
+ fpu_control_t cw;
+ _FPU_GETCW(cw);
+ cw = (cw & ~_FPU_EXTENDED) | _FPU_DOUBLE;
+ _FPU_SETCW(cw);
+#endif
+
exitCode = 99;
// parse args
@@ -117,9 +140,6 @@ int main(int argc, char *argv[]) {
goto err0;
}
fileName = new GString(argv[1]);
- if (fixedPitch) {
- physLayout = gTrue;
- }
// read config file
globalParams = new GlobalParams(cfgFileName);
@@ -187,7 +207,7 @@ int main(int argc, char *argv[]) {
} else {
textFileName = fileName->copy();
}
- textFileName->append(htmlMeta ? ".html" : ".txt");
+ textFileName->append(".txt");
}
// get page range
@@ -198,50 +218,25 @@ int main(int argc, char *argv[]) {
lastPage = doc->getNumPages();
}
- // write HTML header
- if (htmlMeta) {
- if (!textFileName->cmp("-")) {
- f = stdout;
- } else {
- if (!(f = fopen(textFileName->getCString(), "wb"))) {
- error(errIO, -1, "Couldn't open text file '{0:t}'", textFileName);
- 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, fixedPitch, rawOrder, htmlMeta);
+ if (tableLayout) {
+ textOutControl.mode = textOutTableLayout;
+ textOutControl.fixedPitch = fixedPitch;
+ } else if (physLayout) {
+ textOutControl.mode = textOutPhysLayout;
+ textOutControl.fixedPitch = fixedPitch;
+ } else if (linePrinter) {
+ textOutControl.mode = textOutLinePrinter;
+ textOutControl.fixedPitch = fixedPitch;
+ textOutControl.fixedLineSpacing = fixedLineSpacing;
+ } else if (rawOrder) {
+ textOutControl.mode = textOutRawOrder;
+ } else {
+ textOutControl.mode = textOutReadingOrder;
+ }
+ textOutControl.clipText = clipText;
+ textOut = new TextOutputDev(textFileName->getCString(), &textOutControl,
+ gFalse);
if (textOut->isOk()) {
doc->displayPages(textOut, firstPage, lastPage, 72, 72, 0,
gFalse, gTrue, gFalse);
@@ -252,25 +247,6 @@ int main(int argc, char *argv[]) {
}
delete textOut;
- // write end of HTML file
- if (htmlMeta) {
- if (!textFileName->cmp("-")) {
- f = stdout;
- } else {
- if (!(f = fopen(textFileName->getCString(), "ab"))) {
- error(errIO, -1, "Couldn't open text file '{0:t}'", textFileName);
- 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
@@ -289,56 +265,3 @@ int main(int argc, char *argv[]) {
return exitCode;
}
-
-static void printInfoString(FILE *f, Dict *infoDict, const char *key,
- const char *text1, const 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, const char *key,
- const 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/vms_make.com b/xpdf/vms_make.com
deleted file mode 100644
index f4fb74a..0000000
--- a/xpdf/vms_make.com
+++ /dev/null
@@ -1,129 +0,0 @@
-$!========================================================================
-$!
-$! 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
index e5d91ca..1041265 100644
--- a/xpdf/xpdf.cc
+++ b/xpdf/xpdf.cc
@@ -22,7 +22,6 @@
//------------------------------------------------------------------------
static GBool contView = gFalse;
-static char enableT1libStr[16] = "";
static char enableFreeTypeStr[16] = "";
static char antialiasStr[16] = "";
static char vectorAntialiasStr[16] = "";
@@ -66,10 +65,6 @@ static ArgDesc argDesc[] = {
"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"},
@@ -185,11 +180,6 @@ int main(int argc, char *argv[]) {
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");