diff options
author | Carlos Garcia Campos <carlosgc@gnome.org> | 2014-06-06 15:30:34 +0200 |
---|---|---|
committer | Carlos Garcia Campos <carlosgc@gnome.org> | 2014-06-06 15:30:34 +0200 |
commit | a0a97c38508ce2a2297599d65e6ae04abaf092a1 (patch) | |
tree | ee362d967d7d93b354c4f4209ee099608ebbaf45 | |
parent | e865f320891f6e1f97fcc4f612119311aa029a63 (diff) |
Add 3.04 diff
Also added a CHANGES-3.04 file and a README file.
-rw-r--r-- | ALL_DIFF | 54757 | ||||
-rw-r--r-- | CHANGES-3.04 | 173 | ||||
-rw-r--r-- | README | 22 |
3 files changed, 54952 insertions, 0 deletions
@@ -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, +- ®Key) == ERROR_SUCCESS) { ++ if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, path, 0, ++ KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, ++ ®Key) == 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("&"); ++ } else if (u == '<') { ++ s->append("<"); ++ } else if (u == '>') { ++ s->append(">"); ++ } 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", ¶ms); + } + if (obj.isName()) { +- str = makeFilter(obj.getName(), str, ¶ms); ++ str = makeFilter(obj.getName(), str, ¶ms, 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, ¶ms2); ++ str = makeFilter(obj2.getName(), str, ¶ms2, 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. @@ -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/ |