summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Garcia Campos <carlosgc@gnome.org>2014-06-06 15:30:34 +0200
committerCarlos Garcia Campos <carlosgc@gnome.org>2014-06-06 15:30:34 +0200
commita0a97c38508ce2a2297599d65e6ae04abaf092a1 (patch)
treeee362d967d7d93b354c4f4209ee099608ebbaf45
parente865f320891f6e1f97fcc4f612119311aa029a63 (diff)
Add 3.04 diff
Also added a CHANGES-3.04 file and a README file.
-rw-r--r--ALL_DIFF54757
-rw-r--r--CHANGES-3.04173
-rw-r--r--README22
3 files changed, 54952 insertions, 0 deletions
diff --git a/ALL_DIFF b/ALL_DIFF
index e69de29..d7fe2b0 100644
--- a/ALL_DIFF
+++ b/ALL_DIFF
@@ -0,0 +1,54757 @@
+diff -uNr xpdf-3.03/aconf2.h xpdf-3.04/aconf2.h
+--- xpdf-3.03/aconf2.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/aconf2.h 2014-05-28 20:50:50.000000000 +0200
+@@ -28,11 +28,4 @@
+ # endif
+ #endif
+
+-/*
+- * Make sure WIN32 is defined if appropriate.
+- */
+-#if defined(_WIN32) && !defined(WIN32)
+-# define WIN32
+-#endif
+-
+ #endif
+diff -uNr xpdf-3.03/aconf-dj.h xpdf-3.04/aconf-dj.h
+--- xpdf-3.03/aconf-dj.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/aconf-dj.h 2014-05-28 20:50:50.000000000 +0200
+@@ -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 -uNr xpdf-3.03/aconf.h.in xpdf-3.04/aconf.h.in
+--- xpdf-3.03/aconf.h.in 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/aconf.h.in 2014-05-28 20:50:50.000000000 +0200
+@@ -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 -uNr xpdf-3.03/aconf-win32.h xpdf-3.04/aconf-win32.h
+--- xpdf-3.03/aconf-win32.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/aconf-win32.h 2014-05-28 20:50:50.000000000 +0200
+@@ -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 -uNr xpdf-3.03/doc/pdfdetach.1 xpdf-3.04/doc/pdfdetach.1
+--- xpdf-3.03/doc/pdfdetach.1 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/doc/pdfdetach.1 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ 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 -uNr xpdf-3.03/doc/pdfdetach.cat xpdf-3.04/doc/pdfdetach.cat
+--- xpdf-3.03/doc/pdfdetach.cat 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/doc/pdfdetach.cat 2014-05-28 20:50:50.000000000 +0200
+@@ -4,7 +4,7 @@
+
+ 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 @@
+ 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 -uNr xpdf-3.03/doc/pdfdetach.hlp xpdf-3.04/doc/pdfdetach.hlp
+--- xpdf-3.03/doc/pdfdetach.hlp 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/doc/pdfdetach.hlp 1970-01-01 01:00:00.000000000 +0100
+@@ -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 -uNr xpdf-3.03/doc/pdffonts.1 xpdf-3.04/doc/pdffonts.1
+--- xpdf-3.03/doc/pdffonts.1 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/doc/pdffonts.1 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ .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 @@
+ .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 @@
+ 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 -uNr xpdf-3.03/doc/pdffonts.cat xpdf-3.04/doc/pdffonts.cat
+--- xpdf-3.03/doc/pdffonts.cat 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/doc/pdffonts.cat 2014-05-28 20:50:50.000000000 +0200
+@@ -3,7 +3,7 @@
+
+
+ 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 @@
+ 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 @@
+ -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 @@
+ 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 -uNr xpdf-3.03/doc/pdffonts.hlp xpdf-3.04/doc/pdffonts.hlp
+--- xpdf-3.03/doc/pdffonts.hlp 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/doc/pdffonts.hlp 1970-01-01 01:00:00.000000000 +0100
+@@ -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 -uNr xpdf-3.03/doc/pdfimages.1 xpdf-3.04/doc/pdfimages.1
+--- xpdf-3.03/doc/pdfimages.1 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/doc/pdfimages.1 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ 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 @@
+ 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 -uNr xpdf-3.03/doc/pdfimages.cat xpdf-3.04/doc/pdfimages.cat
+--- xpdf-3.03/doc/pdfimages.cat 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/doc/pdfimages.cat 2014-05-28 20:50:50.000000000 +0200
+@@ -4,7 +4,7 @@
+
+ NAME
+ pdfimages - Portable Document Format (PDF) image extractor (version
+- 3.03)
++ 3.04)
+
+ SYNOPSIS
+ pdfimages [options] PDF-file image-root
+@@ -14,8 +14,8 @@
+ 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 @@
+ 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 -uNr xpdf-3.03/doc/pdfimages.hlp xpdf-3.04/doc/pdfimages.hlp
+--- xpdf-3.03/doc/pdfimages.hlp 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/doc/pdfimages.hlp 1970-01-01 01:00:00.000000000 +0100
+@@ -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 -uNr xpdf-3.03/doc/pdfinfo.1 xpdf-3.04/doc/pdfinfo.1
+--- xpdf-3.03/doc/pdfinfo.1 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/doc/pdfinfo.1 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ print and copy permissions (if encrypted)
+ .RE
+ .RS
+-page size
++page size and rotation
+ .RE
+ .RS
+ file size
+@@ -150,15 +150,17 @@
+ 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 -uNr xpdf-3.03/doc/pdfinfo.cat xpdf-3.04/doc/pdfinfo.cat
+--- xpdf-3.03/doc/pdfinfo.cat 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/doc/pdfinfo.cat 2014-05-28 20:50:50.000000000 +0200
+@@ -4,7 +4,7 @@
+
+ NAME
+ pdfinfo - Portable Document Format (PDF) document information extractor
+- (version 3.03)
++ (version 3.04)
+
+ SYNOPSIS
+ pdfinfo [options] [PDF-file]
+@@ -31,7 +31,7 @@
+ 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 @@
+ 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 -uNr xpdf-3.03/doc/pdfinfo.hlp xpdf-3.04/doc/pdfinfo.hlp
+--- xpdf-3.03/doc/pdfinfo.hlp 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/doc/pdfinfo.hlp 1970-01-01 01:00:00.000000000 +0100
+@@ -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 -uNr xpdf-3.03/doc/pdftohtml.1 xpdf-3.04/doc/pdftohtml.1
+--- xpdf-3.03/doc/pdftohtml.1 1970-01-01 01:00:00.000000000 +0100
++++ xpdf-3.04/doc/pdftohtml.1 2014-05-28 20:50:50.000000000 +0200
+@@ -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 -uNr xpdf-3.03/doc/pdftohtml.cat xpdf-3.04/doc/pdftohtml.cat
+--- xpdf-3.03/doc/pdftohtml.cat 1970-01-01 01:00:00.000000000 +0100
++++ xpdf-3.04/doc/pdftohtml.cat 2014-05-28 20:50:50.000000000 +0200
+@@ -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 -uNr xpdf-3.03/doc/pdftopng.1 xpdf-3.04/doc/pdftopng.1
+--- xpdf-3.03/doc/pdftopng.1 1970-01-01 01:00:00.000000000 +0100
++++ xpdf-3.04/doc/pdftopng.1 2014-05-28 20:50:50.000000000 +0200
+@@ -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 -uNr xpdf-3.03/doc/pdftopng.cat xpdf-3.04/doc/pdftopng.cat
+--- xpdf-3.03/doc/pdftopng.cat 1970-01-01 01:00:00.000000000 +0100
++++ xpdf-3.04/doc/pdftopng.cat 2014-05-28 20:50:50.000000000 +0200
+@@ -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 -uNr xpdf-3.03/doc/pdftoppm.1 xpdf-3.04/doc/pdftoppm.1
+--- xpdf-3.03/doc/pdftoppm.1 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/doc/pdftoppm.1 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ .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 @@
+ 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 -uNr xpdf-3.03/doc/pdftoppm.cat xpdf-3.04/doc/pdftoppm.cat
+--- xpdf-3.03/doc/pdftoppm.cat 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/doc/pdftoppm.cat 2014-05-28 20:50:50.000000000 +0200
+@@ -4,7 +4,7 @@
+
+ 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 @@
+
+ -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 @@
+ [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 @@
+ 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 -uNr xpdf-3.03/doc/pdftoppm.hlp xpdf-3.04/doc/pdftoppm.hlp
+--- xpdf-3.03/doc/pdftoppm.hlp 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/doc/pdftoppm.hlp 1970-01-01 01:00:00.000000000 +0100
+@@ -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 -uNr xpdf-3.03/doc/pdftops.1 xpdf-3.04/doc/pdftops.1
+--- xpdf-3.03/doc/pdftops.1 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/doc/pdftops.1 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ .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 @@
+ 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 -uNr xpdf-3.03/doc/pdftops.cat xpdf-3.04/doc/pdftops.cat
+--- xpdf-3.03/doc/pdftops.cat 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/doc/pdftops.cat 2014-05-28 20:50:50.000000000 +0200
+@@ -4,7 +4,7 @@
+
+ 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 @@
+ 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 @@
+ 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 @@
+
+ -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 @@
+ 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 -uNr xpdf-3.03/doc/pdftops.hlp xpdf-3.04/doc/pdftops.hlp
+--- xpdf-3.03/doc/pdftops.hlp 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/doc/pdftops.hlp 1970-01-01 01:00:00.000000000 +0100
+@@ -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 -uNr xpdf-3.03/doc/pdftotext.1 xpdf-3.04/doc/pdftotext.1
+--- xpdf-3.03/doc/pdftotext.1 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/doc/pdftotext.1 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ .B \-layout
+ Maintain (as best as possible) the original physical layout of the
+ text. The default is to \'undo' physical layout (columns,
+-hyphenation, etc.) and output the text in reading order.
+-.TP
+-.BI \-fixed " number"
+-Assume fixed-pitch (or tabular) text, with the specified character
+-width (in points). This forces physical layout mode.
++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. This is a hack which often
+-"undoes" column formatting, etc. Use of raw mode is no longer
+-recommended.
+-.TP
+-.B \-htmlmeta
+-Generate a simple HTML file, including the meta information. This
+-simply wraps the text in <pre> and </pre> and prepends the meta
+-headers.
++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"
++Specify the character pitch (character width), in points, for physical
++layout, table, or line printer mode. This is ignored in all other
++modes.
++.TP
++.BI \-linespacing " number"
++Specify the line spacing, in points, for line printer mode. This is
++ignored in all other modes.
++.TP
++.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 @@
+ 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 -uNr xpdf-3.03/doc/pdftotext.cat xpdf-3.04/doc/pdftotext.cat
+--- xpdf-3.03/doc/pdftotext.cat 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/doc/pdftotext.cat 2014-05-28 20:50:50.000000000 +0200
+@@ -4,27 +4,27 @@
+
+ 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 @@
+ 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).
+
+- -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. Depending on how the PDF
++ file was generated, this may or may not be useful.
+
+- -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.
++ -fixed number
++ Specify the character pitch (character width), in points, for
++ physical layout, table, or line printer mode. This is ignored
++ in all other modes.
++
++ -linespacing number
++ Specify the line spacing, in points, for line printer mode.
++ This is ignored in all other modes.
++
++ -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 @@
+ 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 -uNr xpdf-3.03/doc/pdftotext.hlp xpdf-3.04/doc/pdftotext.hlp
+--- xpdf-3.03/doc/pdftotext.hlp 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/doc/pdftotext.hlp 1970-01-01 01:00:00.000000000 +0100
+@@ -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 -uNr xpdf-3.03/doc/sample-xpdfrc xpdf-3.04/doc/sample-xpdfrc
+--- xpdf-3.03/doc/sample-xpdfrc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/doc/sample-xpdfrc 2014-05-28 20:50:50.000000000 +0200
+@@ -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 -uNr xpdf-3.03/doc/xpdf.1 xpdf-3.04/doc/xpdf.1
+--- xpdf-3.03/doc/xpdf.1 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/doc/xpdf.1 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ 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 @@
+ 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 @@
+ %% => %
+
+ .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 @@
+ 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 @@
+ 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 -uNr xpdf-3.03/doc/xpdf.cat xpdf-3.04/doc/xpdf.cat
+--- xpdf-3.03/doc/xpdf.cat 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/doc/xpdf.cat 2014-05-28 20:50:50.000000000 +0200
+@@ -3,7 +3,7 @@
+
+
+ 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 @@
+ 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 @@
+ -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 @@
+ [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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+
+ -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 @@
+ 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 @@
+ 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 @@
+ 'Quit' button
+ Quit xpdf.
+
+-
+ Menu
+ Pressing the right mouse button will post a popup menu with the follow-
+ ing commands:
+@@ -249,11 +244,11 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ %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 @@
+ 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 @@
+ 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 -uNr xpdf-3.03/doc/xpdf.hlp xpdf-3.04/doc/xpdf.hlp
+--- xpdf-3.03/doc/xpdf.hlp 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/doc/xpdf.hlp 1970-01-01 01:00:00.000000000 +0100
+@@ -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 -uNr xpdf-3.03/doc/xpdfrc.5 xpdf-3.04/doc/xpdfrc.5
+--- xpdf-3.03/doc/xpdfrc.5 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/doc/xpdfrc.5 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ "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 @@
+ 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 @@
+ 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 @@
+ textEOL unix
+
+ # misc options
+-enableT1lib yes
+ enableFreeType yes
+ launchCommand viewer-script
+ urlCommand "netscape \-remote 'openURL(%s)'"
+@@ -649,16 +680,18 @@
+ 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 -uNr xpdf-3.03/doc/xpdfrc.cat xpdf-3.04/doc/xpdfrc.cat
+--- xpdf-3.03/doc/xpdfrc.cat 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/doc/xpdfrc.cat 2014-05-28 20:50:50.000000000 +0200
+@@ -3,7 +3,7 @@
+
+
+ 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 @@
+ 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 @@
+
+ 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 @@
+ "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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ textEOL unix
+
+ # misc options
+- enableT1lib yes
+ enableFreeType yes
+ launchCommand viewer-script
+ urlCommand "netscape -remote 'openURL(%s)'"
+@@ -588,14 +620,14 @@
+ 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 -uNr xpdf-3.03/doc/xpdfrc.hlp xpdf-3.04/doc/xpdfrc.hlp
+--- xpdf-3.03/doc/xpdfrc.hlp 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/doc/xpdfrc.hlp 1970-01-01 01:00:00.000000000 +0100
+@@ -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 -uNr xpdf-3.03/fofi/FoFiBase.cc xpdf-3.04/fofi/FoFiBase.cc
+--- xpdf-3.03/fofi/FoFiBase.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/fofi/FoFiBase.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -84,7 +84,7 @@
+ 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::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::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 @@
+ 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::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 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 -uNr xpdf-3.03/fofi/FoFiIdentifier.cc xpdf-3.04/fofi/FoFiIdentifier.cc
+--- xpdf-3.03/fofi/FoFiIdentifier.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/fofi/FoFiIdentifier.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -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::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 @@
+ 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 -uNr xpdf-3.03/fofi/FoFiIdentifier.h xpdf-3.04/fofi/FoFiIdentifier.h
+--- xpdf-3.03/fofi/FoFiIdentifier.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/fofi/FoFiIdentifier.h 2014-05-28 20:50:50.000000000 +0200
+@@ -15,6 +15,8 @@
+ #pragma interface
+ #endif
+
++class GList;
++
+ //------------------------------------------------------------------------
+ // FoFiIdentifier
+ //------------------------------------------------------------------------
+@@ -28,6 +30,7 @@
+ 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 @@
+ 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 -uNr xpdf-3.03/fofi/FoFiTrueType.cc xpdf-3.04/fofi/FoFiTrueType.cc
+--- xpdf-3.03/fofi/FoFiTrueType.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/fofi/FoFiTrueType.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -275,10 +275,11 @@
+ // 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 @@
+ 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 @@
+ 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 @@
+ cmaps = NULL;
+ nCmaps = 0;
+ nameToGID = NULL;
++ isDfont = isDfontA;
+ parsedOk = gFalse;
+
+- parse();
++ parse(fontNum, allowHeadlessCFF);
+ }
+
+ FoFiTrueType::~FoFiTrueType() {
+@@ -363,7 +372,26 @@
+ 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 @@
+ 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 @@
+ // 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 @@
+ *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 @@
+ 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 @@
+ } 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ }
+
+ // 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 @@
+ if (!parsedOk) {
+ return;
+ }
+- } else {
+- nCmaps = 0;
+ }
+
+ // get the number of glyphs from the maxp table
+@@ -2087,6 +2145,74 @@
+ 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 -uNr xpdf-3.03/fofi/FoFiTrueType.h xpdf-3.04/fofi/FoFiTrueType.h
+--- xpdf-3.03/fofi/FoFiTrueType.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/fofi/FoFiTrueType.h 2014-05-28 20:50:50.000000000 +0200
+@@ -30,11 +30,19 @@
+ 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 @@
+ // 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 @@
+
+ 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 @@
+ 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 @@
+ int bbox[4];
+ GHash *nameToGID;
+ GBool openTypeCFF;
++ GBool headlessCFF;
++ GBool isDfont;
+
+ GBool parsedOk;
+ };
+diff -uNr xpdf-3.03/fofi/FoFiType1C.cc xpdf-3.04/fofi/FoFiType1C.cc
+--- xpdf-3.03/fofi/FoFiType1C.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/fofi/FoFiType1C.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -387,24 +387,42 @@
+ 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 @@
+ 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 @@
+ 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 -uNr xpdf-3.03/fofi/Makefile.in xpdf-3.04/fofi/Makefile.in
+--- xpdf-3.03/fofi/Makefile.in 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/fofi/Makefile.in 2014-05-28 20:50:50.000000000 +0200
+@@ -14,7 +14,7 @@
+ 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 @@
+ depend:
+ $(CXX) $(CXXFLAGS) -MM $(CXX_SRC) >Makefile.dep
+
+-include Makefile.dep
++-include Makefile.dep
+diff -uNr xpdf-3.03/goo/FixedPoint.h xpdf-3.04/goo/FixedPoint.h
+--- xpdf-3.03/goo/FixedPoint.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/goo/FixedPoint.h 2014-05-28 20:50:50.000000000 +0200
+@@ -49,35 +49,35 @@
+
+ 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 -uNr xpdf-3.03/goo/gfile.cc xpdf-3.04/goo/gfile.cc
+--- xpdf-3.03/goo/gfile.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/goo/gfile.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ //---------- 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 @@
+
+ #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 @@
+ }
+ return path;
+
+-#elif defined(WIN32)
++#elif defined(_WIN32)
+ //---------- Win32 ----------
+ GString *tmp;
+ char buf[256];
+@@ -155,7 +156,7 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ }
+ 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 @@
+ }
+
+ 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 @@
+ #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 @@
+ #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 @@
+ #endif
+ }
+
+-#ifdef WIN32
++#ifdef _WIN32
+ GString *fileNameToUTF8(char *path) {
+ GString *s;
+ char *p;
+@@ -597,7 +607,7 @@
+ #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 @@
+ 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 @@
+ 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 @@
+ #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 @@
+ 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() {
+ 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 @@
+ 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 @@
+ }
+ #elif defined(ACORN)
+ #elif defined(MACOS)
++#elif defined(ANDROID)
+ #elif defined(VMS)
+ struct dirent *ent;
+ e = NULL;
+@@ -813,17 +850,18 @@
+ }
+
+ 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 -uNr xpdf-3.03/goo/gfile.h xpdf-3.04/goo/gfile.h
+--- xpdf-3.03/goo/gfile.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/goo/gfile.h 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ // 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 @@
+ // 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 @@
+
+ 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 -uNr xpdf-3.03/goo/gmem.cc xpdf-3.04/goo/gmem.cc
+--- xpdf-3.03/goo/gmem.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/goo/gmem.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -44,6 +44,7 @@
+ static int gMemIndex = 0;
+ static int gMemAlloc = 0;
+ static int gMemInUse = 0;
++static int gMaxMemInUse = 0;
+
+ #endif /* DEBUG_MEM */
+
+@@ -56,24 +57,14 @@
+ 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 @@
+ 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 *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 @@
+ 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 *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 @@
+ 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 @@
+ }
+ 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 @@
+ }
+ 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 @@
+ #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 -uNr xpdf-3.03/goo/gmem.h xpdf-3.04/goo/gmem.h
+--- xpdf-3.03/goo/gmem.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/goo/gmem.h 2014-05-28 20:50:50.000000000 +0200
+@@ -58,6 +58,11 @@
+ */
+ 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 -uNr xpdf-3.03/goo/GMutex.h xpdf-3.04/goo/GMutex.h
+--- xpdf-3.03/goo/GMutex.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/goo/GMutex.h 2014-05-28 20:50:50.000000000 +0200
+@@ -22,7 +22,7 @@
+ // ...
+ // gDestroyMutex(&m);
+
+-#ifdef WIN32
++#ifdef _WIN32
+
+ #include <windows.h>
+
+diff -uNr xpdf-3.03/goo/GString.cc xpdf-3.04/goo/GString.cc
+--- xpdf-3.03/goo/GString.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/goo/GString.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -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 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 @@
+ 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 @@
+ 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::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::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(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(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, 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 @@
+ char buf[65];
+ int len, i;
+ const char *p0, *p1;
+- char *str;
++ const char *str;
+
+ argsLen = 0;
+ argsSize = 8;
+@@ -491,13 +513,23 @@
+ 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 @@
+ #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 @@
+ #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::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 @@
+ 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 @@
+ 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 @@
+ 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, 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::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 -uNr xpdf-3.03/goo/GString.h xpdf-3.04/goo/GString.h
+--- xpdf-3.03/goo/GString.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/goo/GString.h 2014-05-28 20:50:50.000000000 +0200
+@@ -129,23 +129,23 @@
+ #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 -uNr xpdf-3.03/goo/Makefile.in xpdf-3.04/goo/Makefile.in
+--- xpdf-3.03/goo/Makefile.in 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/goo/Makefile.in 2014-05-28 20:50:50.000000000 +0200
+@@ -11,8 +11,8 @@
+ 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 @@
+ $(CXX) $(CXXFLAGS) -MM $(CXX_SRC) >Makefile.dep
+ $(CC) $(CFLAGS) -MM $(C_SRC) >>Makefile.dep
+
+-include Makefile.dep
++-include Makefile.dep
+diff -uNr xpdf-3.03/goo/vms_directory.c xpdf-3.04/goo/vms_directory.c
+--- xpdf-3.03/goo/vms_directory.c 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/goo/vms_directory.c 1970-01-01 01:00:00.000000000 +0100
+@@ -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 -uNr xpdf-3.03/goo/vms_dirent.h xpdf-3.04/goo/vms_dirent.h
+--- xpdf-3.03/goo/vms_dirent.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/goo/vms_dirent.h 1970-01-01 01:00:00.000000000 +0100
+@@ -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 -uNr xpdf-3.03/goo/vms_make.com xpdf-3.04/goo/vms_make.com
+--- xpdf-3.03/goo/vms_make.com 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/goo/vms_make.com 1970-01-01 01:00:00.000000000 +0100
+@@ -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 -uNr xpdf-3.03/goo/vms_sys_dirent.h xpdf-3.04/goo/vms_sys_dirent.h
+--- xpdf-3.03/goo/vms_sys_dirent.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/goo/vms_sys_dirent.h 1970-01-01 01:00:00.000000000 +0100
+@@ -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 -uNr xpdf-3.03/goo/vms_unix_time.h xpdf-3.04/goo/vms_unix_time.h
+--- xpdf-3.03/goo/vms_unix_time.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/goo/vms_unix_time.h 1970-01-01 01:00:00.000000000 +0100
+@@ -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 -uNr xpdf-3.03/goo/vms_unix_times.c xpdf-3.04/goo/vms_unix_times.c
+--- xpdf-3.03/goo/vms_unix_times.c 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/goo/vms_unix_times.c 1970-01-01 01:00:00.000000000 +0100
+@@ -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 -uNr xpdf-3.03/goo/vms_unlink.c xpdf-3.04/goo/vms_unlink.c
+--- xpdf-3.03/goo/vms_unlink.c 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/goo/vms_unlink.c 1970-01-01 01:00:00.000000000 +0100
+@@ -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 -uNr xpdf-3.03/splash/Makefile.in xpdf-3.04/splash/Makefile.in
+--- xpdf-3.03/splash/Makefile.in 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/splash/Makefile.in 2014-05-28 20:50:50.000000000 +0200
+@@ -16,7 +16,7 @@
+ 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 @@
+ $(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 @@
+ SplashPattern.o \
+ SplashScreen.o \
+ SplashState.o \
+- SplashT1Font.o \
+- SplashT1FontEngine.o \
+- SplashT1FontFile.o \
+ SplashXPath.o \
+ SplashXPathScanner.o
+
+@@ -96,4 +90,4 @@
+ depend:
+ $(CXX) $(CXXFLAGS) -MM $(CXX_SRC) >Makefile.dep
+
+-include Makefile.dep
++-include Makefile.dep
+diff -uNr xpdf-3.03/splash/Splash.cc xpdf-3.04/splash/Splash.cc
+--- xpdf-3.03/splash/Splash.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/splash/Splash.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ 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 @@
+ #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 @@
+ // 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 @@
+ // 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 @@
+ }
+
+ // 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
+-
+- // static pattern: handled in pipeInit
+- // fixed color: handled in pipeInit
++ if (cSrcPtr && !pipe->pattern) {
++ cSrcStride = bitmapComps;
++ } else {
++ cSrcPtr = pipe->cSrcVal;
++ cSrcStride = 0;
++ }
+
+- // dynamic pattern
+- if (pipe->pattern) {
+- pipe->pattern->getColor(pipe->x, pipe->y, pipe->cSrcVal);
++ 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;
++
++ 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;
+- }
+- if (!(pipe->destColorMask >>= 1)) {
+- pipe->destColorMask = 0x80;
+- ++pipe->destColorPtr;
++ destColorPtr += bitmapComps;
+ }
+- 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
++ }
++
++ } else {
++
++ 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
++ }
++
++ }
+
+- if (state->softMask) {
+- if (pipe->usesShape) {
+- aSrc = div255(div255(pipe->aInput * *pipe->softMaskPtr++) *
+- pipe->shape);
++ if (destAlphaPtr) {
++ aDest = *destAlphaPtr;
+ } else {
+- aSrc = div255(pipe->aInput * *pipe->softMaskPtr++);
++ aDest = 0xff;
++ }
++
++ //----- overprint
++
++ 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
+ }
+- } else if (pipe->usesShape) {
+- aSrc = div255(pipe->aInput * pipe->shape);
+- } else {
+- aSrc = pipe->aInput;
+- }
+
+- //----- non-isolated group correction
++ //----- source alpha
+
+- 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;
++ 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);
++
++ 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);
+
+- ++pipe->x;
++ 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) {
+
+- ++pipe->x;
++ //----- write destination pixel
++ *destColorPtr++ = state->grayTransfer[cSrcPtr[0]];
++ *destAlphaPtr++ = 255;
++
++ 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 && 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 && 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 (x0 > x1) {
++ return;
++ }
++ 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;
++
++ cSrcPtr += cSrcStride;
++ }
++}
++#endif
++
++
++// special case:
++// !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;
++
++ 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 = 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);
++}
++
++// special case:
++// !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;
++
++ 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;
++
++ 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 = shape;
++
++ //----- 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;
++ }
++
++ updateModX(lastX);
++}
++
++// special case:
++// !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;
++
++ 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 = 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->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;
++
++ 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;
++
++ 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 = 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] = cResult2;
++ destColorPtr[1] = cResult1;
++ destColorPtr[2] = cResult0;
++ destColorPtr += 3;
++ *destAlphaPtr++ = aResult;
++
++ cSrcPtr += cSrcStride;
++ ++shapePtr;
++ }
++
++ updateModX(lastX);
+ }
+
+-// 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;
++#if SPLASH_CMYK
++// special case:
++// !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;
++
++ if (cSrcPtr) {
++ cSrcStride = 4;
++ } 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;
++
++ 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;
++ }
++
++ updateModX(lastX);
++}
++#endif
++
++
++// 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;
++ }
++ for (; x0 <= x1; ++x0) {
++ if (*shapePtr) {
++ break;
++ }
++ cSrcPtr += cSrcStride;
++ ++shapePtr;
++ }
++ if (x0 > x1) {
++ return;
++ }
++ 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;
++ }
+
+- ++pipe->x;
++ updateModX(lastX);
+ }
+
+-#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]];
++// !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;
+ }
+- if (state->overprintMask & 4) {
+- pipe->destColorPtr[2] = state->cmykTransferY[pipe->cSrc[2]];
++ for (; x0 <= x1; ++x0) {
++ if (*shapePtr) {
++ break;
++ }
++ cSrcPtr += cSrcStride;
++ ++shapePtr;
+ }
+- if (state->overprintMask & 8) {
+- pipe->destColorPtr[3] = state->cmykTransferK[pipe->cSrc[3]];
++ if (x0 > x1) {
++ return;
+ }
+- pipe->destColorPtr += 4;
+- *pipe->destAlphaPtr++ = 255;
++ updateModX(x0);
++ updateModY(y);
++ lastX = x0;
+
+- ++pipe->x;
+-}
+-#endif
++ destColorPtr = &bitmap->data[y * bitmap->rowSize + x0];
++ destAlphaPtr = &bitmap->alpha[y * bitmap->width + x0];
+
++ for (x = x0; x <= x1; ++x) {
+
+-// 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;
++ //----- shape
++ shape = *shapePtr;
++ if (!shape) {
++ ++destColorPtr;
++ ++destAlphaPtr;
++ cSrcPtr += cSrcStride;
++ ++shapePtr;
++ continue;
++ }
++ lastX = x;
+
+- //----- read destination pixel
+- cDest[0] = (*pipe->destColorPtr & pipe->destColorMask) ? 0xff : 0x00;
++ //----- read destination pixel
++ cDest0 = *destColorPtr;
++ aDest = *destAlphaPtr;
+
+- //----- source alpha
+- aSrc = div255(pipe->aInput * pipe->shape);
++ //----- source alpha
++ aSrc = div255(pipe->aInput * shape);
+
+- //----- result color
+- // note: aDest = alpha2 = aResult = 0xff
+- cResult0 = state->grayTransfer[(Guchar)div255((0xff - aSrc) * cDest[0] +
+- aSrc * pipe->cSrc[0])];
++ //----- result alpha and non-isolated group element correction
++ aResult = aSrc + aDest - div255(aSrc * aDest);
++ alphaI = aResult;
+
+- //----- 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;
++ //----- 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;
+ }
+
+- ++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;
+-
+- //----- read destination pixel
+- cDest[0] = *pipe->destColorPtr;
+- 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;
++// 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;
+
+- //----- result color
+- if (alpha2 == 0) {
+- cResult0 = 0;
++ if (cSrcPtr) {
++ cSrcStride = 3;
+ } else {
+- cResult0 = state->grayTransfer[(Guchar)(((alpha2 - aSrc) * cDest[0] +
+- aSrc * pipe->cSrc[0]) / alpha2)];
++ 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;
+
+- //----- write destination pixel
+- *pipe->destColorPtr++ = cResult0;
+- *pipe->destAlphaPtr++ = aResult;
++ destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0];
++ destAlphaPtr = &bitmap->alpha[y * bitmap->width + x0];
+
+- ++pipe->x;
+-}
++ for (x = x0; x <= x1; ++x) {
+
+-// 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;
+- Guchar cResult0, cResult1, cResult2;
++ //----- shape
++ shape = *shapePtr;
++ if (!shape) {
++ destColorPtr += 3;
++ ++destAlphaPtr;
++ cSrcPtr += cSrcStride;
++ ++shapePtr;
++ continue;
++ }
++ lastX = x;
+
+- //----- read destination pixel
+- cDest[0] = pipe->destColorPtr[0];
+- cDest[1] = pipe->destColorPtr[1];
+- cDest[2] = pipe->destColorPtr[2];
+- 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;
+- } 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)];
+- }
+-
+- //----- write destination pixel
+- *pipe->destColorPtr++ = cResult0;
+- *pipe->destColorPtr++ = cResult1;
+- *pipe->destColorPtr++ = cResult2;
+- *pipe->destAlphaPtr++ = aResult;
++ //----- read destination pixel
++ cDest0 = destColorPtr[0];
++ cDest1 = destColorPtr[1];
++ cDest2 = destColorPtr[2];
++ aDest = *destAlphaPtr;
+
+- ++pipe->x;
+-}
++ //----- source alpha
++ aSrc = div255(pipe->aInput * shape);
+
+-// 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;
+- Guchar cResult0, cResult1, cResult2;
++ //----- 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;
+
+- //----- read destination pixel
+- cDest[0] = pipe->destColorPtr[2];
+- cDest[1] = pipe->destColorPtr[1];
+- cDest[2] = pipe->destColorPtr[0];
+- 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;
+- } 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)];
+- }
+-
+- //----- write destination pixel
+- *pipe->destColorPtr++ = cResult2;
+- *pipe->destColorPtr++ = cResult1;
+- *pipe->destColorPtr++ = cResult0;
+- *pipe->destAlphaPtr++ = aResult;
++ cSrcPtr += cSrcStride;
++ ++shapePtr;
++ }
+
+- ++pipe->x;
++ 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;
+- Guchar cResult0, cResult1, cResult2, cResult3;
+-
+- //----- 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;
+- } 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)];
+- }
+-
+- //----- write destination pixel
+- if (state->overprintMask & 1) {
+- pipe->destColorPtr[0] = cResult0;
+- }
+- if (state->overprintMask & 2) {
+- pipe->destColorPtr[1] = cResult1;
+- }
+- if (state->overprintMask & 4) {
+- pipe->destColorPtr[2] = cResult2;
+- }
+- if (state->overprintMask & 8) {
+- pipe->destColorPtr[3] = cResult3;
+- }
+- pipe->destColorPtr += 4;
+- *pipe->destAlphaPtr++ = aResult;
+-
+- ++pipe->x;
+-}
+-#endif
+-
++// 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;
+
+-inline void Splash::pipeSetXY(SplashPipe *pipe, int x, int y) {
+- pipe->x = x;
+- pipe->y = y;
+- if (state->softMask) {
+- pipe->softMaskPtr =
+- &state->softMask->data[y * state->softMask->rowSize + x];
+- }
+- switch (bitmap->mode) {
+- case splashModeMono1:
+- pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + (x >> 3)];
+- pipe->destColorMask = 0x80 >> (x & 7);
+- break;
+- case splashModeMono8:
+- pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + x];
+- break;
+- case splashModeRGB8:
+- case splashModeBGR8:
+- pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x];
+- break;
+-#if SPLASH_CMYK
+- case splashModeCMYK8:
+- pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x];
+- break;
+-#endif
+- }
+- if (bitmap->alpha) {
+- pipe->destAlphaPtr = &bitmap->alpha[y * bitmap->width + x];
+- } else {
+- pipe->destAlphaPtr = NULL;
+- }
+- if (state->inNonIsolatedGroup && alpha0Bitmap->alpha) {
+- pipe->alpha0Ptr =
+- &alpha0Bitmap->alpha[(alpha0Y + y) * alpha0Bitmap->width +
+- (alpha0X + x)];
++ if (cSrcPtr) {
++ cSrcStride = 3;
+ } else {
+- pipe->alpha0Ptr = NULL;
+- }
+-}
+-
+-inline void Splash::pipeIncX(SplashPipe *pipe) {
+- ++pipe->x;
+- if (state->softMask) {
+- ++pipe->softMaskPtr;
++ 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
++ cSrcPtr += cSrcStride;
++ ++shapePtr;
+ }
+- if (pipe->destAlphaPtr) {
+- ++pipe->destAlphaPtr;
+- }
+- if (pipe->alpha0Ptr) {
+- ++pipe->alpha0Ptr;
++ if (x0 > x1) {
++ return;
+ }
+-}
++ updateModX(x0);
++ updateModY(y);
++ lastX = x0;
+
+-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);
+- }
+-}
++ destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0];
++ destAlphaPtr = &bitmap->alpha[y * bitmap->width + x0];
+
+-inline void Splash::drawAAPixelInit() {
+- aaBufY = -1;
+-}
++ for (x = x0; x <= x1; ++x) {
+
+-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;
++ //----- shape
++ shape = *shapePtr;
++ if (!shape) {
++ destColorPtr += 3;
++ ++destAlphaPtr;
++ cSrcPtr += cSrcStride;
++ ++shapePtr;
++ continue;
++ }
++ lastX = x;
+
+- if (x < 0 || x >= bitmap->width ||
+- y < state->clip->getYMinI() || y > state->clip->getYMaxI()) {
+- return;
+- }
++ //----- read destination pixel
++ cDest0 = destColorPtr[2];
++ cDest1 = destColorPtr[1];
++ cDest2 = destColorPtr[0];
++ aDest = *destAlphaPtr;
+
+- // 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;
+- }
++ //----- source alpha
++ aSrc = div255(pipe->aInput * shape);
+
+- // compute the shape value
+-#if splashAASize == 4
+- p = aaBuf->getDataPtr() + (x >> 1);
+- w = aaBuf->getRowSize();
+- if (x & 1) {
+- t = bitCount4[*p & 0x0f] + bitCount4[p[w] & 0x0f] +
+- bitCount4[p[2*w] & 0x0f] + bitCount4[p[3*w] & 0x0f];
+- } else {
+- t = bitCount4[*p >> 4] + bitCount4[p[w] >> 4] +
+- bitCount4[p[2*w] >> 4] + bitCount4[p[3*w] >> 4];
+- }
+-#else
+- t = 0;
+- for (yy = 0; yy < splashAASize; ++yy) {
+- for (xx = 0; xx < splashAASize; ++xx) {
+- p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() +
+- ((x * splashAASize + xx) >> 3);
+- t += (*p >> (7 - ((x * splashAASize + xx) & 7))) & 1;
++ //----- 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)];
+ }
+- }
+-#endif
+
+- // 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);
+- } 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);
+- }
++ if (cSrcPtr) {
++ cSrcStride = 4;
++ } 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;
+
+-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 (t != 0) {
+- pipe->shape = aaGamma[t];
+- (this->*pipe->run)(pipe);
+- updateModX(x);
+- updateModY(y);
++ if (state->overprintMask & 4) {
++ cSrc2 = cSrcPtr[2];
++ } else {
++ cSrc2 = div255(aDest * cDest2);
++ }
++ if (state->overprintMask & 8) {
++ cSrc3 = cSrcPtr[3];
+ } else {
+- pipeIncX(pipe);
++ 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;
++
++ //----- 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;
+ }
++
++ updateModX(lastX);
+ }
++#endif
++
+
+ //------------------------------------------------------------------------
+
+@@ -1238,21 +1934,18 @@
+ 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 @@
+ 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 @@
+ restoreState();
+ }
+ delete state;
+- if (vectorAntialias) {
+- delete aaBuf;
+- }
++ gfree(scanBuf);
+ }
+
+ //------------------------------------------------------------------------
+@@ -1375,6 +2063,10 @@
+ return state->inNonIsolatedGroup;
+ }
+
++GBool Splash::getInKnockoutGroup() {
++ return state->inKnockoutGroup;
++}
++
+ //------------------------------------------------------------------------
+ // state write
+ //------------------------------------------------------------------------
+@@ -1442,28 +2134,30 @@
+
+ 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 @@
+ 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 @@
+ 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 @@
+
+ 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 @@
+ }
+ }
+
+- // 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 @@
+
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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,93 +2761,180 @@
+ 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 = new SplashXPath(path2, state->matrix, state->flatness, gTrue);
++ if (path2 != path) {
++ delete path2;
+ }
+- xPath->sort();
+- yMinI = state->clip->getYMinI();
+- yMaxI = state->clip->getYMaxI();
+- if (vectorAntialias && !inShading) {
+- yMinI = yMinI * splashAASize;
+- yMaxI = (yMaxI + 1) * splashAASize - 1;
+- }
+- 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);
+ }
+- drawAALine(&pipe, x0, x1, y);
++ for (x = xMin; x <= xMax; ++x) {
++ scanBuf[x] = aaGamma[scanBuf[x]];
++ }
++ (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);
+ }
+ }
+ }
+- opClipRes = clipRes;
++ opClipRes = clipRes;
++
++ delete scanner;
++ delete xPath;
++ 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);
++ }
+
+- delete scanner;
+- delete xPath;
+- return splashOk;
++ return path2;
+ }
+
+ GBool Splash::pathAllOutside(SplashPath *path) {
+@@ -2177,7 +3000,8 @@
+ 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 @@
+ 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::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;
++ int xMin, yMin, xMax, yMax;
++ int x, y, xg, yg, xx, t;
+
+- if ((clipRes = state->clip->testRect(x0 - glyph->x,
+- y0 - glyph->y,
+- x0 - glyph->x + glyph->w - 1,
+- y0 - glyph->y + glyph->h - 1))
++ 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 @@
+ 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 @@
+ 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;
++ 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;
+ }
+- if (y0 == y1) {
+- ++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;
++ scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight,
++ interpolate);
++ horizFlipImage(scaledMask, scaledWidth, scaledHeight, 1);
++ blitMask(scaledMask, x0, y0, clipRes);
++ delete scaledMask;
+ }
+- 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;
+- 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 @@
+ 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 @@
+
+ // 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 @@
+
+ // 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[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 (vy[i] == vy[(i+1) & 3]) {
+- section[0].y0 = imgCoordMungeLowerC(vy[i], glyphMode);
+- section[0].y1 = imgCoordMungeUpperC(vy[(i+2) & 3], glyphMode) - 1;
++ 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 @@
+ }
+ 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 @@
+ 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 @@
+ 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 @@
+ }
+
+ // 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 @@
+ // 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 @@
+ } 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 @@
+ // 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 @@
+ 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 @@
+ 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 @@
+
+ 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 @@
+ 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,82 +4316,315 @@
+ 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;
++ } 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, interpolate);
++ blitImage(scaledImg, srcAlpha, x0, y0, clipRes);
++ delete scaledImg;
++ }
++
++ // scaling plus vertical flip
++ } else if (mat[0] > 0 && minorAxisZero && mat[3] < 0) {
++ 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) {
+- ++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;
+ }
+-
+- // 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;
++
++ // all other cases
++ } else {
++ arbitraryTransformImage(src, srcData, srcMode, nComps, srcAlpha,
++ 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 {
+- ++x1;
++ 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 (y0 == y1) {
+- if (mat[5] + mat[1] * 0.5 < y0) {
+- --y0;
++ if (clipRes != splashClipAllInside) {
++ if (vectorAntialias) {
++ state->clip->clipSpan(scanBuf, y, xMinI, xMaxI - 1,
++ state->strokeAdjust);
+ } else {
+- ++y1;
++ state->clip->clipSpanBinary(scanBuf, y, xMinI, xMaxI - 1,
++ state->strokeAdjust);
+ }
+ }
+- clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1);
+- opClipRes = clipRes;
+- if (clipRes != splashClipAllOutside) {
+- scaledWidth = x1 - x0;
+- scaledHeight = y1 - y0;
+- scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h,
+- scaledWidth, scaledHeight);
+- vertFlipImage(scaledImg, scaledWidth, scaledHeight, nComps);
+- blitImage(scaledImg, srcAlpha, x0, y0, clipRes);
+- delete scaledImg;
+- }
+-
+- // all other cases
+- } else {
+- arbitraryTransformImage(src, srcData, srcMode, nComps, srcAlpha,
+- w, h, mat);
++ (this->*pipe.run)(&pipe, xMinI, xMaxI - 1, y, scanBuf + xMinI, pixelBuf);
+ }
+
+- return splashOk;
++ 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 @@
+ 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 @@
+
+ // 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 @@
+
+ // 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 @@
+ 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 @@
+ }
+ 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 @@
+ 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 @@
+ 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 @@
+ }
+
+ // 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 @@
+ }
+ }
+
++ 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 @@
+ } 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 @@
+ 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 @@
+ 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 @@
+ *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 @@
+
+
+ case splashModeMono1: // mono1 is not allowed
++ case splashModeBGR8: // bgr8 is not allowed
+ default:
+ break;
+ }
+@@ -3812,8 +5193,6 @@
+
+ // 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 @@
+ *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 @@
+ }
+ break;
+ #endif
++ case splashModeMono1: // mono1 is not allowed
++ case splashModeBGR8: // BGR8 is not allowed
++ default:
++ break;
+ }
+
+ // process alpha
+@@ -3942,8 +5318,6 @@
+
+ // 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 @@
+ *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 @@
+ }
+ break;
+ #endif
++ case splashModeMono1: // mono1 is not allowed
++ case splashModeBGR8: // BGR8 is not allowed
++ default:
++ break;
+ }
+
+ // process alpha
+@@ -4071,8 +5441,6 @@
+
+ // 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 @@
+ }
+ }
+ 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 @@
+ }
+ break;
+ #endif
++ case splashModeMono1: // mono1 is not allowed
++ case splashModeBGR8: // BGR8 is not allowed
++ default:
++ break;
+ }
+
+ // process alpha
+@@ -4140,6 +5502,177 @@
+ 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 @@
+ 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 @@
+ x1 = x0;
+ }
+ if ((y1 = splashFloor(state->clip->getYMax()) - yDest) > h) {
+- y1 = h;
+- }
++ y1 = h;
++ }
+ if (y1 < y0) {
+ y1 = y0;
+ }
+@@ -4210,31 +5774,24 @@
+
+ // 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 -uNr xpdf-3.03/splash/SplashClip.cc xpdf-3.04/splash/SplashClip.cc
+--- xpdf-3.03/splash/SplashClip.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/splash/SplashClip.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ 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 @@
+ delete scanners[i];
+ }
+ gfree(paths);
+- gfree(flags);
++ gfree(eo);
+ gfree(scanners);
++ gfree(buf);
+ }
+
+ void SplashClip::grow(int nPaths) {
+@@ -113,7 +114,7 @@
+ 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::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 @@
+ 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 @@
+ 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;
+-
+- } else {
+- grow(1);
+- if (antialias) {
+- xPath->aaScale();
+- }
+- 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;
+- }
+- scanners[length] = new SplashXPathScanner(xPath, eo, yMinAA, yMaxAA);
+- ++length;
++ return splashOk;
+ }
+-
+- return splashOk;
+-}
+-
+-GBool SplashClip::test(int x, int y) {
+- int i;
+-
+- // check the rectangle
+- if (x < xMinI || x > xMaxI || y < yMinI || y > yMaxI) {
+- return gFalse;
++ 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;
+ }
+
+- // check the paths
+- if (antialias) {
+- for (i = 0; i < length; ++i) {
+- if (!scanners[i]->test(x * splashAASize, y * splashAASize)) {
+- return gFalse;
+- }
+- }
+- } else {
+- for (i = 0; i < length; ++i) {
+- if (!scanners[i]->test(x, y)) {
+- return gFalse;
+- }
+- }
++ 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 gTrue;
++ return splashOk;
+ }
+
+ SplashClipResult SplashClip::testRect(int rectXMin, int rectYMin,
+- int rectXMax, int rectYMax) {
+- // This tests the rectangle:
+- // x = [rectXMin, rectXMax + 1) (note: rect coords are ints)
++ 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: clipping coords are fp)
++ // x = [xMin, xMax) (note: coords are fp)
+ // y = [yMin, yMax)
+- if ((SplashCoord)(rectXMax + 1) <= xMin || (SplashCoord)rectXMin >= xMax ||
+- (SplashCoord)(rectYMax + 1) <= yMin || (SplashCoord)rectYMin >= yMax) {
+- return splashClipAllOutside;
+- }
+- if ((SplashCoord)rectXMin >= xMin && (SplashCoord)(rectXMax + 1) <= xMax &&
+- (SplashCoord)rectYMin >= yMin && (SplashCoord)(rectYMax + 1) <= yMax &&
+- length == 0) {
+- return splashClipAllInside;
++
++ 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 {
++ if (xMin >= xMax || yMin >= yMax) {
++ return splashClipAllOutside;
++ }
++ 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;
++ }
+ }
+ return splashClipPartial;
+ }
+
+-SplashClipResult SplashClip::testSpan(int spanXMin, int spanXMax, int spanY) {
+- int i;
++void SplashClip::clipSpan(Guchar *line, int y, int x0, int x1,
++ GBool strokeAdjust) {
++ SplashCoord d;
++ int x0a, x1a, x, i;
+
+- // This tests the rectangle:
+- // x = [spanXMin, spanXMax + 1) (note: span coords are ints)
+- // y = [spanY, spanY + 1)
+- // against the clipping region:
+- // x = [xMin, xMax) (note: clipping coords are fp)
+- // y = [yMin, yMax)
+- if ((SplashCoord)(spanXMax + 1) <= xMin || (SplashCoord)spanXMin >= xMax ||
+- (SplashCoord)(spanY + 1) <= yMin || (SplashCoord)spanY >= yMax) {
+- return splashClipAllOutside;
+- }
+- if (!((SplashCoord)spanXMin >= xMin && (SplashCoord)(spanXMax + 1) <= xMax &&
+- (SplashCoord)spanY >= yMin && (SplashCoord)(spanY + 1) <= yMax)) {
+- return splashClipPartial;
+- }
+- if (antialias) {
+- for (i = 0; i < length; ++i) {
+- if (!scanners[i]->testSpan(spanXMin * splashAASize,
+- spanXMax * splashAASize + (splashAASize - 1),
+- spanY * splashAASize)) {
+- return splashClipPartial;
++ 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;
++ }
++
++ //--- 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]->testSpan(spanXMin, spanXMax, spanY)) {
+- return splashClipPartial;
++
++ // 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 splashClipAllInside;
++
++ if (length == 0) {
++ return;
++ }
++
++ //--- 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]);
++ }
++ }
+ }
+
+-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);
+- }
++GBool SplashClip::clipSpanBinary(Guchar *line, int y, int x0, int x1,
++ GBool strokeAdjust) {
++ int x0a, x1a, x0b, x1b, x, i;
++ Guchar any;
++
++ updateIntBounds(strokeAdjust);
++
++ if (y < yMinI || y > yMaxI ||
++ x1 < xMinI || x0 > xMaxI) {
++ if (x0 <= x1) {
++ memset(line + x0, 0, x1 - x0 + 1);
+ }
+- *x0 = splashFloor(xMin);
++ return gFalse;
+ }
+
+- // 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;
++ 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 gFalse;
++ }
++
++ if (length == 0) {
++ for (x = x0a; x <= x1a; ++x) {
++ if (line[x]) {
++ return gTrue;
+ }
+ }
+- *x1 = splashFloor(xMax);
++ return gFalse;
+ }
+
+- // check the paths
++ any = 0;
+ for (i = 0; i < length; ++i) {
+- scanners[i]->clipAALine(aaBuf, x0, x1, y);
++ scanners[i]->getSpanBinary(buf, y, x0a, x1a);
++ for (x0b = x0a; x0b <= x1a && !buf[x0b]; ++x0b) ;
++ if (x0a < x0b) {
++ memset(line + x0a, 0, x0b - x0a);
++ }
++ 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];
++ }
++ }
++
++ 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 -uNr xpdf-3.03/splash/SplashClip.h xpdf-3.04/splash/SplashClip.h
+--- xpdf-3.03/splash/SplashClip.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/splash/SplashClip.h 2014-05-28 20:50:50.000000000 +0200
+@@ -2,6 +2,8 @@
+ //
+ // SplashClip.h
+ //
++// Copyright 2003-2013 Glyph & Cog, LLC
++//
+ //========================================================================
+
+ #ifndef SPLASHCLIP_H
+@@ -37,9 +39,8 @@
+ 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 @@
+
+ // 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 @@
+ // - splashClipPartial if the rectangle is part inside and part
+ // outside the clipping region
+ SplashClipResult testRect(int rectXMin, int rectYMin,
+- int rectXMax, int rectYMax);
+-
+- // Similar to testRect, but tests a horizontal span.
+- SplashClipResult testSpan(int spanXMin, int spanXMax, int spanY);
++ int rectXMax, int rectYMax,
++ 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);
++ // 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);
++
++ // 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 @@
+ 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 @@
+
+ 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 -uNr xpdf-3.03/splash/SplashFontEngine.cc xpdf-3.04/splash/SplashFontEngine.cc
+--- xpdf-3.03/splash/SplashFontEngine.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/splash/SplashFontEngine.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ //------------------------------------------------------------------------
+
+ 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 @@
+ 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 @@
+ }
+ }
+
+-#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::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::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::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::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::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 @@
+ 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::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 @@
+ 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 @@
+ 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 -uNr xpdf-3.03/splash/SplashFontEngine.h xpdf-3.04/splash/SplashFontEngine.h
+--- xpdf-3.03/splash/SplashFontEngine.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/splash/SplashFontEngine.h 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+
+ // 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 @@
+ 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 @@
+
+ SplashFont *fontCache[splashFontCacheSize];
+
+-#if HAVE_T1LIB_H
+- SplashT1FontEngine *t1Engine;
+-#endif
+ #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
+ SplashFTFontEngine *ftEngine;
+ #endif
+diff -uNr xpdf-3.03/splash/SplashFontFile.cc xpdf-3.04/splash/SplashFontFile.cc
+--- xpdf-3.03/splash/SplashFontFile.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/splash/SplashFontFile.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ // 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 -uNr xpdf-3.03/splash/SplashFontFile.h xpdf-3.04/splash/SplashFontFile.h
+--- xpdf-3.03/splash/SplashFontFile.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/splash/SplashFontFile.h 2014-05-28 20:50:50.000000000 +0200
+@@ -2,6 +2,8 @@
+ //
+ // SplashFontFile.h
+ //
++// Copyright 2003-2013 Glyph & Cog, LLC
++//
+ //========================================================================
+
+ #ifndef SPLASHFONTFILE_H
+@@ -46,12 +48,21 @@
+
+ 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 -uNr xpdf-3.03/splash/SplashFTFont.cc xpdf-3.04/splash/SplashFTFont.cc
+--- xpdf-3.03/splash/SplashFTFont.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/splash/SplashFTFont.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -2,6 +2,8 @@
+ //
+ // SplashFTFont.cc
+ //
++// Copyright 2003-2013 Glyph & Cog, LLC
++//
+ //========================================================================
+
+ #include <aconf.h>
+@@ -242,23 +244,24 @@
+ 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 -uNr xpdf-3.03/splash/SplashFTFontEngine.cc xpdf-3.04/splash/SplashFTFontEngine.cc
+--- xpdf-3.03/splash/SplashFTFontEngine.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/splash/SplashFTFontEngine.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ 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 @@
+ }
+
+ 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 @@
+ 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::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::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 @@
+ unlink(tmpFileName->getCString());
+ }
+ delete tmpFileName;
++#endif
+ return ret;
+ }
+
+diff -uNr xpdf-3.03/splash/SplashFTFontEngine.h xpdf-3.04/splash/SplashFTFontEngine.h
+--- xpdf-3.03/splash/SplashFTFontEngine.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/splash/SplashFTFontEngine.h 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ ~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 -uNr xpdf-3.03/splash/SplashFTFontFile.cc xpdf-3.04/splash/SplashFTFontFile.cc
+--- xpdf-3.03/splash/SplashFTFontFile.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/splash/SplashFTFontFile.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ }
+ }
+
+- 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 -uNr xpdf-3.03/splash/SplashFTFontFile.h xpdf-3.04/splash/SplashFTFontFile.h
+--- xpdf-3.03/splash/SplashFTFontFile.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/splash/SplashFTFontFile.h 2014-05-28 20:50:50.000000000 +0200
+@@ -2,6 +2,8 @@
+ //
+ // SplashFTFontFile.h
+ //
++// Copyright 2003-2013 Glyph & Cog, LLC
++//
+ //========================================================================
+
+ #ifndef SPLASHFTFONTFILE_H
+@@ -30,17 +32,31 @@
+ 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 @@
+
+ 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 -uNr xpdf-3.03/splash/Splash.h xpdf-3.04/splash/Splash.h
+--- xpdf-3.03/splash/Splash.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/splash/Splash.h 2014-05-28 20:50:50.000000000 +0200
+@@ -2,6 +2,8 @@
+ //
+ // Splash.h
+ //
++// Copyright 2003-2013 Glyph & Cog, LLC
++//
+ //========================================================================
+
+ #ifndef SPLASH_H
+@@ -97,6 +99,7 @@
+ SplashClip *getClip();
+ SplashBitmap *getSoftMask();
+ GBool getInNonIsolatedGroup();
++ GBool getInKnockoutGroup();
+
+ //----- state write
+
+@@ -125,8 +128,9 @@
+ // 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 @@
+ // 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 @@
+ // 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 @@
+
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 -uNr xpdf-3.03/splash/SplashMath.h xpdf-3.04/splash/SplashMath.h
+--- xpdf-3.03/splash/SplashMath.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/splash/SplashMath.h 2014-05-28 20:50:50.000000000 +0200
+@@ -2,6 +2,8 @@
+ //
+ // SplashMath.h
+ //
++// Copyright 2003-2013 Glyph & Cog, LLC
++//
+ //========================================================================
+
+ #ifndef SPLASHMATH_H
+@@ -35,19 +37,18 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ #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 -uNr xpdf-3.03/splash/SplashPath.cc xpdf-3.04/splash/SplashPath.cc
+--- xpdf-3.03/splash/SplashPath.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/splash/SplashPath.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -2,6 +2,8 @@
+ //
+ // SplashPath.cc
+ //
++// Copyright 2003-2013 Glyph & Cog, LLC
++//
+ //========================================================================
+
+ #include <aconf.h>
+@@ -53,6 +55,7 @@
+ memcpy(hints, path->hints, hintsLength * sizeof(SplashPathHint));
+ } else {
+ hints = NULL;
++ hintsLength = hintsSize = 0;
+ }
+ }
+
+diff -uNr xpdf-3.03/splash/SplashPath.h xpdf-3.04/splash/SplashPath.h
+--- xpdf-3.03/splash/SplashPath.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/splash/SplashPath.h 2014-05-28 20:50:50.000000000 +0200
+@@ -2,6 +2,8 @@
+ //
+ // SplashPath.h
+ //
++// Copyright 2003-2013 Glyph & Cog, LLC
++//
+ //========================================================================
+
+ #ifndef SPLASHPATH_H
+@@ -94,7 +96,7 @@
+
+ // 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 -uNr xpdf-3.03/splash/SplashState.cc xpdf-3.04/splash/SplashState.cc
+--- xpdf-3.03/splash/SplashState.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/splash/SplashState.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -2,6 +2,8 @@
+ //
+ // SplashState.cc
+ //
++// Copyright 2003-2013 Glyph & Cog, LLC
++//
+ //========================================================================
+
+ #include <aconf.h>
+@@ -45,7 +47,7 @@
+ blendFunc = NULL;
+ strokeAlpha = 1;
+ fillAlpha = 1;
+- lineWidth = 0;
++ lineWidth = 1;
+ lineCap = splashLineCapButt;
+ lineJoin = splashLineJoinMiter;
+ miterLimit = 10;
+@@ -54,10 +56,12 @@
+ 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 @@
+ blendFunc = NULL;
+ strokeAlpha = 1;
+ fillAlpha = 1;
+- lineWidth = 0;
++ lineWidth = 1;
+ lineCap = splashLineCapButt;
+ lineJoin = splashLineJoinMiter;
+ miterLimit = 10;
+@@ -96,10 +100,12 @@
+ 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 @@
+ }
+ 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 @@
+ delete fillPattern;
+ delete screen;
+ gfree(lineDash);
+- delete clip;
++ if (!clipIsShared) {
++ delete clip;
++ }
+ if (deleteSoftMask && softMask) {
+ delete softMask;
+ }
+@@ -192,6 +202,32 @@
+ 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 -uNr xpdf-3.03/splash/SplashState.h xpdf-3.04/splash/SplashState.h
+--- xpdf-3.03/splash/SplashState.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/splash/SplashState.h 2014-05-28 20:50:50.000000000 +0200
+@@ -2,6 +2,8 @@
+ //
+ // SplashState.h
+ //
++// Copyright 2003-2013 Glyph & Cog, LLC
++//
+ //========================================================================
+
+ #ifndef SPLASHSTATE_H
+@@ -19,6 +21,7 @@
+ class SplashScreen;
+ class SplashClip;
+ class SplashBitmap;
++class SplashPath;
+
+ //------------------------------------------------------------------------
+ // line cap values
+@@ -67,6 +70,12 @@
+ 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 @@
+ SplashCoord lineDashPhase;
+ GBool strokeAdjust;
+ SplashClip *clip;
++ GBool clipIsShared;
+ SplashBitmap *softMask;
+ GBool deleteSoftMask;
+ GBool inNonIsolatedGroup;
++ GBool inKnockoutGroup;
+ Guchar rgbTransferR[256],
+ rgbTransferG[256],
+ rgbTransferB[256];
+diff -uNr xpdf-3.03/splash/SplashT1Font.cc xpdf-3.04/splash/SplashT1Font.cc
+--- xpdf-3.03/splash/SplashT1Font.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/splash/SplashT1Font.cc 1970-01-01 01:00:00.000000000 +0100
+@@ -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 -uNr xpdf-3.03/splash/SplashT1FontEngine.cc xpdf-3.04/splash/SplashT1FontEngine.cc
+--- xpdf-3.03/splash/SplashT1FontEngine.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/splash/SplashT1FontEngine.cc 1970-01-01 01:00:00.000000000 +0100
+@@ -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 -uNr xpdf-3.03/splash/SplashT1FontEngine.h xpdf-3.04/splash/SplashT1FontEngine.h
+--- xpdf-3.03/splash/SplashT1FontEngine.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/splash/SplashT1FontEngine.h 1970-01-01 01:00:00.000000000 +0100
+@@ -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 -uNr xpdf-3.03/splash/SplashT1FontFile.cc xpdf-3.04/splash/SplashT1FontFile.cc
+--- xpdf-3.03/splash/SplashT1FontFile.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/splash/SplashT1FontFile.cc 1970-01-01 01:00:00.000000000 +0100
+@@ -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 -uNr xpdf-3.03/splash/SplashT1FontFile.h xpdf-3.04/splash/SplashT1FontFile.h
+--- xpdf-3.03/splash/SplashT1FontFile.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/splash/SplashT1FontFile.h 1970-01-01 01:00:00.000000000 +0100
+@@ -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 -uNr xpdf-3.03/splash/SplashT1Font.h xpdf-3.04/splash/SplashT1Font.h
+--- xpdf-3.03/splash/SplashT1Font.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/splash/SplashT1Font.h 1970-01-01 01:00:00.000000000 +0100
+@@ -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 -uNr xpdf-3.03/splash/SplashXPath.cc xpdf-3.04/splash/SplashXPath.cc
+--- xpdf-3.03/splash/SplashXPath.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/splash/SplashXPath.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -2,6 +2,8 @@
+ //
+ // SplashXPath.cc
+ //
++// Copyright 2003-2013 Glyph & Cog, LLC
++//
+ //========================================================================
+
+ #include <aconf.h>
+@@ -54,12 +56,10 @@
+
+ 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 @@
+ 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 @@
+ xsp = x0;
+ ysp = y0;
+ curSubpath = i;
+- curSubpathX = length;
+ ++i;
+
+ } else {
+@@ -197,32 +134,130 @@
+ }
+
+ 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 @@
+ 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::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 -uNr xpdf-3.03/splash/SplashXPath.h xpdf-3.04/splash/SplashXPath.h
+--- xpdf-3.03/splash/SplashXPath.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/splash/SplashXPath.h 2014-05-28 20:50:50.000000000 +0200
+@@ -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 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
+
+-#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
++ static int cmpX(SplashXPathSeg *seg0, SplashXPathSeg *seg1) {
++ SplashCoord cmp;
++
++ 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 @@
+
+ ~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 @@
+
+ SplashXPathSeg *segs;
+ int length, size; // length and size of segs array
++ int xMin, xMax;
++ int yMin, yMax;
+
+ friend class SplashXPathScanner;
+ friend class SplashClip;
+diff -uNr xpdf-3.03/splash/SplashXPathScanner.cc xpdf-3.04/splash/SplashXPathScanner.cc
+--- xpdf-3.03/splash/SplashXPathScanner.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/splash/SplashXPathScanner.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -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
+-};
++#define minVertStep 0.05
+
+-#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);
+- }
+-};
++SplashXPathScanner::SplashXPathScanner(SplashXPath *xPathA, GBool eoA,
++ int yMinA, int yMaxA) {
++ xPath = xPathA;
++ eo = eoA;
++ yMin = yMinA;
++ yMax = yMaxA;
+
+-#else // HAVE_STD_SORT
++ activeSegs = new GList();
++ nextSeg = 0;
++ yNext = xPath->yMin;
++}
+
+-static int cmpIntersect(const void *p0, const void *p1) {
+- SplashIntersect *i0 = (SplashIntersect *)p0;
+- SplashIntersect *i1 = (SplashIntersect *)p1;
+- int cmp;
++SplashXPathScanner::~SplashXPathScanner() {
++ delete activeSegs;
++}
+
+- if ((cmp = i0->y - i1->y) == 0) {
+- cmp = i0->x0 - i1->x0;
++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->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;
++ }
++ activeSegs->sort(&SplashXPathSeg::cmpXi);
+ }
+- return cmp;
+-}
+
+-#endif // HAVE_STD_SORT
++ //--- process the scan line
++ y0 = y;
++ while (y0 < y + 1) {
+
+-//------------------------------------------------------------------------
+-// SplashXPathScanner
+-//------------------------------------------------------------------------
++ //--- delete finished segs
++ i = 0;
++ while (i < activeSegs->getLength()) {
++ seg = (SplashXPathSeg *)activeSegs->get(i);
++ if (seg->y1 <= y0) {
++ activeSegs->del(i);
++ } else {
++ ++i;
++ }
++ }
+
+-SplashXPathScanner::SplashXPathScanner(SplashXPath *xPathA, GBool eoA,
+- int clipYMin, int clipYMax) {
+- SplashXPathSeg *seg;
+- SplashCoord xMinFP, yMinFP, xMaxFP, yMaxFP;
+- int i;
++ //--- check for bottom of path
++ if (!activeSegs->getLength() && nextSeg >= xPath->length) {
++ break;
++ }
+
+- xPath = xPathA;
+- eo = eoA;
+- partialClip = gFalse;
++ //--- sort activeSegs
++ sortActiveSegs();
+
+- // 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;
++ //--- 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;
+ }
+- if (seg->flags & splashXPathFlip) {
+- yMinFP = seg->y1;
+- yMaxFP = seg->y0;
+- } else {
+- yMinFP = seg->y0;
+- yMaxFP = seg->y1;
++
++ //--- 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 = 1; i < xPath->length; ++i) {
+- seg = &xPath->segs[i];
+- if (seg->x0 < xMinFP) {
+- xMinFP = seg->x0;
+- } else if (seg->x0 > xMaxFP) {
+- xMaxFP = seg->x0;
+- }
+- if (seg->x1 < xMinFP) {
+- xMinFP = seg->x1;
+- } else if (seg->x1 > xMaxFP) {
+- xMaxFP = seg->x1;
+- }
+- if (seg->flags & splashXPathFlip) {
+- if (seg->y0 > yMaxFP) {
+- yMaxFP = seg->y0;
+- }
++ for (i = 0; i < activeSegs->getLength(); ++i) {
++ seg = (SplashXPathSeg *)activeSegs->get(i);
++ if (seg->y1 < y1) {
++ y1 = seg->y1;
++ }
++ }
++
++ //--- 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 {
+- if (seg->y1 > yMaxFP) {
+- yMaxFP = seg->y1;
+- }
++ seg->xCur1 = seg->x0 + (y1 - seg->y0) * seg->dxdy;
+ }
++ if (seg0 && seg0->xCur1 > seg->xCur1) {
++ intersect = gTrue;
++ }
++ seg0 = seg;
+ }
+- 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;
++
++ //--- 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();
++ }
++
++ //--- 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;
++ }
+ }
+- }
+
+- allInter = NULL;
+- inter = NULL;
+- computeIntersections();
+- interY = yMin - 1;
+-}
++ //--- next slice
++ y0 = y1;
++ }
+
+-SplashXPathScanner::~SplashXPathScanner() {
+- gfree(inter);
+- gfree(allInter);
++ yNext = y + 1;
+ }
+
+-void SplashXPathScanner::getBBoxAA(int *xMinA, int *yMinA,
+- int *xMaxA, int *yMaxA) {
+- *xMinA = xMin / splashAASize;
+- *yMinA = yMin / splashAASize;
+- *xMaxA = xMax / splashAASize;
+- *yMaxA = yMax / splashAASize;
+-}
++void SplashXPathScanner::getSpanBinary(Guchar *line, int y, int x0, int x1) {
++ SplashXPathSeg *seg;
++ int xx0, xx1, xx;
++ int eoMask, state0, state1, count, i;
+
+-void SplashXPathScanner::getSpanBounds(int y, int *spanXMin, int *spanXMax) {
+- int interBegin, interEnd, xx, i;
++ //--- clear the scan line buffer
++ memset(line + x0, 0, x1 - x0 + 1);
+
+- 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;
++ //--- 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;
+ }
+- *spanXMax = xx;
+- } else {
+- *spanXMin = xMax + 1;
+- *spanXMax = xMax;
++ activeSegs->sort(&SplashXPathSeg::cmpXi);
+ }
+-}
+
+-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;
++ //--- delete finished segs
++ i = 0;
++ while (i < activeSegs->getLength()) {
++ seg = (SplashXPathSeg *)activeSegs->get(i);
++ if (seg->y1 <= y) {
++ activeSegs->del(i);
++ } else {
++ ++i;
+ }
+- 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;
++ //--- sort activeSegs
++ sortActiveSegs();
+
+- 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;
++ //--- add waiting segs
++ while (nextSeg < xPath->length) {
++ seg = &xPath->segs[nextSeg];
++ if (seg->y0 >= y + 1) {
++ break;
++ }
++ seg->xCur0 = seg->x0;
++ insertActiveSeg(seg);
++ ++nextSeg;
++ }
++
++ //--- 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 {
++ seg->xCur1 = seg->x0 + ((SplashCoord)(y + 1) - seg->y0) * seg->dxdy;
++ }
+ }
+
+- // 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;
++ //--- 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;
++ }
++ xx = splashFloor(seg->xCur1);
++ if (xx < xx0) {
++ xx0 = xx;
++ }
++ xx = splashCeil(seg->xCur1) - 1;
++ if (xx > xx1) {
++ xx1 = xx;
++ }
++ } else {
++ if (seg->xCur0 < seg->xCur1) {
++ xx0 = splashFloor(seg->xCur0);
++ xx1 = splashCeil(seg->xCur1) - 1;
++ } else {
++ xx0 = splashFloor(seg->xCur1);
++ xx1 = splashCeil(seg->xCur0) - 1;
++ }
+ }
+- if (allInter[i].x1 > xx1) {
+- xx1 = allInter[i].x1;
++ if (!state1) {
++ if (xx0 < x0) {
++ xx0 = x0;
++ }
++ if (xx1 > x1) {
++ xx1 = x1;
++ }
++ for (xx = xx0; xx <= xx1; ++xx) {
++ line[xx] = 0xff;
++ }
+ }
+- count += allInter[i].count;
+- ++i;
++ state0 = state1;
++ }
++
++ //--- update xCur0 values
++ for (i = 0; i < activeSegs->getLength(); ++i) {
++ seg = (SplashXPathSeg *)activeSegs->get(i);
++ seg->xCur0 = seg->xCur1;
+ }
+
+- return gTrue;
++ yNext = y + 1;
+ }
+
+-GBool SplashXPathScanner::getNextSpan(int y, int *x0, int *x1) {
+- int interEnd, xx0, xx1;
++inline void SplashXPathScanner::addArea(Guchar *line, int x, SplashCoord a) {
++ int a2, t;
+
+- if (y < yMin || y > yMax) {
+- return gFalse;
++ a2 = splashRound(a * 255);
++ if (a2 <= 0) {
++ return;
+ }
+- if (interY != y) {
+- interY = y;
+- interIdx = inter[y - yMin];
+- interCount = 0;
++ 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;
+ }
+- interEnd = inter[y - yMin + 1];
+- if (interIdx >= interEnd) {
+- return gFalse;
++
++ if (dydxa > 0) {
++ x0 = splashFloor(xa0);
++ x1 = splashFloor(xa1);
++ } else {
++ x0 = splashFloor(xa1);
++ x1 = splashFloor(xa0);
++ }
++ if (x0 < xMin) {
++ x0 = xMin;
++ }
++ if (dydxb > 0) {
++ x2 = splashFloor(xb0);
++ x3 = splashFloor(xb1);
++ } else {
++ x2 = splashFloor(xb1);
++ x3 = splashFloor(xb0);
+ }
+- 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;
++ 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);
+ }
+- interCount += allInter[interIdx].count;
+- ++interIdx;
++ addArea(line, x, a);
+ }
+- *x0 = xx0;
+- *x1 = xx1;
+- return gTrue;
+ }
+
+-void SplashXPathScanner::computeIntersections() {
+- SplashXPathSeg *seg;
+- SplashCoord segXMin, segXMax, segYMin, segYMax, xx0, xx1;
+- int x, y, y0, y1, i;
+-
+- if (yMin > yMax) {
+- return;
+- }
++// 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;
+
+- // 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;
++ 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 {
+- segYMin = seg->y0;
+- segYMax = seg->y1;
++ 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;
++ }
+ }
+- 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;
+- }
+- y1 = splashFloor(segYMax);
+- if (y1 > yMax) {
+- y1 = yMax;
+- }
+- x = splashFloor(seg->x0);
+- for (y = y0; y <= y1; ++y) {
+- addIntersection(segYMin, segYMax, seg->flags, y, x, x);
++ } else {
++ if (x0 <= xp + 1) {
++ if (x1 >= xp) {
++ a = ((x0 + x1) * 0.5 - xp) * (y1 - y0);
++ } else {
++ ya = y0 + ((SplashCoord)xp - x0) * dydx;
++ a = (x0 - xp) * (ya - y0) * 0.5;
+ }
+ } else {
+- if (seg->x0 < seg->x1) {
+- segXMin = seg->x0;
+- segXMax = seg->x1;
+- } else {
+- segXMin = seg->x1;
+- segXMax = seg->x0;
+- }
+- y0 = splashFloor(segYMin);
+- if (y0 < yMin) {
+- y0 = yMin;
+- }
+- y1 = splashFloor(segYMax);
+- if (y1 > yMax) {
+- y1 = yMax;
+- }
+- // 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));
++ 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;
+ }
+ }
+ }
+-#if HAVE_STD_SORT
+- std::sort(allInter, allInter + allInterLen, cmpIntersectFunctor());
+-#else
+- qsort(allInter, allInterLen, sizeof(SplashIntersect), cmpIntersect);
+-#endif
++ return a;
++}
+
+- // 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;
++// 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 {
++ yb = y0 + ((SplashCoord)(xp + 1) - x0) * dydx;
++ a = ((SplashCoord)(xp + 1) - x0) * (yb - y0) * 0.5;
++ }
++ } 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;
++ }
++ }
++ } 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;
++ }
++ } 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;
++ }
+ }
+ }
+- inter[yMax - yMin + 1] = i;
++ return a;
+ }
+
+-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));
+- }
+- allInter[allInterLen].y = y;
+- if (x0 < x1) {
+- allInter[allInterLen].x0 = x0;
+- allInter[allInterLen].x1 = x1;
+- } else {
+- allInter[allInterLen].x0 = x1;
+- allInter[allInterLen].x1 = x0;
++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;
+ }
+- if (segYMin <= y &&
+- (SplashCoord)y < segYMax &&
+- !(segFlags & splashXPathHoriz)) {
+- allInter[allInterLen].count = eo ? 1
+- : (segFlags & splashXPathFlip) ? 1 : -1;
+- } else {
+- allInter[allInterLen].count = 0;
++ xx1 = splashFloor(x1);
++ if (xx1 > xMax) {
++ xx1 = xMax;
+ }
+- ++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];
+- } else {
+- interIdx = inter[splashAASize * y - yMin];
++ dy = y1 - y0;
++ for (x = xx0; x <= xx1; ++x) {
++ a = dy;
++ if ((SplashCoord)x < x0) {
++ a -= (x0 - x) * dy;
+ }
+- 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 {
+- interEnd = inter[splashAASize * y + yy - yMin + 1];
+- }
+- 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;
+- }
+- }
++ if ((SplashCoord)(x + 1) > x1) {
++ a -= ((SplashCoord)(x + 1) - x1) * dy;
+ }
++ addArea(line, x, a);
+ }
+- *x0 = xxMin / splashAASize;
+- *x1 = (xxMax - 1) / splashAASize;
+ }
+
+-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];
+- } 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];
++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;
+ }
+ }
+- 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;
+- }
++ for (k = i; k > j; --k) {
++ activeSegs->put(k, activeSegs->get(k-1));
+ }
++ activeSegs->put(j, seg1);
++ } else {
++ seg0 = seg1;
+ }
+- xx0 = (*x1 + 1) * splashAASize;
+- // set [xx, xx0) to 0
+- if (xx < xx0) {
+- p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() + (xx >> 3);
+- if (xx & 7) {
+- mask = (Guchar)(0xff00 >> (xx & 7));
+- if ((xx & ~7) == (xx0 & ~7)) {
+- mask &= 0xff >> (xx0 & 7);
+- }
+- *p++ &= mask;
+- xx = (xx & ~7) + 8;
+- }
+- for (; xx + 7 < xx0; xx += 8) {
+- *p++ = 0x00;
+- }
+- if (xx < xx0) {
+- *p &= 0xff >> (xx0 & 7);
+- }
++ }
++}
++
++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 -uNr xpdf-3.03/splash/SplashXPathScanner.h xpdf-3.04/splash/SplashXPathScanner.h
+--- xpdf-3.03/splash/SplashXPathScanner.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/splash/SplashXPathScanner.h 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+
+ // 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);
+-
+- // 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);
++ // 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);
++
++ // 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;
++ int yMin, yMax;
+
+- 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
++ GList *activeSegs; // [SplashXPathSeg]
++ int nextSeg;
++ int yNext;
+ };
+
+ #endif
+diff -uNr xpdf-3.03/xpdf/AcroForm.cc xpdf-3.04/xpdf/AcroForm.cc
+--- xpdf-3.03/xpdf/AcroForm.cc 1970-01-01 01:00:00.000000000 +0100
++++ xpdf-3.04/xpdf/AcroForm.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -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 -uNr xpdf-3.03/xpdf/AcroForm.h xpdf-3.04/xpdf/AcroForm.h
+--- xpdf-3.03/xpdf/AcroForm.h 1970-01-01 01:00:00.000000000 +0100
++++ xpdf-3.04/xpdf/AcroForm.h 2014-05-28 20:50:50.000000000 +0200
+@@ -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 -uNr xpdf-3.03/xpdf/Annot.cc xpdf-3.04/xpdf/Annot.cc
+--- xpdf-3.03/xpdf/Annot.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/Annot.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ double borderWidth;
+ double *borderDash;
+ int borderDashLength;
+- double borderR, borderG, borderB;
++ double borderColor[4];
++ int nBorderColorComps;
+ double t;
+ int i;
+
+@@ -160,9 +149,11 @@
+ 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 @@
+ }
+ 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 @@
+ 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;
+-
+- // must be a Widget annotation
+- if (type && type->cmp("Widget")) {
+- return;
+- }
+-
+- appearBuf = new GString();
+-
+- // get the appearance characteristics (MK) dictionary
+- if (annot->lookup("MK", &mkObj)->isDict()) {
+- mkDict = mkObj.getDict();
+- } else {
+- mkDict = NULL;
+- }
+-
+- // draw the background
+- if (mkDict) {
+- if (mkDict->lookup("BG", &obj1)->isArray() &&
+- obj1.arrayGetLength() > 0) {
+- setColor(obj1.getArray(), gTrue, 0);
+- appearBuf->appendf("0 0 {0:.2f} {1:.2f} re f\n",
+- xMax - xMin, yMax - yMin);
+- }
+- obj1.free();
+- }
+-
+- // get the field type
+- fieldLookup(field, 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;
+- }
++void Annot::generateAnnotAppearance() {
++ Object obj;
+
+- // 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);
+- }
++ 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();
+ }
+- obj1.free();
+ }
+ }
++ obj.free();
++}
+
+- // get the resource dictionary
+- fieldLookup(field, acroForm, "DR", &drObj);
++//~ 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;
+
+- // build the font dictionary
+- if (drObj.isDict() && drObj.dictLookup("Font", &obj1)->isDict()) {
+- fontDict = new GfxFontDict(doc->getXRef(), NULL, obj1.getDict());
+- } else {
+- fontDict = NULL;
++ if (!getObject(&annotObj)->isDict()) {
++ annotObj.free();
++ return;
+ }
+- obj1.free();
+
+- // get the default appearance string
+- if (fieldLookup(field, acroForm, "DA", &obj1)->isNull()) {
+- obj1.free();
+- acroForm->lookup("DA", &obj1);
+- }
+- if (obj1.isString()) {
+- da = obj1.getString()->copy();
+- } else {
+- da = NULL;
++ appearBuf = new GString();
++
++ //----- 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();
+
+- // get the rotation value
+- rot = 0;
+- if (mkDict) {
+- if (mkDict->lookup("R", &obj1)->isInt()) {
+- rot = obj1.getInt();
++ //----- 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();
+ }
++ obj1.free();
+
+- // draw the field contents
+- if (ftObj.isName("Btn")) {
+- caption = NULL;
+- if (mkDict) {
+- if (mkDict->lookup("CA", &obj1)->isString()) {
+- caption = obj1.getString()->copy();
+- }
+- obj1.free();
+- }
+- // radio button
+- if (ff & fieldFlagRadio) {
+- //~ Acrobat doesn't draw a caption if there is no AP dict (?)
+- if (fieldLookup(field, 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
++ //----- get line properties
++ if (annotObj.dictLookup("L", &obj1)->isArray() &&
++ obj1.arrayGetLength() == 4) {
++ if (obj1.arrayGet(0, &obj2)->isNum()) {
++ x1 = obj2.getNum();
+ } 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()) {
++ obj2.free();
+ obj1.free();
+- fieldLookup(field, acroForm, "DV", &obj1);
++ goto err1;
+ }
+- if (obj1.isString()) {
+- if (fieldLookup(field, acroForm, "Q", &obj2)->isInt()) {
+- quadding = obj2.getInt();
+- } else {
+- quadding = fieldQuadLeft;
+- }
++ obj2.free();
++ if (obj1.arrayGet(1, &obj2)->isNum()) {
++ y1 = obj2.getNum();
++ } else {
+ 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();
++ goto err1;
+ }
+- obj1.free();
+- } else if (ftObj.isName("Ch")) {
+- //~ value/option strings can be Unicode
+- if (fieldLookup(field, acroForm, "Q", &obj1)->isInt()) {
+- quadding = obj1.getInt();
++ obj2.free();
++ if (obj1.arrayGet(2, &obj2)->isNum()) {
++ x2 = obj2.getNum();
+ } 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
+- }
++ obj2.free();
+ obj1.free();
+- // list box
++ goto err1;
++ }
++ obj2.free();
++ if (obj1.arrayGet(3, &obj2)->isNum()) {
++ y2 = obj2.getNum();
+ } 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);
+- }
++ obj2.free();
+ obj1.free();
++ goto err1;
+ }
+- } else if (ftObj.isName("Sig")) {
+- //~unimp
++ obj2.free();
+ } else {
+- error(errSyntaxError, -1, "Unknown field type");
++ obj1.free();
++ goto err1;
+ }
++ 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 {
++ leaderLen = 0;
++ }
++ obj1.free();
++ if (annotObj.dictLookup("LLE", &obj1)->isNum()) {
++ leaderExtLen = obj1.getNum();
++ } else {
++ leaderExtLen = 0;
++ }
++ obj1.free();
++ if (annotObj.dictLookup("LLO", &obj1)->isNum()) {
++ leaderOffLen = obj1.getNum();
++ } else {
++ leaderOffLen = 0;
++ }
++ 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;
++ }
++ 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 {
++ 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);
++ }
++
++ //----- draw the line
++ appearBuf->appendf("{0:.4f} {1:.4f} m {2:.4f} {3:.4f} l\n",
++ tx1, ty1, tx2, ty2);
++ appearBuf->append("S\n");
+
+- if (da) {
+- delete da;
++ //----- 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 @@
+ 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();
+
+- // 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");
++ //----- draw line
++ if (!annotObj.dictLookup("Vertices", &obj1)->isArray()) {
++ obj1.free();
++ goto err1;
+ }
+-
+- // 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;
+- }
+- 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(' ');
+- }
++ for (i = 0; i+1 < obj1.arrayGetLength(); i += 2) {
++ if (!obj1.arrayGet(i, &obj2)->isNum()) {
++ obj2.free();
++ obj1.free();
++ goto err1;
+ }
+-
+- // 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;
++ x1 = obj2.getNum();
++ obj2.free();
++ if (!obj1.arrayGet(i+1, &obj2)->isNum()) {
++ obj2.free();
++ obj1.free();
++ goto err1;
+ }
+-
+- // 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
++ y1 = obj2.getNum();
++ obj2.free();
++ x1 -= xMin;
++ y1 -= yMin;
++ if (i == 0) {
++ appearBuf->appendf("{0:.4f} {1:.4f} m\n", x1, y1);
+ } else {
++ appearBuf->appendf("{0:.4f} {1:.4f} l\n", x1, y1);
++ }
++ }
++ appearBuf->append("S\n");
++ obj1.free();
+
+- // 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 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);
++ }
+
+- // 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);
+- }
+- }
++ //----- build the appearance stream
++ appearStream = new MemStream(appearBuf->getCString(), 0,
++ appearBuf->getLength(), &appearDict);
++ appearance.free();
++ appearance.initStream(appearStream);
+
+- // 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;
++ err1:
++ annotObj.free();
++}
+
+- // 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);
+- }
+-
+- // 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");
++ err1:
++ annotObj.free();
++}
+
+- // cleanup
+- appearBuf->append("ET\n");
+- appearBuf->append("Q\n");
++void Annot::setLineStyle(AnnotBorderStyle *bs, double *lineWidth) {
++ double *dash;
++ double w;
++ int dashLength, i;
+
+- // next line
+- y -= 1.1 * fontSize;
++ 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("] 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;
++GBool Annot::setFillColor(Object *colorObj) {
++ Object obj;
++ double color[4];
++ int i;
+
+- // 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 (!colorObj->isArray()) {
++ return gFalse;
+ }
+- 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;
++ 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();
++ }
++ 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;
+ }
+- *width = w;
++ return gFalse;
++}
+
+- // next line
+- while (j < text->getLength() && text->getChar(j) == ' ') {
+- ++j;
++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;
+ }
+- 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+
+ // 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 @@
+
+ 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 @@
+ 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 @@
+ 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 @@
+ gfree(annots);
+ }
+
+-void Annots::generateAppearances() {
+- Dict *acroForm;
+- Object obj1, obj2;
+- Ref ref;
+- 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;
++void Annots::generateAnnotAppearances() {
+ 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 -uNr xpdf-3.03/xpdf/Annot.h xpdf-3.04/xpdf/Annot.h
+--- xpdf-3.03/xpdf/Annot.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/Annot.h 2014-05-28 20:50:50.000000000 +0200
+@@ -38,15 +38,15 @@
+
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 -uNr xpdf-3.03/xpdf/Catalog.cc xpdf-3.04/xpdf/Catalog.cc
+--- xpdf-3.03/xpdf/Catalog.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/Catalog.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ 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 @@
+ pageRefs = NULL;
+ numPages = 0;
+ baseURI = NULL;
++ form = NULL;
+ embeddedFiles = NULL;
+
+ xref->getCatalog(&catDict);
+@@ -165,6 +164,10 @@
+ // 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 @@
+ structTreeRoot.free();
+ outline.free();
+ acroForm.free();
++ if (form) {
++ delete form;
++ }
+ ocProperties.free();
+ if (embeddedFiles) {
+ deleteGList(embeddedFiles, EmbeddedFile);
+@@ -236,7 +242,8 @@
+ GString *s;
+ Dict *dict;
+ Object obj;
+- int c;
++ char buf[4096];
++ int n;
+
+ if (!metadata.isStream()) {
+ return NULL;
+@@ -249,8 +256,8 @@
+ 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 @@
+ 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::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 @@
+ 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 @@
+ }
+
+ 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 -uNr xpdf-3.03/xpdf/Catalog.h xpdf-3.04/xpdf/Catalog.h
+--- xpdf-3.03/xpdf/Catalog.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/Catalog.h 2014-05-28 20:50:50.000000000 +0200
+@@ -26,6 +26,7 @@
+ struct Ref;
+ class LinkDest;
+ class PageTreeNode;
++class Form;
+
+ //------------------------------------------------------------------------
+ // Catalog
+@@ -82,12 +83,15 @@
+
+ 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 @@
+ 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 -uNr xpdf-3.03/xpdf/CharCodeToUnicode.cc xpdf-3.04/xpdf/CharCodeToUnicode.cc
+--- xpdf-3.03/xpdf/CharCodeToUnicode.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/CharCodeToUnicode.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -167,7 +167,7 @@
+ 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 @@
+ 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 @@
+ 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 -uNr xpdf-3.03/xpdf/CharCodeToUnicode.h xpdf-3.04/xpdf/CharCodeToUnicode.h
+--- xpdf-3.03/xpdf/CharCodeToUnicode.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/CharCodeToUnicode.h 2014-05-28 20:50:50.000000000 +0200
+@@ -74,6 +74,8 @@
+ // code supported by the mapping.
+ CharCode getLength() { return mapLen; }
+
++ GBool isIdentity() { return !map; }
++
+ private:
+
+ void parseCMap1(int (*getCharFunc)(void *), void *data, int nBits);
+diff -uNr xpdf-3.03/xpdf/CMap.cc xpdf-3.04/xpdf/CMap.cc
+--- xpdf-3.03/xpdf/CMap.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/CMap.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -283,34 +283,36 @@
+
+ void CMap::addCIDs(Guint start, Guint end, Guint nBytes, CID firstCID) {
+ CMapVectorEntry *vec;
+- CID cid;
+- int byte;
+- Guint i, j;
++ int byte, byte0, byte1;
++ Guint start1, end1, i, j, k;
+
+- 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;
++ 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 -uNr xpdf-3.03/xpdf/config.h xpdf-3.04/xpdf/config.h
+--- xpdf-3.03/xpdf/config.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/config.h 2014-05-28 20:50:50.000000000 +0200
+@@ -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 -uNr xpdf-3.03/xpdf/Decrypt.cc xpdf-3.04/xpdf/Decrypt.cc
+--- xpdf-3.03/xpdf/Decrypt.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/Decrypt.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ 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 @@
+
+ *ownerPasswordOk = gFalse;
+
+- if (encRevision == 5) {
++ if (encRevision == 5 || encRevision == 6) {
+
+ // check the owner password
+ if (ownerPassword) {
+@@ -65,6 +65,10 @@
+ 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 @@
+ 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 @@
+ // 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);
++ } 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)) {
++
++ // 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 (!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);
+-
+- return gTrue;
++ 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 @@
+ }
+ }
+
++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 @@
+ }
+
+ void DecryptStream::reset() {
+- int i;
+-
+ str->reset();
+ switch (algo) {
+ case cryptRC4:
+@@ -315,17 +387,13 @@
+ 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 @@
+
+ int DecryptStream::getChar() {
+ Guchar in[16];
+- int c, i;
++ int c;
+
+ c = EOF; // make gcc happy
+ switch (algo) {
+@@ -350,11 +418,8 @@
+ 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 @@
+ 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::lookChar() {
+ Guchar in[16];
+- int c, i;
++ int c;
+
+ c = EOF; // make gcc happy
+ switch (algo) {
+@@ -402,11 +464,8 @@
+ 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 @@
+ 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 @@
+ 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 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 @@
+ 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 @@
+ 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 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 @@
+ }
+ 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 @@
+ 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;
+- }
+-
+- // 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;
+- }
++ 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;
++}
++
++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 @@
+ 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 @@
+ 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 -uNr xpdf-3.03/xpdf/Decrypt.h xpdf-3.04/xpdf/Decrypt.h
+--- xpdf-3.03/xpdf/Decrypt.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/Decrypt.h 2014-05-28 20:50:50.000000000 +0200
+@@ -42,6 +42,8 @@
+
+ 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 @@
+
+ //------------------------------------------------------------------------
+
++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 -uNr xpdf-3.03/xpdf/Dict.cc xpdf-3.04/xpdf/Dict.cc
+--- xpdf-3.03/xpdf/Dict.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/Dict.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ 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 -uNr xpdf-3.03/xpdf/Dict.h xpdf-3.04/xpdf/Dict.h
+--- xpdf-3.03/xpdf/Dict.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/Dict.h 2014-05-28 20:50:50.000000000 +0200
+@@ -17,15 +17,12 @@
+
+ #include "Object.h"
+
++struct DictEntry;
++
+ //------------------------------------------------------------------------
+ // Dict
+ //------------------------------------------------------------------------
+
+-struct DictEntry {
+- char *key;
+- Object val;
+-};
+-
+ class Dict {
+ public:
+
+@@ -67,11 +64,14 @@
+
+ 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 -uNr xpdf-3.03/xpdf/Error.cc xpdf-3.04/xpdf/Error.cc
+--- xpdf-3.03/xpdf/Error.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/Error.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -2,7 +2,7 @@
+ //
+ // Error.cc
+ //
+-// Copyright 1996-2003 Glyph & Cog, LLC
++// Copyright 1996-2013 Glyph & Cog, LLC
+ //
+ //========================================================================
+
+@@ -41,9 +41,12 @@
+ 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 @@
+ 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 -uNr xpdf-3.03/xpdf/Error.h xpdf-3.04/xpdf/Error.h
+--- xpdf-3.03/xpdf/Error.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/Error.h 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ 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 -uNr xpdf-3.03/xpdf/Form.cc xpdf-3.04/xpdf/Form.cc
+--- xpdf-3.03/xpdf/Form.cc 1970-01-01 01:00:00.000000000 +0100
++++ xpdf-3.04/xpdf/Form.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -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 -uNr xpdf-3.03/xpdf/Form.h xpdf-3.04/xpdf/Form.h
+--- xpdf-3.03/xpdf/Form.h 1970-01-01 01:00:00.000000000 +0100
++++ xpdf-3.04/xpdf/Form.h 2014-05-28 20:50:50.000000000 +0200
+@@ -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 -uNr xpdf-3.03/xpdf/Function.cc xpdf-3.04/xpdf/Function.cc
+--- xpdf-3.03/xpdf/Function.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/Function.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ 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 @@
+ // 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 @@
+ "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 @@
+ }
+ 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 @@
+ 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 @@
+ }
+
+ 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 @@
+ 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 @@
+ }
+ }
+
+-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 @@
+ 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 @@
+ comment = gFalse;
+ while (1) {
+ if ((c = str->getChar()) == EOF) {
+- break;
++ delete s;
++ return NULL;
+ }
+ codeString->append(c);
+ if (comment) {
+@@ -1372,323 +1195,362 @@
+ return s;
+ }
+
+-void PostScriptFunction::resizeCode(int newSize) {
+- if (newSize >= codeSize) {
+- codeSize += 64;
+- code = (PSObject *)greallocn(code, codeSize, sizeof(PSObject));
+- }
+-}
+-
+-void PostScriptFunction::exec(PSStack *stack, int codePtr) {
+- int i1, i2;
+- double r1, r2;
+- GBool b1, b2;
+-
+- while (1) {
+- switch (code[codePtr].type) {
+- case psInt:
+- stack->pushInt(code[codePtr++].intg);
+- break;
+- case psReal:
+- stack->pushReal(code[codePtr++].real);
+- break;
+- case psOperator:
+- switch (code[codePtr++].op) {
+- case psOpAbs:
+- if (stack->topIsInt()) {
+- stack->pushInt(abs(stack->popInt()));
+- } else {
+- stack->pushReal(fabs(stack->popNum()));
+- }
+- break;
+- case psOpAdd:
+- if (stack->topTwoAreInts()) {
+- i2 = stack->popInt();
+- i1 = stack->popInt();
+- stack->pushInt(i1 + i2);
+- } else {
+- r2 = stack->popNum();
+- r1 = stack->popNum();
+- stack->pushReal(r1 + r2);
+- }
+- break;
+- case psOpAnd:
+- if (stack->topTwoAreInts()) {
+- i2 = stack->popInt();
+- i1 = stack->popInt();
+- stack->pushInt(i1 & i2);
+- } else {
+- b2 = stack->popBool();
+- b1 = stack->popBool();
+- stack->pushBool(b1 && b2);
+- }
+- break;
+- case psOpAtan:
+- r2 = stack->popNum();
+- r1 = stack->popNum();
+- stack->pushReal(atan2(r1, r2));
+- break;
+- case psOpBitshift:
+- i2 = stack->popInt();
+- i1 = stack->popInt();
+- if (i2 > 0) {
+- stack->pushInt(i1 << i2);
+- } else if (i2 < 0) {
+- stack->pushInt((int)((Guint)i1 >> -i2));
+- } else {
+- stack->pushInt(i1);
+- }
+- break;
+- case psOpCeiling:
+- if (!stack->topIsInt()) {
+- stack->pushReal(ceil(stack->popNum()));
+- }
+- break;
+- case psOpCopy:
+- stack->copy(stack->popInt());
+- break;
+- case psOpCos:
+- stack->pushReal(cos(stack->popNum()));
+- break;
+- case psOpCvi:
+- if (!stack->topIsInt()) {
+- stack->pushInt((int)stack->popNum());
+- }
+- break;
+- case psOpCvr:
+- if (!stack->topIsReal()) {
+- stack->pushReal(stack->popNum());
+- }
+- break;
+- case psOpDiv:
+- r2 = stack->popNum();
+- r1 = stack->popNum();
+- stack->pushReal(r1 / r2);
+- break;
+- case psOpDup:
+- stack->copy(1);
+- break;
+- case psOpEq:
+- if (stack->topTwoAreInts()) {
+- i2 = stack->popInt();
+- i1 = stack->popInt();
+- stack->pushBool(i1 == i2);
+- } else if (stack->topTwoAreNums()) {
+- r2 = stack->popNum();
+- r1 = stack->popNum();
+- stack->pushBool(r1 == r2);
+- } else {
+- b2 = stack->popBool();
+- b1 = stack->popBool();
+- stack->pushBool(b1 == b2);
+- }
+- break;
+- case psOpExch:
+- stack->roll(2, 1);
+- break;
+- case psOpExp:
+- r2 = stack->popNum();
+- r1 = stack->popNum();
+- stack->pushReal(pow(r1, r2));
+- break;
+- case psOpFalse:
+- stack->pushBool(gFalse);
+- break;
+- case psOpFloor:
+- if (!stack->topIsInt()) {
+- stack->pushReal(floor(stack->popNum()));
+- }
+- break;
+- case psOpGe:
+- if (stack->topTwoAreInts()) {
+- i2 = stack->popInt();
+- i1 = stack->popInt();
+- stack->pushBool(i1 >= i2);
+- } else {
+- r2 = stack->popNum();
+- r1 = stack->popNum();
+- stack->pushBool(r1 >= r2);
+- }
+- break;
+- case psOpGt:
+- if (stack->topTwoAreInts()) {
+- i2 = stack->popInt();
+- i1 = stack->popInt();
+- stack->pushBool(i1 > i2);
+- } else {
+- r2 = stack->popNum();
+- r1 = stack->popNum();
+- stack->pushBool(r1 > r2);
+- }
+- break;
+- case psOpIdiv:
+- i2 = stack->popInt();
+- i1 = stack->popInt();
+- stack->pushInt(i1 / i2);
+- break;
+- case psOpIndex:
+- stack->index(stack->popInt());
+- break;
+- case psOpLe:
+- if (stack->topTwoAreInts()) {
+- i2 = stack->popInt();
+- i1 = stack->popInt();
+- stack->pushBool(i1 <= i2);
+- } else {
+- r2 = stack->popNum();
+- r1 = stack->popNum();
+- stack->pushBool(r1 <= r2);
+- }
+- break;
+- case psOpLn:
+- stack->pushReal(log(stack->popNum()));
+- break;
+- case psOpLog:
+- stack->pushReal(log10(stack->popNum()));
+- break;
+- case psOpLt:
+- if (stack->topTwoAreInts()) {
+- i2 = stack->popInt();
+- i1 = stack->popInt();
+- stack->pushBool(i1 < i2);
+- } else {
+- r2 = stack->popNum();
+- r1 = stack->popNum();
+- stack->pushBool(r1 < r2);
+- }
+- break;
+- case psOpMod:
+- i2 = stack->popInt();
+- i1 = stack->popInt();
+- stack->pushInt(i1 % i2);
+- break;
+- case psOpMul:
+- if (stack->topTwoAreInts()) {
+- i2 = stack->popInt();
+- i1 = stack->popInt();
+- //~ should check for out-of-range, and push a real instead
+- stack->pushInt(i1 * i2);
+- } else {
+- r2 = stack->popNum();
+- r1 = stack->popNum();
+- stack->pushReal(r1 * r2);
+- }
+- break;
+- case psOpNe:
+- if (stack->topTwoAreInts()) {
+- i2 = stack->popInt();
+- i1 = stack->popInt();
+- stack->pushBool(i1 != i2);
+- } else if (stack->topTwoAreNums()) {
+- r2 = stack->popNum();
+- r1 = stack->popNum();
+- stack->pushBool(r1 != r2);
+- } else {
+- b2 = stack->popBool();
+- b1 = stack->popBool();
+- stack->pushBool(b1 != b2);
+- }
+- break;
+- case psOpNeg:
+- if (stack->topIsInt()) {
+- stack->pushInt(-stack->popInt());
+- } else {
+- stack->pushReal(-stack->popNum());
+- }
+- break;
+- case psOpNot:
+- if (stack->topIsInt()) {
+- stack->pushInt(~stack->popInt());
+- } else {
+- stack->pushBool(!stack->popBool());
+- }
+- break;
+- case psOpOr:
+- if (stack->topTwoAreInts()) {
+- i2 = stack->popInt();
+- i1 = stack->popInt();
+- stack->pushInt(i1 | i2);
+- } else {
+- b2 = stack->popBool();
+- b1 = stack->popBool();
+- stack->pushBool(b1 || b2);
+- }
+- break;
+- case psOpPop:
+- stack->pop();
+- break;
+- case psOpRoll:
+- i2 = stack->popInt();
+- i1 = stack->popInt();
+- stack->roll(i1, i2);
+- break;
+- case psOpRound:
+- if (!stack->topIsInt()) {
+- r1 = stack->popNum();
+- stack->pushReal((r1 >= 0) ? floor(r1 + 0.5) : ceil(r1 - 0.5));
+- }
+- break;
+- case psOpSin:
+- stack->pushReal(sin(stack->popNum()));
+- break;
+- case psOpSqrt:
+- stack->pushReal(sqrt(stack->popNum()));
+- break;
+- case psOpSub:
+- if (stack->topTwoAreInts()) {
+- i2 = stack->popInt();
+- i1 = stack->popInt();
+- stack->pushInt(i1 - i2);
+- } else {
+- r2 = stack->popNum();
+- r1 = stack->popNum();
+- stack->pushReal(r1 - r2);
+- }
+- break;
+- case psOpTrue:
+- stack->pushBool(gTrue);
+- break;
+- case psOpTruncate:
+- if (!stack->topIsInt()) {
+- r1 = stack->popNum();
+- stack->pushReal((r1 >= 0) ? floor(r1) : ceil(r1));
+- }
+- break;
+- case psOpXor:
+- if (stack->topTwoAreInts()) {
+- i2 = stack->popInt();
+- i1 = stack->popInt();
+- stack->pushInt(i1 ^ i2);
+- } else {
+- b2 = stack->popBool();
+- b1 = stack->popBool();
+- stack->pushBool(b1 ^ b2);
+- }
+- break;
+- case psOpIf:
+- b1 = stack->popBool();
+- if (b1) {
+- exec(stack, codePtr + 2);
+- }
+- codePtr = code[codePtr + 1].blk;
+- break;
+- case psOpIfelse:
+- b1 = stack->popBool();
+- if (b1) {
+- exec(stack, codePtr + 2);
+- } else {
+- exec(stack, code[codePtr].blk);
++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 psOpAdd:
++ if (sp + 1 >= psStackSize) {
++ goto underflow;
++ }
++ stack[sp + 1] = stack[sp + 1] + stack[sp];
++ ++sp;
++ break;
++ 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 -uNr xpdf-3.03/xpdf/Function.h xpdf-3.04/xpdf/Function.h
+--- xpdf-3.03/xpdf/Function.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/Function.h 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ 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 -uNr xpdf-3.03/xpdf/Gfx.cc xpdf-3.04/xpdf/Gfx.cc
+--- xpdf-3.03/xpdf/Gfx.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/Gfx.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ &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 @@
+
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ return NULL;
+ }
+
+-GfxShading *GfxResources::lookupShading(char *name) {
++GfxShading *GfxResources::lookupShading(const char *name
++ ) {
+ GfxResources *resPtr;
+ GfxShading *shading;
+ Object obj;
+@@ -414,7 +446,8 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ markedContentStack = new GList();
+ ocState = gTrue;
+ parser = NULL;
++ contentStreamStack = new GList();
+ abortCheckCbk = abortCheckCbkA;
+ abortCheckCbkData = abortCheckCbkDataA;
+
+@@ -535,6 +569,7 @@
+ markedContentStack = new GList();
+ ocState = gTrue;
+ parser = NULL;
++ contentStreamStack = new GList();
+ abortCheckCbk = abortCheckCbkA;
+ abortCheckCbkData = abortCheckCbkDataA;
+
+@@ -563,41 +598,99 @@
+ 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 @@
+ 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 @@
+ }
+ }
+
++ // 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::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 @@
+ // 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ return gFalse;
+ }
+
+-int Gfx::getPos() {
++GFileOffset Gfx::getPos() {
+ return parser ? parser->getPos() : -1;
+ }
+
+@@ -840,7 +947,7 @@
+ }
+
+ 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 @@
+ 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 @@
+ 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 @@
+ }
+ }
+ }
+- 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 @@
+ 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 @@
+
+ // 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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::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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ // 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ haveSMin = gTrue;
+ }
+ }
+- if (haveSZero && sZero < 0) {
++ if (haveSZero && sZero <= 0) {
+ if (!haveSMin || sZero > sMin) {
+ sMin = sZero;
+ }
+@@ -2865,34 +3006,56 @@
+
+ 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 @@
+ 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 @@
+ 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 @@
+ 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::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 @@
+ 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 @@
+ 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 @@
+ popResources();
+ }
+ charProc.free();
++ charProcRef.free();
+ }
+ restoreStateStack(savedState);
+ curX += tdx;
+@@ -3592,44 +3757,53 @@
+ 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);
++ error(errSyntaxError, getPos(),
++ "XObject subtype is missing or wrong type");
+ }
+- 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();
++ 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 @@
+ int maskWidth, maskHeight;
+ GBool maskInvert;
+ Stream *maskStr;
++ GBool interpolate;
+ Object obj1, obj2;
+ int i, n;
+
+@@ -3710,6 +3885,9 @@
+ }
+ if (obj1.isInt()) {
+ bits = obj1.getInt();
++ if (bits < 1 || bits > 16) {
++ goto err2;
++ }
+ } else if (mask) {
+ bits = 1;
+ } else {
+@@ -3718,6 +3896,15 @@
+ 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 @@
+ // 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 @@
+ }
+ }
+ 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 @@
+ obj2.free();
+ }
+ }
+- maskColorSpace = GfxColorSpace::parse(&obj1);
++ maskColorSpace = GfxColorSpace::parse(&obj1
++ );
+ obj1.free();
+ if (!maskColorSpace || maskColorSpace->getMode() != csDeviceGray) {
+ goto err1;
+@@ -3977,14 +4168,17 @@
+ } 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 @@
+ 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 @@
+ 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 @@
+
+ // draw it
+ ++formDepth;
+- drawForm(str, resDict, m, bbox,
++ drawForm(strRef, resDict, m, bbox,
+ transpGroup, gFalse, blendingColorSpace, isolated, knockout);
+ --formDepth;
+
+@@ -4117,7 +4312,8 @@
+ 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 @@
+ }
+
+ // draw the form
+- display(str, gFalse);
++ display(strRef, gFalse);
+
+ if (softMask || transpGroup) {
+ out->endTransparencyGroup(state);
+@@ -4210,13 +4406,17 @@
+ 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 @@
+ // 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 @@
+ GfxMarkedContent *mc;
+ Object obj;
+ GBool ocStateNew;
+- GString *s;
+- Unicode *u;
+- int uLen, i;
++ TextString *s;
+ GfxMarkedContentKind mcKind;
+
+ if (printCommands) {
+@@ -4351,24 +4551,9 @@
+ 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 @@
+ // 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 @@
+ }
+
+ // 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 @@
+ 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 -uNr xpdf-3.03/xpdf/GfxFont.cc xpdf-3.04/xpdf/GfxFont.cc
+--- xpdf-3.03/xpdf/GfxFont.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/GfxFont.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -146,6 +146,7 @@
+ GfxFontLoc::GfxFontLoc() {
+ path = NULL;
+ fontNum = 0;
++ oblique = 0;
+ encoding = NULL;
+ substIdx = -1;
+ }
+@@ -175,6 +176,8 @@
+ 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 @@
+ }
+ // 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 @@
+ 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 *ctu) {
+ GString *buf;
+ Object obj1;
+- int c;
++ char buf2[4096];
++ int n;
+
+ if (!fontDict->lookup("ToUnicode", &obj1)->isStream()) {
+ obj1.free();
+@@ -497,8 +501,8 @@
+ }
+ 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 @@
+ PSFontParam16 *psFont16;
+ Object refObj, embFontObj;
+ int substIdx, fontNum;
++ double oblique;
+ GBool embed;
+
+ if (type == fontType3) {
+@@ -570,7 +575,7 @@
+ }
+
+ //----- 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+
+ //----- 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 @@
+ 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 @@
+ }
+
+ //----- 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 @@
+ //----- 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::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 @@
+ case fofiIdOpenTypeCFFCID:
+ fontType = fontCIDType0COT;
+ break;
++ case fofiIdDfont:
++ fontType = cid ? fontCIDType2 : fontTrueType;
++ break;
+ case fofiIdUnknown:
+ case fofiIdError:
+ default:
+@@ -784,6 +793,8 @@
+ fontLoc->locType = gfxFontLocExternal;
+ fontLoc->fontType = fontType;
+ fontLoc->path = path;
++ fontLoc->fontNum = fontNum;
++ fontLoc->oblique = oblique;
+ return fontLoc;
+ }
+
+@@ -791,8 +802,7 @@
+ 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 @@
+ }
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ }
+ 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 @@
+ err2:
+ obj1.free();
+ desFontDictObj.free();
+- err1:;
++ err1:
++ error(errSyntaxError, -1, "Failed to parse font object for '{0:t}'", name);
+ }
+
+ GfxCIDFont::~GfxCIDFont() {
+@@ -2014,5 +2032,18 @@
+ return fonts[i];
+ }
+ }
++ 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 -uNr xpdf-3.03/xpdf/GfxFont.h xpdf-3.04/xpdf/GfxFont.h
+--- xpdf-3.03/xpdf/GfxFont.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/GfxFont.h 2014-05-28 20:50:50.000000000 +0200
+@@ -100,8 +100,11 @@
+ // (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 @@
+ 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 @@
+
+ // 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 @@
+
+ // Get the specified font.
+ GfxFont *lookup(char *tag);
++ GfxFont *lookupByRef(Ref ref);
+
+ // Iterative access.
+ int getNumFonts() { return numFonts; }
+diff -uNr xpdf-3.03/xpdf/Gfx.h xpdf-3.04/xpdf/Gfx.h
+--- xpdf-3.03/xpdf/Gfx.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/Gfx.h 2014-05-28 20:50:50.000000000 +0200
+@@ -16,6 +16,7 @@
+ #endif
+
+ #include "gtypes.h"
++#include "gfile.h"
+
+ class GString;
+ class GList;
+@@ -84,13 +85,16 @@
+ ~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 @@
+
+ ~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 @@
+ // 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 @@
+ 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 @@
+
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ // 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 @@
+ // 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 -uNr xpdf-3.03/xpdf/GfxState.cc xpdf-3.04/xpdf/GfxState.cc
+--- xpdf-3.03/xpdf/GfxState.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/GfxState.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -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::parse(Object *csObj, int recursion) {
++GfxColorSpace *GfxColorSpace::parse(Object *csObj,
++ int recursion) {
+ GfxColorSpace *cs;
+ Object obj1;
+
+@@ -112,11 +113,11 @@
+ 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 @@
+ } 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 @@
+ } 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 @@
+ 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 @@
+ }
+
+ 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 @@
+ return cs;
+ }
+
++
+ GfxColorSpace *GfxCalGrayColorSpace::parse(Array *arr, int recursion) {
+ GfxCalGrayColorSpace *cs;
+ Object obj1, obj2, obj3;
+@@ -311,9 +336,13 @@
+ }
+
+ 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 @@
+ 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 @@
+ }
+
+ 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 @@
+ return cs;
+ }
+
++
+ void GfxLabColorSpace::getGray(GfxColor *color, GfxGray *gray) {
+ GfxRGB rgb;
+
+@@ -720,6 +755,7 @@
+ 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 @@
+ GfxRGB rgb;
+ GfxColorComp c, m, y, k;
+
++
+ getRGB(color, &rgb);
+ c = clip01(gfxColorComp1 - rgb.r);
+ m = clip01(gfxColorComp1 - rgb.g);
+@@ -831,7 +868,8 @@
+ 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 @@
+ 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 @@
+ return cs;
+ }
+
++
+ void GfxICCBasedColorSpace::getGray(GfxColor *color, GfxGray *gray) {
+ alt->getGray(color, gray);
+ }
+@@ -981,7 +1021,8 @@
+ 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 @@
+ 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 @@
+ return NULL;
+ }
+
++
+ GfxColor *GfxIndexedColorSpace::mapColorToBase(GfxColor *color,
+ GfxColor *baseColor) {
+ Guchar *p;
+@@ -1146,12 +1189,16 @@
+ }
+
+ 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 @@
+ 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 @@
+ return NULL;
+ }
+
++
+ void GfxSeparationColorSpace::getGray(GfxColor *color, GfxGray *gray) {
+ double x;
+ double c[gfxColorMaxComps];
+@@ -1303,12 +1352,16 @@
+ }
+
+ 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 @@
+ }
+ 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 @@
+ return NULL;
+ }
+
++
+ void GfxDeviceNColorSpace::getGray(GfxColor *color, GfxGray *gray) {
+ double x[gfxColorMaxComps], c[gfxColorMaxComps];
+ GfxColor color2;
+@@ -1438,11 +1493,15 @@
+ }
+
+ 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 @@
+ 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 @@
+ return cs;
+ }
+
++
+ void GfxPatternColorSpace::getGray(GfxColor *color, GfxGray *gray) {
+ *gray = 0;
+ }
+@@ -1495,24 +1556,26 @@
+ 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 @@
+ // GfxTilingPattern
+ //------------------------------------------------------------------------
+
+-GfxTilingPattern *GfxTilingPattern::parse(Object *patObj) {
++GfxTilingPattern *GfxTilingPattern::parse(Object *patObjRef, Object *patObj) {
+ GfxTilingPattern *pat;
+ Dict *dict;
+ int paintTypeA, tilingTypeA;
+@@ -1597,7 +1660,7 @@
+ 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(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 @@
+ 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 @@
+ 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::parse(Object *obj) {
++GfxShading *GfxShading::parse(Object *obj
++ ) {
+ GfxShading *shading;
+ Dict *dict;
+ int typeA;
+@@ -1748,17 +1814,21 @@
+
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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::parse(Dict *dict) {
++GfxFunctionShading *GfxFunctionShading::parse(Dict *dict
++ ) {
+ GfxFunctionShading *shading;
+ double x0A, y0A, x1A, y1A;
+ double matrixA[6];
+@@ -1916,9 +1992,9 @@
+ 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 @@
+
+ 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::parse(Dict *dict) {
++GfxAxialShading *GfxAxialShading::parse(Dict *dict
++ ) {
+ GfxAxialShading *shading;
+ double x0A, y0A, x1A, y1A;
+ double t0A, t1A;
+@@ -2137,7 +2215,8 @@
+
+ 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::parse(Dict *dict) {
++GfxRadialShading *GfxRadialShading::parse(Dict *dict
++ ) {
+ GfxRadialShading *shading;
+ double x0A, y0A, r0A, x1A, y1A, r1A;
+ double t0A, t1A;
+@@ -2307,7 +2387,8 @@
+
+ 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 @@
+ 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 @@
+ nVertices = nVerticesA;
+ triangles = trianglesA;
+ nTriangles = nTrianglesA;
++ nComps = nCompsA;
+ nFuncs = nFuncsA;
+ for (i = 0; i < nFuncs; ++i) {
+ funcs[i] = funcsA[i];
+@@ -2440,6 +2522,7 @@
+ 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::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 @@
+ 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 @@
+ 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 @@
+ !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 @@
+ }
+ 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 @@
+
+ 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 @@
+
+ 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 @@
+
+ GfxPatchMeshShading::GfxPatchMeshShading(int typeA,
+ GfxPatch *patchesA, int nPatchesA,
++ int nCompsA,
+ Function **funcsA, int nFuncsA):
+ GfxShading(typeA)
+ {
+@@ -2740,6 +2816,7 @@
+
+ patches = patchesA;
+ nPatches = nPatchesA;
++ nComps = nCompsA;
+ nFuncs = nFuncsA;
+ for (i = 0; i < nFuncs; ++i) {
+ funcs[i] = funcsA[i];
+@@ -2754,6 +2831,7 @@
+ 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::parse(int typeA, Dict *dict,
+- Stream *str) {
++ Stream *str
++ ) {
+ GfxPatchMeshShading *shading;
+ Function *funcsA[gfxColorMaxComps];
+ int nFuncsA;
+@@ -2780,11 +2859,11 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ }
+
+ 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 @@
+ 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 @@
+
+ // 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 @@
+ 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::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 @@
+ //------------------------------------------------------------------------
+
+ 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 @@
+ 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 -uNr xpdf-3.03/xpdf/GfxState.h xpdf-3.04/xpdf/GfxState.h
+--- xpdf-3.03/xpdf/GfxState.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/GfxState.h 2014-05-28 20:50:50.000000000 +0200
+@@ -140,7 +140,13 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ double xStep, yStep;
+ Object resDict;
+ double matrix[6];
+- Object contentStream;
++ Object contentStreamRef;
+ };
+
+ //------------------------------------------------------------------------
+@@ -626,7 +638,8 @@
+ class GfxShadingPattern: public GfxPattern {
+ public:
+
+- static GfxShadingPattern *parse(Object *patObj);
++ static GfxShadingPattern *parse(Object *patObj
++ );
+ virtual ~GfxShadingPattern();
+
+ virtual GfxPattern *copy();
+@@ -653,7 +666,8 @@
+ GfxShading(GfxShading *shading);
+ virtual ~GfxShading();
+
+- static GfxShading *parse(Object *obj);
++ static GfxShading *parse(Object *obj
++ );
+
+ virtual GfxShading *copy() = 0;
+
+@@ -667,7 +681,8 @@
+
+ protected:
+
+- GBool init(Dict *dict);
++ GBool init(Dict *dict
++ );
+
+ int type;
+ GfxColorSpace *colorSpace;
+@@ -691,7 +706,8 @@
+ GfxFunctionShading(GfxFunctionShading *shading);
+ virtual ~GfxFunctionShading();
+
+- static GfxFunctionShading *parse(Dict *dict);
++ static GfxFunctionShading *parse(Dict *dict
++ );
+
+ virtual GfxShading *copy();
+
+@@ -725,7 +741,8 @@
+ GfxAxialShading(GfxAxialShading *shading);
+ virtual ~GfxAxialShading();
+
+- static GfxAxialShading *parse(Dict *dict);
++ static GfxAxialShading *parse(Dict *dict
++ );
+
+ virtual GfxShading *copy();
+
+@@ -763,7 +780,8 @@
+ GfxRadialShading(GfxRadialShading *shading);
+ virtual ~GfxRadialShading();
+
+- static GfxRadialShading *parse(Dict *dict);
++ static GfxRadialShading *parse(Dict *dict
++ );
+
+ virtual GfxShading *copy();
+
+@@ -793,7 +811,7 @@
+
+ struct GfxGouraudVertex {
+ double x, y;
+- GfxColor color;
++ double color[gfxColorMaxComps];
+ };
+
+ class GfxGouraudTriangleShading: public GfxShading {
+@@ -802,18 +820,21 @@
+ 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 @@
+ int (*triangles)[3];
+ int nTriangles;
+ Function *funcs[gfxColorMaxComps];
++ int nComps; // number of color components (1 if nFuncs > 0)
+ int nFuncs;
+ };
+
+@@ -832,29 +854,33 @@
+ 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 @@
+ // 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 -uNr xpdf-3.03/xpdf/GlobalParams.cc xpdf-3.04/xpdf/GlobalParams.cc
+--- xpdf-3.03/xpdf/GlobalParams.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/GlobalParams.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ 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 @@
+ "/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 @@
+ ~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 @@
+ }
+ 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 @@
+ return fi;
+ }
+
+-#ifdef WIN32
++#ifdef _WIN32
+ void SysFontList::scanWindowsFonts(char *winFontDir) {
+ OSVERSIONINFO version;
+ char *path;
+@@ -341,15 +368,15 @@
+ } 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 @@
+ 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 @@
+ int i;
+ SysFontType type;
+
+- n = strlen(name);
++ n = (int)strlen(name);
+ bold = italic = gFalse;
+
+ // remove trailing ' (TrueType)'
+@@ -419,7 +446,7 @@
+ }
+
+ // 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 @@
+
+ private:
+
+-#ifdef WIN32
++#ifdef _WIN32
+ Plugin(HMODULE libA);
+ HMODULE lib;
+ #else
+@@ -504,7 +531,7 @@
+ Plugin *plugin;
+ XpdfPluginVecTable *vt;
+ XpdfBool (*xpdfInitPlugin)(void);
+-#ifdef WIN32
++#ifdef _WIN32
+ HMODULE libA;
+ #else
+ void *dlA;
+@@ -515,9 +542,9 @@
+ 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 @@
+ }
+ 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 @@
+ goto err2;
+ }
+
+-#ifdef WIN32
++#ifdef _WIN32
+ plugin = new Plugin(libA);
+ #else
+ plugin = new Plugin(dlA);
+@@ -578,7 +605,7 @@
+ return plugin;
+
+ err2:
+-#ifdef WIN32
++#ifdef _WIN32
+ FreeLibrary(libA);
+ #else
+ dlclose(dlA);
+@@ -588,7 +615,7 @@
+ return NULL;
+ }
+
+-#ifdef WIN32
++#ifdef _WIN32
+ Plugin::Plugin(HMODULE libA) {
+ lib = libA;
+ }
+@@ -601,7 +628,7 @@
+ Plugin::~Plugin() {
+ void (*xpdfFreePlugin)(void);
+
+-#ifdef WIN32
++#ifdef _WIN32
+ if ((xpdfFreePlugin = (void (*)(void))
+ GetProcAddress(lib, "xpdfFreePlugin"))) {
+ (*xpdfFreePlugin)();
+@@ -621,7 +648,7 @@
+ // parsing
+ //------------------------------------------------------------------------
+
+-GlobalParams::GlobalParams(char *cfgFileName) {
++GlobalParams::GlobalParams(const char *cfgFileName) {
+ UnicodeMap *map;
+ GString *fileName;
+ FILE *f;
+@@ -644,7 +671,7 @@
+ }
+ }
+
+-#ifdef WIN32
++#ifdef _WIN32
+ // baseDir will be set by a call to setBaseDir
+ baseDir = new GString();
+ #else
+@@ -660,6 +687,7 @@
+ 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 @@
+ psImageableURX = psPaperWidth;
+ psImageableURY = psPaperHeight;
+ psCrop = gTrue;
++ psUseCropBoxAsPage = gFalse;
+ psExpandSmaller = gFalse;
+ psShrinkLarger = gTrue;
+ psCenter = gTrue;
+@@ -700,12 +729,15 @@
+ 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 @@
+ 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 @@
+ movieCommand = NULL;
+ mapNumericCharNames = gTrue;
+ mapUnknownCharNames = gFalse;
++ mapExtTrueTypeFontsViaUnicode = gTrue;
++ enableXFA = gTrue;
+ createDefaultKeyBindings();
+ printCommands = gFalse;
+ errQuiet = gFalse;
+@@ -791,9 +824,9 @@
+ }
+ }
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ } 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 @@
+ !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::parseBind(GList *tokens, GString *fileName, int line) {
+ KeyBinding *binding;
+ GList *cmds;
+@@ -1836,6 +1887,7 @@
+ deleteGHash(fontFiles, GString);
+ deleteGList(fontDirs, GString);
+ deleteGHash(ccFontFiles, GString);
++ deleteGHash(base14SysFonts, Base14FontInfo);
+ delete sysFonts;
+ if (psFile) {
+ delete psFile;
+@@ -1886,35 +1938,77 @@
+ 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 @@
+ }
+ 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 @@
+ 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 @@
+ 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 @@
+ }
+ }
+ }
+-#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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ return f;
+ }
+
++GBool GlobalParams::getPSUseCropBoxAsPage() {
++ GBool f;
++
++ lockGlobalParams;
++ f = psUseCropBoxAsPage;
++ unlockGlobalParams;
++ return f;
++}
++
+ GBool GlobalParams::getPSExpandSmaller() {
+ GBool f;
+
+@@ -2242,7 +2426,9 @@
+ GString *psName;
+
+ lockGlobalParams;
+- psName = (GString *)psResidentFonts->lookup(fontName);
++ if ((psName = (GString *)psResidentFonts->lookup(fontName))) {
++ psName = psName->copy();
++ }
+ unlockGlobalParams;
+ return psName;
+ }
+@@ -2371,6 +2557,15 @@
+ return ah;
+ }
+
++GBool GlobalParams::getPSLZW() {
++ GBool ah;
++
++ lockGlobalParams;
++ ah = psLZW;
++ unlockGlobalParams;
++ return ah;
++}
++
+ GBool GlobalParams::getPSUncompressPreloadedImages() {
+ GBool ah;
+
+@@ -2380,6 +2575,15 @@
+ return ah;
+ }
+
++double GlobalParams::getPSMinLineWidth() {
++ double w;
++
++ lockGlobalParams;
++ w = psMinLineWidth;
++ unlockGlobalParams;
++ return w;
++}
++
+ double GlobalParams::getPSRasterResolution() {
+ double res;
+
+@@ -2398,6 +2602,15 @@
+ return mono;
+ }
+
++int GlobalParams::getPSRasterSliceSize() {
++ int slice;
++
++ lockGlobalParams;
++ slice = psRasterSliceSize;
++ unlockGlobalParams;
++ return slice;
++}
++
+ GBool GlobalParams::getPSAlwaysRasterize() {
+ GBool rast;
+
+@@ -2461,15 +2674,6 @@
+ return f;
+ }
+
+-GBool GlobalParams::getEnableT1lib() {
+- GBool f;
+-
+- lockGlobalParams;
+- f = enableT1lib;
+- unlockGlobalParams;
+- return f;
+-}
+-
+ GBool GlobalParams::getEnableFreeType() {
+ GBool f;
+
+@@ -2597,6 +2801,7 @@
+ return draw;
+ }
+
++
+ GBool GlobalParams::getMapNumericCharNames() {
+ GBool map;
+
+@@ -2615,6 +2820,24 @@
+ 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 @@
+ unlockGlobalParams;
+ }
+
++void GlobalParams::setPSUseCropBoxAsPage(GBool crop) {
++ lockGlobalParams;
++ psUseCropBoxAsPage = crop;
++ unlockGlobalParams;
++}
++
+ void GlobalParams::setPSExpandSmaller(GBool expand) {
+ lockGlobalParams;
+ psExpandSmaller = expand;
+@@ -2882,7 +3111,7 @@
+ unlockGlobalParams;
+ }
+
+-void GlobalParams::setTextEncoding(char *encodingName) {
++void GlobalParams::setTextEncoding(const char *encodingName) {
+ lockGlobalParams;
+ delete textEncoding;
+ textEncoding = new GString(encodingName);
+@@ -2930,15 +3159,6 @@
+ 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 @@
+ 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 -uNr xpdf-3.03/xpdf/GlobalParams.h xpdf-3.04/xpdf/GlobalParams.h
+--- xpdf-3.03/xpdf/GlobalParams.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/GlobalParams.h 2014-05-28 20:50:50.000000000 +0200
+@@ -173,7 +173,7 @@
+
+ // Initialize the global parameters by attempting to read a config
+ // file.
+- GlobalParams(char *cfgFileName);
++ GlobalParams(const char *cfgFileName);
+
+ ~GlobalParams();
+
+@@ -193,6 +193,8 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ GBool getTextKeepTinyChars();
+ GString *getInitialZoom();
+ GBool getContinuousView();
+- GBool getEnableT1lib();
+ GBool getEnableFreeType();
+ GBool getDisableFreeTypeHinting();
+ GBool getAntialias();
+@@ -249,6 +254,8 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ // 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 @@
+ 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 @@
+ 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 -uNr xpdf-3.03/xpdf/HTMLGen.cc xpdf-3.04/xpdf/HTMLGen.cc
+--- xpdf-3.03/xpdf/HTMLGen.cc 1970-01-01 01:00:00.000000000 +0100
++++ xpdf-3.04/xpdf/HTMLGen.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -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 -uNr xpdf-3.03/xpdf/HTMLGen.h xpdf-3.04/xpdf/HTMLGen.h
+--- xpdf-3.03/xpdf/HTMLGen.h 1970-01-01 01:00:00.000000000 +0100
++++ xpdf-3.04/xpdf/HTMLGen.h 2014-05-28 20:50:50.000000000 +0200
+@@ -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 -uNr xpdf-3.03/xpdf/ImageOutputDev.cc xpdf-3.04/xpdf/ImageOutputDev.cc
+--- xpdf-3.03/xpdf/ImageOutputDev.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/ImageOutputDev.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -37,7 +37,8 @@
+ 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::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 @@
+ 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 @@
+ } 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 @@
+
+ // 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::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 @@
+ !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 @@
+ 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 @@
+ 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 @@
+
+ // 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 @@
+ } 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 @@
+ }
+ }
+ }
++
++ 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 -uNr xpdf-3.03/xpdf/ImageOutputDev.h xpdf-3.04/xpdf/ImageOutputDev.h
+--- xpdf-3.03/xpdf/ImageOutputDev.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/ImageOutputDev.h 2014-05-28 20:50:50.000000000 +0200
+@@ -62,7 +62,7 @@
+ 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 @@
+ //----- 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 -uNr xpdf-3.03/xpdf/JArithmeticDecoder.cc xpdf-3.04/xpdf/JArithmeticDecoder.cc
+--- xpdf-3.03/xpdf/JArithmeticDecoder.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/JArithmeticDecoder.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -92,10 +92,18 @@
+ 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::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 -uNr xpdf-3.03/xpdf/JArithmeticDecoder.h xpdf-3.04/xpdf/JArithmeticDecoder.h
+--- xpdf-3.03/xpdf/JArithmeticDecoder.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/JArithmeticDecoder.h 2014-05-28 20:50:50.000000000 +0200
+@@ -108,6 +108,7 @@
+ Guint nBytesRead;
+ int dataLen;
+ GBool limitStream;
++ int readBuf;
+ };
+
+ #endif
+diff -uNr xpdf-3.03/xpdf/JBIG2Stream.cc xpdf-3.04/xpdf/JBIG2Stream.cc
+--- xpdf-3.03/xpdf/JBIG2Stream.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/JBIG2Stream.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -623,11 +623,11 @@
+ }
+
+ 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 @@
+ }
+ 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 @@
+ 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 @@
+ gfree(refSegs);
+ break;
+ }
+- while (byteCounter < segLength) {
+- if (curStr->getChar() == EOF) {
+- break;
+- }
+- ++byteCounter;
+- }
++ byteCounter += curStr->discardChars(segLength - byteCounter);
+ }
+
+ gfree(refSegs);
+@@ -1502,9 +1494,8 @@
+ 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 @@
+ 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 @@
+ Guchar mask;
+ int x, y, x0, x1, a0i, b1i, blackPixels, pix, i;
+
+-
+ bitmap = new JBIG2Bitmap(0, w, h);
+ bitmap->clearToZero();
+
+@@ -2994,7 +2978,7 @@
+ 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::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::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 -uNr xpdf-3.03/xpdf/JPXStream.cc xpdf-3.04/xpdf/JPXStream.cc
+--- xpdf-3.03/xpdf/JPXStream.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/JPXStream.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ #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 @@
+ 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 @@
+
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ break;
+ } else {
+ cover(4);
+- for (i = 0; i < dataLen; ++i) {
+- bufStr->getChar();
+- }
++ bufStr->discardChars(dataLen);
+ }
+ }
+ }
+@@ -529,7 +544,7 @@
+ 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 @@
+ } 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 @@
+ 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 @@
+ }
+ width = img.xSize - img.xOffset;
+ height = img.ySize - img.yOffset;
+- return gTrue;
++ return result;
+ }
+
+ while (readBoxHdr(&boxType, &boxLen, &dataLen)) {
+@@ -614,12 +628,12 @@
+ !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 @@
+ 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 @@
+ 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 @@
+ (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 @@
+ !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 @@
+ !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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ !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 @@
+ 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 @@
+ 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 @@
+ !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 @@
+ 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 @@
+ !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 @@
+ 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 @@
+ 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 @@
+ !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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ !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 @@
+ 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 @@
+ !(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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ }
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ }
+ 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 @@
+ cover(96);
+ if (tileComp->transform == 0) {
+ cover(97);
+- val &= -1 << fracBits;
++ val &= -1 << (fracBits - tileComp->prec);
+ }
+ } else {
+ cover(98);
+@@ -2782,7 +2843,7 @@
+
+ //----- 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 @@
+ }
+ if (tileComp->transform == 0) {
+ cover(103);
+- shift += fracBits;
++ shift += fracBits - tileComp->prec;
+ }
+
+ // fixed point adjustment and dequantization
+@@ -2868,7 +2929,7 @@
+ if (qStyle == 0) {
+ cover(76);
+ if (tileComp->transform == 0) {
+- val &= -1 << fracBits;
++ val &= -1 << (fracBits - tileComp->prec);
+ }
+ } else {
+ cover(77);
+@@ -3103,8 +3164,8 @@
+ 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 @@
+ } 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 @@
+ 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 @@
+ 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 @@
+ }
+
+ 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::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 -uNr xpdf-3.03/xpdf/JPXStream.h xpdf-3.04/xpdf/JPXStream.h
+--- xpdf-3.03/xpdf/JPXStream.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/JPXStream.h 2014-05-28 20:50:50.000000000 +0200
+@@ -199,7 +199,7 @@
+
+ //----- 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 @@
+ 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 @@
+ 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 @@
+
+ //------------------------------------------------------------------------
+
++enum JPXDecodeResult {
++ jpxDecodeOk,
++ jpxDecodeNonFatalError,
++ jpxDecodeFatalError
++};
++
++//------------------------------------------------------------------------
++
+ class JPXStream: public FilterStream {
+ public:
+
+@@ -273,14 +289,15 @@
+ 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 @@
+ 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 -uNr xpdf-3.03/xpdf/Lexer.h xpdf-3.04/xpdf/Lexer.h
+--- xpdf-3.03/xpdf/Lexer.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/Lexer.h 2014-05-28 20:50:50.000000000 +0200
+@@ -53,13 +53,12 @@
+ 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 -uNr xpdf-3.03/xpdf/Link.cc xpdf-3.04/xpdf/Link.cc
+--- xpdf-3.03/xpdf/Link.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/Link.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -39,7 +39,7 @@
+
+ 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 @@
+ 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 @@
+
+ // 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 @@
+
+ // system-dependent path manipulation
+ if (name) {
+-#ifdef WIN32
++#ifdef _WIN32
+ int i, j;
+
+ // "//...." --> "\...."
+@@ -517,7 +541,7 @@
+ 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 @@
+ }
+
+ //------------------------------------------------------------------------
++// 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 @@
+
+ Links::Links(Object *annots, GString *baseURI) {
+ Link *link;
+- Object obj1, obj2;
++ Object obj1, obj2, obj3;
+ int size;
+ int i;
+
+@@ -756,7 +872,10 @@
+ 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 @@
+ delete link;
+ }
+ }
++ obj3.free();
+ obj2.free();
+ }
+ obj1.free();
+diff -uNr xpdf-3.03/xpdf/Link.h xpdf-3.04/xpdf/Link.h
+--- xpdf-3.03/xpdf/Link.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/Link.h 2014-05-28 20:50:50.000000000 +0200
+@@ -32,6 +32,9 @@
+ actionURI, // URI
+ actionNamed, // named action
+ actionMovie, // movie action
++ actionJavaScript, // run JavaScript
++ actionSubmitForm, // submit form
++ actionHide, // hide annotation
+ actionUnknown // anything else
+ };
+
+@@ -244,8 +247,10 @@
+
+ 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 @@
+
+ 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 @@
+ };
+
+ //------------------------------------------------------------------------
++// 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 -uNr xpdf-3.03/xpdf/Makefile.in xpdf-3.04/xpdf/Makefile.in
+--- xpdf-3.03/xpdf/Makefile.in 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/Makefile.in 2014-05-28 20:50:50.000000000 +0200
+@@ -19,18 +19,19 @@
+ 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 @@
+ #------------------------------------------------------------------------
+
+ CXX_SRC = \
++ $(srcdir)/AcroForm.cc \
+ $(srcdir)/Annot.cc \
+ $(srcdir)/Array.cc \
+ $(srcdir)/BuiltinFont.cc \
+@@ -61,11 +63,13 @@
+ $(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 @@
+ $(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-no-x: pdftops$(EXE) pdftotext$(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) \
++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) pdftohtml$(EXE) pdfinfo$(EXE) \
++ pdffonts$(EXE) pdfdetach$(EXE) pdfimages$(EXE)
++
++#------------------------------------------------------------------------
++
++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 @@
+
+ #------------------------------------------------------------------------
+
+-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 @@
+
+ #------------------------------------------------------------------------
+
+-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 @@
+
+ #------------------------------------------------------------------------
+
+-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 @@
+
+ #------------------------------------------------------------------------
+
+-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 @@
+
+ #------------------------------------------------------------------------
+
+-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 @@
+
+ #------------------------------------------------------------------------
+
+-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 @@
+
+ #------------------------------------------------------------------------
+
+-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 @@
+ 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 @@
+ depend:
+ $(CXX) $(CXXFLAGS) -MM $(CXX_SRC) >Makefile.dep
+
+-include Makefile.dep
++-include Makefile.dep
+diff -uNr xpdf-3.03/xpdf/Object.h xpdf-3.04/xpdf/Object.h
+--- xpdf-3.03/xpdf/Object.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/Object.h 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ 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::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 -uNr xpdf-3.03/xpdf/OptionalContent.cc xpdf-3.04/xpdf/OptionalContent.cc
+--- xpdf-3.03/xpdf/OptionalContent.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/OptionalContent.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ }
+ }
+ 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 @@
+ //------------------------------------------------------------------------
+
+ 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 @@
+ 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 @@
+ }
+ 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 @@
+ 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() {
+- 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 @@
+ }
+
+ 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 -uNr xpdf-3.03/xpdf/OptionalContent.h xpdf-3.04/xpdf/OptionalContent.h
+--- xpdf-3.03/xpdf/OptionalContent.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/OptionalContent.h 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+
+ 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 @@
+
+ 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 @@
+ 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 @@
+ 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 -uNr xpdf-3.03/xpdf/Outline.cc xpdf-3.04/xpdf/Outline.cc
+--- xpdf-3.03/xpdf/Outline.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/Outline.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ 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 @@
+
+ //------------------------------------------------------------------------
+
+-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 @@
+ }
+ obj1.free();
+
++ itemRefA->copy(&itemRef);
+ dict->lookupNF("First", &firstRef);
+ dict->lookupNF("Last", &lastRef);
+ dict->lookupNF("Next", &nextRef);
+@@ -104,22 +88,24 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ kids = NULL;
+ }
+ }
++
++Unicode *OutlineItem::getTitle() {
++ return title ? title->getUnicode() : (Unicode *)NULL;
++}
++
++int OutlineItem::getTitleLength() {
++ return title ? title->getLength() : 0;
++}
+diff -uNr xpdf-3.03/xpdf/Outline.h xpdf-3.04/xpdf/Outline.h
+--- xpdf-3.03/xpdf/Outline.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/Outline.h 2014-05-28 20:50:50.000000000 +0200
+@@ -2,7 +2,7 @@
+ //
+ // Outline.h
+ //
+-// Copyright 2002-2003 Glyph & Cog, LLC
++// Copyright 2002-2013 Glyph & Cog, LLC
+ //
+ //========================================================================
+
+@@ -22,6 +22,7 @@
+ class GList;
+ class XRef;
+ class LinkAction;
++class TextString;
+
+ //------------------------------------------------------------------------
+
+@@ -44,17 +45,18 @@
+ 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 @@
+ 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 -uNr xpdf-3.03/xpdf/OutputDev.cc xpdf-3.04/xpdf/OutputDev.cc
+--- xpdf-3.03/xpdf/OutputDev.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/OutputDev.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -43,6 +43,11 @@
+ *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 @@
+
+ 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::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 @@
+ 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 @@
+ 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 -uNr xpdf-3.03/xpdf/OutputDev.h xpdf-3.04/xpdf/OutputDev.h
+--- xpdf-3.03/xpdf/OutputDev.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/OutputDev.h 2014-05-28 20:50:50.000000000 +0200
+@@ -112,6 +112,7 @@
+
+ // 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 @@
+ 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 @@
+ //----- 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 -uNr xpdf-3.03/xpdf/Page.cc xpdf-3.04/xpdf/Page.cc
+--- xpdf-3.03/xpdf/Page.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/Page.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ }
+ 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 @@
+ delete annotList;
+ }
+
++ // draw form fields
++ if ((form = doc->getCatalog()->getForm())) {
++ form->draw(num, gfx, printing);
++ out->dump();
++ }
++
+ delete gfx;
+ #endif
+ }
+@@ -459,6 +468,7 @@
+ 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 @@
+ }
+ delete state;
+ }
++#endif
+diff -uNr xpdf-3.03/xpdf/Page.h xpdf-3.04/xpdf/Page.h
+--- xpdf-3.03/xpdf/Page.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/Page.h 2014-05-28 20:50:50.000000000 +0200
+@@ -77,6 +77,7 @@
+ 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 @@
+ Object metadata;
+ Object pieceInfo;
+ Object separationInfo;
++ double userUnit;
+ Object resources;
+ };
+
+@@ -146,6 +148,7 @@
+ 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 -uNr xpdf-3.03/xpdf/Parser.cc xpdf-3.04/xpdf/Parser.cc
+--- xpdf-3.03/xpdf/Parser.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/Parser.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -153,7 +153,7 @@
+ Object obj;
+ BaseStream *baseStr;
+ Stream *str;
+- Guint pos, endPos, length;
++ GFileOffset pos, endPos, length;
+
+ // get stream start position
+ lexer->skipToNextLine();
+@@ -162,20 +162,21 @@
+ }
+ 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 @@
+ }
+
+ // get filters
+- str = str->addFilters(dict);
++ str = str->addFilters(dict, recursion);
+
+ return str;
+ }
+diff -uNr xpdf-3.03/xpdf/Parser.h xpdf-3.04/xpdf/Parser.h
+--- xpdf-3.03/xpdf/Parser.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/Parser.h 2014-05-28 20:50:50.000000000 +0200
+@@ -42,7 +42,7 @@
+ Stream *getStream() { return lexer->getStream(); }
+
+ // Get current position in file.
+- int getPos() { return lexer->getPos(); }
++ GFileOffset getPos() { return lexer->getPos(); }
+
+ private:
+
+diff -uNr xpdf-3.03/xpdf/PDFCore.cc xpdf-3.04/xpdf/PDFCore.cc
+--- xpdf-3.03/xpdf/PDFCore.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/PDFCore.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -2,7 +2,7 @@
+ //
+ // PDFCore.cc
+ //
+-// Copyright 2004 Glyph & Cog, LLC
++// Copyright 2004-2013 Glyph & Cog, LLC
+ //
+ //========================================================================
+
+@@ -100,7 +100,8 @@
+ 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 @@
+ }
+ 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 @@
+ return err;
+ }
+
+-#ifdef WIN32
++#ifdef _WIN32
+ int PDFCore::loadFile(wchar_t *fileName, int fileNameLen,
+ GString *ownerPassword, GString *userPassword) {
+ int err;
+@@ -423,6 +428,7 @@
+
+ // 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 @@
+ }
+ 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::needTile(PDFCorePage *page, int x, int y) {
+ PDFCoreTile *tile;
++ TextOutputControl textOutCtrl;
+ TextOutputDev *textOut;
+ int xDest, yDest, sliceW, sliceH;
+ int i;
+@@ -893,7 +917,8 @@
+ 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 @@
+ }
+ --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 @@
+ }
+ --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 @@
+ 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 @@
+ }
+ 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::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 @@
+ 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 @@
+
+ // 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 -uNr xpdf-3.03/xpdf/PDFCore.h xpdf-3.04/xpdf/PDFCore.h
+--- xpdf-3.03/xpdf/PDFCore.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/PDFCore.h 2014-05-28 20:50:50.000000000 +0200
+@@ -100,7 +100,11 @@
+ //------------------------------------------------------------------------
+
+ struct PDFHistory {
++#ifdef _WIN32
++ wchar_t *fileName;
++#else
+ GString *fileName;
++#endif
+ int page;
+ };
+
+@@ -125,7 +129,7 @@
+ 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 -uNr xpdf-3.03/xpdf/pdfdetach.cc xpdf-3.04/xpdf/pdfdetach.cc
+--- xpdf-3.03/xpdf/pdfdetach.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/pdfdetach.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -147,7 +147,7 @@
+ } 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 -uNr xpdf-3.03/xpdf/PDFDoc.cc xpdf-3.04/xpdf/PDFDoc.cc
+--- xpdf-3.03/xpdf/PDFDoc.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/PDFDoc.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ GString *userPassword, PDFCore *coreA) {
+ Object obj;
+ GString *fileName1, *fileName2;
+-#ifdef WIN32
++#ifdef _WIN32
+ int n, i;
+ #endif
+
+@@ -71,7 +71,7 @@
+ 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 @@
+ 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(BaseStream *strA, GString *ownerPassword,
+ GString *userPassword, PDFCore *coreA) {
+-#ifdef WIN32
++#ifdef _WIN32
+ int n, i;
+ #endif
+
+@@ -178,7 +178,7 @@
+ 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 @@
+ #endif
+ } else {
+ fileName = NULL;
+-#ifdef WIN32
++#ifdef _WIN32
+ fileNameU = NULL;
+ #endif
+ }
+@@ -231,6 +231,7 @@
+ // read the optional content info
+ optContent = new OptionalContent(this);
+
++
+ // done
+ return gTrue;
+ }
+@@ -294,7 +295,7 @@
+ if (fileName) {
+ delete fileName;
+ }
+-#ifdef WIN32
++#ifdef _WIN32
+ if (fileNameU) {
+ gfree(fileNameU);
+ }
+@@ -309,10 +310,8 @@
+ 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::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 @@
+ 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::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 @@
+ 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 -uNr xpdf-3.03/xpdf/PDFDoc.h xpdf-3.04/xpdf/PDFDoc.h
+--- xpdf-3.03/xpdf/PDFDoc.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/PDFDoc.h 2014-05-28 20:50:50.000000000 +0200
+@@ -39,7 +39,7 @@
+
+ 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 @@
+
+ // Get file name.
+ GString *getFileName() { return fileName; }
+-#ifdef WIN32
++#ifdef _WIN32
+ wchar_t *getFileNameU() { return fileNameU; }
+ #endif
+
+@@ -172,11 +172,12 @@
+ 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 @@
+ GBool saveEmbeddedFile2(int idx, FILE *f);
+
+ GString *fileName;
+-#ifdef WIN32
++#ifdef _WIN32
+ wchar_t *fileNameU;
+ #endif
+ FILE *file;
+diff -uNr xpdf-3.03/xpdf/pdffonts.cc xpdf-3.04/xpdf/pdffonts.cc
+--- xpdf-3.03/xpdf/pdffonts.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/pdffonts.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ "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 @@
+ "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 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 @@
+ 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 @@
+
+ // read config file
+ globalParams = new GlobalParams(cfgFileName);
++ globalParams->setupBaseFonts(NULL);
+
+ // open PDF file
+ if (ownerPassword[0] != '\001') {
+@@ -141,10 +155,17 @@
+ }
+
+ // 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 @@
+ 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 @@
+ 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 @@
+ }
+ 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 @@
+ Object fontObj, toUnicodeObj;
+ GString *name;
+ GBool emb, subset, hasToUnicode;
++ GfxFontLoc *loc;
+ int i;
+
+ fontRef = *font->getID();
+@@ -284,10 +384,38 @@
+ 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 -uNr xpdf-3.03/xpdf/pdfinfo.cc xpdf-3.04/xpdf/pdfinfo.cc
+--- xpdf-3.03/xpdf/pdfinfo.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/pdfinfo.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ wISO /= sqrt(2.0);
+ }
+ }
++ printf(" (rotated %d degrees)", doc->getPageRotate(pg));
+ printf("\n");
+ }
+
+@@ -275,16 +277,8 @@
+ 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 @@
+ 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 -uNr xpdf-3.03/xpdf/pdftohtml.cc xpdf-3.04/xpdf/pdftohtml.cc
+--- xpdf-3.03/xpdf/pdftohtml.cc 1970-01-01 01:00:00.000000000 +0100
++++ xpdf-3.04/xpdf/pdftohtml.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -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 -uNr xpdf-3.03/xpdf/pdftopng.cc xpdf-3.04/xpdf/pdftopng.cc
+--- xpdf-3.03/xpdf/pdftopng.cc 1970-01-01 01:00:00.000000000 +0100
++++ xpdf-3.04/xpdf/pdftopng.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -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 -uNr xpdf-3.03/xpdf/pdftoppm.cc xpdf-3.04/xpdf/pdftoppm.cc
+--- xpdf-3.03/xpdf/pdftoppm.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/pdftoppm.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -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 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 @@
+ "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 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 @@
+ // 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 @@
+ 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 -uNr xpdf-3.03/xpdf/pdftops.cc xpdf-3.04/xpdf/pdftops.cc
+--- xpdf-3.03/xpdf/pdftops.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/pdftops.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ 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 @@
+ if (noCrop) {
+ globalParams->setPSCrop(gFalse);
+ }
++ if (pageCrop) {
++ globalParams->setPSUseCropBoxAsPage(gTrue);
++ }
+ if (expand) {
+ globalParams->setPSExpandSmaller(gTrue);
+ }
+@@ -318,15 +340,18 @@
+ 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 -uNr xpdf-3.03/xpdf/pdftotext.cc xpdf-3.04/xpdf/pdftotext.cc
+--- xpdf-3.03/xpdf/pdftotext.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/pdftotext.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ "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 @@
+ 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 @@
+ goto err0;
+ }
+ fileName = new GString(argv[1]);
+- if (fixedPitch) {
+- physLayout = gTrue;
+- }
+
+ // read config file
+ globalParams = new GlobalParams(cfgFileName);
+@@ -187,7 +207,7 @@
+ } else {
+ textFileName = fileName->copy();
+ }
+- textFileName->append(htmlMeta ? ".html" : ".txt");
++ textFileName->append(".txt");
+ }
+
+ // get page range
+@@ -198,50 +218,25 @@
+ 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 @@
+ }
+ 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 @@
+
+ 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 -uNr xpdf-3.03/xpdf/PreScanOutputDev.cc xpdf-3.04/xpdf/PreScanOutputDev.cc
+--- xpdf-3.03/xpdf/PreScanOutputDev.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/PreScanOutputDev.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -61,13 +61,13 @@
+ }
+
+ 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::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 @@
+
+ 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::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 @@
+
+ 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 @@
+ GfxImageColorMap *colorMap,
+ Stream *maskStr,
+ int maskWidth, int maskHeight,
+- GBool maskInvert) {
++ GBool maskInvert, GBool interpolate) {
+ GfxColorSpace *colorSpace;
+
+ colorSpace = colorMap->getColorSpace();
+@@ -264,7 +258,8 @@
+ GfxImageColorMap *colorMap,
+ Stream *maskStr,
+ int maskWidth, int maskHeight,
+- GfxImageColorMap *maskColorMap) {
++ GfxImageColorMap *maskColorMap,
++ GBool interpolate) {
+ GfxColorSpace *colorSpace;
+
+ colorSpace = colorMap->getColorSpace();
+diff -uNr xpdf-3.03/xpdf/PreScanOutputDev.h xpdf-3.04/xpdf/PreScanOutputDev.h
+--- xpdf-3.03/xpdf/PreScanOutputDev.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/PreScanOutputDev.h 2014-05-28 20:50:50.000000000 +0200
+@@ -67,7 +67,7 @@
+ 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 @@
+ //----- 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 -uNr xpdf-3.03/xpdf/PSOutputDev.cc xpdf-3.04/xpdf/PSOutputDev.cc
+--- xpdf-3.03/xpdf/PSOutputDev.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/PSOutputDev.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ " } 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 @@
+ "} 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 @@
+ 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 @@
+ {"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 @@
+ 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 @@
+ } 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 @@
+ 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 @@
+ Page *page;
+ PDFRectangle *box;
+ PSOutPaperSize *size;
++ PSFontFileInfo *ff;
+ GList *names;
+ int pg, w, h, i;
+
+@@ -1118,8 +1168,13 @@
+ 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 @@
+ 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 @@
+
+ PSOutputDev::~PSOutputDev() {
+ PSOutCustomColor *cc;
+- int i;
+
+ if (ok) {
+ if (!manualCtrl) {
+@@ -1243,7 +1293,7 @@
+ #ifdef HAVE_POPEN
+ else if (fileType == psPipe) {
+ pclose((FILE *)outputStream);
+-#ifndef WIN32
++#ifndef _WIN32
+ signal(SIGPIPE, (SignalFunc)SIG_DFL);
+ #endif
+ }
+@@ -1255,30 +1305,8 @@
+ 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 @@
+ }
+ }
+
++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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ }
+ 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 @@
+ delete s;
+ }
+ }
++ if (mode != psModeForm) {
++ writePS("end\n");
++ }
+ }
+
+ void PSOutputDev::writePageTrailer() {
+@@ -1517,7 +1564,6 @@
+ 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::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 @@
+ 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::setupFont(GfxFont *font, Dict *parentResDict) {
++ PSFontInfo *fi;
+ GfxFontLoc *fontLoc;
+- GString *psName;
+ GBool subst;
+ char buf[16];
+ UnicodeMap *uMap;
+@@ -1693,123 +1788,101 @@
+ 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);
+- } 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());
++ fi->ff = setupType3Font(font, parentResDict);
++ } else {
++ 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 @@
+ 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 @@
+ 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 @@
+ }
+ 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 @@
+ }
+ 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 @@
+ 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;
+- }
+- if (!((start[i] >= '0' && start[i] <= '9') ||
+- (start[i] >= 'A' && start[i] <= 'F') ||
+- (start[i] >= 'a' && start[i] <= 'f')))
+- binMode = gTrue;
+- }
++ // plain text (PFA) format
++ } else {
+
+- // convert binary data to ASCII
+- if (binMode) {
++ // copy ASCII portion of font
++ writePSChar(start[0]);
++ writePSChar(start[1]);
++ for (i = 2; i < length1 && (c = strObj.streamGetChar()) != EOF; ++i) {
++ writePSChar(c);
++ }
++
++ // figure out if encrypted portion is binary or ASCII
++ binMode = gFalse;
+ for (i = 0; i < 4; ++i) {
+- 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 @@
+ 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 @@
+
+ // 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 @@
+
+ // 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 @@
+
+ // 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 @@
+ 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 @@
+ 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 @@
+
+ // 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 @@
+
+ // 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 @@
+
+ // ending comment
+ writePS("%%EndResource\n");
+-}
+
+-void PSOutputDev::setupExternalCIDTrueTypeFont(GfxFont *font,
+- GString *fileName,
+- GString *psName,
+- GBool needVerticalMetrics) {
++ 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;
++}
++
++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 @@
+
+ // 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 @@
+
+ // 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 @@
+
+ // 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 @@
+ 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 @@
+ 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 @@
+
+ // 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 @@
+
+ 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 @@
+ psName->append('_')->append(s);
+ delete s;
+ }
+- fontNames->add(psName->copy(), 1);
+ return psName;
+ }
+
+@@ -2651,7 +2988,7 @@
+ }
+
+ 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 @@
+ //~ 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 @@
+ 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 @@
+ }
+ } 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 @@
+ }
+ } 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 @@
+ 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 @@
+ 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 @@
+
+ // 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 @@
+ }
+ formIDs = (Ref *)greallocn(formIDs, formIDSize, sizeof(Ref));
+ }
+- formIDs[formIDLen++] = id;
++ formIDs[formIDLen++] = strRef->getRef();
+
+ dict = strObj->streamGetDict();
+
+@@ -2875,7 +3221,7 @@
+ 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 @@
+ 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 rasterize;
+ #if HAVE_SPLASH
+ GBool mono;
++ GBool useLZW;
+ double dpi;
+ SplashOutputDev *splashOut;
+ SplashColor paperColor;
+@@ -2915,10 +3262,11 @@
+ 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 @@
+ // 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 @@
+ 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 @@
+ } 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 @@
+ 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 @@
+ }
+ writePS("%%BeginPageSetup\n");
+ }
++ if (mode != psModeForm) {
++ writePS("xpdf begin\n");
++ }
+
+ // underlays
+ if (underlayCbk) {
+@@ -3356,6 +3721,7 @@
+ }
+ writePS("%%PageTrailer\n");
+ writePageTrailer();
++ writePS("end\n");
+ }
+ }
+
+@@ -3371,8 +3737,13 @@
+
+ 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 @@
+ 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 @@
+ 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 @@
+ }
+ inType3Char = gTrue;
+ ++numTilingPatterns;
+- gfx2->display(str);
++ gfx2->display(strRef);
+ --numTilingPatterns;
+ inType3Char = gFalse;
+ writePS("} def\n");
+@@ -3944,7 +4316,7 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ }
+ 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 @@
+ 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 @@
+ 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 @@
+ 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::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::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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ }
+ } 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 @@
+ if ((mode == psModeForm || inType3Char || preload) &&
+ globalParams->getPSUncompressPreloadedImages()) {
+ s = NULL;
+- useRLE = gFalse;
++ useLZW = useRLE = gFalse;
+ useCompressed = gFalse;
+ useASCII = gFalse;
+ } else {
+@@ -5073,11 +5467,17 @@
+ " ");
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+
+ // 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 @@
+ #endif
+
+ // delete encoders
+- if (useRLE || useASCII || inlineImg) {
++ if (useLZW || useRLE || useASCII || inlineImg) {
+ delete str;
+ }
+ }
+@@ -5204,18 +5610,20 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+
+ // 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 @@
+ 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 @@
+ }
+ } 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 @@
+ if ((mode == psModeForm || inType3Char || preload) &&
+ globalParams->getPSUncompressPreloadedImages()) {
+ s = NULL;
+- useRLE = gFalse;
++ useLZW = useRLE = gFalse;
+ useCompressed = gFalse;
+ useASCII = gFalse;
+ } else {
+@@ -5444,11 +5866,17 @@
+ " ");
+ 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 @@
+ 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 @@
+ 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 @@
+
+ // 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 @@
+ writePS("%-EOD-\n");
+
+ // delete encoders
+- if (useRLE || useASCII || inlineImg) {
++ if (useLZW || useRLE || useASCII || inlineImg) {
+ delete str;
+ }
+ }
+@@ -6267,6 +6699,10 @@
+
+ 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::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 @@
+ 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::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 @@
+
+ // 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 @@
+ // 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 @@
+ }
+ }
+ writePS("\n");
++ delete ts;
+ }
+diff -uNr xpdf-3.03/xpdf/PSOutputDev.h xpdf-3.04/xpdf/PSOutputDev.h
+--- xpdf-3.03/xpdf/PSOutputDev.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/PSOutputDev.h 2014-05-28 20:50:50.000000000 +0200
+@@ -30,11 +30,9 @@
+ class GfxColorSpace;
+ class GfxSeparationColorSpace;
+ class PDFRectangle;
+-struct PST1FontName;
+-struct PSFont8Info;
+-struct PSFont16Enc;
+ class PSOutCustomColor;
+ class PSOutputDev;
++class PSFontFileInfo;
+
+ //------------------------------------------------------------------------
+ // PSOutputDev
+@@ -92,6 +90,9 @@
+ // 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 @@
+ 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 @@
+ //----- 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 @@
+ { 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 @@
+ 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 @@
+ 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 -uNr xpdf-3.03/xpdf/SecurityHandler.cc xpdf-3.04/xpdf/SecurityHandler.cc
+--- xpdf-3.03/xpdf/SecurityHandler.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/SecurityHandler.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -158,7 +158,7 @@
+ 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 @@
+ //~ 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 @@
+ 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 @@
+ } 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 -uNr xpdf-3.03/xpdf/SplashOutputDev.cc xpdf-3.04/xpdf/SplashOutputDev.cc
+--- xpdf-3.03/xpdf/SplashOutputDev.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/SplashOutputDev.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ 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 @@
+ 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 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 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 @@
+ 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 @@
+ ((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 @@
+ 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 @@
+ bitmapRowPad = bitmapRowPadA;
+ bitmapTopDown = bitmapTopDownA;
+ bitmapUpsideDown = gFalse;
++ noComposite = gFalse;
+ allowAntialias = allowAntialiasA;
+ vectorAntialias = allowAntialias &&
+ globalParams->getVectorAntialias() &&
+@@ -596,6 +619,7 @@
+ 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 @@
+ 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::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 @@
+
+ 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 @@
+ 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 @@
+ 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 @@
+
+ } else {
+
+- fileName = NULL;
+ fontNum = 0;
+
+ if (!(fontLoc = gfxFont->locateFont(xref, gFalse))) {
+@@ -1125,6 +1178,24 @@
+ // 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ }
+ 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 @@
+ 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 @@
+ }
+ 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 @@
+ 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 @@
+ }
+ 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 @@
+ }
+ 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 @@
+ // 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ // 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 @@
+ 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::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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+
+ 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 @@
+ #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 @@
+
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+
+ 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 @@
+ 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 @@
+ SplashColorMode srcMode;
+ SplashBitmap *maskBitmap;
+ Splash *maskSplash;
+- SplashColor maskColor;
+ GfxGray gray;
+ GfxRGB rgb;
+ #if SPLASH_CMYK
+@@ -2731,6 +2885,9 @@
+ 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 @@
+ 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 @@
+
+ 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 @@
+ 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 @@
+ //~ 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 @@
+ 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 @@
+ 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 @@
+ #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 @@
+ 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 @@
+ 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 @@
+ #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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ }
+ 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 @@
+ }
+
+ // 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 -uNr xpdf-3.03/xpdf/SplashOutputDev.h xpdf-3.04/xpdf/SplashOutputDev.h
+--- xpdf-3.03/xpdf/SplashOutputDev.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/SplashOutputDev.h 2014-05-28 20:50:50.000000000 +0200
+@@ -110,7 +110,7 @@
+ 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 @@
+ //----- 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 @@
+ // 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 @@
+ 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 @@
+ 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 -uNr xpdf-3.03/xpdf/Stream.cc xpdf-3.04/xpdf/Stream.cc
+--- xpdf-3.03/xpdf/Stream.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/Stream.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ } 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ str->reset();
+ }
+
++void ImageStream::close() {
++ str->close();
++}
++
+ GBool ImageStream::getPixel(Guchar *pix) {
+ int i;
+
+@@ -405,6 +428,10 @@
+ }
+ } 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 @@
+ return;
+ }
+ predLine = (Guchar *)gmalloc(rowBytes);
+- memset(predLine, 0, rowBytes);
+- predIdx = rowBytes;
++
++ reset();
+
+ ok = gTrue;
+ }
+@@ -461,6 +488,11 @@
+ gfree(predLine);
+ }
+
++void StreamPredictor::reset() {
++ memset(predLine, 0, rowBytes);
++ predIdx = rowBytes;
++}
++
+ int StreamPredictor::lookChar() {
+ if (predIdx >= rowBytes) {
+ if (!getNextLine()) {
+@@ -573,19 +605,19 @@
+
+ // 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 @@
+ // 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 @@
+ 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::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 @@
+ return gFalse;
+ }
+ if (limited && bufPos + fileStreamBufSize > start + length) {
+- n = start + length - bufPos;
++ n = (int)(start + length - bufPos);
+ } else {
+ n = fileStreamBufSize;
+ }
+@@ -717,41 +735,20 @@
+ 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 @@
+ }
+ }
+
+-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 {
+- newLength = lengthA;
++ newStart = (int)startA;
+ }
+- subStr = new MemStream(buf, startA, newLength, dictA);
++ if (!limited || newStart + lengthA > start + length) {
++ newLength = start + length - newStart;
++ } else {
++ newLength = (Guint)lengthA;
++ }
++ subStr = new MemStream(buf, newStart, newLength, dictA);
+ return subStr;
+ }
+
+@@ -819,13 +823,13 @@
+ 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 @@
+ //------------------------------------------------------------------------
+
+ 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 *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 @@
+ 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 @@
+
+ void LZWStream::reset() {
+ str->reset();
++ if (pred) {
++ pred->reset();
++ }
+ eof = gFalse;
+ inputBits = 0;
+ clearTable();
+@@ -2068,14 +2075,16 @@
+ //------------------------------------------------------------------------
+
+ // 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 @@
+ #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 @@
+
+ DCTStream::DCTStream(Stream *strA, GBool colorXformA):
+ FilterStream(strA) {
+- int i, j;
++ int i;
+
+ colorXform = colorXformA;
+ progressive = interleaved = gFalse;
+@@ -2117,23 +2163,15 @@
+ 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 @@
+ }
+
+ void DCTStream::reset() {
+- int i, j;
++ int i;
+
+ str->reset();
+
+@@ -2157,6 +2195,8 @@
+ restartInterval = 0;
+
+ if (!readHeader()) {
++ // force an EOF condition
++ progressive = gTrue;
+ y = height;
+ return;
+ }
+@@ -2229,17 +2269,11 @@
+
+ // 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::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 @@
+ }
+ }
+ } 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 @@
+ }
+ 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 @@
+ }
+ }
+ --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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ // 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 @@
+ // 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 @@
+
+ // 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 doScan;
+ int n;
+ int c = 0;
+- int i;
+
+ // read headers
+ doScan = gFalse;
+@@ -3163,9 +3231,7 @@
+ // 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::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::readProgressiveSOF() {
+- int length;
+ int prec;
+ int i;
+ int c;
+
+- length = read16();
++ read16(); // length
+ prec = str->getChar();
+ height = read16();
+ width = read16();
+@@ -4194,6 +4258,9 @@
+ eof = gTrue;
+
+ str->reset();
++ if (pred) {
++ pred->reset();
++ }
+
+ // read header
+ //~ need to look at window size?
+@@ -4274,10 +4341,10 @@
+
+ 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 @@
+ 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 -uNr xpdf-3.03/xpdf/Stream.h xpdf-3.04/xpdf/Stream.h
+--- xpdf-3.03/xpdf/Stream.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/Stream.h 2014-05-28 20:50:50.000000000 +0200
+@@ -17,6 +17,7 @@
+
+ #include <stdio.h>
+ #include "gtypes.h"
++#include "gfile.h"
+ #include "Object.h"
+
+ class BaseStream;
+@@ -97,13 +98,18 @@
+ // 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 @@
+
+ // 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 @@
+
+ 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 @@
+ 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 @@
+ 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 @@
+ // 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 @@
+
+ GBool isOk() { return ok; }
+
++ void reset();
++
+ int lookChar();
+ int getChar();
+ int getBlock(char *blk, int size);
+@@ -282,11 +293,11 @@
+ 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 @@
+ 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 @@
+ 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 @@
+
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 -uNr xpdf-3.03/xpdf/TextOutputDev.cc xpdf-3.04/xpdf/TextOutputDev.cc
+--- xpdf-3.03/xpdf/TextOutputDev.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/TextOutputDev.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -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,82 +33,48 @@
+ #include "Link.h"
+ #include "TextOutputDev.h"
+
+-#ifdef MACOS
+-// needed for setting type/creator of MacOS files
+-#include "ICSupport.h"
+-#endif
+-
+ //------------------------------------------------------------------------
+ // parameters
+ //------------------------------------------------------------------------
+
+-// Each bucket in a text pool includes baselines within a range of
+-// this many points.
+-#define textPoolStep 4
+-
+-// Inter-character space width which will cause addChar to start a new
+-// word.
+-#define minWordBreakSpace 0.1
+-
+-// Negative inter-character space width, i.e., overlap, which will
+-// cause addChar to start a new word.
+-#define minDupBreakOverlap 0.2
+-
+-// Max distance between baselines of two lines within a block, as a
+-// fraction of the font size.
+-#define maxLineSpacingDelta 1.5
+-
+-// Max difference in primary font sizes on two lines in the same
+-// block. Delta1 is used when examining new lines above and below the
+-// current block; delta2 is used when examining text that overlaps the
+-// current block; delta3 is used when examining text to the left and
+-// right of the current block.
+-#define maxBlockFontSizeDelta1 0.05
+-#define maxBlockFontSizeDelta2 0.6
+-#define maxBlockFontSizeDelta3 0.2
+-
+-// Max difference in font sizes inside a word.
+-#define maxWordFontSizeDelta 0.05
+-
+-// Maximum distance between baselines of two words on the same line,
+-// e.g., distance between subscript or superscript and the primary
+-// baseline, as a fraction of the font size.
+-#define maxIntraLineDelta 0.5
+-
+-// Minimum inter-word spacing, as a fraction of the font size. (Only
+-// used for raw ordering.)
+-#define minWordSpacing 0.15
+-
+-// Maximum inter-word spacing, as a fraction of the font size.
+-#define maxWordSpacing 1.5
+-
+-// Maximum horizontal spacing which will allow a word to be pulled
+-// into a block.
+-#define minColSpacing1 0.3
+-
+-// Minimum spacing between columns, as a fraction of the font size.
+-#define minColSpacing2 1.0
+-
+-// Maximum vertical spacing between blocks within a flow, as a
+-// multiple of the font size.
+-#define maxBlockSpacing 2.5
+-
+-// Minimum spacing between characters within a word, as a fraction of
+-// the font size.
+-#define minCharSpacing -0.2
+-
+-// Maximum spacing between characters within a word, as a fraction of
+-// the font size, when there is no obvious extra-wide character
+-// spacing.
+-#define maxCharSpacing 0.03
+-
+-// When extra-wide character spacing is detected, the inter-character
+-// space threshold is set to the minimum inter-character space
+-// multiplied by this constant.
+-#define maxWideCharSpacingMul 1.3
+-
+-// Upper limit on spacing between characters in a word.
+-#define maxWideCharSpacing 0.4
++// 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
+
+ // Max difference in primary,secondary coordinates (as a fraction of
+ // the font size) allowed for duplicated text (fake boldface, drop
+@@ -116,24 +82,293 @@
+ #define dupMaxPriDelta 0.1
+ #define dupMaxSecDelta 0.2
+
++// Inter-character spacing that varies by less than this multiple of
++// font size is assumed to be equivalent.
++#define uniformSpacing 0.07
++
++// 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
++
++// Minimum paragraph indent from left margin, as a fraction of font
++// size.
++#define minParagraphIndent 0.5
++
++// If the space between two lines is greater than
++// paragraphSpacingThreshold * avgLineSpacing, start a new paragraph.
++#define paragraphSpacingThreshold 1.2
++
++// If font size changes by at least this much (measured in points)
++// between lines, start a new paragraph.
++#define paragraphFontSizeDelta 1
++
++// Spaces at the start of a line in physical layout mode are this wide
++// (as a multiple of font size).
++#define physLayoutSpaceWidth 0.33
++
++// 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
++
++// Primary axis delta which will cause a line break in raw mode
++// (as a fraction of font size).
++#define rawModeLineDelta 0.5
++
++// Secondary axis delta which will cause a word break in raw mode
++// (as a fraction of font size).
++#define rawModeWordSpacing 0.15
++
++// Secondary axis overlap which will cause a line break in raw mode
++// (as a fraction of font size).
++#define rawModeCharOverlap 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
++
+ // Max width of underlines (in points).
+ #define maxUnderlineWidth 3
+
+-// Min distance between baseline and underline (in points).
+-//~ this should be font-size-dependent
+-#define minUnderlineGap -2
+-
+-// Max distance between baseline and underline (in points).
+-//~ this should be font-size-dependent
+-#define maxUnderlineGap 4
+-
+ // Max horizontal distance between edge of word and start of underline
+-// (in points).
+-//~ this should be font-size-dependent
+-#define underlineSlack 1
++// (as a fraction of font size).
++#define underlineSlack 0.2
++
++// Max vertical distance between baseline of word and start of
++// underline (as a fraction of font size).
++#define underlineBaselineSlack 0.2
++
++// Max distance between edge of text and edge of link border (as a
++// fraction of font size).
++#define hyperlinkSlack 0.2
++
++//------------------------------------------------------------------------
++// TextChar
++//------------------------------------------------------------------------
++
++class TextChar {
++public:
++
++ 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;
++};
++
++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);
++ }
++}
++
++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);
++}
+
+-// Max distance between edge of text and edge of link border
+-#define hyperlinkSlack 2
++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 @@
+ 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 @@
+ 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;
+- }
+- }
+- ++len;
+-}
+-
+-void TextWord::merge(TextWord *word) {
+- int i;
+-
+- if (word->xMin < xMin) {
+- xMin = word->xMin;
+- }
+- if (word->yMin < yMin) {
+- yMin = word->yMin;
+- }
+- if (word->xMax > xMax) {
+- xMax = word->xMax;
+- }
+- if (word->yMax > yMax) {
+- yMax = word->yMax;
+- }
+- if (len + word->len > size) {
+- size = len + word->len;
+- text = (Unicode *)greallocn(text, size, sizeof(Unicode));
+- edge = (double *)greallocn(edge, size + 1, sizeof(double));
+- charPos = (int *)greallocn(charPos, size + 1, sizeof(int));
+- }
+- 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];
+- }
+- 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
+- switch (rot) {
+- case 0:
+- cmp = xMin - word->xMin;
+- break;
+- case 1:
+- cmp = yMin - word->yMin;
+- break;
+- case 2:
+- cmp = word->xMax - xMax;
+- break;
+- case 3:
+- cmp = word->yMax - yMax;
+- break;
+- }
+- return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
+-}
+-
+-double TextWord::primaryDelta(TextWord *word) {
+- double delta;
+-
+- delta = 0; // make gcc happy
++// This is used to append a clipped character to a word.
++void TextWord::appendChar(TextChar *ch) {
++ if (ch->xMin < xMin) {
++ xMin = ch->xMin;
++ }
++ if (ch->xMax > xMax) {
++ xMax = ch->xMax;
++ }
++ if (ch->yMin < yMin) {
++ yMin = ch->yMin;
++ }
++ if (ch->yMax > yMax) {
++ yMax = ch->yMax;
++ }
++ 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:
+- delta = word->xMin - xMax;
++ default:
++ edge[len] = ch->xMin;
++ edge[len+1] = ch->xMax;
+ break;
+ case 1:
+- delta = word->yMin - yMax;
++ edge[len] = ch->yMin;
++ edge[len+1] = ch->yMax;
+ break;
+ case 2:
+- delta = xMin - word->xMax;
++ edge[len] = ch->xMax;
++ edge[len+1] = ch->xMin;
+ break;
+ case 3:
+- delta = yMin - word->yMax;
++ edge[len] = ch->yMax;
++ edge[len+1] = ch->yMin;
+ 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,2591 +707,2931 @@
+ }
+ }
+
+-#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;
++double TextWord::getBaseline() {
++ switch (rot) {
++ case 0:
++ default:
++ return yMax + fontSize * font->descent;
++ case 1:
++ return xMin - fontSize * font->descent;
++ case 2:
++ return yMin - fontSize * font->descent;
++ case 3:
++ return xMax + fontSize * font->descent;
+ }
+- cursor = word;
+- cursorBaseIdx = wordBaseIdx;
++}
++
++GString *TextWord::getLinkURI() {
++ return link ? link->uri : (GString *)NULL;
+ }
+
+ //------------------------------------------------------------------------
+ // 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() {
++TextLine::TextLine(GList *wordsA, double xMinA, double yMinA,
++ double xMaxA, double yMaxA, double fontSizeA) {
+ TextWord *word;
++ int i, j, k;
+
+- 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;
++ words = wordsA;
++ rot = 0;
++ xMin = xMinA;
++ yMin = yMinA;
++ xMax = xMaxA;
++ yMax = yMaxA;
++ fontSize = fontSizeA;
++ px = 0;
++ pw = 0;
+
+- 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;
++ // build the text
++ len = 0;
++ for (i = 0; i < words->getLength(); ++i) {
++ word = (TextWord *)words->get(i);
++ len += word->len;
++ if (word->spaceAfter) {
++ ++len;
+ }
+- if (word->yMax > yMax) {
+- yMax = word->yMax;
++ }
++ text = (Unicode *)gmallocn(len, sizeof(Unicode));
++ edge = (double *)gmallocn(len + 1, sizeof(double));
++ j = 0;
++ for (i = 0; i < words->getLength(); ++i) {
++ word = (TextWord *)words->get(i);
++ if (i == 0) {
++ rot = word->rot;
++ }
++ for (k = 0; k < word->len; ++k) {
++ text[j] = word->text[k];
++ edge[j] = word->edge[k];
++ ++j;
++ }
++ edge[j] = word->edge[word->len];
++ if (word->spaceAfter) {
++ text[j] = (Unicode)0x0020;
++ ++j;
++ edge[j] = edge[j - 1];
+ }
+ }
++ //~ need to check for other Unicode chars used as hyphens
++ hyphenated = text[len - 1] == (Unicode)'-';
+ }
+
+-double TextLine::primaryDelta(TextLine *line) {
+- double delta;
++TextLine::~TextLine() {
++ deleteGList(words, TextWord);
++ gfree(text);
++ gfree(edge);
++}
+
+- delta = 0; // make gcc happy
++double TextLine::getBaseline() {
++ TextWord *word0;
++
++ word0 = (TextWord *)words->get(0);
+ switch (rot) {
+ case 0:
+- delta = line->xMin - xMax;
+- break;
++ default:
++ return yMax + fontSize * word0->font->descent;
+ case 1:
+- delta = line->yMin - yMax;
+- break;
++ return xMin - fontSize * word0->font->descent;
+ case 2:
+- delta = xMin - line->xMax;
+- break;
++ return yMin - fontSize * word0->font->descent;
+ case 3:
+- delta = yMin - line->yMax;
+- break;
++ return xMax + fontSize * word0->font->descent;
+ }
+- return delta;
+ }
+
+-int TextLine::primaryCmp(TextLine *line) {
+- double cmp;
++//------------------------------------------------------------------------
++// TextParagraph
++//------------------------------------------------------------------------
+
+- cmp = 0; // make gcc happy
+- switch (rot) {
+- case 0:
+- cmp = xMin - line->xMin;
+- break;
+- case 1:
+- cmp = yMin - line->yMin;
+- break;
+- case 2:
+- cmp = line->xMax - xMax;
+- break;
+- case 3:
+- cmp = line->yMax - yMax;
+- break;
++TextParagraph::TextParagraph(GList *linesA) {
++ TextLine *line;
++ int i;
++
++ 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;
++ }
++ if (i == 0 || line->yMin < yMin) {
++ yMin = line->yMin;
++ }
++ if (i == 0 || line->xMax > xMax) {
++ xMax = line->xMax;
++ }
++ if (i == 0 || line->yMax > yMax) {
++ yMax = line->yMax;
++ }
+ }
+- 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;
++TextParagraph::~TextParagraph() {
++ deleteGList(lines, TextLine);
+ }
+
+-int TextLine::cmpYX(TextLine *line) {
+- int cmp;
++//------------------------------------------------------------------------
++// TextColumn
++//------------------------------------------------------------------------
+
+- if ((cmp = secondaryCmp(line))) {
+- return cmp;
++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;
++}
++
++TextColumn::~TextColumn() {
++ deleteGList(paragraphs, TextParagraph);
++}
++
++int TextColumn::cmpX(const void *p1, const void *p2) {
++ const TextColumn *col1 = *(const TextColumn **)p1;
++ const TextColumn *col2 = *(const TextColumn **)p2;
++
++ if (col1->xMin < col2->xMin) {
++ return -1;
++ } else if (col1->xMin > col2->xMin) {
++ return 1;
++ } else {
++ return 0;
+ }
+- return primaryCmp(line);
+ }
+
+-int TextLine::cmpXY(const void *p1, const void *p2) {
+- TextLine *line1 = *(TextLine **)p1;
+- TextLine *line2 = *(TextLine **)p2;
+- int cmp;
++int TextColumn::cmpY(const void *p1, const void *p2) {
++ const TextColumn *col1 = *(const TextColumn **)p1;
++ const TextColumn *col2 = *(const TextColumn **)p2;
+
+- if ((cmp = line1->primaryCmp(line2))) {
+- return cmp;
++ if (col1->yMin < col2->yMin) {
++ return -1;
++ } else if (col1->yMin > col2->yMin) {
++ return 1;
++ } else {
++ return 0;
+ }
+- return line1->secondaryCmp(line2);
+ }
+
+-void TextLine::coalesce(UnicodeMap *uMap) {
+- TextWord *word0, *word1;
+- double space, delta, minSpace;
+- GBool isUnicode;
+- char buf[8];
+- int i, j;
++int TextColumn::cmpPX(const void *p1, const void *p2) {
++ const TextColumn *col1 = *(const TextColumn **)p1;
++ const TextColumn *col2 = *(const TextColumn **)p2;
+
+- if (words->next) {
++ if (col1->px < col2->px) {
++ return -1;
++ } else if (col1->px > col2->px) {
++ return 1;
++ } else {
++ return 0;
++ }
++}
+
+- // 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;
+- }
+- }
++//------------------------------------------------------------------------
++// TextWordList
++//------------------------------------------------------------------------
+
+- // 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;
+- }
+- }
+- }
++TextWordList::TextWordList(GList *wordsA) {
++ words = wordsA;
++}
+
+- // build the line text
+- isUnicode = uMap ? uMap->isUnicode() : gFalse;
+- len = 0;
+- for (word1 = words; word1; word1 = word1->next) {
+- len += word1->len;
+- if (word1->spaceAfter) {
+- ++len;
+- }
+- }
+- text = (Unicode *)gmallocn(len, sizeof(Unicode));
+- edge = (double *)gmallocn(len + 1, sizeof(double));
+- i = 0;
+- for (word1 = words; word1; word1 = word1->next) {
+- for (j = 0; j < word1->len; ++j) {
+- text[i] = word1->text[j];
+- edge[i] = word1->edge[j];
+- ++i;
+- }
+- edge[i] = word1->edge[word1->len];
+- if (word1->spaceAfter) {
+- text[i] = (Unicode)0x0020;
+- ++i;
+- }
+- }
++TextWordList::~TextWordList() {
++ deleteGList(words, TextWord);
++}
+
+- // compute convertedLen and set up the col array
+- col = (int *)gmallocn(len + 1, sizeof(int));
+- convertedLen = 0;
+- for (i = 0; i < len; ++i) {
+- col[i] = convertedLen;
+- if (isUnicode) {
+- ++convertedLen;
+- } else if (uMap) {
+- convertedLen += uMap->mapUnicode(text[i], buf, sizeof(buf));
+- }
+- }
+- col[len] = convertedLen;
++int TextWordList::getLength() {
++ return words->getLength();
++}
+
+- // check for hyphen at end of line
+- //~ need to check for other chars used as hyphens
+- hyphenated = text[len - 1] == (Unicode)'-';
++TextWord *TextWordList::get(int idx) {
++ if (idx < 0 || idx >= words->getLength()) {
++ return NULL;
++ }
++ return (TextWord *)words->get(idx);
+ }
+
+ //------------------------------------------------------------------------
+-// TextLineFrag
++// TextPage
+ //------------------------------------------------------------------------
+
+-class TextLineFrag {
+-public:
++TextPage::TextPage(TextOutputControl *controlA) {
++ control = *controlA;
++ pageWidth = pageHeight = 0;
++ charPos = 0;
++ curFont = NULL;
++ curFontSize = 0;
++ curRot = 0;
++ nTinyChars = 0;
++ actualText = NULL;
++ actualTextLen = 0;
++ actualTextX0 = 0;
++ actualTextY0 = 0;
++ actualTextX1 = 0;
++ actualTextY1 = 0;
++ actualTextNBytes = 0;
+
+- 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);
+-};
++ chars = new GList();
++ fonts = new GList();
+
+-void TextLineFrag::init(TextLine *lineA, int startA, int lenA) {
+- line = lineA;
+- start = startA;
+- len = lenA;
+- col = line->col[start];
++ underlines = new GList();
++ links = new GList();
++
++ findCols = NULL;
++ findLR = gTrue;
++ lastFindXMin = lastFindYMin = 0;
++ haveLastFind = gFalse;
+ }
+
+-void TextLineFrag::computeCoords(GBool oneRot) {
+- TextBlock *blk;
+- double d0, d1, d2, d3, d4;
++TextPage::~TextPage() {
++ clear();
++ deleteGList(chars, TextChar);
++ deleteGList(fonts, TextFontInfo);
++ deleteGList(underlines, TextUnderline);
++ deleteGList(links, TextLink);
++ if (findCols) {
++ deleteGList(findCols, TextColumn);
++ }
++}
+
+- if (oneRot) {
++void TextPage::startPage(GfxState *state) {
++ clear();
++ if (state) {
++ pageWidth = state->getPageWidth();
++ pageHeight = state->getPageHeight();
++ } else {
++ pageWidth = pageHeight = 0;
++ }
++}
+
+- 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];
++void TextPage::clear() {
++ pageWidth = pageHeight = 0;
++ charPos = 0;
++ curFont = NULL;
++ curFontSize = 0;
++ curRot = 0;
++ nTinyChars = 0;
++ gfree(actualText);
++ actualText = NULL;
++ actualTextLen = 0;
++ actualTextNBytes = 0;
++ 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) {
++ GfxFont *gfxFont;
++ double *fm;
++ char *name;
++ int code, mCode, letterCode, anyCode;
++ double w;
++ double m[4], m2[4];
++ int i;
++
++ // get the font info object
++ curFont = NULL;
++ for (i = 0; i < fonts->getLength(); ++i) {
++ curFont = (TextFontInfo *)fonts->get(i);
++ if (curFont->matches(state)) {
+ break;
+ }
+- base = line->base;
++ curFont = NULL;
++ }
++ if (!curFont) {
++ curFont = new TextFontInfo(state);
++ fonts->append(curFont);
++ }
++
++ // adjust the font size
++ gfxFont = state->getFont();
++ curFontSize = state->getTransformedFontSize();
++ if (gfxFont && gfxFont->getType() == fontType3) {
++ // This is a hack which makes it possible to deal with some Type 3
++ // fonts. The problem is that it's impossible to know what the
++ // base coordinate system used in the font is without actually
++ // rendering the font. This code tries to guess by looking at the
++ // width of the character 'm' (which breaks if the font is a
++ // subset that doesn't contain 'm').
++ mCode = letterCode = anyCode = -1;
++ for (code = 0; code < 256; ++code) {
++ name = ((Gfx8BitFont *)gfxFont)->getCharName(code);
++ if (name && name[0] == 'm' && name[1] == '\0') {
++ mCode = code;
++ }
++ if (letterCode < 0 && name && name[1] == '\0' &&
++ ((name[0] >= 'A' && name[0] <= 'Z') ||
++ (name[0] >= 'a' && name[0] <= 'z'))) {
++ letterCode = code;
++ }
++ if (anyCode < 0 && name &&
++ ((Gfx8BitFont *)gfxFont)->getWidth(code) > 0) {
++ anyCode = code;
++ }
++ }
++ if (mCode >= 0 &&
++ (w = ((Gfx8BitFont *)gfxFont)->getWidth(mCode)) > 0) {
++ // 0.6 is a generic average 'm' width -- yes, this is a hack
++ curFontSize *= w / 0.6;
++ } else if (letterCode >= 0 &&
++ (w = ((Gfx8BitFont *)gfxFont)->getWidth(letterCode)) > 0) {
++ // even more of a hack: 0.5 is a generic letter width
++ curFontSize *= w / 0.5;
++ } else if (anyCode >= 0 &&
++ (w = ((Gfx8BitFont *)gfxFont)->getWidth(anyCode)) > 0) {
++ // better than nothing: 0.5 is a generic character width
++ curFontSize *= w / 0.5;
++ }
++ fm = gfxFont->getFontMatrix();
++ if (fm[0] != 0) {
++ curFontSize *= fabs(fm[3] / fm[0]);
++ }
++ }
+
++ // compute the rotation
++ state->getFontTransMat(&m[0], &m[1], &m[2], &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])) {
++ curRot = (m[0] > 0 || m[3] < 0) ? 0 : 2;
+ } else {
++ curRot = (m[2] > 0) ? 1 : 3;
++ }
++}
+
+- if (line->rot == 0 && line->blk->page->primaryRot == 0) {
++void TextPage::addChar(GfxState *state, double x, double y,
++ double dx, double dy,
++ CharCode c, int nBytes, Unicode *u, int uLen) {
++ 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;
+
+- xMin = line->edge[start];
+- xMax = line->edge[start + len];
+- yMin = line->yMin;
+- yMax = line->yMax;
+- base = line->base;
++ // if we're in an ActualText span, save the position info (the
++ // ActualText chars will be added by TextPage::endActualText()).
++ if (actualText) {
++ if (!actualTextNBytes) {
++ actualTextX0 = x;
++ actualTextY0 = y;
++ }
++ actualTextX1 = x + dx;
++ actualTextY1 = y + dy;
++ actualTextNBytes += nBytes;
++ return;
++ }
+
+- } else {
++ // subtract char and word spacing from the dx,dy values
++ sp = state->getCharSpace();
++ if (c == (CharCode)0x20) {
++ sp += state->getWordSpace();
++ }
++ state->textTransformDelta(sp * state->getHorizScaling(), 0, &dx2, &dy2);
++ dx -= dx2;
++ dy -= dy2;
++ state->transformDelta(dx, dy, &w1, &h1);
+
+- blk = line->blk;
+- d0 = line->edge[start];
+- d1 = line->edge[start + len];
+- d2 = d3 = d4 = 0; // make gcc happy
++ // throw away chars that aren't inside the page bounds
++ // (and also do a sanity check on the character size)
++ state->transform(x, y, &x1, &y1);
++ if (x1 + w1 < 0 || x1 > pageWidth ||
++ y1 + h1 < 0 || y1 > pageHeight ||
++ w1 > pageWidth || h1 > pageHeight) {
++ charPos += nBytes;
++ return;
++ }
+
+- 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;
++ // check the tiny chars limit
++ if (!globalParams->getTextKeepTinyChars() &&
++ fabs(w1) < 3 && fabs(h1) < 3) {
++ if (++nTinyChars > 50000) {
++ charPos += nBytes;
++ return;
++ }
++ }
++
++ // skip space characters
++ if (uLen == 1 && u[0] == (Unicode)0x20) {
++ charPos += nBytes;
++ return;
++ }
++
++ // 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;
++ }
++ }
++
++ // add the characters
++ if (uLen > 0) {
++
++ // 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;
++ }
+
+- switch (line->blk->page->primaryRot) {
++ w1 /= uLen;
++ h1 /= uLen;
++ ascent = curFont->ascent * curFontSize;
++ descent = curFont->descent * curFontSize;
++ for (i = 0; i < uLen; ++i) {
++ x2 = x1 + i * w1;
++ y2 = y1 + i * h1;
++ switch (curRot) {
+ 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);
++ default:
++ xMin = x2;
++ xMax = x2 + w1;
++ yMin = y2 - ascent;
++ yMax = y2 - descent;
+ 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);
++ xMin = x2 + descent;
++ xMax = x2 + ascent;
++ yMin = y2;
++ yMax = y2 + h1;
+ 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);
++ xMin = x2 + w1;
++ xMax = x2;
++ yMin = y2 + descent;
++ yMax = y2 + ascent;
+ 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);
++ 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;
+ }
+
+-int TextLineFrag::cmpYXPrimaryRot(const void *p1, const void *p2) {
+- TextLineFrag *frag1 = (TextLineFrag *)p1;
+- TextLineFrag *frag2 = (TextLineFrag *)p2;
+- double cmp;
++void TextPage::incCharCount(int nChars) {
++ charPos += nChars;
++}
+
+- 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;
++void TextPage::beginActualText(GfxState *state, Unicode *u, int uLen) {
++ if (actualText) {
++ gfree(actualText);
+ }
+- return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
++ actualText = (Unicode *)gmallocn(uLen, sizeof(Unicode));
++ memcpy(actualText, u, uLen * sizeof(Unicode));
++ actualTextLen = uLen;
++ actualTextNBytes = 0;
+ }
+
+-int TextLineFrag::cmpYXLineRot(const void *p1, const void *p2) {
+- TextLineFrag *frag1 = (TextLineFrag *)p1;
+- TextLineFrag *frag2 = (TextLineFrag *)p2;
+- double cmp;
++void TextPage::endActualText(GfxState *state) {
++ Unicode *u;
+
+- 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;
++ u = actualText;
++ actualText = NULL; // so we can call TextPage::addChar()
++ if (actualTextNBytes) {
++ // now that we have the position info for all of the text inside
++ // the marked content span, we feed the "ActualText" back through
++ // addChar()
++ addChar(state, actualTextX0, actualTextY0,
++ actualTextX1 - actualTextX0, actualTextY1 - actualTextY0,
++ 0, actualTextNBytes, u, actualTextLen);
+ }
+- return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
++ gfree(u);
++ actualText = NULL;
++ actualTextLen = 0;
++ actualTextNBytes = gFalse;
+ }
+
+-int TextLineFrag::cmpXYLineRot(const void *p1, const void *p2) {
+- TextLineFrag *frag1 = (TextLineFrag *)p1;
+- TextLineFrag *frag2 = (TextLineFrag *)p2;
+- double cmp;
++void TextPage::addUnderline(double x0, double y0, double x1, double y1) {
++ underlines->append(new TextUnderline(x0, y0, x1, y1));
++}
+
+- cmp = 0; // make gcc happy
+- switch (frag1->line->rot) {
+- case 0:
+- if ((cmp = frag1->xMin - frag2->xMin) == 0) {
+- cmp = frag1->yMin - frag2->yMin;
+- }
++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 1:
+- if ((cmp = frag1->yMin - frag2->yMin) == 0) {
+- cmp = frag2->xMax - frag1->xMax;
+- }
++ case eolDOS:
++ eolLen = uMap->mapUnicode(0x0d, eol, sizeof(eol));
++ eolLen += uMap->mapUnicode(0x0a, eol + eolLen, sizeof(eol) - eolLen);
+ break;
+- case 2:
+- if ((cmp = frag2->xMax - frag1->xMax) == 0) {
+- cmp = frag2->yMin - frag1->yMin;
+- }
++ case eolMac:
++ eolLen = uMap->mapUnicode(0x0d, eol, sizeof(eol));
+ break;
+- case 3:
+- if ((cmp = frag2->yMax - frag1->yMax) == 0) {
+- cmp = frag1->xMax - frag2->xMax;
+- }
++ }
++ eopLen = uMap->mapUnicode(0x0c, eop, sizeof(eop));
++ pageBreaks = globalParams->getTextPageBreaks();
++
++ 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;
+ }
+- return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
++
++ // end of page
++ if (pageBreaks) {
++ (*outputFunc)(outputStream, eop, eopLen);
++ }
++
++ uMap->decRefCnt();
+ }
+
+-int TextLineFrag::cmpXYColumnPrimaryRot(const void *p1, const void *p2) {
+- TextLineFrag *frag1 = (TextLineFrag *)p1;
+- TextLineFrag *frag2 = (TextLineFrag *)p2;
+- double cmp;
++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;
+
+- // 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;
++ 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 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;
++ 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);
+ }
+- return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
++ (*outputFunc)(outputStream, eol, eolLen);
+ }
+
+- // otherwise, compare starting column
+- return frag1->col - frag2->col;
++ deleteGList(columns, TextColumn);
+ }
+
+-//------------------------------------------------------------------------
+-// TextBlock
+-//------------------------------------------------------------------------
++GList *TextPage::makeColumns() {
++ TextBlock *tree;
++ GList *columns;
+
+-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;
++ tree = splitChars(chars);
++ if (!tree) {
++ // no text
++ return new GList();
++ }
++ columns = buildColumns(tree);
++ delete tree;
++ if (control.html) {
++ generateUnderlinesAndLinks(columns);
++ }
++ return columns;
+ }
+
+-TextBlock::~TextBlock() {
++// 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;
++ GList *columns;
++ GBool primaryLR;
++ int ph, colIdx, parIdx, lineIdx, rot, y, i;
+
+- delete pool;
+- while (lines) {
+- line = lines;
+- lines = lines->next;
+- delete line;
++#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
++
++ 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;
++ }
++ if (parIdx + 1 < col->paragraphs->getLength()) {
++ ++y;
++ }
++ }
+ }
++
++ for (i = 0; i < ph; ++i) {
++ if (out[i]) {
++ (*outputFunc)(outputStream, out[i]->getCString(), out[i]->getLength());
++ delete out[i];
++ }
++ (*outputFunc)(outputStream, eol, eolLen);
++ }
++
++ gfree(out);
++ gfree(outLen);
++
++ deleteGList(columns, TextColumn);
+ }
+
+-void TextBlock::addWord(TextWord *word) {
+- pool->addWord(word);
+- if (xMin > xMax) {
+- xMin = word->xMin;
+- xMax = word->xMax;
+- yMin = word->yMin;
+- yMax = word->yMax;
++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 {
+- if (word->xMin < xMin) {
+- xMin = word->xMin;
++ // 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;
++ }
++ }
++ }
+ }
+- if (word->xMax > xMax) {
+- xMax = word->xMax;
++ }
++
++ // 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;
++ }
+ }
+- if (word->yMin < yMin) {
+- yMin = word->yMin;
++ }
++
++ // 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;
+ }
+- if (word->yMax > yMax) {
+- yMax = word->yMax;
++
++ // 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 {
++ 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 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;
++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;
+
+- // 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;
+- }
++ 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);
+ }
+- if (found) {
+- break;
++ 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);
+ }
+- }
+- if (found) {
+ break;
+ }
+ }
+- if (found) {
+- if (word1) {
+- word1->next = word2->next;
+- } else {
+- pool->setPool(idx1, word2->next);
+- }
+- delete word2;
+- } else {
+- word0 = word0->next;
+- }
++ } else {
++ s->append(eol, eolLen);
++ }
++
++ if (s->getLength() > 1000) {
++ (*outputFunc)(outputStream, s->getCString(), s->getLength());
++ s->clear();
+ }
+ }
+
+- // build the lines
+- curLine = NULL;
+- poolMinBaseIdx = pool->minBaseIdx;
+- charCount = 0;
+- nLines = 0;
+- while (1) {
++ if (s->getLength() > 0) {
++ (*outputFunc)(outputStream, s->getCString(), s->getLength());
++ }
++ delete s;
++}
+
+- // find the first non-empty line in the pool
+- for (;
+- poolMinBaseIdx <= pool->maxBaseIdx && !pool->getPool(poolMinBaseIdx);
+- ++poolMinBaseIdx) ;
+- if (poolMinBaseIdx > pool->maxBaseIdx) {
+- break;
+- }
++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;
+
+- // 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;
+- }
+- }
++ if (uMap->isUnicode()) {
+
+- // 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;
+- }
++ 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;
+ }
+ }
+- 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];
++
++ // 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);
+ }
+- if (col2 > col1) {
+- col1 = col2;
++ 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;
+ }
+ }
+- for (k = 0; k <= line0->len; ++k) {
+- line0->col[k] += col1;
+- }
+- if (line0->col[line0->len] > nColumns) {
+- nColumns = line0->col[line0->len];
+- }
++ s->append(popdf, popdfLen);
++ }
++
++ } else {
++ for (i = 0; i < len; ++i) {
++ n = uMap->mapUnicode(text[i], buf, sizeof(buf));
++ s->append(buf, n);
+ }
+ }
+- gfree(lineArray);
+ }
+
+-void TextBlock::updatePriMinMax(TextBlock *blk) {
+- double newPriMin, newPriMax;
+- GBool gotPriMin, gotPriMax;
++//------------------------------------------------------------------------
++// TextPage: layout analysis
++//------------------------------------------------------------------------
+
+- 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;
++// 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;
+ }
+ }
+-}
+-
+-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) {
++ // rotate
++ switch (rot) {
+ case 0:
+- if ((cmp = blk1->xMin - blk2->xMin) == 0) {
+- cmp = blk1->yMin - blk2->yMin;
+- }
++ default:
+ break;
+ case 1:
+- if ((cmp = blk1->yMin - blk2->yMin) == 0) {
+- cmp = blk2->xMax - blk1->xMax;
+- }
++ 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:
+- if ((cmp = blk2->xMax - blk1->xMax) == 0) {
+- cmp = blk2->yMin - blk1->yMin;
++ 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:
+- if ((cmp = blk2->yMax - blk1->yMax) == 0) {
+- cmp = blk1->xMax - blk2->xMax;
+- }
++ 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 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;
++ 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;
+
+- cmp = 0; // make gcc happy
+- switch (blk1->page->primaryRot) {
++ switch (rot) {
+ case 0:
+- if ((cmp = blk1->yMin - blk2->yMin) == 0) {
+- cmp = blk1->xMin - blk2->xMin;
+- }
++ default:
+ break;
+ case 1:
+- if ((cmp = blk2->xMax - blk1->xMax) == 0) {
+- cmp = blk1->yMin - blk2->yMin;
++ 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:
+- if ((cmp = blk2->yMin - blk1->yMin) == 0) {
+- cmp = blk2->xMax - blk1->xMax;
++ 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:
+- if ((cmp = blk1->xMax - blk2->xMax) == 0) {
+- cmp = blk2->yMax - blk1->yMax;
++ 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;
+ }
+- return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
+ }
+
+-int TextBlock::primaryCmp(TextBlock *blk) {
+- double cmp;
++// Undo the coordinate transform performed by rotateChars().
++void TextPage::unrotateChars(GList *charsA, int rot) {
++ TextChar *ch;
++ double xMin, yMin, xMax, yMax, t;
++ int i;
+
+- cmp = 0; // make gcc happy
+ switch (rot) {
+ case 0:
+- cmp = xMin - blk->xMin;
++ default:
++ // no transform
+ break;
+ case 1:
+- cmp = yMin - blk->yMin;
++ 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:
+- cmp = blk->xMax - xMax;
++ 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:
+- cmp = blk->yMax - yMax;
++ 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;
+ }
+- return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
+ }
+
+-double TextBlock::secondaryDelta(TextBlock *blk) {
+- double delta;
++// 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;
+
+- delta = 0; // make gcc happy
+ switch (rot) {
+ case 0:
+- delta = blk->yMin - yMax;
+- break;
+- case 1:
+- delta = xMin - blk->xMax;
+- break;
+- case 2:
+- delta = yMin - blk->yMax;
+- break;
+- case 3:
+- delta = blk->xMin - xMax;
+- break;
+- }
+- return delta;
+-}
+-
+-GBool TextBlock::isBelow(TextBlock *blk) {
+- GBool below;
+-
+- below = gFalse; // make gcc happy
+- switch (page->primaryRot) {
+- case 0:
+- below = xMin >= blk->priMin && xMax <= blk->priMax &&
+- yMin > blk->yMin;
++ default:
++ // no transform
+ break;
+ case 1:
+- below = yMin >= blk->priMin && yMax <= blk->priMax &&
+- xMax < blk->xMax;
++ 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;
++ }
++ }
++ }
++ }
+ break;
+ case 2:
+- below = xMin >= blk->priMin && xMax <= blk->priMax &&
+- yMax < blk->yMax;
++ 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];
++ }
++ }
++ }
++ }
++ }
+ break;
+ case 3:
+- below = yMin >= blk->priMin && yMax <= blk->priMax &&
+- xMin > blk->xMin;
++ 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;
+ }
+-
+- return below;
+-}
+-
+-//------------------------------------------------------------------------
+-// TextFlow
+-//------------------------------------------------------------------------
+-
+-TextFlow::TextFlow(TextPage *pageA, TextBlock *blk) {
+- page = pageA;
+- xMin = blk->xMin;
+- xMax = blk->xMax;
+- yMin = blk->yMin;
+- yMax = blk->yMax;
+- priMin = blk->priMin;
+- priMax = blk->priMax;
+- blocks = lastBlk = blk;
+- next = NULL;
+-}
+-
+-TextFlow::~TextFlow() {
+- TextBlock *blk;
+-
+- while (blocks) {
+- blk = blocks;
+- blocks = blocks->next;
+- delete blk;
+- }
+-}
+-
+-void TextFlow::addBlock(TextBlock *blk) {
+- if (lastBlk) {
+- lastBlk->next = blk;
+- } else {
+- blocks = blk;
+- }
+- lastBlk = blk;
+- if (blk->xMin < xMin) {
+- xMin = blk->xMin;
+- }
+- if (blk->xMax > xMax) {
+- xMax = blk->xMax;
+- }
+- if (blk->yMin < yMin) {
+- yMin = blk->yMin;
+- }
+- if (blk->yMax > yMax) {
+- yMax = blk->yMax;
+- }
+ }
+
+-GBool TextFlow::blockFits(TextBlock *blk, TextBlock *prevBlk) {
+- GBool fits;
+-
+- // lower blocks must use smaller fonts
+- if (blk->lines->words->fontSize > lastBlk->lines->words->fontSize) {
+- return gFalse;
+- }
++void TextPage::unrotateWords(GList *words, int rot) {
++ TextWord *word;
++ double xMin, yMin, xMax, yMax;
++ int i, j;
+
+- fits = gFalse; // make gcc happy
+- switch (page->primaryRot) {
++ switch (rot) {
+ case 0:
+- fits = blk->xMin >= priMin && blk->xMax <= priMax;
++ default:
++ // no transform
+ break;
+ case 1:
+- fits = blk->yMin >= priMin && blk->yMax <= priMax;
++ 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:
+- fits = blk->xMin >= priMin && blk->xMax <= priMax;
++ 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:
+- fits = blk->yMin >= priMin && blk->yMax <= priMax;
++ 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;
+ }
+- 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();
++// 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;
+
+- if (text->rawOrder) {
+- for (word = text->rawWords; word; word = word->next) {
+- words->append(word);
+- }
++ 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;
++}
++
++// 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;
+
+- } 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;
+- }
++ 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 (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;
+ }
+ }
+ }
+- 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;
+- }
++ } 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;
++ }
++ 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;
+ }
+ }
+ }
+- 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);
++// 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;
++
++ // 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;
+ }
+-}
+-
+-TextWordList::~TextWordList() {
+- delete words;
+-}
+-
+-int TextWordList::getLength() {
+- return words->getLength();
+-}
+
+-TextWord *TextWordList::get(int idx) {
+- if (idx < 0 || idx >= words->getLength()) {
++ // if the page contains no (unclipped) text, just leave an empty
++ // column list
++ if (!tree[0]) {
++ delete clippedChars;
+ return NULL;
+ }
+- return (TextWord *)words->get(idx);
+-}
+-
+-#endif // TEXTOUT_WORD_LIST
+-
+-//------------------------------------------------------------------------
+-// TextPage
+-//------------------------------------------------------------------------
+-
+-TextPage::TextPage(GBool rawOrderA) {
+- int rot;
+
+- rawOrder = rawOrderA;
+- curWord = NULL;
+- charPos = 0;
+- curFont = NULL;
+- curFontSize = 0;
+- nest = 0;
+- nTinyChars = 0;
+- lastCharOverlap = gFalse;
+- actualText = NULL;
+- actualTextLen = 0;
+- actualTextNBytes = 0;
+- if (!rawOrder) {
+- for (rot = 0; rot < 4; ++rot) {
+- pools[rot] = new TextPool();
+- }
++ // 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;
+ }
+- flows = NULL;
+- blocks = NULL;
+- rawWords = NULL;
+- rawLastWord = NULL;
+- fonts = new GList();
+- lastFindXMin = lastFindYMin = 0;
+- haveLastFind = gFalse;
+- underlines = new GList();
+- links = new GList();
+-}
+-
+-TextPage::~TextPage() {
+- int rot;
+
+- clear();
+- if (!rawOrder) {
+- for (rot = 0; rot < 4; ++rot) {
+- delete pools[rot];
++ // 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;
+ }
+ }
+- delete fonts;
+- deleteGList(underlines, TextUnderline);
+- deleteGList(links, TextLink);
+-}
+
+-void TextPage::startPage(GfxState *state) {
+- clear();
+- if (state) {
+- pageWidth = state->getPageWidth();
+- pageHeight = state->getPageHeight();
+- } else {
+- pageWidth = pageHeight = 0;
++ if (clippedChars->getLength()) {
++ insertClippedChars(clippedChars, tree[0]);
+ }
+-}
++ delete clippedChars;
+
+-void TextPage::endPage() {
+- if (curWord) {
+- endWord();
+- }
+-}
++#if 0 //~debug
++ dumpTree(tree[0]);
++#endif
+
+-void TextPage::clear() {
+- int rot;
+- TextFlow *flow;
+- TextWord *word;
++ return tree[0];
++}
+
+- if (curWord) {
+- delete curWord;
+- curWord = NULL;
+- }
+- gfree(actualText);
+- if (rawOrder) {
+- while (rawWords) {
+- word = rawWords;
+- rawWords = rawWords->next;
+- delete word;
++// 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;
+ }
+- } else {
+- for (rot = 0; rot < 4; ++rot) {
+- delete pools[rot];
++ for (y = yMinI2; y <= yMaxI2; ++y) {
++ ++horizProfile[y - yMinI];
+ }
+- while (flows) {
+- flow = flows;
+- flows = flows->next;
+- delete flow;
++ for (x = xMinI2; x <= xMaxI2; ++x) {
++ ++vertProfile[x - xMinI];
+ }
+- gfree(blocks);
+ }
+- deleteGList(fonts, TextFontInfo);
+- deleteGList(underlines, TextUnderline);
+- deleteGList(links, TextLink);
+
+- curWord = NULL;
+- charPos = 0;
+- curFont = NULL;
+- curFontSize = 0;
+- nest = 0;
+- nTinyChars = 0;
+- actualText = NULL;
+- actualTextLen = 0;
+- actualTextNBytes = 0;
+- if (!rawOrder) {
+- for (rot = 0; rot < 4; ++rot) {
+- pools[rot] = new TextPool();
++ //----- 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;
++ }
+ }
+ }
+- flows = NULL;
+- blocks = NULL;
+- rawWords = NULL;
+- rawLastWord = NULL;
+- fonts = new GList();
+- underlines = new GList();
+- links = new GList();
+-}
+-
+-void TextPage::updateFont(GfxState *state) {
+- GfxFont *gfxFont;
+- double *fm;
+- char *name;
+- int code, mCode, letterCode, anyCode;
+- double w;
+- int i;
+-
+- // get the font info object
+- curFont = NULL;
+- for (i = 0; i < fonts->getLength(); ++i) {
+- curFont = (TextFontInfo *)fonts->get(i);
+- if (curFont->matches(state)) {
+- break;
++ 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;
++ }
+ }
+- curFont = NULL;
+ }
+- if (!curFont) {
+- curFont = new TextFontInfo(state);
+- fonts->append(curFont);
++ horizGapSize2 = horizGapSize - splitGapSlack * avgFontSize / splitPrecision;
++ if (horizGapSize2 < 0.99) {
++ horizGapSize2 = 0.99;
++ }
++ vertGapSize2 = vertGapSize - splitGapSlack * avgFontSize / splitPrecision;
++ if (vertGapSize2 < 0.99) {
++ vertGapSize2 = 0.99;
+ }
+
+- // adjust the font size
+- gfxFont = state->getFont();
+- curFontSize = state->getTransformedFontSize();
+- if (gfxFont && gfxFont->getType() == fontType3) {
+- // This is a hack which makes it possible to deal with some Type 3
+- // fonts. The problem is that it's impossible to know what the
+- // base coordinate system used in the font is without actually
+- // rendering the font. This code tries to guess by looking at the
+- // width of the character 'm' (which breaks if the font is a
+- // subset that doesn't contain 'm').
+- mCode = letterCode = anyCode = -1;
+- for (code = 0; code < 256; ++code) {
+- name = ((Gfx8BitFont *)gfxFont)->getCharName(code);
+- if (name && name[0] == 'm' && name[1] == '\0') {
+- mCode = code;
+- }
+- if (letterCode < 0 && name && name[1] == '\0' &&
+- ((name[0] >= 'A' && name[0] <= 'Z') ||
+- (name[0] >= 'a' && name[0] <= 'z'))) {
+- letterCode = code;
+- }
+- if (anyCode < 0 && name &&
+- ((Gfx8BitFont *)gfxFont)->getWidth(code) > 0) {
+- anyCode = code;
++ //----- 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;
++ }
++ prev = y;
+ }
+ }
+- if (mCode >= 0 &&
+- (w = ((Gfx8BitFont *)gfxFont)->getWidth(mCode)) > 0) {
+- // 0.6 is a generic average 'm' width -- yes, this is a hack
+- curFontSize *= w / 0.6;
+- } else if (letterCode >= 0 &&
+- (w = ((Gfx8BitFont *)gfxFont)->getWidth(letterCode)) > 0) {
+- // even more of a hack: 0.5 is a generic letter width
+- curFontSize *= w / 0.5;
+- } else if (anyCode >= 0 &&
+- (w = ((Gfx8BitFont *)gfxFont)->getWidth(anyCode)) > 0) {
+- // better than nothing: 0.5 is a generic character width
+- curFontSize *= w / 0.5;
+- }
+- fm = gfxFont->getFontMatrix();
+- if (fm[0] != 0) {
+- curFontSize *= fabs(fm[3] / fm[0]);
++ }
++ 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;
++ }
+ }
+ }
+-}
+
+-void TextPage::beginWord(GfxState *state, double x0, double y0) {
+- double *fontm;
+- double m[4], m2[4];
+- int rot;
++ //----- compute splitting parameters
+
+- // 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;
++ // 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 rotation
+- state->getFontTransMat(&m[0], &m[1], &m[2], &m[3]);
+- if (state->getFont()->getType() == fontType3) {
+- fontm = state->getFont()->getFontMatrix();
+- m2[0] = fontm[0] * m[0] + fontm[1] * m[2];
+- m2[1] = fontm[0] * m[1] + fontm[1] * m[3];
+- m2[2] = fontm[2] * m[0] + fontm[3] * m[2];
+- m2[3] = fontm[2] * m[1] + fontm[3] * m[3];
+- m[0] = m2[0];
+- m[1] = m2[1];
+- m[2] = m2[2];
+- m[3] = m2[3];
++ // 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;
++ }
+ }
+- if (fabs(m[0] * m[3]) > fabs(m[1] * m[2])) {
+- rot = (m[0] > 0 || m[3] < 0) ? 0 : 2;
++ vertGapThreshold = vertGapThreshold * avgFontSize / splitPrecision;
++
++ // compute the minimum allowed chunk width
++ if (control.mode == textOutTableLayout) {
++ minChunk = 0;
+ } else {
+- rot = (m[2] > 0) ? 1 : 3;
++ 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;
++ }
+ }
+
+- // for vertical writing mode, the lines are effectively rotated 90
+- // degrees
+- if (state->getFont()->getWMode()) {
+- rot = (rot + 1) & 3;
++ // 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;
++ }
++ }
++
++ //----- 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 {
++ chars3->append(ch);
++ }
++ }
++ blk = split(chars3, rot);
++ insertLargeChars(chars2, blk);
++ delete chars2;
++ delete chars3;
++
++ // create a leaf node
++ } else {
++ blk = new TextBlock(blkLeaf, rot);
++ for (i = 0; i < charsA->getLength(); ++i) {
++ blk->addChild((TextChar *)charsA->get(i));
++ }
+ }
+
+- curWord = new TextWord(state, rot, x0, y0, curFont, curFontSize);
++ gfree(horizProfile);
++ gfree(vertProfile);
++
++ tagBlock(blk);
++
++ return blk;
+ }
+
+-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;
++// 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;
+
+- // if we're in an ActualText span, save the position info (the
+- // ActualText chars will be added by TextPage::endActualText()).
+- if (actualText) {
+- if (!actualTextNBytes) {
+- actualTextX0 = x;
+- actualTextY0 = y;
++ 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);
+ }
+- actualTextX1 = x + dx;
+- actualTextY1 = y + dy;
+- actualTextNBytes += nBytes;
+- return;
+ }
++ return ret;
++}
+
+- // subtract char and word spacing from the dx,dy values
+- sp = state->getCharSpace();
+- if (c == (CharCode)0x20) {
+- sp += state->getWordSpace();
+- }
+- state->textTransformDelta(sp * state->getHorizScaling(), 0, &dx2, &dy2);
+- dx -= dx2;
+- dy -= dy2;
+- state->transformDelta(dx, dy, &w1, &h1);
++// 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;
+
+- // throw away chars that aren't inside the page bounds
+- // (and also do a sanity check on the character size)
+- state->transform(x, y, &x1, &y1);
+- if (x1 + w1 < 0 || x1 > pageWidth ||
+- y1 + h1 < 0 || y1 > pageHeight ||
+- w1 > pageWidth || h1 > pageHeight) {
+- charPos += nBytes;
++ 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;
++ }
++ }
++ } else {
++ blk->tag = blkTagMulticolumn;
++ }
+ return;
+ }
+
+- // check the tiny chars limit
+- if (!globalParams->getTextKeepTinyChars() &&
+- fabs(w1) < 3 && fabs(h1) < 3) {
+- if (++nTinyChars > 50000) {
+- charPos += nBytes;
+- return;
+- }
+- }
++ if (blk->type == blkLeaf) {
++ blk->tag = blkTagLine;
+
+- // break words at space character
+- if (uLen == 1 && u[0] == (Unicode)0x20) {
+- charPos += nBytes;
+- endWord();
+- return;
++ } 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;
++ }
++ }
+ }
++}
+
+- // 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;
++// 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;
++
++ //~ 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;
+ }
+- 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();
++ 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;
++ }
+ }
+- lastCharOverlap = overlap;
+- } else {
+- lastCharOverlap = gFalse;
+ }
+
+- if (uLen != 0) {
+- // start a new word if needed
+- if (!curWord) {
+- beginWord(state, x, y);
+- }
+-
+- // page rotation and/or transform matrices can cause text to be
+- // drawn in reverse order -- in this case, swap the begin/end
+- // coordinates and break text into individual chars
+- if ((curWord->rot == 0 && w1 < 0) ||
+- (curWord->rot == 1 && h1 < 0) ||
+- (curWord->rot == 2 && w1 > 0) ||
+- (curWord->rot == 3 && h1 > 0)) {
+- endWord();
+- beginWord(state, x + dx, y + dy);
+- x1 += w1;
+- y1 += h1;
+- w1 = -w1;
+- h1 = -h1;
+- }
+-
+- // add the characters to the current word
+- w1 /= uLen;
+- h1 /= uLen;
+- for (i = 0; i < uLen; ++i) {
+- curWord->addChar(state, x1 + i*w1, y1 + i*h1, w1, h1,
+- charPos, nBytes, u[i]);
++ 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);
+ }
+ }
+- charPos += nBytes;
+ }
+
+-void TextPage::incCharCount(int nChars) {
+- charPos += nChars;
+-}
++// 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;
+
+-void TextPage::beginActualText(GfxState *state, Unicode *u, int uLen) {
+- if (actualText) {
+- gfree(actualText);
++ 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);
+ }
+- actualText = (Unicode *)gmallocn(uLen, sizeof(Unicode));
+- memcpy(actualText, u, uLen * sizeof(Unicode));
+- actualTextLen = uLen;
+- actualTextNBytes = 0;
+ }
+
+-void TextPage::endActualText(GfxState *state) {
+- Unicode *u;
++// 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;
+
+- u = actualText;
+- actualText = NULL; // so we can call TextPage::addChar()
+- if (actualTextNBytes) {
+- // now that we have the position info for all of the text inside
+- // the marked content span, we feed the "ActualText" back through
+- // addChar()
+- addChar(state, actualTextX0, actualTextY0,
+- actualTextX1 - actualTextX0, actualTextY1 - actualTextY0,
+- 0, actualTextNBytes, u, actualTextLen);
+- }
+- gfree(u);
+- actualText = NULL;
+- actualTextLen = 0;
+- actualTextNBytes = gFalse;
+-}
++ //~ this currently works only for characters in the primary rotation
+
+-void TextPage::endWord() {
+- // This check is needed because Type 3 characters can contain
+- // text-drawing operations (when TextPage is being used via
+- // {X,Win}SplashOutputDev rather than TextOutputDev).
+- if (nest > 0) {
+- --nest;
+- return;
+- }
++ //~ 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 (curWord) {
+- addWord(curWord);
+- curWord = NULL;
++ 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;
++ }
++ }
++ } else {
++ insertLargeCharInLeaf(ch, (TextBlock *)blk->children->get(0));
++ blk->updateBounds(0);
+ }
+ }
+
+-void TextPage::addWord(TextWord *word) {
+- // throw away zero-length words -- they don't have valid xMin/xMax
+- // values, and they're useless anyway
+- if (word->len == 0) {
+- delete word;
+- return;
+- }
++// Merge blk (rot != 0) into primaryTree (rot == 0).
++void TextPage::insertIntoTree(TextBlock *blk, TextBlock *primaryTree) {
++ TextBlock *child;
+
+- if (rawOrder) {
+- if (rawLastWord) {
+- rawLastWord->next = word;
+- } else {
+- rawWords = word;
++ // 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);
+ }
+- rawLastWord = word;
++ delete blk;
+ } else {
+- pools[word->rot]->addWord(word);
++ insertColumnIntoTree(blk, primaryTree);
+ }
+ }
+
+-void TextPage::addUnderline(double x0, double y0, double x1, double y1) {
+- underlines->append(new TextUnderline(x0, y0, x1, y1));
+-}
+-
+-void TextPage::addLink(int xMin, int yMin, int xMax, int yMax, Link *link) {
+- links->append(new TextLink(xMin, yMin, xMax, yMax, link));
+-}
++// 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;
+
+-void TextPage::coalesce(GBool physLayout, double fixedPitch, GBool doHTML) {
+- UnicodeMap *uMap;
+- TextPool *pool;
+- TextWord *word0, *word1, *word2;
+- TextLine *line;
+- TextBlock *blkList, *blkStack, *blk, *lastBlk, *blk0, *blk1;
+- TextBlock **blkArray;
+- TextFlow *flow, *lastFlow;
+- TextUnderline *underline;
+- TextLink *link;
+- int rot, poolMinBaseIdx, baseIdx, startBaseIdx, endBaseIdx;
+- double minBase, maxBase, newMinBase, newMaxBase;
+- double fontSize, colSpace1, colSpace2, lineSpace, intraLineSpace, blkSpace;
+- GBool found;
+- int count[4];
+- int lrCount;
+- int firstBlkIdx, nBlocksLeft;
+- int col1, col2;
+- int i, j, n;
+-
+- if (rawOrder) {
+- primaryRot = 0;
+- primaryLR = gTrue;
+- return;
++ 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;
++ }
+ }
+
+- 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);
++ 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;
+ }
+- printf("'\n");
+ }
+ }
++ } 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;
+ }
+- printf("\n");
+-#endif
+-
+-#if 0 //~ for debugging
+- for (i = 0; i < underlines->getLength(); ++i) {
+- underline = (TextUnderline *)underlines->get(i);
+- printf("underline: x=%g..%g y=%g..%g horiz=%d\n",
+- underline->x0, underline->x1, underline->y0, underline->y1,
+- underline->horiz);
+- }
+-#endif
++ tree->children->insert(i, column);
++ tree->tag = blkTagMulticolumn;
++}
+
+- if (doHTML) {
++// Insert clipped characters back into the TextBlock tree.
++void TextPage::insertClippedChars(GList *clippedChars, TextBlock *tree) {
++ TextChar *ch, *ch2;
++ TextBlock *leaf;
++ double y;
++ int i;
+
+- //----- 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;
+- }
+- }
+- }
+- }
++ //~ this currently works only for characters in the primary rotation
+
+- // 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;
+- }
+- }
+- }
+- }
++ 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 {
+- // rot = 1
+- if (pools[1]->minBaseIdx <= pools[1]->maxBaseIdx) {
+- startBaseIdx = pools[1]->getBaseIdx(underline->x0 - maxUnderlineGap);
+- endBaseIdx = pools[1]->getBaseIdx(underline->x0 - minUnderlineGap);
+- for (j = startBaseIdx; j <= endBaseIdx; ++j) {
+- for (word0 = pools[1]->getPool(j); word0; word0 = word0->next) {
+- if (underline->y0 < word0->yMin + underlineSlack &&
+- word0->yMax - underlineSlack < underline->y1) {
+- word0->underlined = gTrue;
+- }
+- }
+- }
+- }
+-
+- // rot = 3
+- if (pools[3]->minBaseIdx <= pools[3]->maxBaseIdx) {
+- startBaseIdx = pools[3]->getBaseIdx(underline->x0 + minUnderlineGap);
+- endBaseIdx = pools[3]->getBaseIdx(underline->x0 + maxUnderlineGap);
+- for (j = startBaseIdx; j <= endBaseIdx; ++j) {
+- for (word0 = pools[3]->getPool(j); word0; word0 = word0->next) {
+- if (underline->y0 < word0->yMin + underlineSlack &&
+- word0->yMax - underlineSlack < underline->y1) {
+- word0->underlined = gTrue;
+- }
+- }
+- }
+- }
++ ++i;
+ }
+ }
++ }
++}
+
+- //----- handle links
+- for (i = 0; i < links->getLength(); ++i) {
+- link = (TextLink *)links->get(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;
+
+- // 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;
+- }
+- }
+- }
+- }
++ //~ this currently works only for characters in the primary rotation
+
+- // 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;
+- }
+- }
+- }
++ 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;
+ }
+-
+- // 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;
+- }
+- }
+- }
++ }
++ } else {
++ for (i = 0; i < tree->children->getLength(); ++i) {
++ child = (TextBlock *)tree->children->get(i);
++ if ((ret = findClippedCharLeaf(ch, child))) {
++ return ret;
+ }
++ }
++ }
++ return NULL;
++}
+
+- // 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;
+- }
+- }
+- }
+- }
++// 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;
++
++ 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);
+ }
++ break;
+ }
++}
+
+- //----- assemble the blocks
++TextColumn *TextPage::buildColumn(TextBlock *blk) {
++ GList *lines, *parLines;
++ GList *paragraphs;
++ TextLine *line0, *line1;
++ double spaceThresh, indent0, indent1, fontSize0, fontSize1;
++ int i;
+
+- //~ add an outer loop for writing mode (vertical text)
++ lines = new GList();
++ buildLines(blk, lines);
+
+- // 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;
+- }
++ spaceThresh = paragraphSpacingThreshold * getAverageLineSpacing(lines);
+
+- // 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;
+- }
+- }
++ //~ could look for bulleted lists here: look for the case where
++ //~ all out-dented lines start with the same char
+
+- // 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;
++ // build the paragraphs
++ paragraphs = new GList();
++ i = 0;
++ while (i < lines->getLength()) {
+
+- // 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;
+- }
++ // 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;
+ }
+- }
+- 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 (fabs(fontSize0 - fontSize1) > paragraphFontSizeDelta) {
++ break;
+ }
++ if (getLineSpacing((TextLine *)lines->get(i - 1), line1)
++ > spaceThresh) {
++ break;
++ }
++ parLines->append(line1);
+ }
+
+- // only check for outlying words (the next two chunks of code)
+- // if we didn't find anything else
+- if (found) {
+- continue;
+- }
+-
+- // scan down the left side of the block, looking for words
+- // that are near (but not overlapping) the block; if there are
+- // three or fewer, add them to the block
+- n = 0;
+- for (baseIdx = pool->getBaseIdx(minBase - intraLineSpace);
+- baseIdx <= pool->getBaseIdx(maxBase + intraLineSpace);
+- ++baseIdx) {
+- word1 = pool->getPool(baseIdx);
+- while (word1) {
+- if (word1->base >= minBase - intraLineSpace &&
+- word1->base <= maxBase + intraLineSpace &&
+- ((rot == 0 || rot == 2)
+- ? (word1->xMax <= blk->xMin &&
+- word1->xMax > blk->xMin - colSpace2)
+- : (word1->yMax <= blk->yMin &&
+- word1->yMax > blk->yMin - colSpace2)) &&
+- fabs(word1->fontSize - fontSize) <
+- maxBlockFontSizeDelta3 * fontSize) {
+- ++n;
+- break;
+- }
+- word1 = word1->next;
++ // 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;
++ }
++ if (getLineSpacing((TextLine *)lines->get(i - 1), line1)
++ > spaceThresh) {
++ break;
+ }
++ parLines->append(line1);
+ }
+- 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;
+- }
+- }
++ 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);
+ }
+
+- // 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;
++ // 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 (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;
+- }
+- }
++ if (fabs(fontSize0 - fontSize1) > paragraphFontSizeDelta) {
++ break;
+ }
++ if (getLineSpacing((TextLine *)lines->get(i - 1), line1)
++ > spaceThresh) {
++ break;
++ }
++ parLines->append(line1);
+ }
++ }
++ }
+
+- } while (found);
++ paragraphs->append(new TextParagraph(parLines));
++ }
+
+- //~ need to compute the primary writing mode (horiz/vert) in
+- //~ addition to primary rotation
++ delete lines;
+
+- // coalesce the block, and add it to the list
+- blk->coalesce(uMap, fixedPitch);
+- if (lastBlk) {
+- lastBlk->next = blk;
+- } else {
+- blkList = blk;
+- }
+- lastBlk = blk;
+- count[rot] += blk->charCount;
+- ++nBlocks;
+- }
++ 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;
+
+- if (count[rot] > count[primaryRot]) {
+- primaryRot = rot;
++ 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;
+ }
+ }
++ if (n > 0) {
++ avg /= n;
++ }
++ return avg;
++}
+
+-#if 0 // for debugging
+- printf("*** rotation ***\n");
+- for (rot = 0; rot < 4; ++rot) {
+- printf(" %d: %6d\n", rot, count[rot]);
++// 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;
+ }
+- printf(" primary rot = %d\n", primaryRot);
+- printf("\n");
+-#endif
++ return sp;
++}
+
+-#if 0 // for debugging
+- printf("*** blocks ***\n");
+- for (blk = blkList; blk; blk = blk->next) {
+- printf("block: rot=%d x=%.2f..%.2f y=%.2f..%.2f\n",
+- blk->rot, blk->xMin, blk->xMax, blk->yMin, blk->yMax);
+- for (line = blk->lines; line; line = line->next) {
+- printf(" line: x=%.2f..%.2f y=%.2f..%.2f base=%.2f\n",
+- line->xMin, line->xMax, line->yMin, line->yMax, line->base);
+- for (word0 = line->words; word0; word0 = word0->next) {
+- printf(" word: x=%.2f..%.2f y=%.2f..%.2f base=%.2f fontSize=%.2f space=%d: '",
+- word0->xMin, word0->xMax, word0->yMin, word0->yMax,
+- word0->base, word0->fontSize, word0->spaceAfter);
+- for (i = 0; i < word0->len; ++i) {
+- fputc(word0->text[i] & 0xff, stdout);
+- }
+- printf("'\n");
+- }
++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;
+ }
+- 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;
+- }
+- }
++TextLine *TextPage::buildLine(TextBlock *blk) {
++ GList *charsA;
++ GList *words;
++ TextChar *ch, *ch2;
++ TextWord *word;
++ double wordSp, lineFontSize, sp;
++ GBool spaceAfter, spaceAfter2;
++ int i, j;
++
++ charsA = new GList();
++ getLineChars(blk, charsA);
++
++ wordSp = computeWordSpacingThreshold(charsA, blk->rot);
++
++ 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;
+ }
+ }
+- primaryLR = lrCount >= 0;
+
+-#if 0 // for debugging
+- printf("*** direction ***\n");
+- printf("lrCount = %d\n", lrCount);
+- printf("primaryLR = %d\n", primaryLR);
+-#endif
++ delete charsA;
+
+- //----- column assignment
++ return new TextLine(words, blk->xMin, blk->yMin, blk->xMax, blk->yMax,
++ lineFontSize);
++}
+
+- if (physLayout && fixedPitch) {
++void TextPage::getLineChars(TextBlock *blk, GList *charsA) {
++ int i;
+
+- 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);
+- break;
+- }
+- blk->col = col1;
+- for (line = blk->lines; line; line = line->next) {
+- for (j = 0; j <= line->len; ++j) {
+- line->col[j] += col1;
+- }
+- }
++ if (blk->type == blkLeaf) {
++ charsA->append(blk->children);
++ } else {
++ for (i = 0; i < blk->children->getLength(); ++i) {
++ getLineChars((TextBlock *)blk->children->get(i), charsA);
+ }
++ }
++}
+
+- } else {
++// 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;
+
+- // sort blocks into xy order for column assignment
+- blocks = (TextBlock **)gmallocn(nBlocks, sizeof(TextBlock *));
+- for (blk = blkList, i = 0; blk; blk = blk->next, ++i) {
+- blocks[i] = blk;
+- }
+- qsort(blocks, nBlocks, sizeof(TextBlock *), &TextBlock::cmpXYPrimaryRot);
+-
+- // column assignment
+- for (i = 0; i < nBlocks; ++i) {
+- blk0 = blocks[i];
+- col1 = 0;
+- for (j = 0; j < i; ++j) {
+- blk1 = blocks[j];
+- col2 = 0; // make gcc happy
+- switch (primaryRot) {
+- case 0:
+- if (blk0->xMin > blk1->xMax) {
+- col2 = blk1->col + blk1->nColumns + 3;
+- } else if (blk1->xMax == blk1->xMin) {
+- col2 = blk1->col;
+- } else {
+- col2 = blk1->col + (int)(((blk0->xMin - blk1->xMin) /
+- (blk1->xMax - blk1->xMin)) *
+- blk1->nColumns);
+- }
+- break;
+- case 1:
+- if (blk0->yMin > blk1->yMax) {
+- col2 = blk1->col + blk1->nColumns + 3;
+- } else if (blk1->yMax == blk1->yMin) {
+- col2 = blk1->col;
+- } else {
+- col2 = blk1->col + (int)(((blk0->yMin - blk1->yMin) /
+- (blk1->yMax - blk1->yMin)) *
+- blk1->nColumns);
+- }
+- break;
+- case 2:
+- if (blk0->xMax < blk1->xMin) {
+- col2 = blk1->col + blk1->nColumns + 3;
+- } else if (blk1->xMin == blk1->xMax) {
+- col2 = blk1->col;
+- } else {
+- col2 = blk1->col + (int)(((blk0->xMax - blk1->xMax) /
+- (blk1->xMin - blk1->xMax)) *
+- blk1->nColumns);
+- }
+- break;
+- case 3:
+- if (blk0->yMax < blk1->yMin) {
+- col2 = blk1->col + blk1->nColumns + 3;
+- } else if (blk1->yMin == blk1->yMax) {
+- col2 = blk1->col;
+- } else {
+- col2 = blk1->col + (int)(((blk0->yMax - blk1->yMax) /
+- (blk1->yMin - blk1->yMax)) *
+- blk1->nColumns);
+- }
+- break;
+- }
+- if (col2 > col1) {
+- col1 = col2;
+- }
+- }
+- blk0->col = col1;
+- for (line = blk0->lines; line; line = line->next) {
+- for (j = 0; j <= line->len; ++j) {
+- line->col[j] += col1;
+- }
++ 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;
+ }
+ }
++ }
++ avgFontSize /= charsA->getLength();
++ if (minSp < 0) {
++ minSp = 0;
++ }
++
++ // 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;
+ }
+
+-#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);
++ 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 (line->px + line->pw > col->pw) {
++ col->pw = line->px + line->pw;
+ }
+- printf("'\n");
+ }
++ col->ph += par->lines->getLength();
+ }
++ col->ph += col->paragraphs->getLength() - 1;
+ }
+- printf("\n");
+-#endif
+
+- //----- reading order sort
++ uMap->decRefCnt();
++}
+
+- // sort blocks into yx order (in preparation for reading order sort)
+- qsort(blocks, nBlocks, sizeof(TextBlock *), &TextBlock::cmpYXPrimaryRot);
++void TextPage::computeLinePhysWidth(TextLine *line, UnicodeMap *uMap) {
++ char buf[8];
++ int n, i;
+
+- // compute space on left and right sides of each block
+- for (i = 0; i < nBlocks; ++i) {
+- blk0 = blocks[i];
+- for (j = 0; j < nBlocks; ++j) {
+- blk1 = blocks[j];
+- if (blk1 != blk0) {
+- blk0->updatePriMinMax(blk1);
+- }
++ if (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;
++ }
+
+-#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);
++ // 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 {
++ 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;
++ }
++ } 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;
++ }
++ }
+ }
+- printf("'\n");
+ }
+ }
+ }
+- printf("\n");
+-#endif
+
+- // build the flows
+- //~ this needs to be adjusted for writing mode (vertical text)
+- //~ this also needs to account for right-to-left column ordering
+- blkArray = (TextBlock **)gmallocn(nBlocks, sizeof(TextBlock *));
+- memcpy(blkArray, blocks, nBlocks * sizeof(TextBlock *));
+- flows = lastFlow = NULL;
+- firstBlkIdx = 0;
+- nBlocksLeft = nBlocks;
+- while (nBlocksLeft > 0) {
+-
+- // find the upper-left-most block
+- for (; !blkArray[firstBlkIdx]; ++firstBlkIdx) ;
+- i = firstBlkIdx;
+- blk = blkArray[i];
+- for (j = firstBlkIdx + 1; j < nBlocks; ++j) {
+- blk1 = blkArray[j];
+- if (blk1) {
+- if (blk && blk->secondaryDelta(blk1) > 0) {
+- break;
++ // 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;
+ }
+- if (blk1->primaryCmp(blk) < 0) {
+- i = j;
+- blk = blk1;
++ } else {
++ 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;
++ }
+ }
+ }
+ }
+- blkArray[i] = NULL;
+- --nBlocksLeft;
+- blk->next = NULL;
+-
+- // create a new flow, starting with the upper-left-most block
+- flow = new TextFlow(this, blk);
+- if (lastFlow) {
+- lastFlow->next = flow;
+- } else {
+- flows = flow;
++ if (col->py + col->ph > ph) {
++ ph = col->py + col->ph;
+ }
+- lastFlow = flow;
+- fontSize = blk->lines->words->fontSize;
++ }
+
+- // push the upper-left-most block on the stack
+- blk->stackNext = NULL;
+- blkStack = blk;
+-
+- // find the other blocks in this flow
+- while (blkStack) {
+-
+- // find the upper-left-most block under (but within
+- // maxBlockSpacing of) the top block on the stack
+- blkSpace = maxBlockSpacing * blkStack->lines->words->fontSize;
+- blk = NULL;
+- i = -1;
+- for (j = firstBlkIdx; j < nBlocks; ++j) {
+- blk1 = blkArray[j];
+- if (blk1) {
+- if (blkStack->secondaryDelta(blk1) > blkSpace) {
+- break;
+- }
+- if (blk && blk->secondaryDelta(blk1) > 0) {
+- break;
+- }
+- if (blk1->isBelow(blkStack) &&
+- (!blk || blk1->primaryCmp(blk) < 0)) {
+- i = j;
+- blk = blk1;
+- }
+- }
+- }
++ return ph;
++}
+
+- // 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;
++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;
+
+- // otherwise (if there is no block under the top block or the
+- // block is not suitable), pop the stack
+- } else {
+- blkStack = blkStack->stackNext;
+- }
+- }
+- }
+- gfree(blkArray);
++ 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;
++ }
++ }
++ }
++ }
+
+-#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);
++ //----- 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 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
+@@ -3176,499 +3695,202 @@
+ yStop = *yMax;
+ }
+
+- found = gFalse;
+- xMin0 = xMax0 = yMin0 = yMax0 = 0; // make gcc happy
+- xMin1 = xMax1 = yMin1 = yMax1 = 0; // make gcc happy
+-
+- for (i = backward ? nBlocks - 1 : 0;
+- backward ? i >= 0 : i < nBlocks;
+- i += backward ? -1 : 1) {
+- blk = blocks[i];
+-
+- // check: is the block above the top limit?
+- // (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)) {
+- 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;
+- }
+-
+- for (line = blk->lines; line; line = line->next) {
+-
+- // 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)) {
+- 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)) {
+- continue;
+- }
+-
+- // convert the line to uppercase
+- m = line->len;
+- if (!caseSensitive) {
+- if (m > txtSize) {
+- txt = (Unicode *)greallocn(txt, m, sizeof(Unicode));
+- txtSize = m;
+- }
+- for (k = 0; k < m; ++k) {
+- txt[k] = unicodeToUpper(line->text[k]);
+- }
+- } else {
+- txt = line->text;
+- }
+-
+- // search each position in this line
+- j = backward ? m - len : 0;
+- p = txt + j;
+- while (backward ? j >= 0 : j <= m - len) {
+- 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;
+- }
+- }
+-
+- // found it
+- if (k == len) {
+- switch (line->rot) {
+- case 0:
+- xMin1 = line->edge[j];
+- xMax1 = line->edge[j + len];
+- yMin1 = line->yMin;
+- yMax1 = line->yMax;
+- break;
+- case 1:
+- xMin1 = line->xMin;
+- xMax1 = line->xMax;
+- yMin1 = line->edge[j];
+- yMax1 = line->edge[j + len];
+- break;
+- case 2:
+- xMin1 = line->edge[j + len];
+- xMax1 = line->edge[j];
+- yMin1 = line->yMin;
+- yMax1 = line->yMax;
+- break;
+- case 3:
+- xMin1 = line->xMin;
+- xMax1 = line->xMax;
+- yMin1 = line->edge[j + len];
+- yMax1 = line->edge[j];
+- break;
+- }
+- if (backward) {
+- if ((startAtTop ||
+- yMin1 < yStart || (yMin1 == yStart && xMin1 < xStart)) &&
+- (stopAtBottom ||
+- yMin1 > yStop || (yMin1 == yStop && xMin1 > xStop))) {
+- if (!found ||
+- yMin1 > yMin0 || (yMin1 == yMin0 && xMin1 > xMin0)) {
+- xMin0 = xMin1;
+- xMax0 = xMax1;
+- yMin0 = yMin1;
+- yMax0 = yMax1;
+- found = gTrue;
+- }
+- }
+- } else {
+- if ((startAtTop ||
+- yMin1 > yStart || (yMin1 == yStart && xMin1 > xStart)) &&
+- (stopAtBottom ||
+- yMin1 < yStop || (yMin1 == yStop && xMin1 < xStop))) {
+- if (!found ||
+- yMin1 < yMin0 || (yMin1 == yMin0 && xMin1 < xMin0)) {
+- xMin0 = xMin1;
+- xMax0 = xMax1;
+- yMin0 = yMin1;
+- yMax0 = yMax1;
+- found = gTrue;
+- }
+- }
+- }
+- }
+- }
+- if (backward) {
+- --j;
+- --p;
+- } else {
+- ++j;
+- ++p;
+- }
+- }
+- }
+- }
+-
+- if (!caseSensitive) {
+- gfree(s2);
+- gfree(txt);
+- }
+-
+- if (found) {
+- *xMin = xMin0;
+- *xMax = xMax0;
+- *yMin = yMin0;
+- *yMax = yMax0;
+- lastFindXMin = xMin0;
+- lastFindYMin = yMin0;
+- haveLastFind = gTrue;
+- return gTrue;
+- }
+-
+- return gFalse;
+-}
+-
+-GString *TextPage::getText(double xMin, double yMin,
+- double xMax, double yMax) {
+- GString *s;
+- UnicodeMap *uMap;
+- GBool isUnicode;
+- TextBlock *blk;
+- TextLine *line;
+- TextLineFrag *frags;
+- int nFrags, fragsSize;
+- TextLineFrag *frag;
+- char space[8], eol[16];
+- int spaceLen, eolLen;
+- int lastRot;
+- double x, y, delta;
+- int col, idx0, idx1, i, j;
+- GBool multiLine, oneRot;
+-
+- s = new GString();
+-
+- if (rawOrder) {
+- return s;
+- }
+-
+- // get the output encoding
+- if (!(uMap = globalParams->getTextEncoding())) {
+- return s;
+- }
+- isUnicode = uMap->isUnicode();
+- spaceLen = uMap->mapUnicode(0x20, space, sizeof(space));
+- eolLen = 0; // make gcc happy
+- switch (globalParams->getTextEOL()) {
+- case eolUnix:
+- eolLen = uMap->mapUnicode(0x0a, eol, sizeof(eol));
+- break;
+- case eolDOS:
+- eolLen = uMap->mapUnicode(0x0d, eol, sizeof(eol));
+- eolLen += uMap->mapUnicode(0x0a, eol + eolLen, sizeof(eol) - eolLen);
+- break;
+- case eolMac:
+- eolLen = uMap->mapUnicode(0x0d, eol, sizeof(eol));
+- break;
+- }
+-
+- //~ writing mode (horiz/vert)
+-
+- // collect the line fragments that are in the rectangle
+- fragsSize = 256;
+- frags = (TextLineFrag *)gmallocn(fragsSize, sizeof(TextLineFrag));
+- nFrags = 0;
+- lastRot = -1;
+- oneRot = gTrue;
+- for (i = 0; i < nBlocks; ++i) {
+- blk = blocks[i];
+- if (xMin < blk->xMax && blk->xMin < xMax &&
+- yMin < blk->yMax && blk->yMin < yMax) {
+- for (line = blk->lines; line; line = line->next) {
+- if (xMin < line->xMax && line->xMin < xMax &&
+- yMin < line->yMax && line->yMin < yMax) {
+- idx0 = idx1 = -1;
+- switch (line->rot) {
+- case 0:
+- y = 0.5 * (line->yMin + line->yMax);
+- if (yMin < y && y < yMax) {
+- j = 0;
+- while (j < line->len) {
+- if (0.5 * (line->edge[j] + line->edge[j+1]) > xMin) {
+- idx0 = j;
+- break;
+- }
+- ++j;
+- }
+- j = line->len - 1;
+- while (j >= 0) {
+- if (0.5 * (line->edge[j] + line->edge[j+1]) < xMax) {
+- idx1 = j;
+- break;
+- }
+- --j;
+- }
+- }
+- break;
+- case 1:
+- x = 0.5 * (line->xMin + line->xMax);
+- if (xMin < x && x < xMax) {
+- j = 0;
+- while (j < line->len) {
+- if (0.5 * (line->edge[j] + line->edge[j+1]) > yMin) {
+- idx0 = j;
+- break;
+- }
+- ++j;
+- }
+- j = line->len - 1;
+- while (j >= 0) {
+- if (0.5 * (line->edge[j] + line->edge[j+1]) < yMax) {
+- idx1 = j;
+- break;
+- }
+- --j;
+- }
+- }
+- break;
+- case 2:
+- y = 0.5 * (line->yMin + line->yMax);
+- if (yMin < y && y < yMax) {
+- j = 0;
+- while (j < line->len) {
+- if (0.5 * (line->edge[j] + line->edge[j+1]) < xMax) {
+- idx0 = j;
+- break;
+- }
+- ++j;
+- }
+- j = line->len - 1;
+- while (j >= 0) {
+- if (0.5 * (line->edge[j] + line->edge[j+1]) > xMin) {
+- idx1 = j;
+- break;
+- }
+- --j;
+- }
+- }
+- break;
+- case 3:
+- x = 0.5 * (line->xMin + line->xMax);
+- if (xMin < x && x < xMax) {
+- j = 0;
+- while (j < line->len) {
+- if (0.5 * (line->edge[j] + line->edge[j+1]) < yMax) {
+- idx0 = j;
+- break;
+- }
+- ++j;
+- }
+- j = line->len - 1;
+- while (j >= 0) {
+- if (0.5 * (line->edge[j] + line->edge[j+1]) > yMin) {
+- idx1 = j;
+- break;
+- }
+- --j;
+- }
+- }
+- break;
+- }
+- if (idx0 >= 0 && idx1 >= 0) {
+- if (nFrags == fragsSize) {
+- fragsSize *= 2;
+- frags = (TextLineFrag *)
+- greallocn(frags, fragsSize, sizeof(TextLineFrag));
+- }
+- frags[nFrags].init(line, idx0, idx1 - idx0 + 1);
+- ++nFrags;
+- if (lastRot >= 0 && line->rot != lastRot) {
+- oneRot = gFalse;
+- }
+- lastRot = line->rot;
+- }
+- }
+- }
+- }
+- }
+-
+- // sort the fragments and generate the string
+- if (nFrags > 0) {
+-
+- for (i = 0; i < nFrags; ++i) {
+- frags[i].computeCoords(oneRot);
+- }
+- assignColumns(frags, nFrags, oneRot);
+-
+- // if all lines in the region have the same rotation, use it;
+- // otherwise, use the page's primary rotation
+- if (oneRot) {
+- qsort(frags, nFrags, sizeof(TextLineFrag),
+- &TextLineFrag::cmpYXLineRot);
+- } else {
+- qsort(frags, nFrags, sizeof(TextLineFrag),
+- &TextLineFrag::cmpYXPrimaryRot);
+- }
+- i = 0;
+- while (i < nFrags) {
+- delta = maxIntraLineDelta * frags[i].line->words->fontSize;
+- for (j = i+1;
+- j < nFrags && fabs(frags[j].base - frags[i].base) < delta;
+- ++j) ;
+- qsort(frags + i, j - i, sizeof(TextLineFrag),
+- oneRot ? &TextLineFrag::cmpXYColumnLineRot
+- : &TextLineFrag::cmpXYColumnPrimaryRot);
+- i = j;
+- }
+-
+- col = 0;
+- multiLine = gFalse;
+- for (i = 0; i < nFrags; ++i) {
+- frag = &frags[i];
+-
+- // insert a return
+- if (frag->col < col ||
+- (i > 0 && fabs(frag->base - frags[i-1].base) >
+- maxIntraLineDelta * frags[i-1].line->words->fontSize)) {
+- s->append(eol, eolLen);
+- col = 0;
+- multiLine = gTrue;
+- }
+-
+- // column alignment
+- for (; col < frag->col; ++col) {
+- s->append(space, spaceLen);
+- }
++ found = gFalse;
++ xMin0 = xMax0 = yMin0 = yMax0 = 0; // make gcc happy
++ xMin1 = xMax1 = yMin1 = yMax1 = 0; // make gcc happy
+
+- // get the fragment text
+- col += dumpFragment(frag->line->text + frag->start, frag->len, uMap, s);
++ 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 column above the top limit?
++ if (!startAtTop && (backward ? column->yMin > yStart
++ : column->yMax < yStart)) {
++ continue;
+ }
+
+- if (multiLine) {
+- s->append(eol, eolLen);
++ // check: is the column below the bottom limit?
++ if (!stopAtBottom && (backward ? column->yMax < yStop
++ : column->yMin > yStop)) {
++ continue;
+ }
+- }
+
+- gfree(frags);
+- uMap->decRefCnt();
++ 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 paragraph above the top limit?
++ if (!startAtTop && (backward ? par->yMin > yStart
++ : par->yMax < yStart)) {
++ continue;
++ }
+
+- return s;
+-}
++ // check: is the paragraph below the bottom limit?
++ if (!stopAtBottom && (backward ? par->yMax < yStop
++ : par->yMin > yStop)) {
++ continue;
++ }
+
+-GBool TextPage::findCharRange(int pos, int length,
+- double *xMin, double *yMin,
+- double *xMax, double *yMax) {
+- TextBlock *blk;
+- TextLine *line;
+- TextWord *word;
+- double xMin0, xMax0, yMin0, yMax0;
+- double xMin1, xMax1, yMin1, yMax1;
+- GBool first;
+- int i, j0, j1;
++ 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;
++ }
+
+- if (rawOrder) {
+- return gFalse;
+- }
++ // check: is the line below the bottom limit?
++ if (!stopAtBottom && (backward ? line->yMax < yStop
++ : line->yMin > yStop)) {
++ continue;
++ }
+
+- //~ this doesn't correctly handle ranges split across multiple lines
+- //~ (the highlighted region is the bounding box of all the parts of
+- //~ the range)
+- 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;
++ // convert the line to uppercase
++ m = line->len;
++ if (!caseSensitive) {
++ if (m > txtSize) {
++ txt = (Unicode *)greallocn(txt, m, sizeof(Unicode));
++ txtSize = m;
+ }
+- if (first || xMax1 > xMax0) {
+- xMax0 = xMax1;
++ for (k = 0; k < m; ++k) {
++ txt[k] = unicodeToUpper(line->text[k]);
+ }
+- if (first || yMin1 < yMin0) {
+- yMin0 = yMin1;
++ } 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 || !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;
++ }
++ }
++
++ // found it
++ if (k == len) {
++ switch (line->rot) {
++ case 0:
++ xMin1 = line->edge[j];
++ xMax1 = line->edge[j + len];
++ yMin1 = line->yMin;
++ yMax1 = line->yMax;
++ break;
++ case 1:
++ xMin1 = line->xMin;
++ xMax1 = line->xMax;
++ yMin1 = line->edge[j];
++ yMax1 = line->edge[j + len];
++ break;
++ case 2:
++ xMin1 = line->edge[j + len];
++ xMax1 = line->edge[j];
++ yMin1 = line->yMin;
++ yMax1 = line->yMax;
++ break;
++ case 3:
++ xMin1 = line->xMin;
++ xMax1 = line->xMax;
++ yMin1 = line->edge[j + len];
++ yMax1 = line->edge[j];
++ break;
++ }
++ if (backward) {
++ if ((startAtTop ||
++ yMin1 < yStart || (yMin1 == yStart && xMin1 < xStart)) &&
++ (stopAtBottom ||
++ yMin1 > yStop || (yMin1 == yStop && xMin1 > xStop))) {
++ if (!found ||
++ yMin1 > yMin0 || (yMin1 == yMin0 && xMin1 > xMin0)) {
++ xMin0 = xMin1;
++ xMax0 = xMax1;
++ yMin0 = yMin1;
++ yMax0 = yMax1;
++ found = gTrue;
++ }
++ }
++ } else {
++ if ((startAtTop ||
++ yMin1 > yStart || (yMin1 == yStart && xMin1 > xStart)) &&
++ (stopAtBottom ||
++ yMin1 < yStop || (yMin1 == yStop && xMin1 < xStop))) {
++ if (!found ||
++ yMin1 < yMin0 || (yMin1 == yMin0 && xMin1 < xMin0)) {
++ xMin0 = xMin1;
++ xMax0 = xMax1;
++ yMin0 = yMin1;
++ yMax0 = yMax1;
++ found = gTrue;
++ }
++ }
++ }
++ }
+ }
+- if (first || yMax1 > yMax0) {
+- yMax0 = yMax1;
++ if (backward) {
++ --j;
++ --p;
++ } else {
++ ++j;
++ ++p;
+ }
+- first = gFalse;
+ }
+ }
+ }
+ }
+- if (!first) {
++
++ if (!caseSensitive) {
++ gfree(s2);
++ gfree(txt);
++ }
++
++ if (found) {
+ *xMin = xMin0;
+ *xMax = xMax0;
+ *yMin = yMin0;
+ *yMax = yMax0;
++ lastFindXMin = xMin0;
++ lastFindYMin = yMin0;
++ haveLastFind = gTrue;
+ return gTrue;
+ }
++
+ return gFalse;
+ }
+
+-void TextPage::dump(void *outputStream, TextOutputFunc outputFunc,
+- GBool physLayout) {
++GString *TextPage::getText(double xMin, double yMin,
++ double xMax, double yMax) {
+ UnicodeMap *uMap;
+- TextFlow *flow;
+- TextBlock *blk;
++ char space[8], eol[16];
++ int spaceLen, eolLen;
++ GList *chars2;
++ GString **out;
++ int *outLen;
++ 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;
++ 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;
++ return NULL;
+ }
+ spaceLen = uMap->mapUnicode(0x20, space, sizeof(space));
+ eolLen = 0; // make gcc happy
+@@ -3684,351 +3906,277 @@
+ eolLen = uMap->mapUnicode(0x0d, eol, sizeof(eol));
+ break;
+ }
+- eopLen = uMap->mapUnicode(0x0c, eop, sizeof(eop));
+- pageBreaks = globalParams->getTextPageBreaks();
+-
+- //~ writing mode (horiz/vert)
+
+- // output the page in raw (content stream) order
+- if (rawOrder) {
+-
+- for (word = rawWords; word; word = word->next) {
+- s = new GString();
+- dumpFragment(word->text, word->len, uMap, s);
+- (*outputFunc)(outputStream, s->getCString(), s->getLength());
+- delete s;
+- if (word->next &&
+- fabs(word->next->base - word->base) <
+- maxIntraLineDelta * word->fontSize &&
+- word->next->xMin >
+- word->xMax - minDupBreakOverlap * word->fontSize) {
+- if (word->next->xMin > word->xMax + minWordSpacing * word->fontSize) {
+- (*outputFunc)(outputStream, space, spaceLen);
+- }
+- } else {
+- (*outputFunc)(outputStream, eol, eolLen);
+- }
++ // 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
+
+- // 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;
++ 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;
++ }
++ if (parIdx + 1 < col->paragraphs->getLength()) {
++ ++y;
+ }
+ }
+- 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");
++ }
++
++ 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);
+ }
+- printf("\n");
+-#endif
++ }
+
+- // generate output
+- col = 0;
+- for (i = 0; i < nFrags; ++i) {
+- frag = &frags[i];
+-
+- // column alignment
+- for (; col < frag->col; ++col) {
+- (*outputFunc)(outputStream, space, spaceLen);
+- }
++ gfree(out);
++ gfree(outLen);
++ deleteGList(columns, TextColumn);
++ uMap->decRefCnt();
+
+- // 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;
++ return ret;
++}
+
+- // 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;
+- }
+- }
++GBool TextPage::findCharRange(int pos, int length,
++ double *xMin, double *yMin,
++ double *xMax, double *yMax) {
++ TextChar *ch;
++ double xMin2, yMin2, xMax2, yMax2;
++ GBool first;
++ int i;
+
+- gfree(frags);
++ //~ this doesn't correctly handle ranges split across multiple lines
++ //~ (the highlighted region is the bounding box of all the parts of
++ //~ the range)
+
+- // 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);
+- }
+- }
+- }
+- }
++ xMin2 = yMin2 = xMax2 = yMax2 = 0;
++ first = gTrue;
++ 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;
+ }
+- (*outputFunc)(outputStream, eol, eolLen);
+- (*outputFunc)(outputStream, eol, eolLen);
++ 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;
+ }
+ }
+-
+- // end of page
+- if (pageBreaks) {
+- (*outputFunc)(outputStream, eop, eopLen);
++ if (first) {
++ return gFalse;
+ }
+-
+- uMap->decRefCnt();
++ *xMin = xMin2;
++ *yMin = yMin2;
++ *xMax = xMax2;
++ *yMax = yMax2;
++ return gTrue;
+ }
+
+-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;
++TextWordList *TextPage::makeWordList() {
++ TextBlock *tree;
++ GList *columns;
++ TextColumn *col;
++ TextParagraph *par;
++ TextLine *line;
++ TextWord *word;
++ GList *words;
++ int rot, colIdx, parIdx, lineIdx, wordIdx;
++
++ rot = rotateChars(chars);
++ tree = splitChars(chars);
++ if (!tree) {
++ // no text
++ unrotateChars(chars, rot);
++ return new TextWordList(new GList());
++ }
++ columns = buildColumns(tree);
++ delete tree;
++ unrotateChars(chars, rot);
++ if (control.html) {
++ rotateUnderlinesAndLinks(rot);
++ generateUnderlinesAndLinks(columns);
++ }
++
++ 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());
+ }
+ }
+- frag0->col = col1;
+ }
++ }
+
+- // the region includes text at different rotations -- use the
+- // globally assigned column numbers, offset by the minimum column
+- // number (i.e., shift everything over to column 0)
+- } else {
+- col1 = frags[0].col;
+- for (i = 1; i < nFrags; ++i) {
+- if (frags[i].col < col1) {
+- col1 = frags[i].col;
+- }
+- }
+- for (i = 0; i < nFrags; ++i) {
+- frags[i].col -= col1;
+- }
++ 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;
+ }
+-}
+
+-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;
++ // this has to be done after sorting with cmpYX
++ unrotateColumns(columns, rot);
++ unrotateWords(words, rot);
+
+- nCols = 0;
++ deleteGList(columns, TextColumn);
+
+- if (uMap->isUnicode()) {
++ return new TextWordList(words);
++}
+
+- lreLen = uMap->mapUnicode(0x202a, lre, sizeof(lre));
+- rleLen = uMap->mapUnicode(0x202b, rle, sizeof(rle));
+- popdfLen = uMap->mapUnicode(0x202c, popdf, sizeof(popdf));
++//------------------------------------------------------------------------
++// TextPage: debug
++//------------------------------------------------------------------------
+
+- if (primaryLR) {
++#if 0 //~debug
+
+- 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;
+- }
+- }
++void TextPage::dumpChars(GList *charsA) {
++ TextChar *ch;
++ int i;
+
+- } else {
++ 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);
++ }
++}
+
+- // 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;
+- }
+- }
+- s->append(popdf, popdfLen);
++void TextPage::dumpTree(TextBlock *tree, int indent) {
++ TextChar *ch;
++ int i;
+
++ 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);
+ }
+-
+ } else {
+- for (i = 0; i < len; ++i) {
+- n = uMap->mapUnicode(text[i], buf, sizeof(buf));
+- s->append(buf, n);
+- nCols += n;
++ for (i = 0; i < tree->children->getLength(); ++i) {
++ dumpTree((TextBlock *)tree->children->get(i), indent + 2);
+ }
+ }
+-
+- return nCols;
+ }
+
+-#if TEXTOUT_WORD_LIST
+-TextWordList *TextPage::makeWordList(GBool physLayout) {
+- return new TextWordList(this, physLayout);
++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");
++ }
++ }
++ }
+ }
+-#endif
++
++#endif //~debug
+
+ //------------------------------------------------------------------------
+ // TextOutputDev
+@@ -4038,14 +4186,10 @@
+ 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 @@
+ }
+
+ // 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::endPage() {
+- text->endPage();
+- text->coalesce(physLayout, fixedPitch, doHTML);
+ if (outputStream) {
+- text->dump(outputStream, outputFunc, physLayout);
++ text->write(outputStream, outputFunc);
+ }
+ }
+
+@@ -4129,7 +4264,7 @@
+ 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 @@
+ GfxSubpath *subpath;
+ double x[2], y[2];
+
+- if (!doHTML) {
++ if (!control.html) {
+ return;
+ }
+ path = state->getPath();
+@@ -4176,7 +4311,7 @@
+ double rx0, ry0, rx1, ry1, t;
+ int i;
+
+- if (!doHTML) {
++ if (!control.html) {
+ return;
+ }
+ path = state->getPath();
+@@ -4238,7 +4373,7 @@
+ }
+
+ void TextOutputDev::eoFill(GfxState *state) {
+- if (!doHTML) {
++ if (!control.html) {
+ return;
+ }
+ fill(state);
+@@ -4248,7 +4383,7 @@
+ 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 @@
+ 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 -uNr xpdf-3.03/xpdf/TextOutputDev.h xpdf-3.04/xpdf/TextOutputDev.h
+--- xpdf-3.03/xpdf/TextOutputDev.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/TextOutputDev.h 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ 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 @@
+
+ GBool matches(GfxState *state);
+
+-#if TEXTOUT_WORD_LIST
+ // Get the font name (which may be NULL).
+ GString *getFontName() { return fontName; }
+
+@@ -62,18 +84,21 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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);
+-
+- double secondaryDelta(TextBlock *blk);
++ TextParagraph(GList *linesA);
++ ~TextParagraph();
+
+- // 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);
+-
+- // Get the head of the linked list of TextBlocks.
+- TextBlock *getBlocks() { return blocks; }
++ TextColumn(GList *paragraphsA, double xMinA, double yMinA,
++ double xMaxA, double yMaxA);
++ ~TextColumn();
+
+- // 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 @@
+ 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 @@
+ GList *words; // [TextWord]
+ };
+
+-#endif // TEXTOUT_WORD_LIST
+-
+ //------------------------------------------------------------------------
+ // TextPage
+ //------------------------------------------------------------------------
+@@ -407,52 +294,11 @@
+ 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 @@
+ 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);
+
+- GBool rawOrder; // keep text in content stream order
++ // 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
++
++ 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 @@
+ 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 @@
+ // <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 @@
+ // 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 @@
+ 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 @@
+ 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 -uNr xpdf-3.03/xpdf/TextString.cc xpdf-3.04/xpdf/TextString.cc
+--- xpdf-3.03/xpdf/TextString.cc 1970-01-01 01:00:00.000000000 +0100
++++ xpdf-3.04/xpdf/TextString.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -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 -uNr xpdf-3.03/xpdf/TextString.h xpdf-3.04/xpdf/TextString.h
+--- xpdf-3.03/xpdf/TextString.h 1970-01-01 01:00:00.000000000 +0100
++++ xpdf-3.04/xpdf/TextString.h 2014-05-28 20:50:50.000000000 +0200
+@@ -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 -uNr xpdf-3.03/xpdf/UnicodeTypeTable.cc xpdf-3.04/xpdf/UnicodeTypeTable.cc
+--- xpdf-3.03/xpdf/UnicodeTypeTable.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/UnicodeTypeTable.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -2,7 +2,7 @@
+ //
+ // UnicodeTypeTable.cc
+ //
+-// Copyright 2004 Glyph & Cog, LLC
++// Copyright 2004-2013 Glyph & Cog, LLC
+ //
+ //========================================================================
+
+@@ -20,21 +20,21 @@
+ };
+
+ 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 @@
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
+- { "LLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNLLLLLLLLNLLNNNNNNNNNNNLLLLLLL#LNLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNN", 'X' },
++ { "LLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNLLLLLLLLNLLNNNNNNNNNNNLLLLLLL.LNLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNN", 'X' },
+ { "NNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
+ { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLNNNNNLLLLLLNLLLLLLNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },
+ { NULL, 'L' },
+@@ -52,9 +52,9 @@
+ { 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 @@
+ { 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 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 -uNr xpdf-3.03/xpdf/UnicodeTypeTable.h xpdf-3.04/xpdf/UnicodeTypeTable.h
+--- xpdf-3.03/xpdf/UnicodeTypeTable.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/UnicodeTypeTable.h 2014-05-28 20:50:50.000000000 +0200
+@@ -2,7 +2,7 @@
+ //
+ // UnicodeTypeTable.h
+ //
+-// Copyright 2003 Glyph & Cog, LLC
++// Copyright 2003-2013 Glyph & Cog, LLC
+ //
+ //========================================================================
+
+@@ -19,6 +19,8 @@
+
+ extern GBool unicodeTypeAlphaNum(Unicode c);
+
++extern GBool unicodeTypeWord(Unicode c);
++
+ extern Unicode unicodeToUpper(Unicode c);
+
+ #endif
+diff -uNr xpdf-3.03/xpdf/XFAForm.cc xpdf-3.04/xpdf/XFAForm.cc
+--- xpdf-3.03/xpdf/XFAForm.cc 1970-01-01 01:00:00.000000000 +0100
++++ xpdf-3.04/xpdf/XFAForm.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -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 -uNr xpdf-3.03/xpdf/XFAForm.h xpdf-3.04/xpdf/XFAForm.h
+--- xpdf-3.03/xpdf/XFAForm.h 1970-01-01 01:00:00.000000000 +0100
++++ xpdf-3.04/xpdf/XFAForm.h 2014-05-28 20:50:50.000000000 +0200
+@@ -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 -uNr xpdf-3.03/xpdf/xpdf.cc xpdf-3.04/xpdf/xpdf.cc
+--- xpdf-3.03/xpdf/xpdf.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/xpdf.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ "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 @@
+ 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");
+diff -uNr xpdf-3.03/xpdf/XPDFCore.cc xpdf-3.04/xpdf/XPDFCore.cc
+--- xpdf-3.03/xpdf/XPDFCore.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/XPDFCore.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -493,7 +493,7 @@
+ }
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ case actionMovie:
+ s = "[movie]";
+ break;
++ case actionJavaScript:
++ case actionSubmitForm:
++ case actionHide:
+ case actionUnknown:
+ s = "[unknown link]";
+ break;
+@@ -1356,6 +1367,8 @@
+ XFillRectangle(display, drawAreaWin, drawAreaGC,
+ xDest, yDest, width, height);
+ }
++
++ XFlush(display);
+ }
+
+ void XPDFCore::updateScrollbars() {
+diff -uNr xpdf-3.03/xpdf/XpdfPluginAPI.cc xpdf-3.04/xpdf/XpdfPluginAPI.cc
+--- xpdf-3.03/xpdf/XpdfPluginAPI.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/XpdfPluginAPI.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -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 -uNr xpdf-3.03/xpdf/XPDFViewer.cc xpdf-3.04/xpdf/XPDFViewer.cc
+--- xpdf-3.03/xpdf/XPDFViewer.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/XPDFViewer.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -165,6 +165,7 @@
+ { "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 @@
+ 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 @@
+ void XPDFViewer::reloadFile() {
+ int pg;
+
+- if (!core->getDoc()) {
++ if (!core->getDoc() || !core->getDoc()->getFileName()) {
+ return;
+ }
+ pg = core->getPageNum();
+@@ -808,6 +811,11 @@
+ 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 @@
+ 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 @@
+ 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 -uNr xpdf-3.03/xpdf/XPDFViewer.h xpdf-3.04/xpdf/XPDFViewer.h
+--- xpdf-3.03/xpdf/XPDFViewer.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/XPDFViewer.h 2014-05-28 20:50:50.000000000 +0200
+@@ -103,6 +103,7 @@
+ 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 -uNr xpdf-3.03/xpdf/XRef.cc xpdf-3.04/xpdf/XRef.cc
+--- xpdf-3.03/xpdf/XRef.cc 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/XRef.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ obj2.free();
+ delete parser;
+ gfree(offsets);
+- goto err1;
++ goto err2;
+ }
+ objNums[i] = obj1.getInt();
+ offsets[i] = obj2.getInt();
+@@ -144,7 +223,7 @@
+ (i > 0 && offsets[i] < offsets[i-1])) {
+ delete parser;
+ gfree(offsets);
+- goto err1;
++ goto err2;
+ }
+ }
+ while (str->getChar() != EOF) ;
+@@ -153,8 +232,8 @@
+ // 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 @@
+ gfree(offsets);
+ ok = gTrue;
+
++ err2:
++ objStr.streamClose();
+ err1:
+ objStr.free();
+ }
+@@ -203,8 +284,10 @@
+ //------------------------------------------------------------------------
+
+ XRef::XRef(BaseStream *strA, GBool repair) {
+- Guint pos;
++ GFileOffset pos;
+ Object obj;
++ XRefPosSet *posSet;
++ int i;
+
+ ok = gTrue;
+ errCode = errNone;
+@@ -213,12 +296,18 @@
+ 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 @@
+ }
+
+ // 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() {
++ 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 @@
+ 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 @@
+ }
+ 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 @@
+ 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 @@
+ }
+
+ // 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 @@
+ }
+
+ // 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 @@
+ 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 @@
+ 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 @@
+ }
+ 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 @@
+ }
+ 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::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 @@
+ }
+ 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 @@
+ }
+ 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 @@
+ Parser *parser;
+ Object newTrailerDict, obj;
+ char buf[256];
+- Guint pos;
++ GFileOffset pos;
+ int num, gen;
+ int newSize;
+ int streamEndsSize;
+@@ -748,7 +899,7 @@
+ 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 @@
+ } 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ 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 -uNr xpdf-3.03/xpdf/XRef.h xpdf-3.04/xpdf/XRef.h
+--- xpdf-3.03/xpdf/XRef.h 2011-08-15 23:08:53.000000000 +0200
++++ xpdf-3.04/xpdf/XRef.h 2014-05-28 20:50:50.000000000 +0200
+@@ -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 @@
+ };
+
+ 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 @@
+ 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 @@
+
+ // 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 @@
+ 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 @@
+ 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 @@
+ 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 -uNr xpdf-3.03/xpdf/Zoox.cc xpdf-3.04/xpdf/Zoox.cc
+--- xpdf-3.03/xpdf/Zoox.cc 1970-01-01 01:00:00.000000000 +0100
++++ xpdf-3.04/xpdf/Zoox.cc 2014-05-28 20:50:50.000000000 +0200
+@@ -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 -uNr xpdf-3.03/xpdf/Zoox.h xpdf-3.04/xpdf/Zoox.h
+--- xpdf-3.03/xpdf/Zoox.h 1970-01-01 01:00:00.000000000 +0100
++++ xpdf-3.04/xpdf/Zoox.h 2014-05-28 20:50:50.000000000 +0200
+@@ -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/CHANGES-3.04 b/CHANGES-3.04
new file mode 100644
index 0000000..bb82fb9
--- /dev/null
+++ b/CHANGES-3.04
@@ -0,0 +1,173 @@
+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/README b/README
new file mode 100644
index 0000000..e49c9e1
--- /dev/null
+++ b/README
@@ -0,0 +1,22 @@
+This repo contains the diff between xpdf 3.03 and xpdf-3.04 to be
+manually merged into poppler.
+
+Workflow:
+
+ - Look for a change to merge. If you find things in the diff that
+ are not relevant to poppler, just remove them from ALL_DIFF file
+ and push.
+ - Try to identify the change in the CHANGES file too if possible
+ - Manually apply the changes to poppler.
+ - Ideally we would merge individual features or fixes, but it's not
+ always possible to indentify the changes. In any case, make sure
+ every single revision you commit to poppler xpdf-304 branch builds
+ and doesn't crash. This will make easier to regtest the changes
+ and bisect to find regressions if needed.
+ - Push the commit to poppler xpdf304merge branch. Please, remember to
+ prefix all the commits with xpdf304: to easily identify those
+ commits once merged into master.
+ - Remove the merged chunks from the ALL_DIFF file and remove also
+ the merged feature/fix from the CHANGES-3.04 file.
+ - Commit and push
+ - Once the ALL_DIFF file is empty, we are done \o/