diff -uNrp 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 -uNrp 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 -uNrp 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 -uNrp 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 @@ Error related to PDF permissions. 99 Other error. .SH AUTHOR -The pdfinfo software and documentation are copyright 1996-2011 Glyph & +The pdfinfo software and documentation are copyright 1996-2014 Glyph & Cog, LLC. .SH "SEE ALSO" .BR xpdf (1), .BR pdftops (1), .BR pdftotext (1), +.BR pdftohtml (1), .BR pdfinfo (1), .BR pdffonts (1), .BR pdftoppm (1), +.BR pdftopng (1), .BR pdfimages (1), .BR xpdfrc (5) .br diff -uNrp 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 @@ pdfdetach(1) NAME pdfdetach - Portable Document Format (PDF) document embedded file - extractor (version 3.03) + extractor (version 3.04) SYNOPSIS pdfdetach [options] [PDF-file] @@ -79,14 +79,14 @@ EXIT CODES 99 Other error. AUTHOR - The pdfinfo software and documentation are copyright 1996-2011 Glyph & + The pdfinfo software and documentation are copyright 1996-2014 Glyph & Cog, LLC. SEE ALSO - xpdf(1), pdftops(1), pdftotext(1), pdfinfo(1), pdffonts(1), - pdftoppm(1), pdfimages(1), xpdfrc(5) + xpdf(1), pdftops(1), pdftotext(1), pdftohtml(1), pdfinfo(1), pdf- + fonts(1), pdftoppm(1), pdftopng(1), pdfimages(1), xpdfrc(5) http://www.foolabs.com/xpdf/ - 15 August 2011 pdfdetach(1) + 28 May 2014 pdfdetach(1) diff -uNrp 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 -uNrp 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 @@ can't be converted to Unicode) .TP .B object ID the font dictionary object ID (number and generation) +.TP +.B location +the font location (see the +.B \-loc +and +.B \-locPS +options). .PP PDF files can contain the following types of fonts: .PP @@ -86,6 +93,14 @@ the corresponding command line option. .BI \-f " number" Specifies the first page to analyze. .TP +.B \-loc +Shows additional information on the location of the font that will be +used when the PDF file is rasterized (with xpdf, pdftoppm, etc.). +.TP +.B \-locPS +Shows additional information on the location of the font that will be +used when the PDF file is converted to PostScript (with pdftops). +.TP .BI \-l " number" Specifies the last page to analyze. .TP @@ -128,15 +143,17 @@ Error related to PDF permissions. 99 Other error. .SH AUTHOR -The pdffonts software and documentation are copyright 1996-2011 Glyph +The pdffonts software and documentation are copyright 1996-2014 Glyph & Cog, LLC. .SH "SEE ALSO" .BR xpdf (1), .BR pdftops (1), .BR pdftotext (1), +.BR pdftohtml (1), .BR pdfinfo (1), .BR pdfdetach (1), .BR pdftoppm (1), +.BR pdftopng (1), .BR pdfimages (1), .BR xpdfrc (5) .br diff -uNrp 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 @@ pdffonts(1) NAME - pdffonts - Portable Document Format (PDF) font analyzer (version 3.03) + pdffonts - Portable Document Format (PDF) font analyzer (version 3.04) SYNOPSIS pdffonts [options] [PDF-file] @@ -30,6 +30,9 @@ DESCRIPTION object ID the font dictionary object ID (number and generation) + location + the font location (see the -loc and -locPS options). + PDF files can contain the following types of fonts: Type 1 @@ -59,6 +62,14 @@ OPTIONS -f number Specifies the first page to analyze. + -loc Shows additional information on the location of the font that + will be used when the PDF file is rasterized (with xpdf, + pdftoppm, etc.). + + -locPS Shows additional information on the location of the font that + will be used when the PDF file is converted to PostScript (with + pdftops). + -l number Specifies the last page to analyze. @@ -91,14 +102,14 @@ EXIT CODES 99 Other error. AUTHOR - The pdffonts software and documentation are copyright 1996-2011 Glyph & + The pdffonts software and documentation are copyright 1996-2014 Glyph & Cog, LLC. SEE ALSO - xpdf(1), pdftops(1), pdftotext(1), pdfinfo(1), pdfdetach(1), - pdftoppm(1), pdfimages(1), xpdfrc(5) + xpdf(1), pdftops(1), pdftotext(1), pdftohtml(1), pdfinfo(1), pdfde- + tach(1), pdftoppm(1), pdftopng(1), pdfimages(1), xpdfrc(5) http://www.foolabs.com/xpdf/ - 15 August 2011 pdffonts(1) + 28 May 2014 pdffonts(1) diff -uNrp 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 -uNrp 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 @@ Pixmap (PPM), Portable Bitmap (PBM), or Pdfimages reads the PDF file, scans one or more pages, .IR PDF-file , and writes one PPM, PBM, or JPEG file for each image, -.IR image-root - nnn . xxx , +.IR image-root - nnnn . xxx , where -.I nnn +.I nnnn is the image number and .I xxx is the image type (.ppm, .pbm, .jpg). @@ -88,16 +88,18 @@ Error related to PDF permissions. 99 Other error. .SH AUTHOR -The pdfimages software and documentation are copyright 1998-2011 Glyph +The pdfimages software and documentation are copyright 1998-2014 Glyph & Cog, LLC. .SH "SEE ALSO" .BR xpdf (1), .BR pdftops (1), .BR pdftotext (1), +.BR pdftohtml (1), .BR pdfinfo (1), .BR pdffonts (1), .BR pdfdetach (1), .BR pdftoppm (1), +.BR pdftopng (1), .BR xpdfrc (5) .br .B http://www.foolabs.com/xpdf/ diff -uNrp 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 @@ pdfimages(1) NAME pdfimages - Portable Document Format (PDF) image extractor (version - 3.03) + 3.04) SYNOPSIS pdfimages [options] PDF-file image-root @@ -14,8 +14,8 @@ DESCRIPTION Portable Pixmap (PPM), Portable Bitmap (PBM), or JPEG files. Pdfimages reads the PDF file, scans one or more pages, PDF-file, and - writes one PPM, PBM, or JPEG file for each image, image-root-nnn.xxx, - where nnn is the image number and xxx is the image type (.ppm, .pbm, + writes one PPM, PBM, or JPEG file for each image, image-root-nnnn.xxx, + where nnnn is the image number and xxx is the image type (.ppm, .pbm, .jpg). NB: pdfimages extracts the raw image data from the PDF file, without @@ -72,14 +72,14 @@ EXIT CODES 99 Other error. AUTHOR - The pdfimages software and documentation are copyright 1998-2011 Glyph + The pdfimages software and documentation are copyright 1998-2014 Glyph & Cog, LLC. SEE ALSO - xpdf(1), pdftops(1), pdftotext(1), pdfinfo(1), pdffonts(1), pdfde- - tach(1), pdftoppm(1), xpdfrc(5) + xpdf(1), pdftops(1), pdftotext(1), pdftohtml(1), pdfinfo(1), pdf- + fonts(1), pdfdetach(1), pdftoppm(1), pdftopng(1), xpdfrc(5) http://www.foolabs.com/xpdf/ - 15 August 2011 pdfimages(1) + 28 May 2014 pdfimages(1) diff -uNrp 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 -uNrp 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 @@ encrypted flag (yes/no) print and copy permissions (if encrypted) .RE .RS -page size +page size and rotation .RE .RS file size @@ -150,15 +150,17 @@ Error related to PDF permissions. 99 Other error. .SH AUTHOR -The pdfinfo software and documentation are copyright 1996-2011 Glyph & +The pdfinfo software and documentation are copyright 1996-2014 Glyph & Cog, LLC. .SH "SEE ALSO" .BR xpdf (1), .BR pdftops (1), .BR pdftotext (1), +.BR pdftohtml (1), .BR pdffonts (1), .BR pdfdetach (1), .BR pdftoppm (1), +.BR pdftopng (1), .BR pdfimages (1), .BR xpdfrc (5) .br diff -uNrp 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 @@ pdfinfo(1) NAME pdfinfo - Portable Document Format (PDF) document information extractor - (version 3.03) + (version 3.04) SYNOPSIS pdfinfo [options] [PDF-file] @@ -31,7 +31,7 @@ DESCRIPTION page count encrypted flag (yes/no) print and copy permissions (if encrypted) - page size + page size and rotation file size linearized (yes/no) PDF version @@ -104,14 +104,14 @@ EXIT CODES 99 Other error. AUTHOR - The pdfinfo software and documentation are copyright 1996-2011 Glyph & + The pdfinfo software and documentation are copyright 1996-2014 Glyph & Cog, LLC. SEE ALSO - xpdf(1), pdftops(1), pdftotext(1), pdffonts(1), pdfdetach(1), - pdftoppm(1), pdfimages(1), xpdfrc(5) + xpdf(1), pdftops(1), pdftotext(1), pdftohtml(1), pdffonts(1), pdfde- + tach(1), pdftoppm(1), pdftopng(1), pdfimages(1), xpdfrc(5) http://www.foolabs.com/xpdf/ - 15 August 2011 pdfinfo(1) + 28 May 2014 pdfinfo(1) diff -uNrp 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 -uNrp 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 -uNrp 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 -uNrp 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 -uNrp 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 -uNrp 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 @@ Generate a monochrome PBM file (instead .B \-gray Generate a grayscale PGM file (instead of a color PPM file). .TP -.BI \-t1lib " yes | no" -Enable or disable t1lib (a Type 1 font rasterizer). This defaults to -"yes". -.RB "[config file: " enableT1lib ] -.TP .BI \-freetype " yes | no" Enable or disable FreeType (a TrueType / Type 1 font rasterizer). This defaults to "yes". @@ -109,15 +104,17 @@ Error related to PDF permissions. 99 Other error. .SH AUTHOR -The pdftoppm software and documentation are copyright 1996-2011 Glyph +The pdftoppm software and documentation are copyright 1996-2014 Glyph & Cog, LLC. .SH "SEE ALSO" .BR xpdf (1), .BR pdftops (1), .BR pdftotext (1), +.BR pdftohtml (1), .BR pdfinfo (1), .BR pdffonts (1), .BR pdfdetach (1), +.BR pdftopng (1), .BR pdfimages (1), .BR xpdfrc (5) .br diff -uNrp 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 @@ pdftoppm(1) NAME pdftoppm - Portable Document Format (PDF) to Portable Pixmap (PPM) con- - verter (version 3.03) + verter (version 3.04) SYNOPSIS pdftoppm [options] PDF-file PPM-root @@ -45,16 +45,12 @@ OPTIONS -gray Generate a grayscale PGM file (instead of a color PPM file). - -t1lib yes | no - Enable or disable t1lib (a Type 1 font rasterizer). This - defaults to "yes". [config file: enableT1lib] - -freetype yes | no - Enable or disable FreeType (a TrueType / Type 1 font raster- + Enable or disable FreeType (a TrueType / Type 1 font raster- izer). This defaults to "yes". [config file: enableFreeType] -aa yes | no - Enable or disable font anti-aliasing. This defaults to "yes". + Enable or disable font anti-aliasing. This defaults to "yes". [config file: antialias] -aaVector yes | no @@ -62,7 +58,7 @@ OPTIONS [config file: vectorAntialias] -opw password - Specify the owner password for the PDF file. Providing this + Specify the owner password for the PDF file. Providing this will bypass all security restrictions. -upw password @@ -88,14 +84,14 @@ EXIT CODES 99 Other error. AUTHOR - The pdftoppm software and documentation are copyright 1996-2011 Glyph & + The pdftoppm software and documentation are copyright 1996-2014 Glyph & Cog, LLC. SEE ALSO - xpdf(1), pdftops(1), pdftotext(1), pdfinfo(1), pdffonts(1), pdfde- - tach(1), pdfimages(1), xpdfrc(5) + xpdf(1), pdftops(1), pdftotext(1), pdftohtml(1), pdfinfo(1), pdf- + fonts(1), pdfdetach(1), pdftopng(1), pdfimages(1), xpdfrc(5) http://www.foolabs.com/xpdf/ - 15 August 2011 pdftoppm(1) + 28 May 2014 pdftoppm(1) diff -uNrp 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 -uNrp 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 @@ lower-left corner of the paper instead. .B \-pagecrop Treat the CropBox as the PDF page size. By default, the MediaBox is used as the page size. +.RB "[config file: " psUseCropBoxAsPage ] .TP .B \-duplex Set the Duplex pagedevice entry in the PostScript file. This tells @@ -221,15 +222,17 @@ Error related to PDF permissions. 99 Other error. .SH AUTHOR -The pdftops software and documentation are copyright 1996-2011 Glyph & +The pdftops software and documentation are copyright 1996-2014 Glyph & Cog, LLC. .SH "SEE ALSO" .BR xpdf (1), .BR pdftotext (1), +.BR pdftohtml (1), .BR pdfinfo (1), .BR pdffonts (1), .BR pdfdetach (1), .BR pdftoppm (1), +.BR pdftopng (1), .BR pdfimages (1), .BR xpdfrc (5) .br diff -uNrp 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 @@ pdftops(1) NAME pdftops - Portable Document Format (PDF) to PostScript converter (ver- - sion 3.03) + sion 3.04) SYNOPSIS pdftops [options] [PDF-file [PS-file]] @@ -73,54 +73,54 @@ OPTIONS page PDF file, you must use -f and -l to specify a single page. No more than one of the mode options (-eps, -form) may be given. - -form Generate a PostScript form which can be imported by software - that understands forms. A form contains a single page, so if - you use this option with a multi-page PDF file, you must use -f - and -l to specify a single page. The -level1 option cannot be + -form Generate a PostScript form which can be imported by software + that understands forms. A form contains a single page, so if + you use this option with a multi-page PDF file, you must use -f + and -l to specify a single page. The -level1 option cannot be used with -form. - -opi Generate OPI comments for all images and forms which have OPI + -opi Generate OPI comments for all images and forms which have OPI information. (This option is only available if pdftops was com- piled with OPI support.) [config file: psOPI] -noembt1 - By default, any Type 1 fonts which are embedded in the PDF file + By default, any Type 1 fonts which are embedded in the PDF file are copied into the PostScript file. This option causes pdftops - to substitute base fonts instead. Embedded fonts make Post- - Script files larger, but may be necessary for readable output. + to substitute base fonts instead. Embedded fonts make Post- + Script files larger, but may be necessary for readable output. [config file: psEmbedType1Fonts] -noembtt - By default, any TrueType fonts which are embedded in the PDF - file are copied into the PostScript file. This option causes - pdftops to substitute base fonts instead. Embedded fonts make - PostScript files larger, but may be necessary for readable out- - put. Also, some PostScript interpreters do not have TrueType + By default, any TrueType fonts which are embedded in the PDF + file are copied into the PostScript file. This option causes + pdftops to substitute base fonts instead. Embedded fonts make + PostScript files larger, but may be necessary for readable out- + put. Also, some PostScript interpreters do not have TrueType rasterizers. [config file: psEmbedTrueTypeFonts] -noembcidps - By default, any CID PostScript fonts which are embedded in the - PDF file are copied into the PostScript file. This option dis- + By default, any CID PostScript fonts which are embedded in the + PDF file are copied into the PostScript file. This option dis- ables that embedding. No attempt is made to substitute for non- - embedded CID PostScript fonts. [config file: psEmbedCID- + embedded CID PostScript fonts. [config file: psEmbedCID- PostScriptFonts] -noembcidtt By default, any CID TrueType fonts which are embedded in the PDF - file are copied into the PostScript file. This option disables + file are copied into the PostScript file. This option disables that embedding. No attempt is made to substitute for non-embed- ded CID TrueType fonts. [config file: psEmbedCIDTrueTypeFonts] -preload - Convert PDF forms to PS procedures, and preload image data. - This uses more memory in the PostScript interpreter, but gener- - ates significantly smaller PS files in situations where, e.g., + Convert PDF forms to PS procedures, and preload image data. + This uses more memory in the PostScript interpreter, but gener- + ates significantly smaller PS files in situations where, e.g., the same image is drawn on every page of a long document. -paper size - Set the paper size to one of "letter", "legal", "A4", or "A3". - This can also be set to "match", which will set the paper size - to match the size specified in the PDF file. [config file: + Set the paper size to one of "letter", "legal", "A4", or "A3". + This can also be set to "match", which will set the paper size + to match the size specified in the PDF file. [config file: psPaperSize] -paperw size @@ -130,7 +130,7 @@ OPTIONS Set the paper height, in points. [config file: psPaperSize] -nocrop - By default, output is cropped to the CropBox specified in the + By default, output is cropped to the CropBox specified in the PDF file. This option disables cropping. [config file: psCrop] -expand @@ -151,7 +151,7 @@ OPTIONS -pagecrop Treat the CropBox as the PDF page size. By default, the Media- - Box is used as the page size. + Box is used as the page size. [config file: psUseCropBoxAsPage] -duplex Set the Duplex pagedevice entry in the PostScript file. This @@ -189,14 +189,14 @@ EXIT CODES 99 Other error. AUTHOR - The pdftops software and documentation are copyright 1996-2011 Glyph & + The pdftops software and documentation are copyright 1996-2014 Glyph & Cog, LLC. SEE ALSO - xpdf(1), pdftotext(1), pdfinfo(1), pdffonts(1), pdfdetach(1), - pdftoppm(1), pdfimages(1), xpdfrc(5) + xpdf(1), pdftotext(1), pdftohtml(1), pdfinfo(1), pdffonts(1), pdfde- + tach(1), pdftoppm(1), pdftopng(1), pdfimages(1), xpdfrc(5) http://www.foolabs.com/xpdf/ - 15 August 2011 pdftops(1) + 28 May 2014 pdftops(1) diff -uNrp 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 -uNrp 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 @@ Specifies the last page to convert. .B \-layout Maintain (as best as possible) the original physical layout of the text. The default is to \'undo' physical layout (columns, -hyphenation, etc.) and output the text in reading order. -.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
andand 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 @@ Error related to PDF permissions. 99 Other error. .SH AUTHOR -The pdftotext software and documentation are copyright 1996-2011 Glyph +The pdftotext software and documentation are copyright 1996-2014 Glyph & Cog, LLC. .SH "SEE ALSO" .BR xpdf (1), .BR pdftops (1), +.BR pdftohtml (1), .BR pdfinfo (1), .BR pdffonts (1), .BR pdfdetach (1), .BR pdftoppm (1), +.BR pdftopng (1), .BR pdfimages (1), .BR xpdfrc (5) .br diff -uNrp 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 @@ pdftotext(1) NAME pdftotext - Portable Document Format (PDF) to text converter (version - 3.03) + 3.04) SYNOPSIS pdftotext [options] [PDF-file [text-file]] DESCRIPTION - Pdftotext converts Portable Document Format (PDF) files to plain text. + Pdftotext converts Portable Document Format (PDF) files to plain text. - Pdftotext reads the PDF file, PDF-file, and writes a text file, text- - file. If text-file is not specified, pdftotext converts file.pdf to + Pdftotext reads the PDF file, PDF-file, and writes a text file, text- + file. If text-file is not specified, pdftotext converts file.pdf to file.txt. If text-file is '-', the text is sent to stdout. CONFIGURATION FILE - Pdftotext reads a configuration file at startup. It first tries to + Pdftotext reads a configuration file at startup. It first tries to find the user's private config file, ~/.xpdfrc. If that doesn't exist, it looks for a system-wide config file, typically /usr/local/etc/xpdfrc - (but this location can be changed when pdftotext is built). See the + (but this location can be changed when pdftotext is built). See the xpdfrc(5) man page for details. OPTIONS - Many of the following options can be set with configuration file com- + Many of the following options can be set with configuration file com- mands. These are listed in square brackets with the description of the corresponding command line option. @@ -35,22 +35,44 @@ OPTIONS Specifies the last page to convert. -layout - Maintain (as best as possible) the original physical layout of - the text. The default is to 'undo' physical layout (columns, - hyphenation, etc.) and output the text in reading order. + Maintain (as best as possible) the original physical layout of + the text. The default is to 'undo' physical layout (columns, + hyphenation, etc.) and output the text in reading order. If the + -fixed option is given, character spacing within each line will + be determined by the specified character pitch. + + -table Table mode is similar to physical layout mode, but optimized for + tabular data, with the goal of keeping rows and columns aligned + (at the expense of inserting extra whitespace). If the -fixed + option is given, character spacing within each line will be + determined by the specified character pitch. + + -lineprinter + Line printer mode uses a strict fixed-character-pitch and + -height layout. That is, the page is broken into a grid, and + characters are placed into that grid. If the grid spacing is + too small for the actual characters, the result is extra white- + space. If the grid spacing is too large, the result is missing + whitespace. The grid spacing can be specified using the -fixed + and -linespacing options. If one or both are not given on the + command line, pdftotext will attempt to compute appropriate + value(s). - -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
andand 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 @@ EXIT CODES 99 Other error. AUTHOR - The pdftotext software and documentation are copyright 1996-2011 Glyph + The pdftotext software and documentation are copyright 1996-2014 Glyph & Cog, LLC. SEE ALSO - xpdf(1), pdftops(1), pdfinfo(1), pdffonts(1), pdfdetach(1), - pdftoppm(1), pdfimages(1), xpdfrc(5) + xpdf(1), pdftops(1), pdftohtml(1), pdfinfo(1), pdffonts(1), pdfde- + tach(1), pdftoppm(1), pdftopng(1), pdfimages(1), xpdfrc(5) http://www.foolabs.com/xpdf/ - 15 August 2011 pdftotext(1) + 28 May 2014 pdftotext(1) diff -uNrp 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
andand 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 -uNrp 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 -uNrp 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 @@ Start in continuous view mode, i.e., wit the whole document. .RB "[config file: " continuousView ] .TP -.BI \-t1lib " yes | no" -Enable or disable t1lib (a Type 1 font rasterizer). This defaults to -"yes". -.RB "[config file: " enableT1lib ] -.TP .BI \-freetype " yes | no" Enable or disable FreeType (a TrueType / Type 1 font rasterizer). This defaults to "yes". @@ -638,7 +633,11 @@ Redraw the window. Raise the window to the front. .TP .B closeWindow -Close the window. +Close the window. If this was the last open window, clear the window, +but don't quit from Xpdf. +.TP +.B closeWindowOrQuit +Close the window. If this was the last open window, quit from Xpdf. .TP .BI run( external-command-string ) Run an external command. The following escapes are allowed in the @@ -668,6 +667,13 @@ command string: %% => % .fi +The external command string will often contain spaces, so the whole +command must be quoted in the xpdfrc file: +.nf + + bind x "run(ls -l)" + +.fi .TP .B openOutline Open the outline pane. @@ -799,7 +805,7 @@ The default key bindings are as follows: bind w any zoomFitWidth bind alt-f any toggleFullScreenMode bind ctrl-l any redraw - bind ctrl-w any closeWindow + bind ctrl-w any closeWindowOrQuit bind ? any about bind q any quit bind Q any quit @@ -863,15 +869,17 @@ Error related to PDF permissions. 99 Other error. .SH AUTHOR -The xpdf software and documentation are copyright 1996-2011 Glyph & +The xpdf software and documentation are copyright 1996-2014 Glyph & Cog, LLC. .SH "SEE ALSO" .BR pdftops (1), .BR pdftotext (1), +.BR pdftohtml (1), .BR pdfinfo (1), .BR pdffonts (1), .BR pdfdetach (1), .BR pdftoppm (1), +.BR pdftopng (1), .BR pdfimages (1), .BR xpdfrc (5) .br diff -uNrp 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 @@ xpdf(1) NAME - xpdf - Portable Document Format (PDF) file viewer for X (version 3.03) + xpdf - Portable Document Format (PDF) file viewer for X (version 3.04) SYNOPSIS xpdf [options] [PDF-file [page | +dest]] @@ -60,15 +60,15 @@ OPTIONS serve color table entries. This is ignored with private col- ormaps and on TrueColor visuals. [X resource: xpdf.rgbCubeSize] - -rv Set reverse video mode. This reverses the colors of everything - except images. It may not always produce great results for PDF - files which do weird things with color. This also causes the - paper color to default to black. [X resource: xpdf.reverseV- + -rv Set reverse video mode. This reverses the colors of everything + except images. It may not always produce great results for PDF + files which do weird things with color. This also causes the + paper color to default to black. [X resource: xpdf.reverseV- ideo] -papercolor color Set the "paper color", i.e., the background of the page display. - This will not work too well with PDF files that do things like + This will not work too well with PDF files that do things like filling in white behind the text. [X resource: xpdf.paperColor] -mattecolor color @@ -87,16 +87,12 @@ OPTIONS -cont Start in continuous view mode, i.e., with one vertical scroll bar for the whole document. [config file: continuousView] - -t1lib yes | no - Enable or disable t1lib (a Type 1 font rasterizer). This - defaults to "yes". [config file: enableT1lib] - -freetype yes | no - Enable or disable FreeType (a TrueType / Type 1 font raster- + Enable or disable FreeType (a TrueType / Type 1 font raster- izer). This defaults to "yes". [config file: enableFreeType] -aa yes | no - Enable or disable font anti-aliasing. This defaults to "yes". + Enable or disable font anti-aliasing. This defaults to "yes". [config file: antialias] -aaVector yes | no @@ -104,15 +100,15 @@ OPTIONS [config file: vectorAntialias] -ps PS-file - Set the default file name for PostScript output (i.e., the name + Set the default file name for PostScript output (i.e., the name which will appear in the print dialog). This can also be of the form '|command' to pipe the PostScript through a command. [con- fig file: psFile] -paper size - Set the paper size to one of "letter", "legal", "A4", or "A3". - This can also be set to "match", which will set the paper size - to match the size specified in the PDF file. [config file: + Set the paper size to one of "letter", "legal", "A4", or "A3". + This can also be set to "match", which will set the paper size + to match the size specified in the PDF file. [config file: psPaperSize] -paperw size @@ -122,14 +118,14 @@ OPTIONS Set the paper height, in points. [config file: psPaperSize] -level1 - Generate Level 1 PostScript. The resulting PostScript files - will be significantly larger (if they contain images), but will - print on Level 1 printers. This also converts all images to + Generate Level 1 PostScript. The resulting PostScript files + will be significantly larger (if they contain images), but will + print on Level 1 printers. This also converts all images to black and white. [config file: psLevel] -enc encoding-name - Sets the encoding to use for text output. The encoding-name - must be defined with the unicodeMap command (see xpdfrc(5)). + Sets the encoding to use for text output. The encoding-name + must be defined with the unicodeMap command (see xpdfrc(5)). This defaults to "Latin1" (which is a built-in encoding). [con- fig file: textEncoding] @@ -138,7 +134,7 @@ OPTIONS file: textEOL] -opw password - Specify the owner password for the PDF file. Providing this + Specify the owner password for the PDF file. Providing this will bypass all security restrictions. -upw password @@ -148,11 +144,11 @@ OPTIONS Open xpdf in full-screen mode, useful for presentations. -remote name - Start/contact xpdf remote server with specified name (see the + Start/contact xpdf remote server with specified name (see the REMOTE SERVER MODE section below). -exec command - Execute a command (see the COMMANDS section below) in an xpdf + Execute a command (see the COMMANDS section below) in an xpdf remote server window (with -remote only). -reload @@ -162,7 +158,7 @@ OPTIONS -quit Kill xpdf remote server (with -remote only). - -cmd Print commands as they're executed (useful for debugging). + -cmd Print commands as they're executed (useful for debugging). [config file: printCommands] -q Don't print any messages or errors. [config file: errQuiet] @@ -199,7 +195,7 @@ OPTIONS tips on the toolbar buttons. xpdf.fullScreenMatteColor - Sets the matte color to be used in full-screen mode. The + Sets the matte color to be used in full-screen mode. The default setting is "black". CONTROLS @@ -214,19 +210,19 @@ CONTROLS Move backward or forward along the history path. 'Page' entry box - Move to a specific page number. Click in the box to activate + Move to a specific page number. Click in the box to activate it, type the page number, then hit return. zoom popup menu - Change the zoom factor (see the description of the -z option + Change the zoom factor (see the description of the -z option above). binoculars button Find a text string. print button - Bring up a dialog for generating a PostScript file. The dialog - has options to set the pages to be printed and the PostScript + Bring up a dialog for generating a PostScript file. The dialog + has options to set the pages to be printed and the PostScript file name. The file name can be '-' for stdout or '|command' to pipe the PostScript through a command, e.g., '|lpr'. @@ -240,7 +236,6 @@ CONTROLS 'Quit' button Quit xpdf. - Menu Pressing the right mouse button will post a popup menu with the follow- ing commands: @@ -249,11 +244,11 @@ CONTROLS Open a new PDF file via a file requester. Open in new window... - Create a new window and open a new PDF file via a file + Create a new window and open a new PDF file via a file requester. - Reload Reload the current PDF file. Note that Xpdf will reload the - file automatically (on a page change or redraw) if it has + Reload Reload the current PDF file. Note that Xpdf will reload the + file automatically (on a page change or redraw) if it has changed since it was last loaded. Save as... @@ -266,50 +261,45 @@ CONTROLS Rotate the page 90 degrees counterclockwise. Rotate clockwise - Rotate the page 90 degrees clockwise. The two rotate commands - are intended primarily for PDF files where the rotation isn't + Rotate the page 90 degrees clockwise. The two rotate commands + are intended primarily for PDF files where the rotation isn't correctly specified in the file. Zoom to selection Zoom in to the currently selected rectangle. - Close Close the current window. If this is the only open window, the + Close Close the current window. If this is the only open window, the document is closed, but the window is left open (i.e., this menu command won't quit xpdf). Quit Quit xpdf. - Outline - If the PDF contains an outline (a.k.a., bookmarks), there will be an - outline pane on the left side of the window. The width of the outline + If the PDF contains an outline (a.k.a., bookmarks), there will be an + outline pane on the left side of the window. The width of the outline pane is adjustable with a vertical split bar via the knob near its bot- tom end. - Text selection - Dragging the mouse with the left button held down will highlight an - arbitrary rectangle. Any text inside this rectangle will be copied to + Dragging the mouse with the left button held down will highlight an + arbitrary rectangle. Any text inside this rectangle will be copied to the X selection buffer. - Links Clicking on a hyperlink will jump to the link's destination. A link to - another PDF document will make xpdf load that document. A 'launch' - link to an executable program will display a dialog, and if you click + another PDF document will make xpdf load that document. A 'launch' + link to an executable program will display a dialog, and if you click 'ok', execute the program. URL links call an external command (see the WEB BROWSERS section below). - Panning Dragging the mouse with the middle button held down pans the window. - Key bindings o Open a new PDF file via a file requester. - r Reload the current PDF file. Note that Xpdf will reload the - file automatically (on a page change or redraw) if it has + r Reload the current PDF file. Note that Xpdf will reload the + file automatically (on a page change or redraw) if it has changed since it was last loaded. control-L @@ -327,14 +317,14 @@ CONTROLS control-P Print. - n Move to the next page. Scrolls to the top of the page, unless + n Move to the next page. Scrolls to the top of the page, unless scroll lock is turned on. - p Move to the previous page. Scrolls to the top of the page, + p Move to the previous page. Scrolls to the top of the page, unless scroll lock is turned on.
.
Object *getCharProc(int code, Object *proc);
+ Object *getCharProcNF(int code, Object *proc);
// Return the Type 3 Resources dictionary, or NULL if none.
Dict *getResources();
@@ -347,6 +352,7 @@ public:
// Get the specified font.
GfxFont *lookup(char *tag);
+ GfxFont *lookupByRef(Ref ref);
// Iterative access.
int getNumFonts() { return numFonts; }
diff -uNrp 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 @@ public:
~GfxResources();
GfxFont *lookupFont(char *name);
- GBool lookupXObject(char *name, Object *obj);
- GBool lookupXObjectNF(char *name, Object *obj);
- void lookupColorSpace(char *name, Object *obj);
- GfxPattern *lookupPattern(char *name);
- GfxShading *lookupShading(char *name);
- GBool lookupGState(char *name, Object *obj);
- GBool lookupPropertiesNF(char *name, Object *obj);
+ GfxFont *lookupFontByRef(Ref ref);
+ GBool lookupXObject(const char *name, Object *obj);
+ GBool lookupXObjectNF(const char *name, Object *obj);
+ void lookupColorSpace(const char *name, Object *obj);
+ GfxPattern *lookupPattern(const char *name
+ );
+ GfxShading *lookupShading(const char *name
+ );
+ GBool lookupGState(const char *name, Object *obj);
+ GBool lookupPropertiesNF(const char *name, Object *obj);
GfxResources *getNext() { return next; }
@@ -152,12 +156,13 @@ public:
~Gfx();
- // Interpret a stream or array of streams.
- void display(Object *obj, GBool topLevel = gTrue);
+ // Interpret a stream or array of streams. should be a
+ // reference wherever possible (for loop-checking).
+ void display(Object *objRef, GBool topLevel = gTrue);
// Display an annotation, given its appearance (a Form XObject),
// border style, and bounding box (in default user space).
- void drawAnnot(Object *str, AnnotBorderStyle *borderStyle,
+ void drawAnnot(Object *strRef, AnnotBorderStyle *borderStyle,
double xMin, double yMin, double xMax, double yMax);
// Save graphics state.
@@ -169,13 +174,21 @@ public:
// Get the current graphics state object.
GfxState *getState() { return state; }
- void drawForm(Object *str, Dict *resDict, double *matrix, double *bbox,
+ void drawForm(Object *strRef, Dict *resDict, double *matrix, double *bbox,
GBool transpGroup = gFalse, GBool softMask = gFalse,
GfxColorSpace *blendingColorSpace = NULL,
GBool isolated = gFalse, GBool knockout = gFalse,
GBool alpha = gFalse, Function *transferFunc = NULL,
GfxColor *backdropColor = NULL);
+ // Take all of the content stream stack entries from . This
+ // is useful when creating a new Gfx object to handle a pattern,
+ // etc., where it's useful to check for loops that span both Gfx
+ // objects. This function should be called immediately after the
+ // Gfx constructor, i.e., before processing any content streams with
+ // the new Gfx object.
+ void takeContentStreamStack(Gfx *oldGfx);
+
private:
PDFDoc *doc;
@@ -201,6 +214,8 @@ private:
GList *markedContentStack; // BMC/BDC/EMC stack [GfxMarkedContent]
Parser *parser; // parser for page content stream(s)
+ GList *contentStreamStack; // stack of open content streams, used
+ // for loop-checking
GBool // callback to check for an abort
(*abortCheckCbk)(void *data);
@@ -208,11 +223,12 @@ private:
static Operator opTab[]; // table of operators
+ GBool checkForContentStreamLoop(Object *ref);
void go(GBool topLevel);
- void execOp(Object *cmd, Object args[], int numArgs);
+ GBool execOp(Object *cmd, Object args[], int numArgs);
Operator *findOp(char *name);
GBool checkArg(Object *arg, TchkType type);
- int getPos();
+ GFileOffset getPos();
// graphics state operators
void opSave(Object args[], int numArgs);
@@ -225,7 +241,7 @@ private:
void opSetMiterLimit(Object args[], int numArgs);
void opSetLineWidth(Object args[], int numArgs);
void opSetExtGState(Object args[], int numArgs);
- void doSoftMask(Object *str, GBool alpha,
+ void doSoftMask(Object *str, Object *strRef, GBool alpha,
GfxColorSpace *blendingColorSpace,
GBool isolated, GBool knockout,
Function *transferFunc, GfxColor *backdropColor);
@@ -268,7 +284,7 @@ private:
void doPatternStroke();
void doPatternText();
void doPatternImageMask(Object *ref, Stream *str, int width, int height,
- GBool invert, GBool inlineImg);
+ GBool invert, GBool inlineImg, GBool interpolate);
void doTilingPatternFill(GfxTilingPattern *tPat,
GBool stroke, GBool eoFill, GBool text);
void doShadingPatternFill(GfxShadingPattern *sPat,
@@ -282,12 +298,12 @@ private:
void doAxialShFill(GfxAxialShading *shading);
void doRadialShFill(GfxRadialShading *shading);
void doGouraudTriangleShFill(GfxGouraudTriangleShading *shading);
- void gouraudFillTriangle(double x0, double y0, GfxColor *color0,
- double x1, double y1, GfxColor *color1,
- double x2, double y2, GfxColor *color2,
- int nComps, int depth);
+ void gouraudFillTriangle(double x0, double y0, double *color0,
+ double x1, double y1, double *color1,
+ double x2, double y2, double *color2,
+ GfxGouraudTriangleShading *shading, int depth);
void doPatchMeshShFill(GfxPatchMeshShading *shading);
- void fillPatch(GfxPatch *patch, int nComps, int depth);
+ void fillPatch(GfxPatch *patch, GfxPatchMeshShading *shading, int depth);
void doEndPath();
// path clipping operators
@@ -301,6 +317,7 @@ private:
// text state operators
void opSetCharSpacing(Object args[], int numArgs);
void opSetFont(Object args[], int numArgs);
+ void doSetFont(GfxFont *font, double size);
void opSetTextLeading(Object args[], int numArgs);
void opSetTextRender(Object args[], int numArgs);
void opSetTextRise(Object args[], int numArgs);
@@ -324,7 +341,7 @@ private:
// XObject operators
void opXObject(Object args[], int numArgs);
void doImage(Object *ref, Stream *str, GBool inlineImg);
- void doForm(Object *str);
+ void doForm(Object *strRef, Object *str);
// in-line image operators
void opBeginImage(Object args[], int numArgs);
diff -uNrp 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() {
}
-GfxColorSpace *GfxColorSpace::parse(Object *csObj, int recursion) {
+GfxColorSpace *GfxColorSpace::parse(Object *csObj,
+ int recursion) {
GfxColorSpace *cs;
Object obj1;
@@ -112,11 +113,11 @@ GfxColorSpace *GfxColorSpace::parse(Obje
cs = NULL;
if (csObj->isName()) {
if (csObj->isName("DeviceGray") || csObj->isName("G")) {
- cs = new GfxDeviceGrayColorSpace();
+ cs = GfxColorSpace::create(csDeviceGray);
} else if (csObj->isName("DeviceRGB") || csObj->isName("RGB")) {
- cs = new GfxDeviceRGBColorSpace();
+ cs = GfxColorSpace::create(csDeviceRGB);
} else if (csObj->isName("DeviceCMYK") || csObj->isName("CMYK")) {
- cs = new GfxDeviceCMYKColorSpace();
+ cs = GfxColorSpace::create(csDeviceCMYK);
} else if (csObj->isName("Pattern")) {
cs = new GfxPatternColorSpace(NULL);
} else {
@@ -125,11 +126,11 @@ GfxColorSpace *GfxColorSpace::parse(Obje
} else if (csObj->isArray() && csObj->arrayGetLength() > 0) {
csObj->arrayGet(0, &obj1);
if (obj1.isName("DeviceGray") || obj1.isName("G")) {
- cs = new GfxDeviceGrayColorSpace();
+ cs = GfxColorSpace::create(csDeviceGray);
} else if (obj1.isName("DeviceRGB") || obj1.isName("RGB")) {
- cs = new GfxDeviceRGBColorSpace();
+ cs = GfxColorSpace::create(csDeviceRGB);
} else if (obj1.isName("DeviceCMYK") || obj1.isName("CMYK")) {
- cs = new GfxDeviceCMYKColorSpace();
+ cs = GfxColorSpace::create(csDeviceCMYK);
} else if (obj1.isName("CalGray")) {
cs = GfxCalGrayColorSpace::parse(csObj->getArray(), recursion);
} else if (obj1.isName("CalRGB")) {
@@ -137,15 +138,20 @@ GfxColorSpace *GfxColorSpace::parse(Obje
} else if (obj1.isName("Lab")) {
cs = GfxLabColorSpace::parse(csObj->getArray(), recursion);
} else if (obj1.isName("ICCBased")) {
- cs = GfxICCBasedColorSpace::parse(csObj->getArray(), recursion);
+ cs = GfxICCBasedColorSpace::parse(csObj->getArray(),
+ recursion);
} else if (obj1.isName("Indexed") || obj1.isName("I")) {
- cs = GfxIndexedColorSpace::parse(csObj->getArray(), recursion);
+ cs = GfxIndexedColorSpace::parse(csObj->getArray(),
+ recursion);
} else if (obj1.isName("Separation")) {
- cs = GfxSeparationColorSpace::parse(csObj->getArray(), recursion);
+ cs = GfxSeparationColorSpace::parse(csObj->getArray(),
+ recursion);
} else if (obj1.isName("DeviceN")) {
- cs = GfxDeviceNColorSpace::parse(csObj->getArray(), recursion);
+ cs = GfxDeviceNColorSpace::parse(csObj->getArray(),
+ recursion);
} else if (obj1.isName("Pattern")) {
- cs = GfxPatternColorSpace::parse(csObj->getArray(), recursion);
+ cs = GfxPatternColorSpace::parse(csObj->getArray(),
+ recursion);
} else {
error(errSyntaxError, -1, "Bad color space");
}
@@ -156,6 +162,20 @@ GfxColorSpace *GfxColorSpace::parse(Obje
return cs;
}
+GfxColorSpace *GfxColorSpace::create(GfxColorSpaceMode mode) {
+ GfxColorSpace *cs;
+
+ cs = NULL;
+ if (mode == csDeviceGray) {
+ cs = new GfxDeviceGrayColorSpace();
+ } else if (mode == csDeviceRGB) {
+ cs = new GfxDeviceRGBColorSpace();
+ } else if (mode == csDeviceCMYK) {
+ cs = new GfxDeviceCMYKColorSpace();
+ }
+ return cs;
+}
+
void GfxColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange,
int maxImgPixel) {
int i;
@@ -185,9 +205,13 @@ GfxDeviceGrayColorSpace::~GfxDeviceGrayC
}
GfxColorSpace *GfxDeviceGrayColorSpace::copy() {
- return new GfxDeviceGrayColorSpace();
+ GfxDeviceGrayColorSpace *cs;
+
+ cs = new GfxDeviceGrayColorSpace();
+ return cs;
}
+
void GfxDeviceGrayColorSpace::getGray(GfxColor *color, GfxGray *gray) {
*gray = clip01(color->c[0]);
}
@@ -233,6 +257,7 @@ GfxColorSpace *GfxCalGrayColorSpace::cop
return cs;
}
+
GfxColorSpace *GfxCalGrayColorSpace::parse(Array *arr, int recursion) {
GfxCalGrayColorSpace *cs;
Object obj1, obj2, obj3;
@@ -311,9 +336,13 @@ GfxDeviceRGBColorSpace::~GfxDeviceRGBCol
}
GfxColorSpace *GfxDeviceRGBColorSpace::copy() {
- return new GfxDeviceRGBColorSpace();
+ GfxDeviceRGBColorSpace *cs;
+
+ cs = new GfxDeviceRGBColorSpace();
+ return cs;
}
+
void GfxDeviceRGBColorSpace::getGray(GfxColor *color, GfxGray *gray) {
*gray = clip01((GfxColorComp)(0.3 * color->c[0] +
0.59 * color->c[1] +
@@ -456,6 +485,7 @@ GfxColorSpace *GfxCalRGBColorSpace::pars
return cs;
}
+
void GfxCalRGBColorSpace::getGray(GfxColor *color, GfxGray *gray) {
*gray = clip01((GfxColorComp)(0.299 * color->c[0] +
0.587 * color->c[1] +
@@ -505,9 +535,13 @@ GfxDeviceCMYKColorSpace::~GfxDeviceCMYKC
}
GfxColorSpace *GfxDeviceCMYKColorSpace::copy() {
- return new GfxDeviceCMYKColorSpace();
+ GfxDeviceCMYKColorSpace *cs;
+
+ cs = new GfxDeviceCMYKColorSpace();
+ return cs;
}
+
void GfxDeviceCMYKColorSpace::getGray(GfxColor *color, GfxGray *gray) {
*gray = clip01((GfxColorComp)(gfxColorComp1 - color->c[3]
- 0.3 * color->c[0]
@@ -706,6 +740,7 @@ GfxColorSpace *GfxLabColorSpace::parse(A
return cs;
}
+
void GfxLabColorSpace::getGray(GfxColor *color, GfxGray *gray) {
GfxRGB rgb;
@@ -720,6 +755,7 @@ void GfxLabColorSpace::getRGB(GfxColor *
double t1, t2;
double r, g, b;
+
// convert L*a*b* to CIE 1931 XYZ color space
t1 = (colToDbl(color->c[0]) + 16) / 116;
t2 = t1 + colToDbl(color->c[1]) / 500;
@@ -756,6 +792,7 @@ void GfxLabColorSpace::getCMYK(GfxColor
GfxRGB rgb;
GfxColorComp c, m, y, k;
+
getRGB(color, &rgb);
c = clip01(gfxColorComp1 - rgb.r);
m = clip01(gfxColorComp1 - rgb.g);
@@ -831,7 +868,8 @@ GfxColorSpace *GfxICCBasedColorSpace::co
return cs;
}
-GfxColorSpace *GfxICCBasedColorSpace::parse(Array *arr, int recursion) {
+GfxColorSpace *GfxICCBasedColorSpace::parse(Array *arr,
+ int recursion) {
GfxICCBasedColorSpace *cs;
Ref iccProfileStreamA;
int nCompsA;
@@ -874,16 +912,17 @@ GfxColorSpace *GfxICCBasedColorSpace::pa
nCompsA = 4;
}
if (dict->lookup("Alternate", &obj2)->isNull() ||
- !(altA = GfxColorSpace::parse(&obj2, recursion + 1))) {
+ !(altA = GfxColorSpace::parse(&obj2,
+ recursion + 1))) {
switch (nCompsA) {
case 1:
- altA = new GfxDeviceGrayColorSpace();
+ altA = GfxColorSpace::create(csDeviceGray);
break;
case 3:
- altA = new GfxDeviceRGBColorSpace();
+ altA = GfxColorSpace::create(csDeviceRGB);
break;
case 4:
- altA = new GfxDeviceCMYKColorSpace();
+ altA = GfxColorSpace::create(csDeviceCMYK);
break;
default:
error(errSyntaxError, -1, "Bad ICCBased color space - invalid N");
@@ -910,6 +949,7 @@ GfxColorSpace *GfxICCBasedColorSpace::pa
return cs;
}
+
void GfxICCBasedColorSpace::getGray(GfxColor *color, GfxGray *gray) {
alt->getGray(color, gray);
}
@@ -981,7 +1021,8 @@ GfxColorSpace *GfxIndexedColorSpace::cop
return cs;
}
-GfxColorSpace *GfxIndexedColorSpace::parse(Array *arr, int recursion) {
+GfxColorSpace *GfxIndexedColorSpace::parse(Array *arr,
+ int recursion) {
GfxIndexedColorSpace *cs;
GfxColorSpace *baseA;
int indexHighA;
@@ -995,7 +1036,8 @@ GfxColorSpace *GfxIndexedColorSpace::par
goto err1;
}
arr->get(1, &obj1);
- if (!(baseA = GfxColorSpace::parse(&obj1, recursion + 1))) {
+ if (!(baseA = GfxColorSpace::parse(&obj1,
+ recursion + 1))) {
error(errSyntaxError, -1, "Bad Indexed color space (base color space)");
goto err2;
}
@@ -1060,6 +1102,7 @@ GfxColorSpace *GfxIndexedColorSpace::par
return NULL;
}
+
GfxColor *GfxIndexedColorSpace::mapColorToBase(GfxColor *color,
GfxColor *baseColor) {
Guchar *p;
@@ -1146,12 +1189,16 @@ GfxSeparationColorSpace::~GfxSeparationC
}
GfxColorSpace *GfxSeparationColorSpace::copy() {
- return new GfxSeparationColorSpace(name->copy(), alt->copy(), func->copy(),
- nonMarking, overprintMask);
+ GfxSeparationColorSpace *cs;
+
+ cs = new GfxSeparationColorSpace(name->copy(), alt->copy(), func->copy(),
+ nonMarking, overprintMask);
+ return cs;
}
//~ handle the 'All' and 'None' colorants
-GfxColorSpace *GfxSeparationColorSpace::parse(Array *arr, int recursion) {
+GfxColorSpace *GfxSeparationColorSpace::parse(Array *arr,
+ int recursion) {
GfxSeparationColorSpace *cs;
GString *nameA;
GfxColorSpace *altA;
@@ -1169,7 +1216,8 @@ GfxColorSpace *GfxSeparationColorSpace::
nameA = new GString(obj1.getName());
obj1.free();
arr->get(2, &obj1);
- if (!(altA = GfxColorSpace::parse(&obj1, recursion + 1))) {
+ if (!(altA = GfxColorSpace::parse(&obj1,
+ recursion + 1))) {
error(errSyntaxError, -1,
"Bad Separation color space (alternate color space)");
goto err3;
@@ -1193,6 +1241,7 @@ GfxColorSpace *GfxSeparationColorSpace::
return NULL;
}
+
void GfxSeparationColorSpace::getGray(GfxColor *color, GfxGray *gray) {
double x;
double c[gfxColorMaxComps];
@@ -1303,12 +1352,16 @@ GfxDeviceNColorSpace::~GfxDeviceNColorSp
}
GfxColorSpace *GfxDeviceNColorSpace::copy() {
- return new GfxDeviceNColorSpace(nComps, names, alt->copy(), func->copy(),
- nonMarking, overprintMask);
+ GfxDeviceNColorSpace *cs;
+
+ cs = new GfxDeviceNColorSpace(nComps, names, alt->copy(), func->copy(),
+ nonMarking, overprintMask);
+ return cs;
}
//~ handle the 'None' colorant
-GfxColorSpace *GfxDeviceNColorSpace::parse(Array *arr, int recursion) {
+GfxColorSpace *GfxDeviceNColorSpace::parse(Array *arr,
+ int recursion) {
GfxDeviceNColorSpace *cs;
int nCompsA;
GString *namesA[gfxColorMaxComps];
@@ -1343,7 +1396,8 @@ GfxColorSpace *GfxDeviceNColorSpace::par
}
obj1.free();
arr->get(2, &obj1);
- if (!(altA = GfxColorSpace::parse(&obj1, recursion + 1))) {
+ if (!(altA = GfxColorSpace::parse(&obj1,
+ recursion + 1))) {
error(errSyntaxError, -1,
"Bad DeviceN color space (alternate color space)");
goto err3;
@@ -1369,6 +1423,7 @@ GfxColorSpace *GfxDeviceNColorSpace::par
return NULL;
}
+
void GfxDeviceNColorSpace::getGray(GfxColor *color, GfxGray *gray) {
double x[gfxColorMaxComps], c[gfxColorMaxComps];
GfxColor color2;
@@ -1438,11 +1493,15 @@ GfxPatternColorSpace::~GfxPatternColorSp
}
GfxColorSpace *GfxPatternColorSpace::copy() {
- return new GfxPatternColorSpace(under ? under->copy() :
- (GfxColorSpace *)NULL);
+ GfxPatternColorSpace *cs;
+
+ cs = new GfxPatternColorSpace(under ? under->copy() :
+ (GfxColorSpace *)NULL);
+ return cs;
}
-GfxColorSpace *GfxPatternColorSpace::parse(Array *arr, int recursion) {
+GfxColorSpace *GfxPatternColorSpace::parse(Array *arr,
+ int recursion) {
GfxPatternColorSpace *cs;
GfxColorSpace *underA;
Object obj1;
@@ -1454,7 +1513,8 @@ GfxColorSpace *GfxPatternColorSpace::par
underA = NULL;
if (arr->getLength() == 2) {
arr->get(1, &obj1);
- if (!(underA = GfxColorSpace::parse(&obj1, recursion + 1))) {
+ if (!(underA = GfxColorSpace::parse(&obj1,
+ recursion + 1))) {
error(errSyntaxError, -1,
"Bad Pattern color space (underlying color space)");
obj1.free();
@@ -1466,6 +1526,7 @@ GfxColorSpace *GfxPatternColorSpace::par
return cs;
}
+
void GfxPatternColorSpace::getGray(GfxColor *color, GfxGray *gray) {
*gray = 0;
}
@@ -1495,24 +1556,26 @@ GfxPattern::GfxPattern(int typeA) {
GfxPattern::~GfxPattern() {
}
-GfxPattern *GfxPattern::parse(Object *obj) {
+GfxPattern *GfxPattern::parse(Object *objRef, Object *obj
+ ) {
GfxPattern *pattern;
- Object obj1;
+ Object typeObj;
if (obj->isDict()) {
- obj->dictLookup("PatternType", &obj1);
+ obj->dictLookup("PatternType", &typeObj);
} else if (obj->isStream()) {
- obj->streamGetDict()->lookup("PatternType", &obj1);
+ obj->streamGetDict()->lookup("PatternType", &typeObj);
} else {
return NULL;
}
pattern = NULL;
- if (obj1.isInt() && obj1.getInt() == 1) {
- pattern = GfxTilingPattern::parse(obj);
- } else if (obj1.isInt() && obj1.getInt() == 2) {
- pattern = GfxShadingPattern::parse(obj);
+ if (typeObj.isInt() && typeObj.getInt() == 1) {
+ pattern = GfxTilingPattern::parse(objRef, obj);
+ } else if (typeObj.isInt() && typeObj.getInt() == 2) {
+ pattern = GfxShadingPattern::parse(obj
+ );
}
- obj1.free();
+ typeObj.free();
return pattern;
}
@@ -1520,7 +1583,7 @@ GfxPattern *GfxPattern::parse(Object *ob
// GfxTilingPattern
//------------------------------------------------------------------------
-GfxTilingPattern *GfxTilingPattern::parse(Object *patObj) {
+GfxTilingPattern *GfxTilingPattern::parse(Object *patObjRef, Object *patObj) {
GfxTilingPattern *pat;
Dict *dict;
int paintTypeA, tilingTypeA;
@@ -1597,7 +1660,7 @@ GfxTilingPattern *GfxTilingPattern::pars
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::pars
GfxTilingPattern::GfxTilingPattern(int paintTypeA, int tilingTypeA,
double *bboxA, double xStepA, double yStepA,
Object *resDictA, double *matrixA,
- Object *contentStreamA):
+ Object *contentStreamRefA):
GfxPattern(1)
{
int i;
@@ -1621,24 +1684,25 @@ GfxTilingPattern::GfxTilingPattern(int p
for (i = 0; i < 6; ++i) {
matrix[i] = matrixA[i];
}
- contentStreamA->copy(&contentStream);
+ contentStreamRefA->copy(&contentStreamRef);
}
GfxTilingPattern::~GfxTilingPattern() {
resDict.free();
- contentStream.free();
+ contentStreamRef.free();
}
GfxPattern *GfxTilingPattern::copy() {
return new GfxTilingPattern(paintType, tilingType, bbox, xStep, yStep,
- &resDict, matrix, &contentStream);
+ &resDict, matrix, &contentStreamRef);
}
//------------------------------------------------------------------------
// GfxShadingPattern
//------------------------------------------------------------------------
-GfxShadingPattern *GfxShadingPattern::parse(Object *patObj) {
+GfxShadingPattern *GfxShadingPattern::parse(Object *patObj
+ ) {
Dict *dict;
GfxShading *shadingA;
double matrixA[6];
@@ -1651,7 +1715,8 @@ GfxShadingPattern *GfxShadingPattern::pa
dict = patObj->getDict();
dict->lookup("Shading", &obj1);
- shadingA = GfxShading::parse(&obj1);
+ shadingA = GfxShading::parse(&obj1
+ );
obj1.free();
if (!shadingA) {
return NULL;
@@ -1724,7 +1789,8 @@ GfxShading::~GfxShading() {
}
}
-GfxShading *GfxShading::parse(Object *obj) {
+GfxShading *GfxShading::parse(Object *obj
+ ) {
GfxShading *shading;
Dict *dict;
int typeA;
@@ -1748,17 +1814,21 @@ GfxShading *GfxShading::parse(Object *ob
switch (typeA) {
case 1:
- shading = GfxFunctionShading::parse(dict);
+ shading = GfxFunctionShading::parse(dict
+ );
break;
case 2:
- shading = GfxAxialShading::parse(dict);
+ shading = GfxAxialShading::parse(dict
+ );
break;
case 3:
- shading = GfxRadialShading::parse(dict);
+ shading = GfxRadialShading::parse(dict
+ );
break;
case 4:
if (obj->isStream()) {
- shading = GfxGouraudTriangleShading::parse(4, dict, obj->getStream());
+ shading = GfxGouraudTriangleShading::parse(4, dict, obj->getStream()
+ );
} else {
error(errSyntaxError, -1, "Invalid Type 4 shading object");
goto err1;
@@ -1766,7 +1836,8 @@ GfxShading *GfxShading::parse(Object *ob
break;
case 5:
if (obj->isStream()) {
- shading = GfxGouraudTriangleShading::parse(5, dict, obj->getStream());
+ shading = GfxGouraudTriangleShading::parse(5, dict, obj->getStream()
+ );
} else {
error(errSyntaxError, -1, "Invalid Type 5 shading object");
goto err1;
@@ -1774,7 +1845,8 @@ GfxShading *GfxShading::parse(Object *ob
break;
case 6:
if (obj->isStream()) {
- shading = GfxPatchMeshShading::parse(6, dict, obj->getStream());
+ shading = GfxPatchMeshShading::parse(6, dict, obj->getStream()
+ );
} else {
error(errSyntaxError, -1, "Invalid Type 6 shading object");
goto err1;
@@ -1782,7 +1854,8 @@ GfxShading *GfxShading::parse(Object *ob
break;
case 7:
if (obj->isStream()) {
- shading = GfxPatchMeshShading::parse(7, dict, obj->getStream());
+ shading = GfxPatchMeshShading::parse(7, dict, obj->getStream()
+ );
} else {
error(errSyntaxError, -1, "Invalid Type 7 shading object");
goto err1;
@@ -1799,12 +1872,14 @@ GfxShading *GfxShading::parse(Object *ob
return NULL;
}
-GBool GfxShading::init(Dict *dict) {
+GBool GfxShading::init(Dict *dict
+ ) {
Object obj1, obj2;
int i;
dict->lookup("ColorSpace", &obj1);
- if (!(colorSpace = GfxColorSpace::parse(&obj1))) {
+ if (!(colorSpace = GfxColorSpace::parse(&obj1
+ ))) {
error(errSyntaxError, -1, "Bad color space in shading dictionary");
obj1.free();
return gFalse;
@@ -1901,7 +1976,8 @@ GfxFunctionShading::~GfxFunctionShading(
}
}
-GfxFunctionShading *GfxFunctionShading::parse(Dict *dict) {
+GfxFunctionShading *GfxFunctionShading::parse(Dict *dict
+ ) {
GfxFunctionShading *shading;
double x0A, y0A, x1A, y1A;
double matrixA[6];
@@ -1916,9 +1992,9 @@ GfxFunctionShading *GfxFunctionShading::
obj1.arrayGetLength() == 4) {
x0A = obj1.arrayGet(0, &obj2)->getNum();
obj2.free();
- y0A = obj1.arrayGet(1, &obj2)->getNum();
+ x1A = obj1.arrayGet(1, &obj2)->getNum();
obj2.free();
- x1A = obj1.arrayGet(2, &obj2)->getNum();
+ y0A = obj1.arrayGet(2, &obj2)->getNum();
obj2.free();
y1A = obj1.arrayGet(3, &obj2)->getNum();
obj2.free();
@@ -1970,7 +2046,8 @@ GfxFunctionShading *GfxFunctionShading::
shading = new GfxFunctionShading(x0A, y0A, x1A, y1A, matrixA,
funcsA, nFuncsA);
- if (!shading->init(dict)) {
+ if (!shading->init(dict
+ )) {
delete shading;
return NULL;
}
@@ -2060,7 +2137,8 @@ GfxAxialShading::~GfxAxialShading() {
}
}
-GfxAxialShading *GfxAxialShading::parse(Dict *dict) {
+GfxAxialShading *GfxAxialShading::parse(Dict *dict
+ ) {
GfxAxialShading *shading;
double x0A, y0A, x1A, y1A;
double t0A, t1A;
@@ -2137,7 +2215,8 @@ GfxAxialShading *GfxAxialShading::parse(
shading = new GfxAxialShading(x0A, y0A, x1A, y1A, t0A, t1A,
funcsA, nFuncsA, extend0A, extend1A);
- if (!shading->init(dict)) {
+ if (!shading->init(dict
+ )) {
delete shading;
return NULL;
}
@@ -2226,7 +2305,8 @@ GfxRadialShading::~GfxRadialShading() {
}
}
-GfxRadialShading *GfxRadialShading::parse(Dict *dict) {
+GfxRadialShading *GfxRadialShading::parse(Dict *dict
+ ) {
GfxRadialShading *shading;
double x0A, y0A, r0A, x1A, y1A, r1A;
double t0A, t1A;
@@ -2307,7 +2387,8 @@ GfxRadialShading *GfxRadialShading::pars
shading = new GfxRadialShading(x0A, y0A, r0A, x1A, y1A, r1A, t0A, t1A,
funcsA, nFuncsA, extend0A, extend1A);
- if (!shading->init(dict)) {
+ if (!shading->init(dict
+ )) {
delete shading;
return NULL;
}
@@ -2413,7 +2494,7 @@ GfxGouraudTriangleShading::GfxGouraudTri
int typeA,
GfxGouraudVertex *verticesA, int nVerticesA,
int (*trianglesA)[3], int nTrianglesA,
- Function **funcsA, int nFuncsA):
+ int nCompsA, Function **funcsA, int nFuncsA):
GfxShading(typeA)
{
int i;
@@ -2422,6 +2503,7 @@ GfxGouraudTriangleShading::GfxGouraudTri
nVertices = nVerticesA;
triangles = trianglesA;
nTriangles = nTrianglesA;
+ nComps = nCompsA;
nFuncs = nFuncsA;
for (i = 0; i < nFuncs; ++i) {
funcs[i] = funcsA[i];
@@ -2440,6 +2522,7 @@ GfxGouraudTriangleShading::GfxGouraudTri
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::~GfxGouraudTr
}
}
-GfxGouraudTriangleShading *GfxGouraudTriangleShading::parse(int typeA,
- Dict *dict,
- Stream *str) {
+GfxGouraudTriangleShading *GfxGouraudTriangleShading::parse(
+ int typeA, Dict *dict, Stream *str
+ ) {
GfxGouraudTriangleShading *shading;
Function *funcsA[gfxColorMaxComps];
int nFuncsA;
@@ -2469,7 +2552,7 @@ GfxGouraudTriangleShading *GfxGouraudTri
double cMul[gfxColorMaxComps];
GfxGouraudVertex *verticesA;
int (*trianglesA)[3];
- int nComps, nVerticesA, nTrianglesA, vertSize, triSize;
+ int nCompsA, nVerticesA, nTrianglesA, vertSize, triSize;
Guint x, y, flag;
Guint c[gfxColorMaxComps];
GfxShadingBitBuf *bitBuf;
@@ -2531,7 +2614,7 @@ GfxGouraudTriangleShading *GfxGouraudTri
obj2.free();
cMul[i] = (cMax[i] - cMin[i]) / (double)((1 << compBits) - 1);
}
- nComps = i;
+ nCompsA = i;
} else {
error(errSyntaxError, -1,
"Missing or invalid Decode array in shading dictionary");
@@ -2585,12 +2668,12 @@ GfxGouraudTriangleShading *GfxGouraudTri
!bitBuf->getBits(coordBits, &y)) {
break;
}
- for (i = 0; i < nComps; ++i) {
+ for (i = 0; i < nCompsA; ++i) {
if (!bitBuf->getBits(compBits, &c[i])) {
break;
}
}
- if (i < nComps) {
+ if (i < nCompsA) {
break;
}
if (nVerticesA == vertSize) {
@@ -2600,9 +2683,8 @@ GfxGouraudTriangleShading *GfxGouraudTri
}
verticesA[nVerticesA].x = xMin + xMul * (double)x;
verticesA[nVerticesA].y = yMin + yMul * (double)y;
- for (i = 0; i < nComps; ++i) {
- verticesA[nVerticesA].color.c[i] =
- dblToCol(cMin[i] + cMul[i] * (double)c[i]);
+ for (i = 0; i < nCompsA; ++i) {
+ verticesA[nVerticesA].color[i] = cMin[i] + cMul[i] * (double)c[i];
}
++nVerticesA;
bitBuf->flushBits();
@@ -2657,8 +2739,9 @@ GfxGouraudTriangleShading *GfxGouraudTri
shading = new GfxGouraudTriangleShading(typeA, verticesA, nVerticesA,
trianglesA, nTrianglesA,
- funcsA, nFuncsA);
- if (!shading->init(dict)) {
+ nCompsA, funcsA, nFuncsA);
+ if (!shading->init(dict
+ )) {
delete shading;
return NULL;
}
@@ -2676,54 +2759,46 @@ GfxShading *GfxGouraudTriangleShading::c
void GfxGouraudTriangleShading::getTriangle(
int i,
- double *x0, double *y0, GfxColor *color0,
- double *x1, double *y1, GfxColor *color1,
- double *x2, double *y2, GfxColor *color2) {
- double in;
- double out[gfxColorMaxComps];
+ double *x0, double *y0, double *color0,
+ double *x1, double *y1, double *color1,
+ double *x2, double *y2, double *color2) {
int v, j;
v = triangles[i][0];
*x0 = vertices[v].x;
*y0 = vertices[v].y;
- if (nFuncs > 0) {
- in = colToDbl(vertices[v].color.c[0]);
- for (j = 0; j < nFuncs; ++j) {
- funcs[j]->transform(&in, &out[j]);
- }
- for (j = 0; j < gfxColorMaxComps; ++j) {
- color0->c[j] = dblToCol(out[j]);
- }
- } else {
- *color0 = vertices[v].color;
+ for (j = 0; j < nComps; ++j) {
+ color0[j] = vertices[v].color[j];
}
v = triangles[i][1];
*x1 = vertices[v].x;
*y1 = vertices[v].y;
- if (nFuncs > 0) {
- in = colToDbl(vertices[v].color.c[0]);
- for (j = 0; j < nFuncs; ++j) {
- funcs[j]->transform(&in, &out[j]);
- }
- for (j = 0; j < gfxColorMaxComps; ++j) {
- color1->c[j] = dblToCol(out[j]);
- }
- } else {
- *color1 = vertices[v].color;
+ for (j = 0; j < nComps; ++j) {
+ color1[j] = vertices[v].color[j];
}
v = triangles[i][2];
*x2 = vertices[v].x;
*y2 = vertices[v].y;
+ for (j = 0; j < nComps; ++j) {
+ color2[j] = vertices[v].color[j];
+ }
+}
+
+void GfxGouraudTriangleShading::getColor(double *in, GfxColor *out) {
+ double c[gfxColorMaxComps];
+ int i;
+
if (nFuncs > 0) {
- in = colToDbl(vertices[v].color.c[0]);
- for (j = 0; j < nFuncs; ++j) {
- funcs[j]->transform(&in, &out[j]);
+ for (i = 0; i < nFuncs; ++i) {
+ funcs[i]->transform(in, &c[i]);
}
- for (j = 0; j < gfxColorMaxComps; ++j) {
- color2->c[j] = dblToCol(out[j]);
+ for (i = 0; i < colorSpace->getNComps(); ++i) {
+ out->c[i] = dblToCol(c[i]);
}
} else {
- *color2 = vertices[v].color;
+ for (i = 0; i < nComps; ++i) {
+ out->c[i] = dblToCol(in[i]);
+ }
}
}
@@ -2733,6 +2808,7 @@ void GfxGouraudTriangleShading::getTrian
GfxPatchMeshShading::GfxPatchMeshShading(int typeA,
GfxPatch *patchesA, int nPatchesA,
+ int nCompsA,
Function **funcsA, int nFuncsA):
GfxShading(typeA)
{
@@ -2740,6 +2816,7 @@ GfxPatchMeshShading::GfxPatchMeshShading
patches = patchesA;
nPatches = nPatchesA;
+ nComps = nCompsA;
nFuncs = nFuncsA;
for (i = 0; i < nFuncs; ++i) {
funcs[i] = funcsA[i];
@@ -2754,6 +2831,7 @@ GfxPatchMeshShading::GfxPatchMeshShading
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::~GfxPatchMeshShadin
}
GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
- Stream *str) {
+ Stream *str
+ ) {
GfxPatchMeshShading *shading;
Function *funcsA[gfxColorMaxComps];
int nFuncsA;
@@ -2780,11 +2859,11 @@ GfxPatchMeshShading *GfxPatchMeshShading
double xMul, yMul;
double cMul[gfxColorMaxComps];
GfxPatch *patchesA, *p;
- int nComps, nPatchesA, patchesSize, nPts, nColors;
+ int nCompsA, nPatchesA, patchesSize, nPts, nColors;
Guint flag;
double x[16], y[16];
Guint xi, yi;
- GfxColorComp c[4][gfxColorMaxComps];
+ double c[4][gfxColorMaxComps];
Guint ci;
GfxShadingBitBuf *bitBuf;
Object obj1, obj2;
@@ -2833,7 +2912,7 @@ GfxPatchMeshShading *GfxPatchMeshShading
obj2.free();
cMul[i] = (cMax[i] - cMin[i]) / (double)((1 << compBits) - 1);
}
- nComps = i;
+ nCompsA = i;
} else {
error(errSyntaxError, -1,
"Missing or invalid Decode array in shading dictionary");
@@ -2907,13 +2986,13 @@ GfxPatchMeshShading *GfxPatchMeshShading
break;
}
for (i = 0; i < nColors; ++i) {
- for (j = 0; j < nComps; ++j) {
+ for (j = 0; j < nCompsA; ++j) {
if (!bitBuf->getBits(compBits, &ci)) {
break;
}
- c[i][j] = dblToCol(cMin[j] + cMul[j] * (double)ci);
+ c[i][j] = cMin[j] + cMul[j] * (double)ci;
}
- if (j < nComps) {
+ if (j < nCompsA) {
break;
}
}
@@ -2953,11 +3032,11 @@ GfxPatchMeshShading *GfxPatchMeshShading
p->y[2][0] = y[10];
p->x[1][0] = x[11];
p->y[1][0] = y[11];
- for (j = 0; j < nComps; ++j) {
- p->color[0][0].c[j] = c[0][j];
- p->color[0][1].c[j] = c[1][j];
- p->color[1][1].c[j] = c[2][j];
- p->color[1][0].c[j] = c[3][j];
+ for (j = 0; j < nCompsA; ++j) {
+ p->color[0][0][j] = c[0][j];
+ p->color[0][1][j] = c[1][j];
+ p->color[1][1][j] = c[2][j];
+ p->color[1][0][j] = c[3][j];
}
break;
case 1:
@@ -2985,11 +3064,11 @@ GfxPatchMeshShading *GfxPatchMeshShading
p->y[2][0] = y[6];
p->x[1][0] = x[7];
p->y[1][0] = y[7];
- for (j = 0; j < nComps; ++j) {
- p->color[0][0].c[j] = patchesA[nPatchesA-1].color[0][1].c[j];
- p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][1].c[j];
- p->color[1][1].c[j] = c[0][j];
- p->color[1][0].c[j] = c[1][j];
+ for (j = 0; j < nCompsA; ++j) {
+ p->color[0][0][j] = patchesA[nPatchesA-1].color[0][1][j];
+ p->color[0][1][j] = patchesA[nPatchesA-1].color[1][1][j];
+ p->color[1][1][j] = c[0][j];
+ p->color[1][0][j] = c[1][j];
}
break;
case 2:
@@ -3017,11 +3096,11 @@ GfxPatchMeshShading *GfxPatchMeshShading
p->y[2][0] = y[6];
p->x[1][0] = x[7];
p->y[1][0] = y[7];
- for (j = 0; j < nComps; ++j) {
- p->color[0][0].c[j] = patchesA[nPatchesA-1].color[1][1].c[j];
- p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][0].c[j];
- p->color[1][1].c[j] = c[0][j];
- p->color[1][0].c[j] = c[1][j];
+ for (j = 0; j < nCompsA; ++j) {
+ p->color[0][0][j] = patchesA[nPatchesA-1].color[1][1][j];
+ p->color[0][1][j] = patchesA[nPatchesA-1].color[1][0][j];
+ p->color[1][1][j] = c[0][j];
+ p->color[1][0][j] = c[1][j];
}
break;
case 3:
@@ -3049,11 +3128,11 @@ GfxPatchMeshShading *GfxPatchMeshShading
p->y[2][0] = y[6];
p->x[1][0] = x[7];
p->y[1][0] = y[7];
- for (j = 0; j < nComps; ++j) {
- p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][0].c[j];
- p->color[0][1].c[j] = patchesA[nPatchesA-1].color[0][0].c[j];
- p->color[1][1].c[j] = c[0][j];
- p->color[1][0].c[j] = c[1][j];
+ for (j = 0; j < nCompsA; ++j) {
+ p->color[0][1][j] = patchesA[nPatchesA-1].color[1][0][j];
+ p->color[0][1][j] = patchesA[nPatchesA-1].color[0][0][j];
+ p->color[1][1][j] = c[0][j];
+ p->color[1][0][j] = c[1][j];
}
break;
}
@@ -3092,11 +3171,11 @@ GfxPatchMeshShading *GfxPatchMeshShading
p->y[2][2] = y[14];
p->x[2][1] = x[15];
p->y[2][1] = y[15];
- for (j = 0; j < nComps; ++j) {
- p->color[0][0].c[j] = c[0][j];
- p->color[0][1].c[j] = c[1][j];
- p->color[1][1].c[j] = c[2][j];
- p->color[1][0].c[j] = c[3][j];
+ for (j = 0; j < nCompsA; ++j) {
+ p->color[0][0][j] = c[0][j];
+ p->color[0][1][j] = c[1][j];
+ p->color[1][1][j] = c[2][j];
+ p->color[1][0][j] = c[3][j];
}
break;
case 1:
@@ -3132,11 +3211,11 @@ GfxPatchMeshShading *GfxPatchMeshShading
p->y[2][2] = y[10];
p->x[2][1] = x[11];
p->y[2][1] = y[11];
- for (j = 0; j < nComps; ++j) {
- p->color[0][0].c[j] = patchesA[nPatchesA-1].color[0][1].c[j];
- p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][1].c[j];
- p->color[1][1].c[j] = c[0][j];
- p->color[1][0].c[j] = c[1][j];
+ for (j = 0; j < nCompsA; ++j) {
+ p->color[0][0][j] = patchesA[nPatchesA-1].color[0][1][j];
+ p->color[0][1][j] = patchesA[nPatchesA-1].color[1][1][j];
+ p->color[1][1][j] = c[0][j];
+ p->color[1][0][j] = c[1][j];
}
break;
case 2:
@@ -3172,11 +3251,11 @@ GfxPatchMeshShading *GfxPatchMeshShading
p->y[2][2] = y[10];
p->x[2][1] = x[11];
p->y[2][1] = y[11];
- for (j = 0; j < nComps; ++j) {
- p->color[0][0].c[j] = patchesA[nPatchesA-1].color[1][1].c[j];
- p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][0].c[j];
- p->color[1][1].c[j] = c[0][j];
- p->color[1][0].c[j] = c[1][j];
+ for (j = 0; j < nCompsA; ++j) {
+ p->color[0][0][j] = patchesA[nPatchesA-1].color[1][1][j];
+ p->color[0][1][j] = patchesA[nPatchesA-1].color[1][0][j];
+ p->color[1][1][j] = c[0][j];
+ p->color[1][0][j] = c[1][j];
}
break;
case 3:
@@ -3212,11 +3291,11 @@ GfxPatchMeshShading *GfxPatchMeshShading
p->y[2][2] = y[10];
p->x[2][1] = x[11];
p->y[2][1] = y[11];
- for (j = 0; j < nComps; ++j) {
- p->color[0][0].c[j] = patchesA[nPatchesA-1].color[1][0].c[j];
- p->color[0][1].c[j] = patchesA[nPatchesA-1].color[0][0].c[j];
- p->color[1][1].c[j] = c[0][j];
- p->color[1][0].c[j] = c[1][j];
+ for (j = 0; j < nCompsA; ++j) {
+ p->color[0][0][j] = patchesA[nPatchesA-1].color[1][0][j];
+ p->color[0][1][j] = patchesA[nPatchesA-1].color[0][0][j];
+ p->color[1][1][j] = c[0][j];
+ p->color[1][0][j] = c[1][j];
}
break;
}
@@ -3273,8 +3352,9 @@ GfxPatchMeshShading *GfxPatchMeshShading
}
shading = new GfxPatchMeshShading(typeA, patchesA, nPatchesA,
- funcsA, nFuncsA);
- if (!shading->init(dict)) {
+ nCompsA, funcsA, nFuncsA);
+ if (!shading->init(dict
+ )) {
delete shading;
return NULL;
}
@@ -3290,6 +3370,24 @@ GfxShading *GfxPatchMeshShading::copy()
return new GfxPatchMeshShading(this);
}
+void GfxPatchMeshShading::getColor(double *in, GfxColor *out) {
+ double c[gfxColorMaxComps];
+ int i;
+
+ if (nFuncs > 0) {
+ for (i = 0; i < nFuncs; ++i) {
+ funcs[i]->transform(in, &c[i]);
+ }
+ for (i = 0; i < colorSpace->getNComps(); ++i) {
+ out->c[i] = dblToCol(c[i]);
+ }
+ } else {
+ for (i = 0; i < nComps; ++i) {
+ out->c[i] = dblToCol(in[i]);
+ }
+ }
+}
+
//------------------------------------------------------------------------
// GfxImageColorMap
//------------------------------------------------------------------------
@@ -3310,7 +3408,11 @@ GfxImageColorMap::GfxImageColorMap(int b
// bits per component and color space
bits = bitsA;
- maxPixel = (1 << bits) - 1;
+ if (bits <= 8) {
+ maxPixel = (1 << bits) - 1;
+ } else {
+ maxPixel = 0xff;
+ }
colorSpace = colorSpaceA;
// initialize
@@ -3431,7 +3533,11 @@ GfxImageColorMap::GfxImageColorMap(GfxIm
lookup[k] = NULL;
lookup2[k] = NULL;
}
- n = 1 << bits;
+ if (bits <= 8) {
+ n = 1 << bits;
+ } else {
+ n = 256;
+ }
for (k = 0; k < nComps; ++k) {
lookup[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp));
memcpy(lookup[k], colorMap->lookup[k], n * sizeof(GfxColorComp));
@@ -3521,7 +3627,11 @@ void GfxImageColorMap::getCMYK(Guchar *x
void GfxImageColorMap::getColor(Guchar *x, GfxColor *color) {
int maxPixel, i;
- maxPixel = (1 << bits) - 1;
+ if (bits <= 8) {
+ maxPixel = (1 << bits) - 1;
+ } else {
+ maxPixel = 0xff;
+ }
for (i = 0; i < nComps; ++i) {
color->c[i] = dblToCol(decodeLow[i] + (x[i] * decodeRange[i]) / maxPixel);
}
@@ -3811,7 +3921,8 @@ void GfxPath::offset(double dx, double d
//------------------------------------------------------------------------
GfxState::GfxState(double hDPIA, double vDPIA, PDFRectangle *pageBox,
- int rotateA, GBool upsideDown) {
+ int rotateA, GBool upsideDown
+ ) {
double kx, ky;
hDPI = hDPIA;
@@ -3861,8 +3972,8 @@ GfxState::GfxState(double hDPIA, double
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 -uNrp 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 @@ public:
virtual GfxColorSpaceMode getMode() = 0;
// Construct a color space. Returns NULL if unsuccessful.
- static GfxColorSpace *parse(Object *csObj, int recursion = 0);
+ static GfxColorSpace *parse(Object *csObj,
+ int recursion = 0);
+
+ // Construct a simple color space. The argument can be
+ // csDeviceGray, csDeviceRGB, or csDeviceCMYK.
+ static GfxColorSpace *create(GfxColorSpaceMode mode);
+
// Convert to gray, RGB, or CMYK.
virtual void getGray(GfxColor *color, GfxGray *gray) = 0;
@@ -381,7 +387,8 @@ public:
virtual GfxColorSpaceMode getMode() { return csICCBased; }
// Construct an ICCBased color space. Returns NULL if unsuccessful.
- static GfxColorSpace *parse(Array *arr, int recursion);
+ static GfxColorSpace *parse(Array *arr,
+ int recursion);
virtual void getGray(GfxColor *color, GfxGray *gray);
virtual void getRGB(GfxColor *color, GfxRGB *rgb);
@@ -418,7 +425,8 @@ public:
virtual GfxColorSpaceMode getMode() { return csIndexed; }
// Construct an Indexed color space. Returns NULL if unsuccessful.
- static GfxColorSpace *parse(Array *arr, int recursion);
+ static GfxColorSpace *parse(Array *arr,
+ int recursion);
virtual void getGray(GfxColor *color, GfxGray *gray);
virtual void getRGB(GfxColor *color, GfxRGB *rgb);
@@ -457,7 +465,8 @@ public:
virtual GfxColorSpaceMode getMode() { return csSeparation; }
// Construct a Separation color space. Returns NULL if unsuccessful.
- static GfxColorSpace *parse(Array *arr, int recursion);
+ static GfxColorSpace *parse(Array *arr,
+ int recursion);
virtual void getGray(GfxColor *color, GfxGray *gray);
virtual void getRGB(GfxColor *color, GfxRGB *rgb);
@@ -499,7 +508,8 @@ public:
virtual GfxColorSpaceMode getMode() { return csDeviceN; }
// Construct a DeviceN color space. Returns NULL if unsuccessful.
- static GfxColorSpace *parse(Array *arr, int recursion);
+ static GfxColorSpace *parse(Array *arr,
+ int recursion);
virtual void getGray(GfxColor *color, GfxGray *gray);
virtual void getRGB(GfxColor *color, GfxRGB *rgb);
@@ -542,7 +552,8 @@ public:
virtual GfxColorSpaceMode getMode() { return csPattern; }
// Construct a Pattern color space. Returns NULL if unsuccessful.
- static GfxColorSpace *parse(Array *arr, int recursion);
+ static GfxColorSpace *parse(Array *arr,
+ int recursion);
virtual void getGray(GfxColor *color, GfxGray *gray);
virtual void getRGB(GfxColor *color, GfxRGB *rgb);
@@ -570,7 +581,8 @@ public:
GfxPattern(int typeA);
virtual ~GfxPattern();
- static GfxPattern *parse(Object *obj);
+ static GfxPattern *parse(Object *objRef, Object *obj
+ );
virtual GfxPattern *copy() = 0;
@@ -588,7 +600,7 @@ private:
class GfxTilingPattern: public GfxPattern {
public:
- static GfxTilingPattern *parse(Object *patObj);
+ static GfxTilingPattern *parse(Object *patObjRef, Object *patObj);
virtual ~GfxTilingPattern();
virtual GfxPattern *copy();
@@ -601,7 +613,7 @@ public:
Dict *getResDict()
{ return resDict.isDict() ? resDict.getDict() : (Dict *)NULL; }
double *getMatrix() { return matrix; }
- Object *getContentStream() { return &contentStream; }
+ Object *getContentStreamRef() { return &contentStreamRef; }
private:
@@ -616,7 +628,7 @@ private:
double xStep, yStep;
Object resDict;
double matrix[6];
- Object contentStream;
+ Object contentStreamRef;
};
//------------------------------------------------------------------------
@@ -626,7 +638,8 @@ private:
class GfxShadingPattern: public GfxPattern {
public:
- static GfxShadingPattern *parse(Object *patObj);
+ static GfxShadingPattern *parse(Object *patObj
+ );
virtual ~GfxShadingPattern();
virtual GfxPattern *copy();
@@ -653,7 +666,8 @@ public:
GfxShading(GfxShading *shading);
virtual ~GfxShading();
- static GfxShading *parse(Object *obj);
+ static GfxShading *parse(Object *obj
+ );
virtual GfxShading *copy() = 0;
@@ -667,7 +681,8 @@ public:
protected:
- GBool init(Dict *dict);
+ GBool init(Dict *dict
+ );
int type;
GfxColorSpace *colorSpace;
@@ -691,7 +706,8 @@ public:
GfxFunctionShading(GfxFunctionShading *shading);
virtual ~GfxFunctionShading();
- static GfxFunctionShading *parse(Dict *dict);
+ static GfxFunctionShading *parse(Dict *dict
+ );
virtual GfxShading *copy();
@@ -725,7 +741,8 @@ public:
GfxAxialShading(GfxAxialShading *shading);
virtual ~GfxAxialShading();
- static GfxAxialShading *parse(Dict *dict);
+ static GfxAxialShading *parse(Dict *dict
+ );
virtual GfxShading *copy();
@@ -763,7 +780,8 @@ public:
GfxRadialShading(GfxRadialShading *shading);
virtual ~GfxRadialShading();
- static GfxRadialShading *parse(Dict *dict);
+ static GfxRadialShading *parse(Dict *dict
+ );
virtual GfxShading *copy();
@@ -793,7 +811,7 @@ private:
struct GfxGouraudVertex {
double x, y;
- GfxColor color;
+ double color[gfxColorMaxComps];
};
class GfxGouraudTriangleShading: public GfxShading {
@@ -802,18 +820,21 @@ public:
GfxGouraudTriangleShading(int typeA,
GfxGouraudVertex *verticesA, int nVerticesA,
int (*trianglesA)[3], int nTrianglesA,
- Function **funcsA, int nFuncsA);
+ int nCompsA, Function **funcsA, int nFuncsA);
GfxGouraudTriangleShading(GfxGouraudTriangleShading *shading);
virtual ~GfxGouraudTriangleShading();
- static GfxGouraudTriangleShading *parse(int typeA, Dict *dict, Stream *str);
+ static GfxGouraudTriangleShading *parse(int typeA, Dict *dict, Stream *str
+ );
virtual GfxShading *copy();
+ int getNComps() { return nComps; }
int getNTriangles() { return nTriangles; }
- void getTriangle(int i, double *x0, double *y0, GfxColor *color0,
- double *x1, double *y1, GfxColor *color1,
- double *x2, double *y2, GfxColor *color2);
+ void getTriangle(int i, double *x0, double *y0, double *color0,
+ double *x1, double *y1, double *color1,
+ double *x2, double *y2, double *color2);
+ void getColor(double *in, GfxColor *out);
private:
@@ -822,6 +843,7 @@ private:
int (*triangles)[3];
int nTriangles;
Function *funcs[gfxColorMaxComps];
+ int nComps; // number of color components (1 if nFuncs > 0)
int nFuncs;
};
@@ -832,29 +854,33 @@ private:
struct GfxPatch {
double x[4][4];
double y[4][4];
- GfxColor color[2][2];
+ double color[2][2][gfxColorMaxComps];
};
class GfxPatchMeshShading: public GfxShading {
public:
GfxPatchMeshShading(int typeA, GfxPatch *patchesA, int nPatchesA,
- Function **funcsA, int nFuncsA);
+ int nCompsA, Function **funcsA, int nFuncsA);
GfxPatchMeshShading(GfxPatchMeshShading *shading);
virtual ~GfxPatchMeshShading();
- static GfxPatchMeshShading *parse(int typeA, Dict *dict, Stream *str);
+ static GfxPatchMeshShading *parse(int typeA, Dict *dict, Stream *str
+ );
virtual GfxShading *copy();
+ int getNComps() { return nComps; }
int getNPatches() { return nPatches; }
GfxPatch *getPatch(int i) { return &patches[i]; }
+ void getColor(double *in, GfxColor *out);
private:
GfxPatch *patches;
int nPatches;
Function *funcs[gfxColorMaxComps];
+ int nComps; // number of color components (1 if nFuncs > 0)
int nFuncs;
};
@@ -1040,7 +1066,8 @@ public:
// x , page box , page rotation , and
// coordinate system specified by .
GfxState(double hDPIA, double vDPIA, PDFRectangle *pageBox,
- int rotateA, GBool upsideDown);
+ int rotateA, GBool upsideDown
+ );
// Destructor.
~GfxState();
diff -uNrp 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
+#endif
#include
#include
#include
#ifdef ENABLE_PLUGINS
@@ -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
# define strcasecmp stricmp
+# define strncasecmp strnicmp
#endif
#if MULTITHREADED
@@ -84,25 +90,29 @@ static struct {
const char *name;
const char *t1FileName;
const char *ttFileName;
+ const char *macFileName; // may be .dfont, .ttf, or .ttc
+ const char *macFontName; // font name inside .dfont or .ttc
+ const char *obliqueFont; // name of font to oblique
+ double obliqueFactor; // oblique sheer factor
} displayFontTab[] = {
- {"Courier", "n022003l.pfb", "cour.ttf"},
- {"Courier-Bold", "n022004l.pfb", "courbd.ttf"},
- {"Courier-BoldOblique", "n022024l.pfb", "courbi.ttf"},
- {"Courier-Oblique", "n022023l.pfb", "couri.ttf"},
- {"Helvetica", "n019003l.pfb", "arial.ttf"},
- {"Helvetica-Bold", "n019004l.pfb", "arialbd.ttf"},
- {"Helvetica-BoldOblique", "n019024l.pfb", "arialbi.ttf"},
- {"Helvetica-Oblique", "n019023l.pfb", "ariali.ttf"},
- {"Symbol", "s050000l.pfb", NULL},
- {"Times-Bold", "n021004l.pfb", "timesbd.ttf"},
- {"Times-BoldItalic", "n021024l.pfb", "timesbi.ttf"},
- {"Times-Italic", "n021023l.pfb", "timesi.ttf"},
- {"Times-Roman", "n021003l.pfb", "times.ttf"},
- {"ZapfDingbats", "d050000l.pfb", NULL},
+ {"Courier", "n022003l.pfb", "cour.ttf", "Courier", "Courier", NULL, 0},
+ {"Courier-Bold", "n022004l.pfb", "courbd.ttf", "Courier", "Courier Bold", NULL, 0},
+ {"Courier-BoldOblique", "n022024l.pfb", "courbi.ttf", "Courier", "Courier Bold Oblique", "Courier-Bold", 0.212557},
+ {"Courier-Oblique", "n022023l.pfb", "couri.ttf", "Courier", "Courier Oblique", "Courier", 0.212557},
+ {"Helvetica", "n019003l.pfb", "arial.ttf", "Helvetica", "Helvetica", NULL, 0},
+ {"Helvetica-Bold", "n019004l.pfb", "arialbd.ttf", "Helvetica", "Helvetica-Bold", NULL, 0},
+ {"Helvetica-BoldOblique", "n019024l.pfb", "arialbi.ttf", "Helvetica", "Helvetica Bold Oblique", "Helvetica-Bold", 0.212557},
+ {"Helvetica-Oblique", "n019023l.pfb", "ariali.ttf", "Helvetica", "Helvetica Oblique", "Helvetica", 0.212557},
+ {"Symbol", "s050000l.pfb", NULL, "Symbol", "Symbol", NULL, 0},
+ {"Times-Bold", "n021004l.pfb", "timesbd.ttf", "Times", "Times-Bold", NULL, 0},
+ {"Times-BoldItalic", "n021024l.pfb", "timesbi.ttf", "Times", "Times-BoldItalic", NULL, 0},
+ {"Times-Italic", "n021023l.pfb", "timesi.ttf", "Times", "Times-Italic", NULL, 0},
+ {"Times-Roman", "n021003l.pfb", "times.ttf", "Times", "Times-Roman", NULL, 0},
+ {"ZapfDingbats", "d050000l.pfb", NULL, "ZapfDingbats", "Zapf Dingbats", NULL, 0},
{NULL}
};
-#ifdef WIN32
+#ifdef _WIN32
static const char *displayFontDirs[] = {
"c:/windows/fonts",
"c:/winnt/fonts",
@@ -115,10 +125,31 @@ static const char *displayFontDirs[] = {
"/usr/share/fonts/default/Type1",
"/usr/share/fonts/default/ghostscript",
"/usr/share/fonts/type1/gsfonts",
+#if defined(__sun) && defined(__SVR4)
+ "/usr/sfw/share/ghostscript/fonts",
+#endif
NULL
};
#endif
+#ifdef __APPLE__
+static const char *macSystemFontPath = "/System/Library/Fonts";
+#endif
+
+struct Base14FontInfo {
+ Base14FontInfo(GString *fileNameA, int fontNumA, double obliqueA) {
+ fileName = fileNameA;
+ fontNum = fontNumA;
+ oblique = obliqueA;
+ }
+ ~Base14FontInfo() {
+ delete fileName;
+ }
+ GString *fileName;
+ int fontNum;
+ double oblique;
+};
+
//------------------------------------------------------------------------
GlobalParams *globalParams = NULL;
@@ -241,40 +272,36 @@ SysFontInfo *SysFontList::find(GString *
}
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"
@@ -341,15 +368,15 @@ void SysFontList::scanWindowsFonts(char
} 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 @@ void SysFontList::scanWindowsFonts(char
dataLen > 0 && dataLen < sizeof(data)) {
valName[valNameLen] = '\0';
data[dataLen] = '\0';
- n = strlen(data);
+ n = (int)strlen(data);
if (!strcasecmp(data + n - 4, ".ttf") ||
!strcasecmp(data + n - 4, ".ttc")) {
fontPath = new GString(data);
@@ -398,7 +425,7 @@ SysFontInfo *SysFontList::makeWindowsFon
int i;
SysFontType type;
- n = strlen(name);
+ n = (int)strlen(name);
bold = italic = gFalse;
// remove trailing ' (TrueType)'
@@ -419,7 +446,7 @@ SysFontInfo *SysFontList::makeWindowsFon
}
// remove trailing ' Regular'
- if (n > 5 && !strncmp(name + n - 8, " Regular", 8)) {
+ if (n > 8 && !strncmp(name + n - 8, " Regular", 8)) {
n -= 8;
}
@@ -515,9 +542,9 @@ Plugin *Plugin::load(char *type, char *n
appendToPath(path, type);
appendToPath(path, name);
#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;
}
@@ -621,7 +648,7 @@ Plugin::~Plugin() {
// parsing
//------------------------------------------------------------------------
-GlobalParams::GlobalParams(char *cfgFileName) {
+GlobalParams::GlobalParams(const char *cfgFileName) {
UnicodeMap *map;
GString *fileName;
FILE *f;
@@ -660,6 +687,7 @@ GlobalParams::GlobalParams(char *cfgFile
fontFiles = new GHash(gTrue);
fontDirs = new GList();
ccFontFiles = new GHash(gTrue);
+ base14SysFonts = new GHash(gTrue);
sysFonts = new SysFontList();
#if HAVE_PAPER_H
char *paperName;
@@ -683,6 +711,7 @@ GlobalParams::GlobalParams(char *cfgFile
psImageableURX = psPaperWidth;
psImageableURY = psPaperHeight;
psCrop = gTrue;
+ psUseCropBoxAsPage = gFalse;
psExpandSmaller = gFalse;
psShrinkLarger = gTrue;
psCenter = gTrue;
@@ -700,12 +729,15 @@ GlobalParams::GlobalParams(char *cfgFile
psPreload = gFalse;
psOPI = gFalse;
psASCIIHex = gFalse;
+ psLZW = gTrue;
psUncompressPreloadedImages = gFalse;
+ psMinLineWidth = 0;
psRasterResolution = 300;
psRasterMono = gFalse;
+ psRasterSliceSize = 20000000;
psAlwaysRasterize = gFalse;
textEncoding = new GString("Latin1");
@@ -713,10 +745,9 @@ GlobalParams::GlobalParams(char *cfgFile
textEOL = eolUnix;
#endif
textPageBreaks = gTrue;
- textKeepTinyChars = gFalse;
+ textKeepTinyChars = gTrue;
initialZoom = new GString("125");
continuousView = gFalse;
- enableT1lib = gTrue;
enableFreeType = gTrue;
disableFreeTypeHinting = gFalse;
antialias = gTrue;
@@ -737,6 +768,8 @@ GlobalParams::GlobalParams(char *cfgFile
movieCommand = NULL;
mapNumericCharNames = gTrue;
mapUnknownCharNames = gFalse;
+ mapExtTrueTypeFontsViaUnicode = gTrue;
+ enableXFA = gTrue;
createDefaultKeyBindings();
printCommands = gFalse;
errQuiet = gFalse;
@@ -791,9 +824,9 @@ GlobalParams::GlobalParams(char *cfgFile
}
}
if (!f) {
#ifdef _WIN32
char buf[512];
- i = GetModuleFileName(NULL, buf, sizeof(buf));
+ i = GetModuleFileNameA(NULL, buf, sizeof(buf));
if (i <= 0 || i >= sizeof(buf)) {
// error or path too long for buffer - just use the current dir
buf[0] = '\0';
@@ -928,7 +961,7 @@ void GlobalParams::createDefaultKeyBindi
keyBindings->append(new KeyBinding('l', xpdfKeyModCtrl,
xpdfKeyContextAny, "redraw"));
keyBindings->append(new KeyBinding('w', xpdfKeyModCtrl,
- xpdfKeyContextAny, "closeWindow"));
+ xpdfKeyContextAny, "closeWindowOrQuit"));
keyBindings->append(new KeyBinding('?', xpdfKeyModNone,
xpdfKeyContextAny, "about"));
keyBindings->append(new KeyBinding('q', xpdfKeyModNone,
@@ -1017,6 +1050,9 @@ void GlobalParams::parseLine(char *buf,
parsePSImageableArea(tokens, fileName, line);
} else if (!cmd->cmp("psCrop")) {
parseYesNo("psCrop", &psCrop, tokens, fileName, line);
+ } else if (!cmd->cmp("psUseCropBoxAsPage")) {
+ parseYesNo("psUseCropBoxAsPage", &psUseCropBoxAsPage,
+ tokens, fileName, line);
} else if (!cmd->cmp("psExpandSmaller")) {
parseYesNo("psExpandSmaller", &psExpandSmaller,
tokens, fileName, line);
@@ -1054,14 +1090,22 @@ void GlobalParams::parseLine(char *buf,
parseYesNo("psOPI", &psOPI, tokens, fileName, line);
} else if (!cmd->cmp("psASCIIHex")) {
parseYesNo("psASCIIHex", &psASCIIHex, tokens, fileName, line);
+ } else if (!cmd->cmp("psLZW")) {
+ parseYesNo("psLZW", &psLZW, tokens, fileName, line);
} else if (!cmd->cmp("psUncompressPreloadedImages")) {
parseYesNo("psUncompressPreloadedImages", &psUncompressPreloadedImages,
tokens, fileName, line);
+ } else if (!cmd->cmp("psMinLineWidth")) {
+ parseFloat("psMinLineWidth", &psMinLineWidth,
+ tokens, fileName, line);
} else if (!cmd->cmp("psRasterResolution")) {
parseFloat("psRasterResolution", &psRasterResolution,
tokens, fileName, line);
} else if (!cmd->cmp("psRasterMono")) {
parseYesNo("psRasterMono", &psRasterMono, tokens, fileName, line);
+ } else if (!cmd->cmp("psRasterSliceSize")) {
+ parseInteger("psRasterSliceSize", &psRasterSliceSize,
+ tokens, fileName, line);
} else if (!cmd->cmp("psAlwaysRasterize")) {
parseYesNo("psAlwaysRasterize", &psAlwaysRasterize,
tokens, fileName, line);
@@ -1079,8 +1123,6 @@ void GlobalParams::parseLine(char *buf,
parseInitialZoom(tokens, fileName, line);
} else if (!cmd->cmp("continuousView")) {
parseYesNo("continuousView", &continuousView, tokens, fileName, line);
- } else if (!cmd->cmp("enableT1lib")) {
- parseYesNo("enableT1lib", &enableT1lib, tokens, fileName, line);
} else if (!cmd->cmp("enableFreeType")) {
parseYesNo("enableFreeType", &enableFreeType, tokens, fileName, line);
} else if (!cmd->cmp("disableFreeTypeHinting")) {
@@ -1133,6 +1175,12 @@ void GlobalParams::parseLine(char *buf,
} else if (!cmd->cmp("mapUnknownCharNames")) {
parseYesNo("mapUnknownCharNames", &mapUnknownCharNames,
tokens, fileName, line);
+ } else if (!cmd->cmp("mapExtTrueTypeFontsViaUnicode")) {
+ parseYesNo("mapExtTrueTypeFontsViaUnicode",
+ &mapExtTrueTypeFontsViaUnicode,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("enableXFA")) {
+ parseYesNo("enableXFA", &enableXFA, tokens, fileName, line);
} else if (!cmd->cmp("bind")) {
parseBind(tokens, fileName, line);
} else if (!cmd->cmp("unbind")) {
@@ -1148,6 +1196,8 @@ void GlobalParams::parseLine(char *buf,
!cmd->cmp("displayNamedCIDFontX") ||
!cmd->cmp("displayCIDFontX")) {
error(errConfig, -1, "Xpdf no longer supports X fonts");
+ } else if (!cmd->cmp("enableT1lib")) {
+ error(errConfig, -1, "Xpdf no longer uses t1lib");
} else if (!cmd->cmp("t1libControl") || !cmd->cmp("freetypeControl")) {
error(errConfig, -1,
"The t1libControl and freetypeControl options have been replaced by the enableT1lib, enableFreeType, and antialias options");
@@ -1520,6 +1570,7 @@ void GlobalParams::parseScreenType(GList
}
}
+
void GlobalParams::parseBind(GList *tokens, GString *fileName, int line) {
KeyBinding *binding;
GList *cmds;
@@ -1836,6 +1887,7 @@ GlobalParams::~GlobalParams() {
deleteGHash(fontFiles, GString);
deleteGList(fontDirs, GString);
deleteGHash(ccFontFiles, GString);
+ deleteGHash(base14SysFonts, Base14FontInfo);
delete sysFonts;
if (psFile) {
delete psFile;
@@ -1886,35 +1938,77 @@ void GlobalParams::setBaseDir(char *dir)
baseDir = new GString(dir);
}
-void GlobalParams::setupBaseFonts(char *dir) {
- GString *fontName;
- GString *fileName;
-#ifdef WIN32
+#ifdef _WIN32
+static void getWinFontDir(char *winFontDir) {
HMODULE shell32Lib;
BOOL (__stdcall *SHGetSpecialFolderPathFunc)(HWND hwndOwner,
- LPTSTR lpszPath,
+ LPSTR lpszPath,
int nFolder,
BOOL fCreate);
- char winFontDir[MAX_PATH];
-#endif
- FILE *f;
- int i, j;
+ char *p;
+ int i;
-#ifdef WIN32
// SHGetSpecialFolderPath isn't available in older versions of
// shell32.dll (Win95 and WinNT4), so do a dynamic load
winFontDir[0] = '\0';
- if ((shell32Lib = LoadLibrary("shell32.dll"))) {
+ if ((shell32Lib = LoadLibraryA("shell32.dll"))) {
if ((SHGetSpecialFolderPathFunc =
- (BOOL (__stdcall *)(HWND hwndOwner, LPTSTR lpszPath,
+ (BOOL (__stdcall *)(HWND hwndOwner, LPSTR lpszPath,
int nFolder, BOOL fCreate))
GetProcAddress(shell32Lib, "SHGetSpecialFolderPathA"))) {
if (!(*SHGetSpecialFolderPathFunc)(NULL, winFontDir,
CSIDL_FONTS, FALSE)) {
winFontDir[0] = '\0';
}
+ // kludge: Terminal Server changes CSIDL_FONTS to something like
+ // "C:\Users\whatever\Windows\Fonts", which doesn't actually
+ // contain any fonts -- kill that, so we hit the fallback code
+ // below.
+ for (p = winFontDir; *p; ++p) {
+ if (!strncasecmp(p, "\\Users\\", 7)) {
+ winFontDir[0] = '\0';
+ break;
+ }
+ }
+ }
+ }
+ // if something went wrong, or we're on a Terminal Server, try using
+ // %SYSTEMROOT%\Fonts
+ if (!winFontDir[0]) {
+ GetSystemWindowsDirectoryA(winFontDir, MAX_PATH - 6);
+ winFontDir[MAX_PATH - 7] = '\0';
+ i = (int)strlen(winFontDir);
+ if (winFontDir[i-1] != '\\') {
+ winFontDir[i++] = '\\';
}
+ strcpy(winFontDir + i, "Fonts");
}
+}
+#endif
+
+void GlobalParams::setupBaseFonts(char *dir) {
+ GString *fontName;
+ GString *fileName;
+ int fontNum;
+ const char *s;
+ Base14FontInfo *base14;
+#ifdef _WIN32
+ char winFontDir[MAX_PATH];
+#endif
+#ifdef __APPLE__
+ static const char *macFontExts[3] = { "dfont", "ttc", "ttf" };
+ GList *dfontFontNames;
+ GBool found;
+ int k;
+#endif
+ FILE *f;
+ int i, j;
+
+#ifdef _WIN32
+ getWinFontDir(winFontDir);
+#endif
+#ifdef __APPLE__
+ dfontFontNames = NULL;
#endif
for (i = 0; displayFontTab[i].name; ++i) {
if (fontFiles->lookup(displayFontTab[i].name)) {
@@ -1922,6 +2016,7 @@ void GlobalParams::setupBaseFonts(char *
}
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"))) {
@@ -1942,13 +2037,67 @@ void GlobalParams::setupBaseFonts(char *
fileName = NULL;
}
}
- // SHGetSpecialFolderPath(CSIDL_FONTS) doesn't work on Win 2k Server
- // or Win2003 Server, or with older versions of shell32.dll, so check
- // the "standard" directories
- if (displayFontTab[i].ttFileName) {
+#endif
+#ifdef __APPLE__
+ // Check for Mac OS X system fonts.
+ s = displayFontTab[i].macFileName;
+ if (dfontFontNames && i > 0 &&
+ (!s || strcmp(s, displayFontTab[i-1].macFileName))) {
+ deleteGList(dfontFontNames, GString);
+ dfontFontNames = NULL;
+ }
+ if (!fileName && s) {
+ for (j = 0; j < 3; ++j) {
+ fileName = GString::format("{0:s}/{1:s}.{2:s}",
+ macSystemFontPath, s, macFontExts[j]);
+ if (!(f = fopen(fileName->getCString(), "rb"))) {
+ delete fileName;
+ fileName = NULL;
+ } else {
+ fclose(f);
+ found = gFalse;
+ // for .dfont or .ttc, we need to scan the font list
+ if (j < 2) {
+ if (!dfontFontNames) {
+ dfontFontNames =
+ FoFiIdentifier::getFontList(fileName->getCString());
+ }
+ if (dfontFontNames) {
+ for (k = 0; k < dfontFontNames->getLength(); ++k) {
+ if (!((GString *)dfontFontNames->get(k))
+ ->cmp(displayFontTab[i].macFontName)) {
+ fontNum = k;
+ found = gTrue;
+ break;
+ }
+ }
+ }
+ // for .ttf, we just use the font
+ } else {
+ found = gTrue;
+ }
+ if (!found) {
+ delete fileName;
+ fileName = NULL;
+ }
+ break;
+ }
+ }
+ }
+#endif // __APPLE__
+ // On Linux, this checks the "standard" ghostscript font
+ // directories. On Windows, it checks the "standard" system font
+ // directories (because SHGetSpecialFolderPath(CSIDL_FONTS)
+ // doesn't work on Win 2k Server or Win2003 Server, or with older
+ // versions of shell32.dll).
+#ifdef _WIN32
+ s = displayFontTab[i].ttFileName;
+#else
+ s = displayFontTab[i].t1FileName;
+#endif
+ if (!fileName && s) {
for (j = 0; !fileName && displayFontDirs[j]; ++j) {
- fileName = appendToPath(new GString(displayFontDirs[j]),
- displayFontTab[i].ttFileName);
+ fileName = appendToPath(new GString(displayFontDirs[j]), s);
if ((f = fopen(fileName->getCString(), "rb"))) {
fclose(f);
} else {
@@ -1957,28 +2106,35 @@ void GlobalParams::setupBaseFonts(char *
}
}
}
-#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);
}
@@ -2120,6 +2276,25 @@ GString *GlobalParams::findFontFile(GStr
return NULL;
}
+GString *GlobalParams::findBase14FontFile(GString *fontName, int *fontNum,
+ double *oblique) {
+ Base14FontInfo *fi;
+ GString *path;
+
+ lockGlobalParams;
+ if ((fi = (Base14FontInfo *)base14SysFonts->lookup(fontName))) {
+ path = fi->fileName->copy();
+ *fontNum = fi->fontNum;
+ *oblique = fi->oblique;
+ unlockGlobalParams;
+ return path;
+ }
+ unlockGlobalParams;
+ *fontNum = 0;
+ *oblique = 0;
+ return findFontFile(fontName);
+}
+
GString *GlobalParams::findSystemFontFile(GString *fontName,
SysFontType *type,
int *fontNum) {
@@ -2193,6 +2368,15 @@ GBool GlobalParams::getPSCrop() {
return f;
}
+GBool GlobalParams::getPSUseCropBoxAsPage() {
+ GBool f;
+
+ lockGlobalParams;
+ f = psUseCropBoxAsPage;
+ unlockGlobalParams;
+ return f;
+}
+
GBool GlobalParams::getPSExpandSmaller() {
GBool f;
@@ -2242,7 +2426,9 @@ GString *GlobalParams::getPSResidentFont
GString *psName;
lockGlobalParams;
- psName = (GString *)psResidentFonts->lookup(fontName);
+ if ((psName = (GString *)psResidentFonts->lookup(fontName))) {
+ psName = psName->copy();
+ }
unlockGlobalParams;
return psName;
}
@@ -2371,6 +2557,15 @@ GBool GlobalParams::getPSASCIIHex() {
return ah;
}
+GBool GlobalParams::getPSLZW() {
+ GBool ah;
+
+ lockGlobalParams;
+ ah = psLZW;
+ unlockGlobalParams;
+ return ah;
+}
+
GBool GlobalParams::getPSUncompressPreloadedImages() {
GBool ah;
@@ -2380,6 +2575,15 @@ GBool GlobalParams::getPSUncompressPrelo
return ah;
}
+double GlobalParams::getPSMinLineWidth() {
+ double w;
+
+ lockGlobalParams;
+ w = psMinLineWidth;
+ unlockGlobalParams;
+ return w;
+}
+
double GlobalParams::getPSRasterResolution() {
double res;
@@ -2398,6 +2602,15 @@ GBool GlobalParams::getPSRasterMono() {
return mono;
}
+int GlobalParams::getPSRasterSliceSize() {
+ int slice;
+
+ lockGlobalParams;
+ slice = psRasterSliceSize;
+ unlockGlobalParams;
+ return slice;
+}
+
GBool GlobalParams::getPSAlwaysRasterize() {
GBool rast;
@@ -2461,15 +2674,6 @@ GBool GlobalParams::getContinuousView()
return f;
}
-GBool GlobalParams::getEnableT1lib() {
- GBool f;
-
- lockGlobalParams;
- f = enableT1lib;
- unlockGlobalParams;
- return f;
-}
-
GBool GlobalParams::getEnableFreeType() {
GBool f;
@@ -2597,6 +2801,7 @@ GBool GlobalParams::getDrawAnnotations()
return draw;
}
+
GBool GlobalParams::getMapNumericCharNames() {
GBool map;
@@ -2615,6 +2820,24 @@ GBool GlobalParams::getMapUnknownCharNam
return map;
}
+GBool GlobalParams::getMapExtTrueTypeFontsViaUnicode() {
+ GBool map;
+
+ lockGlobalParams;
+ map = mapExtTrueTypeFontsViaUnicode;
+ unlockGlobalParams;
+ return map;
+}
+
+GBool GlobalParams::getEnableXFA() {
+ GBool enable;
+
+ lockGlobalParams;
+ enable = enableXFA;
+ unlockGlobalParams;
+ return enable;
+}
+
GList *GlobalParams::getKeyBinding(int code, int mods, int context) {
KeyBinding *binding;
GList *cmds;
@@ -2804,6 +3027,12 @@ void GlobalParams::setPSCrop(GBool crop)
unlockGlobalParams;
}
+void GlobalParams::setPSUseCropBoxAsPage(GBool crop) {
+ lockGlobalParams;
+ psUseCropBoxAsPage = crop;
+ unlockGlobalParams;
+}
+
void GlobalParams::setPSExpandSmaller(GBool expand) {
lockGlobalParams;
psExpandSmaller = expand;
@@ -2882,7 +3111,7 @@ void GlobalParams::setPSASCIIHex(GBool h
unlockGlobalParams;
}
-void GlobalParams::setTextEncoding(char *encodingName) {
+void GlobalParams::setTextEncoding(const char *encodingName) {
lockGlobalParams;
delete textEncoding;
textEncoding = new GString(encodingName);
@@ -2930,15 +3159,6 @@ void GlobalParams::setContinuousView(GBo
unlockGlobalParams;
}
-GBool GlobalParams::setEnableT1lib(char *s) {
- GBool ok;
-
- lockGlobalParams;
- ok = parseYesNo2(s, &enableT1lib);
- unlockGlobalParams;
- return ok;
-}
-
GBool GlobalParams::setEnableFreeType(char *s) {
GBool ok;
@@ -3015,6 +3235,18 @@ void GlobalParams::setMapUnknownCharName
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 -uNrp 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 @@ public:
// Initialize the global parameters by attempting to read a config
// file.
- GlobalParams(char *cfgFileName);
+ GlobalParams(const char *cfgFileName);
~GlobalParams();
@@ -193,6 +193,8 @@ public:
FILE *findCMapFile(GString *collection, GString *cMapName);
FILE *findToUnicodeFile(GString *name);
GString *findFontFile(GString *fontName);
+ GString *findBase14FontFile(GString *fontName, int *fontNum,
+ double *oblique);
GString *findSystemFontFile(GString *fontName, SysFontType *type,
int *fontNum);
GString *findCCFontFile(GString *collection);
@@ -202,6 +204,7 @@ public:
void getPSImageableArea(int *llx, int *lly, int *urx, int *ury);
GBool getPSDuplex();
GBool getPSCrop();
+ GBool getPSUseCropBoxAsPage();
GBool getPSExpandSmaller();
GBool getPSShrinkLarger();
GBool getPSCenter();
@@ -218,9 +221,12 @@ public:
GBool getPSPreload();
GBool getPSOPI();
GBool getPSASCIIHex();
+ GBool getPSLZW();
GBool getPSUncompressPreloadedImages();
+ double getPSMinLineWidth();
double getPSRasterResolution();
GBool getPSRasterMono();
+ int getPSRasterSliceSize();
GBool getPSAlwaysRasterize();
GString *getTextEncodingName();
EndOfLineKind getTextEOL();
@@ -228,7 +234,6 @@ public:
GBool getTextKeepTinyChars();
GString *getInitialZoom();
GBool getContinuousView();
- GBool getEnableT1lib();
GBool getEnableFreeType();
GBool getDisableFreeTypeHinting();
GBool getAntialias();
@@ -249,6 +254,8 @@ public:
GString *getMovieCommand() { return movieCommand; }
GBool getMapNumericCharNames();
GBool getMapUnknownCharNames();
+ GBool getMapExtTrueTypeFontsViaUnicode();
+ GBool getEnableXFA();
GList *getKeyBinding(int code, int mods, int context);
GBool getPrintCommands();
GBool getErrQuiet();
@@ -269,6 +276,7 @@ public:
void setPSImageableArea(int llx, int lly, int urx, int ury);
void setPSDuplex(GBool duplex);
void setPSCrop(GBool crop);
+ void setPSUseCropBoxAsPage(GBool crop);
void setPSExpandSmaller(GBool expand);
void setPSShrinkLarger(GBool shrink);
void setPSCenter(GBool center);
@@ -281,13 +289,12 @@ public:
void setPSPreload(GBool preload);
void setPSOPI(GBool opi);
void setPSASCIIHex(GBool hex);
- void setTextEncoding(char *encodingName);
+ void setTextEncoding(const char *encodingName);
GBool setTextEOL(char *s);
void setTextPageBreaks(GBool pageBreaks);
void setTextKeepTinyChars(GBool keep);
void setInitialZoom(char *s);
void setContinuousView(GBool cont);
- GBool setEnableT1lib(char *s);
GBool setEnableFreeType(char *s);
GBool setAntialias(char *s);
GBool setVectorAntialias(char *s);
@@ -299,6 +306,8 @@ public:
void setScreenWhiteThreshold(double thresh);
void setMapNumericCharNames(GBool map);
void setMapUnknownCharNames(GBool map);
+ void setMapExtTrueTypeFontsViaUnicode(GBool map);
+ void setEnableXFA(GBool enable);
void setPrintCommands(GBool printCommandsA);
void setErrQuiet(GBool errQuietA);
@@ -379,6 +388,8 @@ private:
GList *fontDirs; // list of font dirs [GString]
GHash *ccFontFiles; // character collection font files:
// collection name mapped to path [GString]
+ GHash *base14SysFonts; // Base-14 system font files: font name
+ // mapped to path [Base14FontInfo]
SysFontList *sysFonts; // system fonts
GString *psFile; // PostScript file or command (for xpdf)
int psPaperWidth; // paper size, in PostScript points, for
@@ -388,6 +399,7 @@ private:
psImageableURX,
psImageableURY;
GBool psCrop; // crop PS output to CropBox
+ GBool psUseCropBoxAsPage; // use CropBox as page size
GBool psExpandSmaller; // expand smaller pages to fill paper
GBool psShrinkLarger; // shrink larger pages to fit paper
GBool psCenter; // center pages on the paper
@@ -411,11 +423,15 @@ private:
// memory
GBool psOPI; // generate PostScript OPI comments?
GBool psASCIIHex; // use ASCIIHex instead of ASCII85?
+ GBool psLZW; // false to use RLE instead of LZW
GBool psUncompressPreloadedImages; // uncompress all preloaded images
+ double psMinLineWidth; // minimum line width for PostScript output
double psRasterResolution; // PostScript rasterization resolution (dpi)
GBool psRasterMono; // true to do PostScript rasterization
// in monochrome (gray); false to do it
// in color (RGB/CMYK)
+ int psRasterSliceSize; // maximum size (pixels) of PostScript
+ // rasterization slice
GBool psAlwaysRasterize; // force PostScript rasterization
GString *textEncoding; // encoding (unicodeMap) to use for text
// output
@@ -425,7 +441,6 @@ private:
GBool textKeepTinyChars; // keep all characters in text output
GString *initialZoom; // initial zoom level
GBool continuousView; // continuous view mode
- GBool enableT1lib; // t1lib enable flag
GBool enableFreeType; // FreeType enable flag
GBool disableFreeTypeHinting; // FreeType hinting disable flag
GBool antialias; // font anti-aliasing enable flag
@@ -446,6 +461,9 @@ private:
GString *movieCommand; // command executed for movie annotations
GBool mapNumericCharNames; // map numeric char names (from font subsets)?
GBool mapUnknownCharNames; // map unknown char names?
+ GBool mapExtTrueTypeFontsViaUnicode; // map char codes to GID via Unicode
+ // for external TrueType fonts?
+ GBool enableXFA; // enable XFA form rendering
GList *keyBindings; // key & mouse button bindings [KeyBinding]
GBool printCommands; // print the drawing commands
GBool errQuiet; // suppress error messages?
diff -uNrp 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
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include
+#include
+#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, "\n");
+ pr(writeHTML, htmlStream, "\n");
+ pr(writeHTML, htmlStream, "\n");
+ pr(writeHTML, htmlStream, "\n");
+ pr(writeHTML, htmlStream, "\n");
+ pr(writeHTML, htmlStream, "\n");
+ pf(writeHTML, htmlStream, "\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("");
+ }
+ for (i = 0; i < fonts->getLength(); ++i) {
+ if (word1->getFontInfo() == (TextFontInfo *)fonts->get(i)) {
+ break;
+ }
+ }
+ s->appendf("",
+ 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("");
+ pf(writeHTML, htmlStream, "{2:t}\n",
+ (int)line->getXMin(), (int)line->getYMin(), s);
+ delete s;
+ }
+ }
+ }
+ gfree(fontScales);
+ delete text;
+ deleteGList(cols, TextColumn);
+
+ // HTML trailer
+ pr(writeHTML, htmlStream, "\n");
+ pr(writeHTML, htmlStream, "\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 -uNrp 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
+
+#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 -uNrp 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 @@ ImageOutputDev::~ImageOutputDev() {
gfree(fileRoot);
}
-void ImageOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
+void ImageOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx,
+ Object *strRef,
int paintType, Dict *resDict,
double *mat, double *bbox,
int x0, int y0, int x1, int y1,
@@ -47,16 +48,16 @@ void ImageOutputDev::tilingPatternFill(G
void ImageOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
int width, int height, GBool invert,
- GBool inlineImg) {
+ GBool inlineImg, GBool interpolate) {
FILE *f;
- int c;
- int size, i;
+ char buf[4096];
+ int size, n, i;
// dump JPEG file
if (dumpJPEG && str->getKind() == strDCT && !inlineImg) {
// open the image file
- sprintf(fileName, "%s-%03d.jpg", fileRoot, imgNum);
+ sprintf(fileName, "%s-%04d.jpg", fileRoot, imgNum);
++imgNum;
if (!(f = fopen(fileName, "wb"))) {
error(errIO, -1, "Couldn't open image file '{0:s}'", fileName);
@@ -68,8 +69,9 @@ void ImageOutputDev::drawImageMask(GfxSt
str->reset();
// copy the stream
- while ((c = str->getChar()) != EOF)
- fputc(c, f);
+ while ((n = str->getBlock(buf, sizeof(buf))) > 0) {
+ fwrite(buf, 1, n, f);
+ }
str->close();
fclose(f);
@@ -78,7 +80,7 @@ void ImageOutputDev::drawImageMask(GfxSt
} else {
// open the image file and write the PBM header
- sprintf(fileName, "%s-%03d.pbm", fileRoot, imgNum);
+ sprintf(fileName, "%s-%04d.pbm", fileRoot, imgNum);
++imgNum;
if (!(f = fopen(fileName, "wb"))) {
error(errIO, -1, "Couldn't open image file '{0:s}'", fileName);
@@ -92,8 +94,14 @@ void ImageOutputDev::drawImageMask(GfxSt
// copy the stream
size = height * ((width + 7) / 8);
- for (i = 0; i < size; ++i) {
- fputc(str->getChar(), f);
+ while (size > 0) {
+ i = size < (int)sizeof(buf) ? size : (int)sizeof(buf);
+ n = str->getBlock(buf, i);
+ fwrite(buf, 1, n, f);
+ if (n < i) {
+ break;
+ }
+ size -= n;
}
str->close();
@@ -104,14 +112,15 @@ void ImageOutputDev::drawImageMask(GfxSt
void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
- int *maskColors, GBool inlineImg) {
+ int *maskColors, GBool inlineImg,
+ GBool interpolate) {
FILE *f;
ImageStream *imgStr;
Guchar *p;
GfxRGB rgb;
int x, y;
- int c;
- int size, i;
+ char buf[4096];
+ int size, n, i;
// dump JPEG file
if (dumpJPEG && str->getKind() == strDCT &&
@@ -120,7 +129,7 @@ void ImageOutputDev::drawImage(GfxState
!inlineImg) {
// open the image file
- sprintf(fileName, "%s-%03d.jpg", fileRoot, imgNum);
+ sprintf(fileName, "%s-%04d.jpg", fileRoot, imgNum);
++imgNum;
if (!(f = fopen(fileName, "wb"))) {
error(errIO, -1, "Couldn't open image file '{0:s}'", fileName);
@@ -132,8 +141,9 @@ void ImageOutputDev::drawImage(GfxState
str->reset();
// copy the stream
- while ((c = str->getChar()) != EOF)
- fputc(c, f);
+ while ((n = str->getBlock(buf, sizeof(buf))) > 0) {
+ fwrite(buf, 1, n, f);
+ }
str->close();
fclose(f);
@@ -143,7 +153,7 @@ void ImageOutputDev::drawImage(GfxState
colorMap->getBits() == 1) {
// open the image file and write the PBM header
- sprintf(fileName, "%s-%03d.pbm", fileRoot, imgNum);
+ sprintf(fileName, "%s-%04d.pbm", fileRoot, imgNum);
++imgNum;
if (!(f = fopen(fileName, "wb"))) {
error(errIO, -1, "Couldn't open image file '{0:s}'", fileName);
@@ -157,8 +167,14 @@ void ImageOutputDev::drawImage(GfxState
// copy the stream
size = height * ((width + 7) / 8);
- for (i = 0; i < size; ++i) {
- fputc(str->getChar() ^ 0xff, f);
+ while (size > 0) {
+ i = size < (int)sizeof(buf) ? size : (int)sizeof(buf);
+ n = str->getBlock(buf, i);
+ fwrite(buf, 1, n, f);
+ if (n < i) {
+ break;
+ }
+ size -= n;
}
str->close();
@@ -168,7 +184,7 @@ void ImageOutputDev::drawImage(GfxState
} else {
// open the image file and write the PPM header
- sprintf(fileName, "%s-%03d.ppm", fileRoot, imgNum);
+ sprintf(fileName, "%s-%04d.ppm", fileRoot, imgNum);
++imgNum;
if (!(f = fopen(fileName, "wb"))) {
error(errIO, -1, "Couldn't open image file '{0:s}'", fileName);
@@ -203,8 +219,36 @@ void ImageOutputDev::drawImage(GfxState
}
}
}
+
+ 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 -uNrp 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 @@ public:
virtual GBool useDrawChar() { return gFalse; }
//----- path painting
- virtual void tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
+ virtual void tilingPatternFill(GfxState *state, Gfx *gfx, Object *strRef,
int paintType, Dict *resDict,
double *mat, double *bbox,
int x0, int y0, int x1, int y1,
@@ -71,10 +71,22 @@ public:
//----- image drawing
virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
int width, int height, GBool invert,
- GBool inlineImg);
+ GBool inlineImg, GBool interpolate);
virtual void drawImage(GfxState *state, Object *ref, Stream *str,
int width, int height, GfxImageColorMap *colorMap,
- int *maskColors, GBool inlineImg);
+ int *maskColors, GBool inlineImg, GBool interpolate);
+ virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height,
+ GfxImageColorMap *colorMap,
+ Stream *maskStr, int maskWidth, int maskHeight,
+ GBool maskInvert, GBool interpolate);
+ virtual void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height,
+ GfxImageColorMap *colorMap,
+ Stream *maskStr,
+ int maskWidth, int maskHeight,
+ GfxImageColorMap *maskColorMap,
+ GBool interpolate);
private:
diff -uNrp 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 @@ JArithmeticDecoder::JArithmeticDecoder()
dataLen = 0;
limitStream = gFalse;
nBytesRead = 0;
+ readBuf = -1;
}
inline Guint JArithmeticDecoder::readByte() {
+ Guint x;
+
if (limitStream) {
+ if (readBuf >= 0) {
+ x = (Guint)readBuf;
+ readBuf = -1;
+ return x;
+ }
--dataLen;
if (dataLen < 0) {
return 0xff;
@@ -162,9 +170,15 @@ void JArithmeticDecoder::restart(int dat
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 -uNrp 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 @@ private:
Guint nBytesRead;
int dataLen;
GBool limitStream;
+ int readBuf;
};
#endif
diff -uNrp 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 @@ Guint JBIG2MMRDecoder::get24Bits() {
}
void JBIG2MMRDecoder::skipTo(Guint length) {
- while (nBytesRead < length) {
- str->getChar();
- ++nBytesRead;
- ++byteCounter;
- }
+ int n;
+
+ n = str->discardChars(length - nBytesRead);
+ nBytesRead += n;
+ byteCounter += n;
}
//------------------------------------------------------------------------
@@ -1310,10 +1310,9 @@ void JBIG2Stream::readSegments() {
}
refFlags = (refFlags << 24) | (c1 << 16) | (c2 << 8) | c3;
nRefSegs = refFlags & 0x1fffffff;
- for (i = 0; i < (nRefSegs + 9) >> 3; ++i) {
- if ((c1 = curStr->getChar()) == EOF) {
- goto eofError1;
- }
+ i = (nRefSegs + 9) >> 3;
+ if (curStr->discardChars(i) != i) {
+ goto eofError1;
}
}
@@ -1436,10 +1435,8 @@ void JBIG2Stream::readSegments() {
break;
default:
error(errSyntaxError, getPos(), "Unknown segment type in JBIG2 stream");
- for (i = 0; i < segLength; ++i) {
- if ((c1 = curStr->getChar()) == EOF) {
- goto eofError2;
- }
+ if (curStr->discardChars(segLength) != segLength) {
+ goto eofError2;
}
break;
}
@@ -1460,12 +1457,7 @@ void JBIG2Stream::readSegments() {
gfree(refSegs);
break;
}
- while (byteCounter < segLength) {
- if (curStr->getChar() == EOF) {
- break;
- }
- ++byteCounter;
- }
+ byteCounter += curStr->discardChars(segLength - byteCounter);
}
gfree(refSegs);
@@ -1502,9 +1494,8 @@ GBool JBIG2Stream::readSymbolDictSeg(Gui
Guint symHeight, symWidth, totalWidth, x, symID;
int dh, dw, refAggNum, refDX, refDY, bmSize;
GBool ex;
- int run, cnt, c;
+ int run, cnt;
Guint i, j, k;
- Guchar *p;
symWidths = NULL;
@@ -1814,14 +1805,8 @@ GBool JBIG2Stream::readSymbolDictSeg(Gui
if (bmSize == 0) {
collBitmap = new JBIG2Bitmap(0, totalWidth, symHeight);
bmSize = symHeight * ((totalWidth + 7) >> 3);
- p = collBitmap->getDataPtr();
- for (k = 0; k < (Guint)bmSize; ++k) {
- if ((c = curStr->getChar()) == EOF) {
- break;
- }
- *p++ = (Guchar)c;
- ++byteCounter;
- }
+ byteCounter += curStr->getBlock((char *)collBitmap->getDataPtr(),
+ bmSize);
} else {
collBitmap = readGenericBitmap(gTrue, totalWidth, symHeight,
0, gFalse, gFalse, NULL, NULL, NULL,
@@ -2781,7 +2766,6 @@ JBIG2Bitmap *JBIG2Stream::readGenericBit
Guchar mask;
int x, y, x0, x1, a0i, b1i, blackPixels, pix, i;
-
bitmap = new JBIG2Bitmap(0, w, h);
bitmap->clearToZero();
@@ -2994,7 +2978,7 @@ JBIG2Bitmap *JBIG2Stream::readGenericBit
ltpCX = 0x0e3; // 001 1100 01 1
break;
case 3:
- ltpCX = 0x18a; // 01100 0101 1
+ ltpCX = 0x18b; // 01100 0101 1
break;
}
}
@@ -3821,27 +3805,13 @@ void JBIG2Stream::readPageInfoSeg(Guint
}
void JBIG2Stream::readEndOfStripeSeg(Guint length) {
- Guint i;
-
// skip the segment
- for (i = 0; i < length; ++i) {
- if (curStr->getChar() == EOF) {
- break;
- }
- ++byteCounter;
- }
+ byteCounter += curStr->discardChars(length);
}
void JBIG2Stream::readProfilesSeg(Guint length) {
- Guint i;
-
// skip the segment
- for (i = 0; i < length; ++i) {
- if (curStr->getChar() == EOF) {
- break;
- }
- ++byteCounter;
- }
+ byteCounter += curStr->discardChars(length);
}
void JBIG2Stream::readCodeTableSeg(Guint segNum, Guint length) {
@@ -3909,15 +3879,8 @@ void JBIG2Stream::readCodeTableSeg(Guint
}
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 -uNrp 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 @@ static Guint signContext[5][5][2] = {
#define idwtKappa 1.230174104914001
#define idwtIKappa (1.0 / idwtKappa)
-// number of bits to the right of the decimal point for the fixed
-// point arithmetic used in the IDWT
-#define fracBits 16
+// sum of the sample size (number of bits) and the number of bits to
+// the right of the decimal point for the fixed point arithmetic used
+// in the IDWT
+#define fracBits 24
//------------------------------------------------------------------------
@@ -233,12 +234,25 @@ JPXStream::JPXStream(Stream *strA):
nComps = 0;
bpc = NULL;
width = height = 0;
+ reduction = 0;
haveCS = gFalse;
+
+ palette.bpc = NULL;
+ palette.c = NULL;
havePalette = gFalse;
+
+ compMap.comp = NULL;
+ compMap.type = NULL;
+ compMap.pComp = NULL;
haveCompMap = gFalse;
+
+ channelDefn.idx = NULL;
+ channelDefn.type = NULL;
+ channelDefn.assoc = NULL;
haveChannelDefn = gFalse;
img.tiles = NULL;
+
bitBuf = 0;
bitBufLen = 0;
bitBufSkip = gFalse;
@@ -252,13 +266,13 @@ JPXStream::~JPXStream() {
void JPXStream::reset() {
bufStr->reset();
- if (readBoxes()) {
- curY = img.yOffset;
- } else {
+ if (readBoxes() == jpxDecodeFatalError) {
// readBoxes reported an error, so we go immediately to EOF
- curY = img.ySize;
+ curY = img.ySizeR;
+ } else {
+ curY = img.yOffsetR;
}
- curX = img.xOffset;
+ curX = img.xOffsetR;
curComp = 0;
readBufLen = 0;
}
@@ -387,23 +401,25 @@ int JPXStream::lookChar() {
void JPXStream::fillReadBuf() {
JPXTileComp *tileComp;
Guint tileIdx, tx, ty;
- int pix, pixBits;
+ int pix, pixBits, k;
+ GBool eol;
do {
- if (curY >= img.ySize) {
+ if (curY >= img.ySizeR) {
return;
}
- tileIdx = ((curY - img.yTileOffset) / img.yTileSize) * img.nXTiles
- + (curX - img.xTileOffset) / img.xTileSize;
+ tileIdx = ((curY - img.yTileOffsetR) / img.yTileSizeR) * img.nXTiles
+ + (curX - img.xTileOffsetR) / img.xTileSizeR;
#if 1 //~ ignore the palette, assume the PDF ColorSpace object is valid
tileComp = &img.tiles[tileIdx].tileComps[curComp];
#else
tileComp = &img.tiles[tileIdx].tileComps[havePalette ? 0 : curComp];
#endif
- tx = jpxCeilDiv((curX - img.xTileOffset) % img.xTileSize, tileComp->hSep);
- ty = jpxCeilDiv((curY - img.yTileOffset) % img.yTileSize, tileComp->vSep);
- pix = (int)tileComp->data[ty * (tileComp->x1 - tileComp->x0) + tx];
+ tx = jpxCeilDiv((curX - img.xTileOffsetR) % img.xTileSizeR, tileComp->hSep);
+ ty = jpxCeilDiv((curY - img.yTileOffsetR) % img.yTileSizeR, tileComp->vSep);
+ pix = (int)tileComp->data[ty * tileComp->w + tx];
pixBits = tileComp->prec;
+ eol = gFalse;
#if 1 //~ ignore the palette, assume the PDF ColorSpace object is valid
if (++curComp == img.nComps) {
#else
@@ -418,13 +434,10 @@ void JPXStream::fillReadBuf() {
if (++curComp == (Guint)(havePalette ? palette.nComps : img.nComps)) {
#endif
curComp = 0;
- if (++curX == img.xSize) {
- curX = img.xOffset;
+ if (++curX == img.xSizeR) {
+ curX = img.xOffsetR;
++curY;
- if (pixBits < 8) {
- pix <<= 8 - pixBits;
- pixBits = 8;
- }
+ eol = gTrue;
}
}
if (pixBits == 8) {
@@ -433,6 +446,10 @@ void JPXStream::fillReadBuf() {
readBuf = (readBuf << pixBits) | (pix & ((1 << pixBits) - 1));
}
readBufLen += pixBits;
+ if (eol && (k = readBufLen & 7)) {
+ readBuf <<= 8 - k;
+ readBufLen += 8 - k;
+ }
} while (readBufLen < 8);
}
@@ -447,7 +464,7 @@ GBool JPXStream::isBinary(GBool last) {
void JPXStream::getImageParams(int *bitsPerComponent,
StreamColorSpaceMode *csMode) {
Guint boxType, boxLen, dataLen, csEnum;
- Guint bpc1, dummy, i;
+ Guint bpc1, dummy;
int csMeth, csPrec, csPrec1, dummy2;
StreamColorSpaceMode csMode1;
GBool haveBPC, haveCSMode;
@@ -498,13 +515,13 @@ void JPXStream::getImageParams(int *bits
csPrec = csPrec1;
haveCSMode = gTrue;
}
- for (i = 0; i < dataLen - 7; ++i) {
- bufStr->getChar();
+ if (dataLen > 7) {
+ bufStr->discardChars(dataLen - 7);
}
}
} else {
- for (i = 0; i < dataLen - 3; ++i) {
- bufStr->getChar();
+ if (dataLen > 3) {
+ bufStr->discardChars(dataLen - 3);
}
}
}
@@ -516,9 +533,7 @@ void JPXStream::getImageParams(int *bits
break;
} else {
cover(4);
- for (i = 0; i < dataLen; ++i) {
- bufStr->getChar();
- }
+ bufStr->discardChars(dataLen);
}
}
}
@@ -529,7 +544,7 @@ void JPXStream::getImageParams(int *bits
void JPXStream::getImageParams2(int *bitsPerComponent,
StreamColorSpaceMode *csMode) {
int segType;
- Guint segLen, nComps1, bpc1, dummy, i;
+ Guint segLen, nComps1, bpc1, dummy;
while (readMarkerHdr(&segType, &segLen)) {
if (segType == 0x51) { // SIZ - image and tile size
@@ -559,15 +574,14 @@ void JPXStream::getImageParams2(int *bit
} else {
cover(6);
if (segLen > 2) {
- for (i = 0; i < segLen - 2; ++i) {
- bufStr->getChar();
- }
+ bufStr->discardChars(segLen - 2);
}
}
}
}
-GBool JPXStream::readBoxes() {
+JPXDecodeResult JPXStream::readBoxes() {
+ JPXDecodeResult result;
Guint boxType, boxLen, dataLen;
Guint bpc1, compression, unknownColorspace, ipr;
Guint i, j;
@@ -581,8 +595,8 @@ GBool JPXStream::readBoxes() {
cover(7);
error(errSyntaxWarning, getPos(),
"Naked JPEG 2000 codestream, missing JP2/JPX wrapper");
- if (!readCodestream(0)) {
- return gFalse;
+ if ((result = readCodestream(0)) == jpxDecodeFatalError) {
+ return result;
}
nComps = img.nComps;
bpc = (Guint *)gmallocn(nComps, sizeof(Guint));
@@ -591,7 +605,7 @@ GBool JPXStream::readBoxes() {
}
width = img.xSize - img.xOffset;
height = img.ySize - img.yOffset;
- return gTrue;
+ return result;
}
while (readBoxHdr(&boxType, &boxLen, &dataLen)) {
@@ -614,12 +628,12 @@ GBool JPXStream::readBoxes() {
!readUByte(&unknownColorspace) ||
!readUByte(&ipr)) {
error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
if (compression != 7) {
error(errSyntaxError, getPos(),
"Unknown compression type in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
bpc = (Guint *)gmallocn(nComps, sizeof(Guint));
for (i = 0; i < nComps; ++i) {
@@ -632,24 +646,24 @@ GBool JPXStream::readBoxes() {
if (!haveImgHdr) {
error(errSyntaxError, getPos(),
"Found bits per component box before image header box in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
if (dataLen != nComps) {
error(errSyntaxError, getPos(),
"Invalid bits per component box in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
for (i = 0; i < nComps; ++i) {
if (!readUByte(&bpc[i])) {
error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
}
break;
case 0x636F6C72: // color specification
cover(11);
if (!readColorSpecBox(dataLen)) {
- return gFalse;
+ return jpxDecodeFatalError;
}
break;
case 0x70636c72: // palette
@@ -657,15 +671,16 @@ GBool JPXStream::readBoxes() {
if (!readUWord(&palette.nEntries) ||
!readUByte(&palette.nComps)) {
error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
+ havePalette = gTrue;
palette.bpc = (Guint *)gmallocn(palette.nComps, sizeof(Guint));
palette.c =
(int *)gmallocn(palette.nEntries * palette.nComps, sizeof(int));
for (i = 0; i < palette.nComps; ++i) {
if (!readUByte(&palette.bpc[i])) {
error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
++palette.bpc[i];
}
@@ -675,14 +690,14 @@ GBool JPXStream::readBoxes() {
(palette.bpc[j] & 0x80) ? gTrue : gFalse,
&palette.c[i * palette.nComps + j])) {
error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
}
}
- havePalette = gTrue;
break;
case 0x636d6170: // component mapping
cover(13);
+ haveCompMap = gTrue;
compMap.nChannels = dataLen / 4;
compMap.comp = (Guint *)gmallocn(compMap.nChannels, sizeof(Guint));
compMap.type = (Guint *)gmallocn(compMap.nChannels, sizeof(Guint));
@@ -692,17 +707,17 @@ GBool JPXStream::readBoxes() {
!readUByte(&compMap.type[i]) ||
!readUByte(&compMap.pComp[i])) {
error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
}
- haveCompMap = gTrue;
break;
case 0x63646566: // channel definition
cover(14);
if (!readUWord(&channelDefn.nChannels)) {
error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
+ haveChannelDefn = gTrue;
channelDefn.idx =
(Guint *)gmallocn(channelDefn.nChannels, sizeof(Guint));
channelDefn.type =
@@ -714,10 +729,9 @@ GBool JPXStream::readBoxes() {
!readUWord(&channelDefn.type[i]) ||
!readUWord(&channelDefn.assoc[i])) {
error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
}
- haveChannelDefn = gTrue;
break;
case 0x6A703263: // contiguous codestream
cover(15);
@@ -729,28 +743,25 @@ GBool JPXStream::readBoxes() {
error(errSyntaxError, getPos(),
"JPX stream has no supported color spec");
}
- if (!readCodestream(dataLen)) {
- return gFalse;
+ if ((result = readCodestream(dataLen)) != jpxDecodeOk) {
+ return result;
}
break;
default:
cover(16);
- for (i = 0; i < dataLen; ++i) {
- if (bufStr->getChar() == EOF) {
- error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream");
- return gFalse;
- }
+ if (bufStr->discardChars(dataLen) != dataLen) {
+ error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream");
+ return jpxDecodeFatalError;
}
break;
}
}
- return gTrue;
+ return jpxDecodeOk;
}
GBool JPXStream::readColorSpecBox(Guint dataLen) {
JPXColorSpec newCS;
Guint csApprox, csEnum;
- Guint i;
GBool ok;
ok = gFalse;
@@ -852,10 +863,9 @@ GBool JPXStream::readColorSpecBox(Guint
case 3: // any ICC profile (JPX)
case 4: // vendor color (JPX)
cover(18);
- for (i = 0; i < dataLen - 3; ++i) {
- if (bufStr->getChar() == EOF) {
- goto err;
- }
+ if (dataLen > 3 &&
+ bufStr->discardChars(dataLen - 3) != dataLen - 3) {
+ goto err;
}
break;
}
@@ -872,11 +882,11 @@ GBool JPXStream::readColorSpecBox(Guint
return gFalse;
}
-GBool JPXStream::readCodestream(Guint len) {
+JPXDecodeResult JPXStream::readCodestream(Guint len) {
JPXTile *tile;
JPXTileComp *tileComp;
int segType;
- GBool haveSIZ, haveCOD, haveQCD, haveSOT;
+ GBool haveSIZ, haveCOD, haveQCD, haveSOT, ok;
Guint precinctSize, style;
Guint segLen, capabilities, comp, i, j, r;
@@ -885,7 +895,7 @@ GBool JPXStream::readCodestream(Guint le
do {
if (!readMarkerHdr(&segType, &segLen)) {
error(errSyntaxError, getPos(), "Error in JPX codestream");
- return gFalse;
+ return jpxDecodeFatalError;
}
switch (segType) {
case 0x4f: // SOC - start of codestream
@@ -897,7 +907,7 @@ GBool JPXStream::readCodestream(Guint le
if (haveSIZ) {
error(errSyntaxError, getPos(),
"Duplicate SIZ marker segment in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
if (!readUWord(&capabilities) ||
!readULong(&img.xSize) ||
@@ -910,12 +920,12 @@ GBool JPXStream::readCodestream(Guint le
!readULong(&img.yTileOffset) ||
!readUWord(&img.nComps)) {
error(errSyntaxError, getPos(), "Error in JPX SIZ marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
if (haveImgHdr && img.nComps != nComps) {
error(errSyntaxError, getPos(),
"Different number of components in JPX SIZ marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
if (img.xSize == 0 || img.ySize == 0 ||
img.xOffset >= img.xSize || img.yOffset >= img.ySize ||
@@ -925,8 +935,16 @@ GBool JPXStream::readCodestream(Guint le
img.xTileSize + img.xTileOffset <= img.xOffset ||
img.yTileSize + img.yTileOffset <= img.yOffset) {
error(errSyntaxError, getPos(), "Error in JPX SIZ marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
+ img.xSizeR = img.xSize >> reduction;
+ img.ySizeR = img.ySize >> reduction;
+ img.xOffsetR = img.xOffset >> reduction;
+ img.yOffsetR = img.yOffset >> reduction;
+ img.xTileSizeR = img.xTileSize >> reduction;
+ img.yTileSizeR = img.yTileSize >> reduction;
+ img.xTileOffsetR = img.xTileOffset >> reduction;
+ img.yTileOffsetR = img.yTileOffset >> reduction;
img.nXTiles = (img.xSize - img.xTileOffset + img.xTileSize - 1)
/ img.xTileSize;
img.nYTiles = (img.ySize - img.yTileOffset + img.yTileSize - 1)
@@ -936,12 +954,16 @@ GBool JPXStream::readCodestream(Guint le
img.nXTiles >= INT_MAX / img.nYTiles) {
error(errSyntaxError, getPos(),
"Bad tile count in JPX SIZ marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
img.tiles = (JPXTile *)gmallocn(img.nXTiles * img.nYTiles,
sizeof(JPXTile));
for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
img.tiles[i].init = gFalse;
+ img.tiles[i].nextTilePart = 0;
+ img.tiles[i].tileComps = NULL;
+ }
+ for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
img.tiles[i].tileComps = (JPXTileComp *)gmallocn(img.nComps,
sizeof(JPXTileComp));
for (comp = 0; comp < img.nComps; ++comp) {
@@ -956,12 +978,12 @@ GBool JPXStream::readCodestream(Guint le
!readUByte(&img.tiles[0].tileComps[comp].hSep) ||
!readUByte(&img.tiles[0].tileComps[comp].vSep)) {
error(errSyntaxError, getPos(), "Error in JPX SIZ marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
if (img.tiles[0].tileComps[comp].hSep == 0 ||
img.tiles[0].tileComps[comp].vSep == 0) {
error(errSyntaxError, getPos(), "Error in JPX SIZ marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
img.tiles[0].tileComps[comp].sgned =
(img.tiles[0].tileComps[comp].prec & 0x80) ? gTrue : gFalse;
@@ -978,7 +1000,7 @@ GBool JPXStream::readCodestream(Guint le
if (!haveSIZ) {
error(errSyntaxError, getPos(),
"JPX COD marker segment before SIZ segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
if (!readUByte(&img.tiles[0].tileComps[0].style) ||
!readUByte(&img.tiles[0].progOrder) ||
@@ -990,14 +1012,21 @@ GBool JPXStream::readCodestream(Guint le
!readUByte(&img.tiles[0].tileComps[0].codeBlockStyle) ||
!readUByte(&img.tiles[0].tileComps[0].transform)) {
error(errSyntaxError, getPos(), "Error in JPX COD marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
if (img.tiles[0].tileComps[0].nDecompLevels > 32 ||
img.tiles[0].tileComps[0].codeBlockW > 8 ||
img.tiles[0].tileComps[0].codeBlockH > 8) {
error(errSyntaxError, getPos(), "Error in JPX COD marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
+ }
+#if 1 //~ progression orders 2-4 are unimplemented
+ if (img.tiles[0].progOrder >= 2) {
+ error(errUnimplemented, -1,
+ "JPX progression order {0:d} is unimplemented",
+ img.tiles[0].progOrder);
}
+#endif
img.tiles[0].tileComps[0].codeBlockW += 2;
img.tiles[0].tileComps[0].codeBlockH += 2;
for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
@@ -1035,7 +1064,7 @@ GBool JPXStream::readCodestream(Guint le
cover(91);
if (!readUByte(&precinctSize)) {
error(errSyntaxError, getPos(), "Error in JPX COD marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
img.tiles[0].tileComps[0].resLevels[r].precinctWidth =
precinctSize & 0x0f;
@@ -1065,7 +1094,7 @@ GBool JPXStream::readCodestream(Guint le
if (!haveCOD) {
error(errSyntaxError, getPos(),
"JPX COC marker segment before COD segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
if ((img.nComps > 256 && !readUWord(&comp)) ||
(img.nComps <= 256 && !readUByte(&comp)) ||
@@ -1077,13 +1106,13 @@ GBool JPXStream::readCodestream(Guint le
!readUByte(&img.tiles[0].tileComps[comp].codeBlockStyle) ||
!readUByte(&img.tiles[0].tileComps[comp].transform)) {
error(errSyntaxError, getPos(), "Error in JPX COC marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
if (img.tiles[0].tileComps[comp].nDecompLevels > 32 ||
img.tiles[0].tileComps[comp].codeBlockW > 8 ||
img.tiles[0].tileComps[comp].codeBlockH > 8) {
error(errSyntaxError, getPos(), "Error in JPX COC marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
img.tiles[0].tileComps[comp].style =
(img.tiles[0].tileComps[comp].style & ~1) | (style & 1);
@@ -1117,7 +1146,7 @@ GBool JPXStream::readCodestream(Guint le
if (img.tiles[0].tileComps[comp].style & 0x01) {
if (!readUByte(&precinctSize)) {
error(errSyntaxError, getPos(), "Error in JPX COD marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
img.tiles[0].tileComps[comp].resLevels[r].precinctWidth =
precinctSize & 0x0f;
@@ -1142,16 +1171,16 @@ GBool JPXStream::readCodestream(Guint le
if (!haveSIZ) {
error(errSyntaxError, getPos(),
"JPX QCD marker segment before SIZ segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
if (!readUByte(&img.tiles[0].tileComps[0].quantStyle)) {
error(errSyntaxError, getPos(), "Error in JPX QCD marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
if ((img.tiles[0].tileComps[0].quantStyle & 0x1f) == 0x00) {
if (segLen <= 3) {
error(errSyntaxError, getPos(), "Error in JPX QCD marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
img.tiles[0].tileComps[0].nQuantSteps = segLen - 3;
img.tiles[0].tileComps[0].quantSteps =
@@ -1161,7 +1190,7 @@ GBool JPXStream::readCodestream(Guint le
for (i = 0; i < img.tiles[0].tileComps[0].nQuantSteps; ++i) {
if (!readUByte(&img.tiles[0].tileComps[0].quantSteps[i])) {
error(errSyntaxError, getPos(), "Error in JPX QCD marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
}
} else if ((img.tiles[0].tileComps[0].quantStyle & 0x1f) == 0x01) {
@@ -1172,12 +1201,12 @@ GBool JPXStream::readCodestream(Guint le
sizeof(Guint));
if (!readUWord(&img.tiles[0].tileComps[0].quantSteps[0])) {
error(errSyntaxError, getPos(), "Error in JPX QCD marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
} else if ((img.tiles[0].tileComps[0].quantStyle & 0x1f) == 0x02) {
if (segLen < 5) {
error(errSyntaxError, getPos(), "Error in JPX QCD marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
img.tiles[0].tileComps[0].nQuantSteps = (segLen - 3) / 2;
img.tiles[0].tileComps[0].quantSteps =
@@ -1187,12 +1216,12 @@ GBool JPXStream::readCodestream(Guint le
for (i = 0; i < img.tiles[0].tileComps[0].nQuantSteps; ++i) {
if (!readUWord(&img.tiles[0].tileComps[0].quantSteps[i])) {
error(errSyntaxError, getPos(), "Error in JPX QCD marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
}
} else {
error(errSyntaxError, getPos(), "Error in JPX QCD marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
for (comp = 0; comp < img.nComps; ++comp) {
@@ -1219,19 +1248,19 @@ GBool JPXStream::readCodestream(Guint le
if (!haveQCD) {
error(errSyntaxError, getPos(),
"JPX QCC marker segment before QCD segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
if ((img.nComps > 256 && !readUWord(&comp)) ||
(img.nComps <= 256 && !readUByte(&comp)) ||
comp >= img.nComps ||
!readUByte(&img.tiles[0].tileComps[comp].quantStyle)) {
error(errSyntaxError, getPos(), "Error in JPX QCC marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
if ((img.tiles[0].tileComps[comp].quantStyle & 0x1f) == 0x00) {
if (segLen <= (img.nComps > 256 ? 5U : 4U)) {
error(errSyntaxError, getPos(), "Error in JPX QCC marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
img.tiles[0].tileComps[comp].nQuantSteps =
segLen - (img.nComps > 256 ? 5 : 4);
@@ -1242,7 +1271,7 @@ GBool JPXStream::readCodestream(Guint le
for (i = 0; i < img.tiles[0].tileComps[comp].nQuantSteps; ++i) {
if (!readUByte(&img.tiles[0].tileComps[comp].quantSteps[i])) {
error(errSyntaxError, getPos(), "Error in JPX QCC marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
}
} else if ((img.tiles[0].tileComps[comp].quantStyle & 0x1f) == 0x01) {
@@ -1253,12 +1282,12 @@ GBool JPXStream::readCodestream(Guint le
sizeof(Guint));
if (!readUWord(&img.tiles[0].tileComps[comp].quantSteps[0])) {
error(errSyntaxError, getPos(), "Error in JPX QCC marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
} else if ((img.tiles[0].tileComps[comp].quantStyle & 0x1f) == 0x02) {
if (segLen < (img.nComps > 256 ? 5U : 4U) + 2) {
error(errSyntaxError, getPos(), "Error in JPX QCC marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
img.tiles[0].tileComps[comp].nQuantSteps =
(segLen - (img.nComps > 256 ? 5 : 4)) / 2;
@@ -1269,12 +1298,12 @@ GBool JPXStream::readCodestream(Guint le
for (i = 0; i < img.tiles[0].tileComps[comp].nQuantSteps; ++i) {
if (!readUWord(&img.tiles[0].tileComps[comp].quantSteps[i])) {
error(errSyntaxError, getPos(), "Error in JPX QCD marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
}
} else {
error(errSyntaxError, getPos(), "Error in JPX QCC marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
for (i = 1; i < img.nXTiles * img.nYTiles; ++i) {
img.tiles[i].tileComps[comp].quantStyle =
@@ -1295,11 +1324,10 @@ GBool JPXStream::readCodestream(Guint le
cover(25);
#if 1 //~ ROI is unimplemented
error(errUnimplemented, -1, "got a JPX RGN segment");
- for (i = 0; i < segLen - 2; ++i) {
- if (bufStr->getChar() == EOF) {
- error(errSyntaxError, getPos(), "Error in JPX RGN marker segment");
- return gFalse;
- }
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX RGN marker segment");
+ return jpxDecodeFatalError;
}
#else
if ((img.nComps > 256 && !readUWord(&comp)) ||
@@ -1308,7 +1336,7 @@ GBool JPXStream::readCodestream(Guint le
!readUByte(&compInfo[comp].defROI.style) ||
!readUByte(&compInfo[comp].defROI.shift)) {
error(errSyntaxError, getPos(), "Error in JPX RGN marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
#endif
break;
@@ -1316,11 +1344,10 @@ GBool JPXStream::readCodestream(Guint le
cover(26);
#if 1 //~ progression order changes are unimplemented
error(errUnimplemented, -1, "got a JPX POC segment");
- for (i = 0; i < segLen - 2; ++i) {
- if (bufStr->getChar() == EOF) {
- error(errSyntaxError, getPos(), "Error in JPX POC marker segment");
- return gFalse;
- }
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX POC marker segment");
+ return jpxDecodeFatalError;
}
#else
nProgs = (segLen - 2) / (img.nComps > 256 ? 9 : 7);
@@ -1335,7 +1362,7 @@ GBool JPXStream::readCodestream(Guint le
!(img.nComps <= 256 && readUByte(&progs[i].endComp)) ||
!readUByte(&progs[i].progOrder)) {
error(errSyntaxError, getPos(), "Error in JPX POC marker segment");
- return gFalse;
+ return jpxDecodeFatalError;
}
}
#endif
@@ -1344,52 +1371,47 @@ GBool JPXStream::readCodestream(Guint le
cover(27);
#if 1 //~ packed packet headers are unimplemented
error(errUnimplemented, -1, "Got a JPX PPM segment");
- for (i = 0; i < segLen - 2; ++i) {
- if (bufStr->getChar() == EOF) {
- error(errSyntaxError, getPos(), "Error in JPX PPM marker segment");
- return gFalse;
- }
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX PPM marker segment");
+ return jpxDecodeFatalError;
}
#endif
break;
case 0x55: // TLM - tile-part lengths
// skipped
cover(28);
- for (i = 0; i < segLen - 2; ++i) {
- if (bufStr->getChar() == EOF) {
- error(errSyntaxError, getPos(), "Error in JPX TLM marker segment");
- return gFalse;
- }
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX TLM marker segment");
+ return jpxDecodeFatalError;
}
break;
case 0x57: // PLM - packet length, main header
// skipped
cover(29);
- for (i = 0; i < segLen - 2; ++i) {
- if (bufStr->getChar() == EOF) {
- error(errSyntaxError, getPos(), "Error in JPX PLM marker segment");
- return gFalse;
- }
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX PLM marker segment");
+ return jpxDecodeFatalError;
}
break;
case 0x63: // CRG - component registration
// skipped
cover(30);
- for (i = 0; i < segLen - 2; ++i) {
- if (bufStr->getChar() == EOF) {
- error(errSyntaxError, getPos(), "Error in JPX CRG marker segment");
- return gFalse;
- }
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX CRG marker segment");
+ return jpxDecodeFatalError;
}
break;
case 0x64: // COM - comment
// skipped
cover(31);
- for (i = 0; i < segLen - 2; ++i) {
- if (bufStr->getChar() == EOF) {
- error(errSyntaxError, getPos(), "Error in JPX COM marker segment");
- return gFalse;
- }
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX COM marker segment");
+ return jpxDecodeFatalError;
}
break;
case 0x90: // SOT - start of tile
@@ -1400,10 +1422,8 @@ GBool JPXStream::readCodestream(Guint le
cover(33);
error(errSyntaxError, getPos(),
"Unknown marker segment {0:02x} in JPX stream", segType);
- for (i = 0; i < segLen - 2; ++i) {
- if (bufStr->getChar() == EOF) {
- break;
- }
+ if (segLen > 2) {
+ bufStr->discardChars(segLen - 2);
}
break;
}
@@ -1412,27 +1432,30 @@ GBool JPXStream::readCodestream(Guint le
if (!haveSIZ) {
error(errSyntaxError, getPos(),
"Missing SIZ marker segment in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
if (!haveCOD) {
error(errSyntaxError, getPos(),
"Missing COD marker segment in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
if (!haveQCD) {
error(errSyntaxError, getPos(),
"Missing QCD marker segment in JPX stream");
- return gFalse;
+ return jpxDecodeFatalError;
}
//----- read the tile-parts
+ ok = gTrue;
while (1) {
if (!readTilePart()) {
- return gFalse;
+ ok = gFalse;
+ break;
}
if (!readMarkerHdr(&segType, &segLen)) {
error(errSyntaxError, getPos(), "Error in JPX codestream");
- return gFalse;
+ ok = gFalse;
+ break;
}
if (segType != 0x90) { // SOT - start of tile
break;
@@ -1441,7 +1464,7 @@ GBool JPXStream::readCodestream(Guint le
if (segType != 0xd9) { // EOC - end of codestream
error(errSyntaxError, getPos(), "Missing EOC marker in JPX codestream");
- return gFalse;
+ ok = gFalse;
}
//----- finish decoding the image
@@ -1449,20 +1472,20 @@ GBool JPXStream::readCodestream(Guint le
tile = &img.tiles[i];
if (!tile->init) {
error(errSyntaxError, getPos(), "Uninitialized tile in JPX codestream");
- return gFalse;
+ return jpxDecodeFatalError;
}
for (comp = 0; comp < img.nComps; ++comp) {
tileComp = &tile->tileComps[comp];
inverseTransform(tileComp);
}
if (!inverseMultiCompAndDC(tile)) {
- return gFalse;
+ return jpxDecodeFatalError;
}
}
//~ can free memory below tileComps here, and also tileComp.buf
- return gTrue;
+ return ok ? jpxDecodeOk : jpxDecodeNonFatalError;
}
GBool JPXStream::readTilePart() {
@@ -1490,11 +1513,16 @@ GBool JPXStream::readTilePart() {
return gFalse;
}
- if ((tilePartIdx > 0 && !img.tiles[tileIdx].init) ||
- tileIdx >= img.nXTiles * img.nYTiles) {
- error(errSyntaxError, getPos(), "Weird tile index in JPX stream");
+ // check tileIdx and tilePartIdx
+ // (this ignores nTileParts, because some encoders get it wrong)
+ if (tileIdx >= img.nXTiles * img.nYTiles ||
+ tilePartIdx != img.tiles[tileIdx].nextTilePart ||
+ (tilePartIdx > 0 && !img.tiles[tileIdx].init) ||
+ (tilePartIdx == 0 && img.tiles[tileIdx].init)) {
+ error(errSyntaxError, getPos(), "Weird tile-part header in JPX stream");
return gFalse;
}
+ ++img.tiles[tileIdx].nextTilePart;
tilePartToEOC = tilePartLen == 0;
tilePartLen -= 12; // subtract size of SOT segment
@@ -1527,6 +1555,13 @@ GBool JPXStream::readTilePart() {
error(errSyntaxError, getPos(), "Error in JPX COD marker segment");
return gFalse;
}
+#if 1 //~ progression orders 2-4 are unimplemented
+ if (img.tiles[tileIdx].progOrder >= 2) {
+ error(errUnimplemented, -1,
+ "JPX progression order {0:d} is unimplemented",
+ img.tiles[tileIdx].progOrder);
+ }
+#endif
img.tiles[tileIdx].tileComps[0].codeBlockW += 2;
img.tiles[tileIdx].tileComps[0].codeBlockH += 2;
for (comp = 0; comp < img.nComps; ++comp) {
@@ -1760,11 +1795,10 @@ GBool JPXStream::readTilePart() {
cover(38);
#if 1 //~ ROI is unimplemented
error(errUnimplemented, -1, "Got a JPX RGN segment");
- for (i = 0; i < segLen - 2; ++i) {
- if (bufStr->getChar() == EOF) {
- error(errSyntaxError, getPos(), "Error in JPX RGN marker segment");
- return gFalse;
- }
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX RGN marker segment");
+ return gFalse;
}
#else
if ((img.nComps > 256 && !readUWord(&comp)) ||
@@ -1781,11 +1815,10 @@ GBool JPXStream::readTilePart() {
cover(39);
#if 1 //~ progression order changes are unimplemented
error(errUnimplemented, -1, "Got a JPX POC segment");
- for (i = 0; i < segLen - 2; ++i) {
- if (bufStr->getChar() == EOF) {
- error(errSyntaxError, getPos(), "Error in JPX POC marker segment");
- return gFalse;
- }
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX POC marker segment");
+ return gFalse;
}
#else
nTileProgs = (segLen - 2) / (img.nComps > 256 ? 9 : 7);
@@ -1809,31 +1842,28 @@ GBool JPXStream::readTilePart() {
cover(40);
#if 1 //~ packed packet headers are unimplemented
error(errUnimplemented, -1, "Got a JPX PPT segment");
- for (i = 0; i < segLen - 2; ++i) {
- if (bufStr->getChar() == EOF) {
- error(errSyntaxError, getPos(), "Error in JPX PPT marker segment");
- return gFalse;
- }
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX PPT marker segment");
+ return gFalse;
}
#endif
case 0x58: // PLT - packet length, tile-part header
// skipped
cover(41);
- for (i = 0; i < segLen - 2; ++i) {
- if (bufStr->getChar() == EOF) {
- error(errSyntaxError, getPos(), "Error in JPX PLT marker segment");
- return gFalse;
- }
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX PLT marker segment");
+ return gFalse;
}
break;
case 0x64: // COM - comment
// skipped
cover(42);
- for (i = 0; i < segLen - 2; ++i) {
- if (bufStr->getChar() == EOF) {
- error(errSyntaxError, getPos(), "Error in JPX COM marker segment");
- return gFalse;
- }
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX COM marker segment");
+ return gFalse;
}
break;
case 0x93: // SOD - start of data
@@ -1845,10 +1875,8 @@ GBool JPXStream::readTilePart() {
error(errSyntaxError, getPos(),
"Unknown marker segment {0:02x} in JPX tile-part stream",
segType);
- for (i = 0; i < segLen - 2; ++i) {
- if (bufStr->getChar() == EOF) {
- break;
- }
+ if (segLen > 2) {
+ bufStr->discardChars(segLen - 2);
}
break;
}
@@ -1886,12 +1914,13 @@ GBool JPXStream::readTilePart() {
tileComp->y0 = jpxCeilDiv(tile->y0, tileComp->vSep);
tileComp->x1 = jpxCeilDiv(tile->x1, tileComp->hSep);
tileComp->y1 = jpxCeilDiv(tile->y1, tileComp->vSep);
- tileComp->w = tileComp->x1 - tileComp->x0;
tileComp->cbW = 1 << tileComp->codeBlockW;
tileComp->cbH = 1 << tileComp->codeBlockH;
- tileComp->data = (int *)gmallocn((tileComp->x1 - tileComp->x0) *
- (tileComp->y1 - tileComp->y0),
- sizeof(int));
+ tileComp->w = (tileComp->x1 - tileComp->x0 + (1 << reduction) - 1)
+ >> reduction;
+ tileComp->h = (tileComp->y1 - tileComp->y0 + (1 << reduction) - 1)
+ >> reduction;
+ tileComp->data = (int *)gmallocn(tileComp->w * tileComp->h, sizeof(int));
if (tileComp->x1 - tileComp->x0 > tileComp->y1 - tileComp->y0) {
n = tileComp->x1 - tileComp->x0;
} else {
@@ -1927,6 +1956,9 @@ GBool JPXStream::readTilePart() {
}
resLevel->precincts = (JPXPrecinct *)gmallocn(1, sizeof(JPXPrecinct));
for (pre = 0; pre < 1; ++pre) {
+ resLevel->precincts[pre].subbands = NULL;
+ }
+ for (pre = 0; pre < 1; ++pre) {
precinct = &resLevel->precincts[pre];
precinct->x0 = resLevel->x0;
precinct->y0 = resLevel->y0;
@@ -1936,6 +1968,11 @@ GBool JPXStream::readTilePart() {
precinct->subbands =
(JPXSubband *)gmallocn(nSBs, sizeof(JPXSubband));
for (sb = 0; sb < nSBs; ++sb) {
+ precinct->subbands[sb].inclusion = NULL;
+ precinct->subbands[sb].zeroBitPlane = NULL;
+ precinct->subbands[sb].cbs = NULL;
+ }
+ for (sb = 0; sb < nSBs; ++sb) {
subband = &precinct->subbands[sb];
subband->x0 = resLevel->bx0[sb];
subband->y0 = resLevel->by0[sb];
@@ -1973,6 +2010,12 @@ GBool JPXStream::readTilePart() {
subband->cbs = (JPXCodeBlock *)gmallocn(subband->nXCBs *
subband->nYCBs,
sizeof(JPXCodeBlock));
+ for (k = 0; k < subband->nXCBs * subband->nYCBs; ++k) {
+ subband->cbs[k].dataLen = NULL;
+ subband->cbs[k].touched = NULL;
+ subband->cbs[k].arithDecoder = NULL;
+ subband->cbs[k].stats = NULL;
+ }
sbx0 = jpxFloorDivPow2(subband->x0, tileComp->codeBlockW);
sby0 = jpxFloorDivPow2(subband->y0, tileComp->codeBlockH);
if (r == 0) { // (NL)LL
@@ -2013,21 +2056,25 @@ GBool JPXStream::readTilePart() {
cb->nZeroBitPlanes = 0;
cb->dataLenSize = 1;
cb->dataLen = (Guint *)gmalloc(sizeof(Guint));
- cb->coeffs = sbCoeffs
- + (cb->y0 - subband->y0) * tileComp->w
- + (cb->x0 - subband->x0);
- cb->touched = (char *)gmalloc(1 << (tileComp->codeBlockW
- + tileComp->codeBlockH));
- cb->len = 0;
- for (cbj = 0; cbj < cb->y1 - cb->y0; ++cbj) {
- for (cbi = 0; cbi < cb->x1 - cb->x0; ++cbi) {
- cb->coeffs[cbj * tileComp->w + cbi] = 0;
+ if (r <= tileComp->nDecompLevels - reduction) {
+ cb->coeffs = sbCoeffs
+ + (cb->y0 - subband->y0) * tileComp->w
+ + (cb->x0 - subband->x0);
+ cb->touched = (char *)gmalloc(1 << (tileComp->codeBlockW
+ + tileComp->codeBlockH));
+ cb->len = 0;
+ for (cbj = 0; cbj < cb->y1 - cb->y0; ++cbj) {
+ for (cbi = 0; cbi < cb->x1 - cb->x0; ++cbi) {
+ cb->coeffs[cbj * tileComp->w + cbi] = 0;
+ }
}
+ memset(cb->touched, 0,
+ (1 << (tileComp->codeBlockW + tileComp->codeBlockH)));
+ } else {
+ cb->coeffs = NULL;
+ cb->touched = NULL;
+ cb->len = 0;
}
- memset(cb->touched, 0,
- (1 << (tileComp->codeBlockW + tileComp->codeBlockH)));
- cb->arithDecoder = NULL;
- cb->stats = NULL;
++cb;
}
}
@@ -2381,7 +2428,21 @@ GBool JPXStream::readCodeBlockData(JPXTi
Guint horiz, vert, diag, all, cx, xorBit;
int horizSign, vertSign, bit;
int segSym;
- Guint i, x, y0, y1;
+ Guint n, i, x, y0, y1;
+
+ if (res > tileComp->nDecompLevels - reduction) {
+ // skip the codeblock data
+ if (tileComp->codeBlockStyle & 0x04) {
+ n = 0;
+ for (i = 0; i < cb->nCodingPasses; ++i) {
+ n += cb->dataLen[i];
+ }
+ } else {
+ n = cb->dataLen[0];
+ }
+ bufStr->discardChars(n);
+ return gTrue;
+ }
if (cb->arithDecoder) {
cover(63);
@@ -2735,7 +2796,7 @@ void JPXStream::inverseTransform(JPXTile
}
if (tileComp->transform == 0) {
cover(71);
- shift += fracBits;
+ shift += fracBits - tileComp->prec;
}
// do fixed point adjustment and dequantization on (NL)LL
@@ -2766,7 +2827,7 @@ void JPXStream::inverseTransform(JPXTile
cover(96);
if (tileComp->transform == 0) {
cover(97);
- val &= -1 << fracBits;
+ val &= -1 << (fracBits - tileComp->prec);
}
} else {
cover(98);
@@ -2782,7 +2843,7 @@ void JPXStream::inverseTransform(JPXTile
//----- IDWT for each level
- for (r = 1; r <= tileComp->nDecompLevels; ++r) {
+ for (r = 1; r <= tileComp->nDecompLevels - reduction; ++r) {
resLevel = &tileComp->resLevels[r];
// (n)LL is already in the upper-left corner of the
@@ -2837,7 +2898,7 @@ void JPXStream::inverseTransformLevel(JP
}
if (tileComp->transform == 0) {
cover(103);
- shift += fracBits;
+ shift += fracBits - tileComp->prec;
}
// fixed point adjustment and dequantization
@@ -2868,7 +2929,7 @@ void JPXStream::inverseTransformLevel(JP
if (qStyle == 0) {
cover(76);
if (tileComp->transform == 0) {
- val &= -1 << fracBits;
+ val &= -1 << (fracBits - tileComp->prec);
}
} else {
cover(77);
@@ -3103,8 +3164,8 @@ GBool JPXStream::inverseMultiCompAndDC(J
if (tile->tileComps[0].transform == 0) {
cover(87);
j = 0;
- for (y = 0; y < tile->tileComps[0].y1 - tile->tileComps[0].y0; ++y) {
- for (x = 0; x < tile->tileComps[0].x1 - tile->tileComps[0].x0; ++x) {
+ for (y = 0; y < tile->tileComps[0].h; ++y) {
+ for (x = 0; x < tile->tileComps[0].w; ++x) {
d0 = tile->tileComps[0].data[j];
d1 = tile->tileComps[1].data[j];
d2 = tile->tileComps[2].data[j];
@@ -3120,8 +3181,8 @@ GBool JPXStream::inverseMultiCompAndDC(J
} else {
cover(88);
j = 0;
- for (y = 0; y < tile->tileComps[0].y1 - tile->tileComps[0].y0; ++y) {
- for (x = 0; x < tile->tileComps[0].x1 - tile->tileComps[0].x0; ++x) {
+ for (y = 0; y < tile->tileComps[0].h; ++y) {
+ for (x = 0; x < tile->tileComps[0].w; ++x) {
d0 = tile->tileComps[0].data[j];
d1 = tile->tileComps[1].data[j];
d2 = tile->tileComps[2].data[j];
@@ -3144,12 +3205,12 @@ GBool JPXStream::inverseMultiCompAndDC(J
minVal = -(1 << (tileComp->prec - 1));
maxVal = (1 << (tileComp->prec - 1)) - 1;
dataPtr = tileComp->data;
- for (y = 0; y < tileComp->y1 - tileComp->y0; ++y) {
- for (x = 0; x < tileComp->x1 - tileComp->x0; ++x) {
+ for (y = 0; y < tileComp->h; ++y) {
+ for (x = 0; x < tileComp->w; ++x) {
coeff = *dataPtr;
if (tileComp->transform == 0) {
cover(109);
- coeff >>= fracBits;
+ coeff >>= fracBits - tileComp->prec;
}
if (coeff < minVal) {
cover(110);
@@ -3168,12 +3229,12 @@ GBool JPXStream::inverseMultiCompAndDC(J
maxVal = (1 << tileComp->prec) - 1;
zeroVal = 1 << (tileComp->prec - 1);
dataPtr = tileComp->data;
- for (y = 0; y < tileComp->y1 - tileComp->y0; ++y) {
- for (x = 0; x < tileComp->x1 - tileComp->x0; ++x) {
+ for (y = 0; y < tileComp->h; ++y) {
+ for (x = 0; x < tileComp->w; ++x) {
coeff = *dataPtr;
if (tileComp->transform == 0) {
cover(112);
- coeff >>= fracBits;
+ coeff >>= fracBits - tileComp->prec;
}
coeff += zeroVal;
if (coeff < 0) {
@@ -3339,16 +3400,12 @@ GBool JPXStream::readBits(int nBits, Gui
}
void JPXStream::skipSOP() {
- int i;
-
// SOP occurs at the start of the packet header, so we don't need to
// worry about bit-stuff prior to it
if (byteCount >= 6 &&
bufStr->lookChar(0) == 0xff &&
bufStr->lookChar(1) == 0x91) {
- for (i = 0; i < 6; ++i) {
- bufStr->getChar();
- }
+ bufStr->discardChars(6);
byteCount -= 6;
bitBufLen = 0;
bitBufSkip = gFalse;
@@ -3356,15 +3413,13 @@ void JPXStream::skipSOP() {
}
void JPXStream::skipEPH() {
- int i, k;
+ int k;
k = bitBufSkip ? 1 : 0;
if (byteCount >= (Guint)(k + 2) &&
bufStr->lookChar(k) == 0xff &&
bufStr->lookChar(k + 1) == 0x92) {
- for (i = 0; i < k + 2; ++i) {
- bufStr->getChar();
- }
+ bufStr->discardChars(k + 2);
byteCount -= k + 2;
bitBufLen = 0;
bitBufSkip = gFalse;
diff -uNrp 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 @@ struct JPXTileComp {
//----- computed
Guint x0, y0, x1, y1; // bounds of the tile-comp, in ref coords
- Guint w; // x1 - x0
+ Guint w, h; // data size = {x1 - x0, y1 - y0} >> reduction
Guint cbW; // code-block width
Guint cbH; // code-block height
@@ -234,6 +234,9 @@ struct JPXTile {
Guint precinct; // precinct
Guint layer; // layer
+ //----- tile part info
+ Guint nextTilePart; // next expected tile-part
+
//----- children
JPXTileComp *tileComps; // the tile-components (len = JPXImage.nComps)
};
@@ -247,6 +250,11 @@ struct JPXImage {
Guint xTileSize, yTileSize; // size of tiles
Guint xTileOffset, // offset of first tile
yTileOffset;
+ Guint xSizeR, ySizeR; // size of reference grid >> reduction
+ Guint xOffsetR, yOffsetR; // image offset >> reduction
+ Guint xTileSizeR, yTileSizeR; // size of tiles >> reduction
+ Guint xTileOffsetR, // offset of first tile >> reduction
+ yTileOffsetR;
Guint nComps; // number of components
//----- computed
@@ -259,6 +267,14 @@ struct JPXImage {
//------------------------------------------------------------------------
+enum JPXDecodeResult {
+ jpxDecodeOk,
+ jpxDecodeNonFatalError,
+ jpxDecodeFatalError
+};
+
+//------------------------------------------------------------------------
+
class JPXStream: public FilterStream {
public:
@@ -273,14 +289,15 @@ public:
virtual GBool isBinary(GBool last = gTrue);
virtual void getImageParams(int *bitsPerComponent,
StreamColorSpaceMode *csMode);
+ void reduceResolution(int reductionA) { reduction = reductionA; }
private:
void fillReadBuf();
void getImageParams2(int *bitsPerComponent, StreamColorSpaceMode *csMode);
- GBool readBoxes();
+ JPXDecodeResult readBoxes();
GBool readColorSpecBox(Guint dataLen);
- GBool readCodestream(Guint len);
+ JPXDecodeResult readCodestream(Guint len);
GBool readTilePart();
GBool readTilePartData(Guint tileIdx,
Guint tilePartLen, GBool tilePartToEOC);
@@ -314,6 +331,7 @@ private:
Guint nComps; // number of components
Guint *bpc; // bits per component, for each component
Guint width, height; // image size
+ int reduction; // log2(reduction in resolution)
GBool haveImgHdr; // set if a JP2/JPX image header has been
// found
JPXColorSpec cs; // color specification
diff -uNrp 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 @@ public:
Stream *getStream()
{ return curStr.isNone() ? (Stream *)NULL : curStr.getStream(); }
- // Get current position in file. This is only used for error
- // messages, so it returns an int instead of a Guint.
- int getPos()
- { return curStr.isNone() ? -1 : (int)curStr.streamGetPos(); }
+ // Get current position in file.
+ GFileOffset getPos()
+ { return curStr.isNone() ? -1 : curStr.streamGetPos(); }
// Set position in file.
- void setPos(Guint pos, int dir = 0)
+ void setPos(GFileOffset pos, int dir = 0)
{ if (!curStr.isNone()) curStr.streamSetPos(pos, dir); }
// Returns true if is a whitespace character.
diff -uNrp 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::parseDest(Object
LinkAction *LinkAction::parseAction(Object *obj, GString *baseURI) {
LinkAction *action;
- Object obj2, obj3, obj4;
+ Object obj2, obj3, obj4, obj5;
if (!obj->isDict()) {
error(errSyntaxWarning, -1, "Bad annotation action");
@@ -86,6 +86,30 @@ LinkAction *LinkAction::parseAction(Obje
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());
@@ -644,6 +668,98 @@ LinkMovie::~LinkMovie() {
}
//------------------------------------------------------------------------
+// LinkJavaScript
+//------------------------------------------------------------------------
+
+LinkJavaScript::LinkJavaScript(Object *jsObj) {
+ char buf[4096];
+ int n;
+
+ if (jsObj->isString()) {
+ js = jsObj->getString()->copy();
+ } else if (jsObj->isStream()) {
+ js = new GString();
+ jsObj->streamReset();
+ while ((n = jsObj->getStream()->getBlock(buf, sizeof(buf))) > 0) {
+ js->append(buf, n);
+ }
+ jsObj->streamClose();
+ } else {
+ error(errSyntaxError, -1, "JavaScript action JS key is wrong type");
+ js = NULL;
+ }
+}
+
+LinkJavaScript::~LinkJavaScript() {
+ if (js) {
+ delete js;
+ }
+}
+
+//------------------------------------------------------------------------
+// LinkSubmitForm
+//------------------------------------------------------------------------
+
+LinkSubmitForm::LinkSubmitForm(Object *urlObj, Object *fieldsObj,
+ Object *flagsObj) {
+ if (urlObj->isString()) {
+ url = urlObj->getString()->copy();
+ } else {
+ error(errSyntaxError, -1, "SubmitForm action URL is wrong type");
+ url = NULL;
+ }
+
+ if (fieldsObj->isArray()) {
+ fieldsObj->copy(&fields);
+ } else {
+ if (!fieldsObj->isNull()) {
+ error(errSyntaxError, -1, "SubmitForm action Fields value is wrong type");
+ }
+ fields.initNull();
+ }
+
+ if (flagsObj->isInt()) {
+ flags = flagsObj->getInt();
+ } else {
+ if (!flagsObj->isNull()) {
+ error(errSyntaxError, -1, "SubmitForm action Flags value is wrong type");
+ }
+ flags = 0;
+ }
+}
+
+LinkSubmitForm::~LinkSubmitForm() {
+ if (url) {
+ delete url;
+ }
+ fields.free();
+}
+
+//------------------------------------------------------------------------
+// LinkHide
+//------------------------------------------------------------------------
+
+LinkHide::LinkHide(Object *fieldsObj, Object *hideFlagObj) {
+ if (fieldsObj->isRef() || fieldsObj->isString() || fieldsObj->isArray()) {
+ fieldsObj->copy(&fields);
+ } else {
+ error(errSyntaxError, -1, "Hide action T value is wrong type");
+ fields.initNull();
+ }
+
+ if (hideFlagObj->isBool()) {
+ hideFlag = hideFlagObj->getBool();
+ } else {
+ error(errSyntaxError, -1, "Hide action H value is wrong type");
+ hideFlag = gFalse;
+ }
+}
+
+LinkHide::~LinkHide() {
+ fields.free();
+}
+
+//------------------------------------------------------------------------
// LinkUnknown
//------------------------------------------------------------------------
@@ -745,7 +861,7 @@ Link::~Link() {
Links::Links(Object *annots, GString *baseURI) {
Link *link;
- Object obj1, obj2;
+ Object obj1, obj2, obj3;
int size;
int i;
@@ -756,7 +872,10 @@ Links::Links(Object *annots, GString *ba
if (annots->isArray()) {
for (i = 0; i < annots->arrayGetLength(); ++i) {
if (annots->arrayGet(i, &obj1)->isDict()) {
- if (obj1.dictLookup("Subtype", &obj2)->isName("Link")) {
+ obj1.dictLookup("Subtype", &obj2);
+ obj1.dictLookup("FT", &obj3);
+ if (obj2.isName("Link") ||
+ (obj2.isName("Widget") && (obj3.isName("Btn") || obj3.isNull()))) {
link = new Link(obj1.getDict(), baseURI);
if (link->isOk()) {
if (numLinks >= size) {
@@ -768,6 +887,7 @@ Links::Links(Object *annots, GString *ba
delete link;
}
}
+ obj3.free();
obj2.free();
}
obj1.free();
diff -uNrp 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 @@ enum LinkActionKind {
actionURI, // URI
actionNamed, // named action
actionMovie, // movie action
+ actionJavaScript, // run JavaScript
+ actionSubmitForm, // submit form
+ actionHide, // hide annotation
actionUnknown // anything else
};
@@ -244,8 +247,10 @@ public:
virtual ~LinkNamed();
+ // Was the LinkNamed created successfully?
virtual GBool isOk() { return name != NULL; }
+ // Accessors.
virtual LinkActionKind getKind() { return actionNamed; }
GString *getName() { return name; }
@@ -265,8 +270,10 @@ public:
virtual ~LinkMovie();
+ // Was the LinkMovie created successfully?
virtual GBool isOk() { return annotRef.num >= 0 || title != NULL; }
+ // Accessors.
virtual LinkActionKind getKind() { return actionMovie; }
GBool hasAnnotRef() { return annotRef.num >= 0; }
Ref *getAnnotRef() { return &annotRef; }
@@ -279,6 +286,81 @@ private:
};
//------------------------------------------------------------------------
+// LinkJavaScript
+//------------------------------------------------------------------------
+
+class LinkJavaScript: public LinkAction {
+public:
+
+ LinkJavaScript(Object *jsObj);
+
+ virtual ~LinkJavaScript();
+
+ // Was the LinkJavaScript created successfully?
+ virtual GBool isOk() { return js != NULL; }
+
+ // Accessors.
+ virtual LinkActionKind getKind() { return actionJavaScript; }
+ GString *getJS() { return js; }
+
+private:
+
+ GString *js;
+};
+
+//------------------------------------------------------------------------
+// LinkSubmitForm
+//------------------------------------------------------------------------
+
+class LinkSubmitForm: public LinkAction {
+public:
+
+ LinkSubmitForm(Object *urlObj, Object *fieldsObj, Object *flagsObj);
+
+ virtual ~LinkSubmitForm();
+
+ // Was the LinkSubmitForm created successfully?
+ virtual GBool isOk() { return url != NULL; }
+
+ // Accessors.
+ virtual LinkActionKind getKind() { return actionSubmitForm; }
+ GString *getURL() { return url; }
+ Object *getFields() { return &fields; }
+ int getFlags() { return flags; }
+
+private:
+
+ GString *url;
+ Object fields;
+ int flags;
+};
+
+//------------------------------------------------------------------------
+// LinkHide
+//------------------------------------------------------------------------
+
+class LinkHide: public LinkAction {
+public:
+
+ LinkHide(Object *fieldsObj, Object *hideFlagObj);
+
+ virtual ~LinkHide();
+
+ // Was the LinkHide created successfully?
+ virtual GBool isOk() { return !fields.isNull(); }
+
+ // Accessors.
+ virtual LinkActionKind getKind() { return actionHide; }
+ Object *getFields() { return &fields; }
+ GBool getHideFlag() { return hideFlag; }
+
+private:
+
+ Object fields;
+ GBool hideFlag;
+};
+
+//------------------------------------------------------------------------
// LinkUnknown
//------------------------------------------------------------------------
diff -uNrp 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 @@ FOFILIBDIR = ../fofi
SPLASHSRCDIR = $(srcdir)/../splash
SPLASHLIBDIR = ../splash
-CXXFLAGS = @CXXFLAGS@ @DEFS@ -I.. -I$(GOOSRCDIR) -I$(FOFISRCDIR) -I$(SPLASHSRCDIR) -I$(srcdir) @freetype2_CFLAGS@ @Sgm_CFLAGS@ @Xm_CFLAGS@ @Xt_CFLAGS@ @Xp_CFLAGS@ @Xext_CFLAGS@ @Xpm_CFLAGS@ @t1_CFLAGS@ @libpaper_CFLAGS@ @X_CFLAGS@
+CXXFLAGS = @CXXFLAGS@ @DEFS@ -I.. -I$(srcdir)/.. -I$(GOOSRCDIR) -I$(FOFISRCDIR) -I$(SPLASHSRCDIR) -I$(srcdir) @freetype2_CFLAGS@ @Sgm_CFLAGS@ @Xm_CFLAGS@ @Xt_CFLAGS@ @Xp_CFLAGS@ @Xext_CFLAGS@ @Xpm_CFLAGS@ @libpng_CFLAGS@ @libpaper_CFLAGS@ @X_CFLAGS@ @EXTRA_CFLAGS@
LDFLAGS = @LDFLAGS@
-T1LIBS = @t1_LIBS@
-FTLIBS = @freetype2_LIBS@
+FTLIBS = @freetype2_LIBS@ -lz
XLIBS = @Sgm_LIBS@ @Xm_LIBS@ @Xt_LIBS@ @Xp_LIBS@ @Xext_LIBS@ @Xpm_LIBS@ @X_PRE_LIBS@ @X_LIBS@ -lX11 @X_EXTRA_LIBS@
+PNGLIBS = @libpng_LIBS@
+
SPLASHLIBS = -L$(SPLASHLIBDIR) -lsplash
-OTHERLIBS = @LIBS@ @libpaper_LIBS@ \
+OTHERLIBS = @LIBS@ @libpaper_LIBS@ @EXTRA_LIBS@ \
-L$(FOFILIBDIR) -lfofi \
-L$(GOOLIBDIR) -lGoo
@@ -49,6 +50,7 @@ EXE = @EXE@
#------------------------------------------------------------------------
CXX_SRC = \
+ $(srcdir)/AcroForm.cc \
$(srcdir)/Annot.cc \
$(srcdir)/Array.cc \
$(srcdir)/BuiltinFont.cc \
@@ -61,11 +63,13 @@ CXX_SRC = \
$(srcdir)/Dict.cc \
$(srcdir)/Error.cc \
$(srcdir)/FontEncodingTables.cc \
+ $(srcdir)/Form.cc \
$(srcdir)/Function.cc \
$(srcdir)/Gfx.cc \
$(srcdir)/GfxFont.cc \
$(srcdir)/GfxState.cc \
$(srcdir)/GlobalParams.cc \
+ $(srcdir)/HTMLGen.cc \
$(srcdir)/ImageOutputDev.cc \
$(srcdir)/JArithmeticDecoder.cc \
$(srcdir)/JBIG2Stream.cc \
@@ -89,44 +93,94 @@ CXX_SRC = \
$(srcdir)/SplashOutputDev.cc \
$(srcdir)/Stream.cc \
$(srcdir)/TextOutputDev.cc \
+ $(srcdir)/TextString.cc \
$(srcdir)/UnicodeMap.cc \
$(srcdir)/UnicodeTypeTable.cc \
+ $(srcdir)/XFAForm.cc \
$(srcdir)/XPDFApp.cc \
$(srcdir)/XPDFCore.cc \
$(srcdir)/XPDFTree.cc \
$(srcdir)/XPDFViewer.cc \
$(srcdir)/XpdfPluginAPI.cc \
$(srcdir)/XRef.cc \
+ $(srcdir)/Zoox.cc \
$(srcdir)/pdftops.cc \
$(srcdir)/pdftotext.cc \
+ $(srcdir)/pdftohtml.cc \
$(srcdir)/pdfinfo.cc \
$(srcdir)/pdffonts.cc \
$(srcdir)/pdfdetach.cc \
$(srcdir)/pdftoppm.cc \
+ $(srcdir)/pdftopng.cc \
$(srcdir)/pdfimages.cc \
$(srcdir)/xpdf.cc
#------------------------------------------------------------------------
-all: xpdf$(EXE) pdftops$(EXE) pdftotext$(EXE) pdfinfo$(EXE) \
- pdffonts$(EXE) pdfdetach$(EXE) pdftoppm$(EXE) pdfimages$(EXE)
-
-all-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 @@ xpdf$(EXE): $(XPDF_OBJS) $(GOOLIBDIR)/$(
#------------------------------------------------------------------------
-PDFTOPS_OBJS = Annot.o Array.o BuiltinFont.o BuiltinFontTables.o \
- Catalog.o CharCodeToUnicode.o CMap.o Decrypt.o Dict.o Error.o \
- FontEncodingTables.o Function.o Gfx.o GfxFont.o \
- GfxState.o GlobalParams.o JArithmeticDecoder.o JBIG2Stream.o \
- JPXStream.o Lexer.o Link.o NameToCharCode.o OptionalContent.o \
- Outline.o Object.o OutputDev.o Page.o Parser.o PDFDoc.o \
- PDFDocEncoding.o PreScanOutputDev.o PSOutputDev.o PSTokenizer.o \
- SecurityHandler.o SplashOutputDev.o Stream.o UnicodeMap.o \
- XpdfPluginAPI.o XRef.o pdftops.o
-PDFTOPS_LIBS = -L$(GOOLIBDIR) -lGoo $(SPLASHLIBS) $(T1LIBS) $(FTLIBS) \
+PDFTOPS_OBJS = \
+ AcroForm.o \
+ Annot.o \
+ Array.o \
+ BuiltinFont.o \
+ BuiltinFontTables.o \
+ Catalog.o \
+ CharCodeToUnicode.o \
+ CMap.o \
+ Decrypt.o \
+ Dict.o \
+ Error.o \
+ FontEncodingTables.o \
+ Form.o \
+ Function.o \
+ Gfx.o \
+ GfxFont.o \
+ GfxState.o \
+ GlobalParams.o \
+ JArithmeticDecoder.o \
+ JBIG2Stream.o \
+ JPXStream.o \
+ Lexer.o \
+ Link.o \
+ NameToCharCode.o \
+ OptionalContent.o \
+ Outline.o \
+ Object.o \
+ OutputDev.o \
+ Page.o \
+ Parser.o \
+ PDFDoc.o \
+ PDFDocEncoding.o \
+ PreScanOutputDev.o \
+ PSOutputDev.o \
+ PSTokenizer.o \
+ SecurityHandler.o \
+ SplashOutputDev.o \
+ Stream.o \
+ TextString.o \
+ UnicodeMap.o \
+ XFAForm.o \
+ XpdfPluginAPI.o \
+ XRef.o \
+ Zoox.o \
+ pdftops.o
+PDFTOPS_LIBS = -L$(GOOLIBDIR) -lGoo $(SPLASHLIBS) $(FTLIBS) \
$(OTHERLIBS) -lm
pdftops$(EXE): $(PDFTOPS_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
@@ -152,15 +243,51 @@ pdftops$(EXE): $(PDFTOPS_OBJS) $(GOOLIBD
#------------------------------------------------------------------------
-PDFTOTEXT_OBJS = Annot.o Array.o BuiltinFont.o BuiltinFontTables.o \
- Catalog.o CharCodeToUnicode.o CMap.o Decrypt.o Dict.o Error.o \
- FontEncodingTables.o Function.o Gfx.o GfxFont.o \
- GfxState.o GlobalParams.o JArithmeticDecoder.o JBIG2Stream.o \
- JPXStream.o Lexer.o Link.o NameToCharCode.o Object.o \
- OptionalContent.o Outline.o OutputDev.o Page.o Parser.o PDFDoc.o \
- PDFDocEncoding.o PSTokenizer.o SecurityHandler.o Stream.o \
- TextOutputDev.o UnicodeMap.o UnicodeTypeTable.o XpdfPluginAPI.o \
- XRef.o pdftotext.o
+PDFTOTEXT_OBJS = \
+ AcroForm.o \
+ Annot.o \
+ Array.o \
+ BuiltinFont.o \
+ BuiltinFontTables.o \
+ Catalog.o \
+ CharCodeToUnicode.o \
+ CMap.o \
+ Decrypt.o \
+ Dict.o \
+ Error.o \
+ FontEncodingTables.o \
+ Form.o \
+ Function.o \
+ Gfx.o \
+ GfxFont.o \
+ GfxState.o \
+ GlobalParams.o \
+ JArithmeticDecoder.o \
+ JBIG2Stream.o \
+ JPXStream.o \
+ Lexer.o \
+ Link.o \
+ NameToCharCode.o \
+ Object.o \
+ OptionalContent.o \
+ Outline.o \
+ OutputDev.o \
+ Page.o \
+ Parser.o \
+ PDFDoc.o \
+ PDFDocEncoding.o \
+ PSTokenizer.o \
+ SecurityHandler.o \
+ Stream.o \
+ TextOutputDev.o \
+ TextString.o \
+ UnicodeMap.o \
+ UnicodeTypeTable.o \
+ XFAForm.o \
+ XpdfPluginAPI.o \
+ XRef.o \
+ Zoox.o \
+ pdftotext.o
PDFTOTEXT_LIBS = -L$(GOOLIBDIR) -lGoo $(OTHERLIBS) -lm
pdftotext$(EXE): $(PDFTOTEXT_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
@@ -169,14 +296,105 @@ pdftotext$(EXE): $(PDFTOTEXT_OBJS) $(GOO
#------------------------------------------------------------------------
-PDFINFO_OBJS = Annot.o Array.o BuiltinFont.o BuiltinFontTables.o \
- Catalog.o CharCodeToUnicode.o CMap.o Decrypt.o Dict.o Error.o \
- FontEncodingTables.o Function.o Gfx.o GfxFont.o \
- GfxState.o GlobalParams.o JArithmeticDecoder.o JBIG2Stream.o \
- JPXStream.o Lexer.o Link.o NameToCharCode.o Object.o \
- OptionalContent.o Outline.o OutputDev.o Page.o Parser.o PDFDoc.o \
- PDFDocEncoding.o PSTokenizer.o SecurityHandler.o Stream.o \
- UnicodeMap.o XpdfPluginAPI.o XRef.o pdfinfo.o
+PDFTOHTML_OBJS = \
+ AcroForm.o \
+ Annot.o \
+ Array.o \
+ BuiltinFont.o \
+ BuiltinFontTables.o \
+ Catalog.o \
+ CharCodeToUnicode.o \
+ CMap.o \
+ Decrypt.o \
+ Dict.o \
+ Error.o \
+ FontEncodingTables.o \
+ Form.o \
+ Function.o \
+ Gfx.o \
+ GfxFont.o \
+ GfxState.o \
+ GlobalParams.o \
+ HTMLGen.o \
+ JArithmeticDecoder.o \
+ JBIG2Stream.o \
+ JPXStream.o \
+ Lexer.o \
+ Link.o \
+ NameToCharCode.o \
+ Object.o \
+ OptionalContent.o \
+ Outline.o \
+ OutputDev.o \
+ Page.o \
+ Parser.o \
+ PDFDoc.o \
+ PDFDocEncoding.o \
+ PSTokenizer.o \
+ SecurityHandler.o \
+ SplashOutputDev.o \
+ Stream.o \
+ TextOutputDev.o \
+ TextString.o \
+ UnicodeMap.o \
+ UnicodeTypeTable.o \
+ XFAForm.o \
+ XpdfPluginAPI.o \
+ XRef.o \
+ Zoox.o \
+ pdftohtml.o
+PDFTOHTML_LIBS = -L$(GOOLIBDIR) -lGoo $(SPLASHLIBS) $(FTLIBS) \
+ $(OTHERLIBS) $(PNGLIBS) -lm
+
+pdftohtml$(EXE): $(PDFTOHTML_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
+ $(CXX) $(CXXFLAGS) $(LDFLAGS) -o pdftohtml$(EXE) $(PDFTOHTML_OBJS) \
+ $(PDFTOHTML_LIBS)
+
+#------------------------------------------------------------------------
+
+PDFINFO_OBJS = \
+ AcroForm.o \
+ Annot.o \
+ Array.o \
+ BuiltinFont.o \
+ BuiltinFontTables.o \
+ Catalog.o \
+ CharCodeToUnicode.o \
+ CMap.o \
+ Decrypt.o \
+ Dict.o \
+ Error.o \
+ FontEncodingTables.o \
+ Form.o \
+ Function.o \
+ Gfx.o \
+ GfxFont.o \
+ GfxState.o \
+ GlobalParams.o \
+ JArithmeticDecoder.o \
+ JBIG2Stream.o \
+ JPXStream.o \
+ Lexer.o \
+ Link.o \
+ NameToCharCode.o \
+ Object.o \
+ OptionalContent.o \
+ Outline.o \
+ OutputDev.o \
+ Page.o \
+ Parser.o \
+ PDFDoc.o \
+ PDFDocEncoding.o \
+ PSTokenizer.o \
+ SecurityHandler.o \
+ Stream.o \
+ TextString.o \
+ UnicodeMap.o \
+ XFAForm.o \
+ XpdfPluginAPI.o \
+ XRef.o \
+ Zoox.o \
+ pdfinfo.o
PDFINFO_LIBS = -L$(GOOLIBDIR) -lGoo $(OTHERLIBS) -lm
pdfinfo$(EXE): $(PDFINFO_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
@@ -185,14 +403,49 @@ pdfinfo$(EXE): $(PDFINFO_OBJS) $(GOOLIBD
#------------------------------------------------------------------------
-PDFFONTS_OBJS = Annot.o Array.o BuiltinFont.o BuiltinFontTables.o \
- Catalog.o CharCodeToUnicode.o CMap.o Decrypt.o Dict.o Error.o \
- FontEncodingTables.o Function.o Gfx.o GfxFont.o \
- GfxState.o GlobalParams.o JArithmeticDecoder.o JBIG2Stream.o \
- JPXStream.o Lexer.o Link.o NameToCharCode.o Object.o \
- OptionalContent.o Outline.o OutputDev.o Page.o Parser.o PDFDoc.o \
- PDFDocEncoding.o PSTokenizer.o SecurityHandler.o Stream.o \
- UnicodeMap.o XpdfPluginAPI.o XRef.o pdffonts.o
+PDFFONTS_OBJS = \
+ AcroForm.o \
+ Annot.o \
+ Array.o \
+ BuiltinFont.o \
+ BuiltinFontTables.o \
+ Catalog.o \
+ CharCodeToUnicode.o \
+ CMap.o \
+ Decrypt.o \
+ Dict.o \
+ Error.o \
+ FontEncodingTables.o \
+ Form.o \
+ Function.o \
+ Gfx.o \
+ GfxFont.o \
+ GfxState.o \
+ GlobalParams.o \
+ JArithmeticDecoder.o \
+ JBIG2Stream.o \
+ JPXStream.o \
+ Lexer.o \
+ Link.o \
+ NameToCharCode.o \
+ Object.o \
+ OptionalContent.o \
+ Outline.o \
+ OutputDev.o \
+ Page.o \
+ Parser.o \
+ PDFDoc.o \
+ PDFDocEncoding.o \
+ PSTokenizer.o \
+ SecurityHandler.o \
+ Stream.o \
+ TextString.o \
+ UnicodeMap.o \
+ XFAForm.o \
+ XpdfPluginAPI.o \
+ XRef.o \
+ Zoox.o \
+ pdffonts.o
PDFFONTS_LIBS = -L$(GOOLIBDIR) -lGoo $(OTHERLIBS) -lm
pdffonts$(EXE): $(PDFFONTS_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
@@ -201,14 +454,49 @@ pdffonts$(EXE): $(PDFFONTS_OBJS) $(GOOLI
#------------------------------------------------------------------------
-PDFDETACH_OBJS = Annot.o Array.o BuiltinFont.o BuiltinFontTables.o \
- Catalog.o CharCodeToUnicode.o CMap.o Decrypt.o Dict.o Error.o \
- FontEncodingTables.o Function.o Gfx.o GfxFont.o \
- GfxState.o GlobalParams.o JArithmeticDecoder.o JBIG2Stream.o \
- JPXStream.o Lexer.o Link.o NameToCharCode.o Object.o \
- OptionalContent.o Outline.o OutputDev.o Page.o Parser.o PDFDoc.o \
- PDFDocEncoding.o PSTokenizer.o SecurityHandler.o Stream.o \
- UnicodeMap.o XpdfPluginAPI.o XRef.o pdfdetach.o
+PDFDETACH_OBJS = \
+ AcroForm.o \
+ Annot.o \
+ Array.o \
+ BuiltinFont.o \
+ BuiltinFontTables.o \
+ Catalog.o \
+ CharCodeToUnicode.o \
+ CMap.o \
+ Decrypt.o \
+ Dict.o \
+ Error.o \
+ FontEncodingTables.o \
+ Form.o \
+ Function.o \
+ Gfx.o \
+ GfxFont.o \
+ GfxState.o \
+ GlobalParams.o \
+ JArithmeticDecoder.o \
+ JBIG2Stream.o \
+ JPXStream.o \
+ Lexer.o \
+ Link.o \
+ NameToCharCode.o \
+ Object.o \
+ OptionalContent.o \
+ Outline.o \
+ OutputDev.o \
+ Page.o \
+ Parser.o \
+ PDFDoc.o \
+ PDFDocEncoding.o \
+ PSTokenizer.o \
+ SecurityHandler.o \
+ Stream.o \
+ TextString.o \
+ UnicodeMap.o \
+ XFAForm.o \
+ XpdfPluginAPI.o \
+ XRef.o \
+ Zoox.o \
+ pdfdetach.o
PDFDETACH_LIBS = -L$(GOOLIBDIR) -lGoo $(OTHERLIBS) -lm
pdfdetach$(EXE): $(PDFDETACH_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
@@ -217,16 +505,53 @@ pdfdetach$(EXE): $(PDFDETACH_OBJS) $(GOO
#------------------------------------------------------------------------
-PDFTOPPM_OBJS = Annot.o Array.o BuiltinFont.o BuiltinFontTables.o \
- Catalog.o CharCodeToUnicode.o CMap.o Decrypt.o Dict.o Error.o \
- FontEncodingTables.o Function.o Gfx.o GfxFont.o GfxState.o \
- GlobalParams.o JArithmeticDecoder.o JBIG2Stream.o JPXStream.o \
- Lexer.o Link.o NameToCharCode.o Object.o OptionalContent.o \
- Outline.o OutputDev.o Page.o Parser.o PDFDoc.o PDFDocEncoding.o \
- PSTokenizer.o SecurityHandler.o SplashOutputDev.o Stream.o \
- TextOutputDev.o UnicodeMap.o UnicodeTypeTable.o XpdfPluginAPI.o \
- XRef.o pdftoppm.o
-PDFTOPPM_LIBS = -L$(GOOLIBDIR) -lGoo $(SPLASHLIBS) $(T1LIBS) $(FTLIBS) \
+PDFTOPPM_OBJS = \
+ AcroForm.o \
+ Annot.o \
+ Array.o \
+ BuiltinFont.o \
+ BuiltinFontTables.o \
+ Catalog.o \
+ CharCodeToUnicode.o \
+ CMap.o \
+ Decrypt.o \
+ Dict.o \
+ Error.o \
+ FontEncodingTables.o \
+ Form.o \
+ Function.o \
+ Gfx.o \
+ GfxFont.o \
+ GfxState.o \
+ GlobalParams.o \
+ JArithmeticDecoder.o \
+ JBIG2Stream.o \
+ JPXStream.o \
+ Lexer.o \
+ Link.o \
+ NameToCharCode.o \
+ Object.o \
+ OptionalContent.o \
+ Outline.o \
+ OutputDev.o \
+ Page.o \
+ Parser.o \
+ PDFDoc.o \
+ PDFDocEncoding.o \
+ PSTokenizer.o \
+ SecurityHandler.o \
+ SplashOutputDev.o \
+ Stream.o \
+ TextOutputDev.o \
+ TextString.o \
+ UnicodeMap.o \
+ UnicodeTypeTable.o \
+ XFAForm.o \
+ XpdfPluginAPI.o \
+ XRef.o \
+ Zoox.o \
+ pdftoppm.o
+PDFTOPPM_LIBS = -L$(GOOLIBDIR) -lGoo $(SPLASHLIBS) $(FTLIBS) \
$(OTHERLIBS) -lm
pdftoppm$(EXE): $(PDFTOPPM_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
@@ -235,14 +560,105 @@ pdftoppm$(EXE): $(PDFTOPPM_OBJS) $(GOOLI
#------------------------------------------------------------------------
-PDFIMAGES_OBJS = Annot.o Array.o BuiltinFont.o BuiltinFontTables.o \
- Catalog.o CharCodeToUnicode.o CMap.o Decrypt.o Dict.o Error.o \
- FontEncodingTables.o Function.o Gfx.o GfxFont.o GfxState.o \
- GlobalParams.o ImageOutputDev.o JArithmeticDecoder.o \
- JBIG2Stream.o JPXStream.o Lexer.o Link.o NameToCharCode.o Object.o \
- OptionalContent.o Outline.o OutputDev.o Page.o Parser.o PDFDoc.o \
- PDFDocEncoding.o PSTokenizer.o SecurityHandler.o Stream.o \
- UnicodeMap.o XpdfPluginAPI.o XRef.o pdfimages.o
+PDFTOPNG_OBJS = \
+ AcroForm.o \
+ Annot.o \
+ Array.o \
+ BuiltinFont.o \
+ BuiltinFontTables.o \
+ Catalog.o \
+ CharCodeToUnicode.o \
+ CMap.o \
+ Decrypt.o \
+ Dict.o \
+ Error.o \
+ FontEncodingTables.o \
+ Form.o \
+ Function.o \
+ Gfx.o \
+ GfxFont.o \
+ GfxState.o \
+ GlobalParams.o \
+ JArithmeticDecoder.o \
+ JBIG2Stream.o \
+ JPXStream.o \
+ Lexer.o \
+ Link.o \
+ NameToCharCode.o \
+ Object.o \
+ OptionalContent.o \
+ Outline.o \
+ OutputDev.o \
+ Page.o \
+ Parser.o \
+ PDFDoc.o \
+ PDFDocEncoding.o \
+ PSTokenizer.o \
+ SecurityHandler.o \
+ SplashOutputDev.o \
+ Stream.o \
+ TextOutputDev.o \
+ TextString.o \
+ UnicodeMap.o \
+ UnicodeTypeTable.o \
+ XFAForm.o \
+ XpdfPluginAPI.o \
+ XRef.o \
+ Zoox.o \
+ pdftopng.o
+PDFTOPNG_LIBS = -L$(GOOLIBDIR) -lGoo $(SPLASHLIBS) $(FTLIBS) \
+ $(OTHERLIBS) $(PNGLIBS) -lm
+
+pdftopng$(EXE): $(PDFTOPNG_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
+ $(CXX) $(CXXFLAGS) $(LDFLAGS) -o pdftopng$(EXE) $(PDFTOPNG_OBJS) \
+ $(PDFTOPNG_LIBS)
+
+#------------------------------------------------------------------------
+
+PDFIMAGES_OBJS = \
+ AcroForm.o \
+ Annot.o \
+ Array.o \
+ BuiltinFont.o \
+ BuiltinFontTables.o \
+ Catalog.o \
+ CharCodeToUnicode.o \
+ CMap.o \
+ Decrypt.o \
+ Dict.o \
+ Error.o \
+ FontEncodingTables.o \
+ Form.o \
+ Function.o \
+ Gfx.o \
+ GfxFont.o \
+ GfxState.o \
+ GlobalParams.o \
+ ImageOutputDev.o \
+ JArithmeticDecoder.o \
+ JBIG2Stream.o \
+ JPXStream.o \
+ Lexer.o \
+ Link.o \
+ NameToCharCode.o \
+ Object.o \
+ OptionalContent.o \
+ Outline.o \
+ OutputDev.o \
+ Page.o \
+ Parser.o \
+ PDFDoc.o \
+ PDFDocEncoding.o \
+ PSTokenizer.o \
+ SecurityHandler.o \
+ Stream.o \
+ TextString.o \
+ UnicodeMap.o \
+ XFAForm.o \
+ XpdfPluginAPI.o \
+ XRef.o \
+ Zoox.o \
+ pdfimages.o
PDFIMAGES_LIBS = -L$(GOOLIBDIR) -lGoo $(OTHERLIBS) -lm
pdfimages$(EXE): $(PDFIMAGES_OBJS) $(GOOLIBDIR)/$(LIBPREFIX)Goo.a
@@ -255,10 +671,12 @@ clean:
rm -f $(XPDF_OBJS) xpdf$(EXE)
rm -f $(PDFTOPS_OBJS) pdftops$(EXE)
rm -f $(PDFTOTEXT_OBJS) pdftotext$(EXE)
+ rm -f $(PDFTOHTML_OBJS) pdftohtml$(EXE)
rm -f $(PDFINFO_OBJS) pdfinfo$(EXE)
rm -f $(PDFFONTS_OBJS) pdffonts$(EXE)
rm -f $(PDFDETACH_OBJS) pdfdetach$(EXE)
rm -f $(PDFTOPPM_OBJS) pdftoppm$(EXE)
+ rm -f $(PDFTOPNG_OBJS) pdftopng$(EXE)
rm -f $(PDFIMAGES_OBJS) pdfimages$(EXE)
#------------------------------------------------------------------------
@@ -266,4 +684,4 @@ clean:
depend:
$(CXX) $(CXXFLAGS) -MM $(CXX_SRC) >Makefile.dep
-include Makefile.dep
+-include Makefile.dep
diff -uNrp 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
#include "gtypes.h"
#include "gmem.h"
+#include "gfile.h"
#include "GString.h"
class XRef;
@@ -179,9 +180,10 @@ public:
void streamClose();
int streamGetChar();
int streamLookChar();
+ int streamGetBlock(char *blk, int size);
char *streamGetLine(char *buf, int size);
- Guint streamGetPos();
- void streamSetPos(Guint pos, int dir = 0);
+ GFileOffset streamGetPos();
+ void streamSetPos(GFileOffset pos, int dir = 0);
Dict *streamGetDict();
// Output.
@@ -288,13 +290,16 @@ inline int Object::streamGetChar()
inline int Object::streamLookChar()
{ return stream->lookChar(); }
+inline int Object::streamGetBlock(char *blk, int size)
+ { return stream->getBlock(blk, size); }
+
inline char *Object::streamGetLine(char *buf, int size)
{ return stream->getLine(buf, size); }
-inline Guint Object::streamGetPos()
+inline GFileOffset Object::streamGetPos()
{ return stream->getPos(); }
-inline void Object::streamSetPos(Guint pos, int dir)
+inline void Object::streamSetPos(GFileOffset pos, int dir)
{ stream->setPos(pos, dir); }
inline Dict *Object::streamGetDict()
diff -uNrp 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 @@ GBool OptionalContent::evalOCObject(Obje
}
}
obj->fetch(xref, &obj2);
- if (obj2.isDict("OCMD")) {
- if (obj2.dictLookup("VE", &obj3)->isArray()) {
- *visible = evalOCVisibilityExpr(&obj3, 0);
- obj3.free();
- } else {
- obj3.free();
- policy = ocPolicyAnyOn;
- if (obj2.dictLookup("P", &obj3)->isName()) {
- if (obj3.isName("AllOn")) {
- policy = ocPolicyAllOn;
- } else if (obj3.isName("AnyOn")) {
- policy = ocPolicyAnyOn;
- } else if (obj3.isName("AnyOff")) {
- policy = ocPolicyAnyOff;
- } else if (obj3.isName("AllOff")) {
- policy = ocPolicyAllOff;
- }
+ if (!obj2.isDict("OCMD")) {
+ obj2.free();
+ return gFalse;
+ }
+ if (obj2.dictLookup("VE", &obj3)->isArray()) {
+ *visible = evalOCVisibilityExpr(&obj3, 0);
+ obj3.free();
+ } else {
+ obj3.free();
+ policy = ocPolicyAnyOn;
+ if (obj2.dictLookup("P", &obj3)->isName()) {
+ if (obj3.isName("AllOn")) {
+ policy = ocPolicyAllOn;
+ } else if (obj3.isName("AnyOn")) {
+ policy = ocPolicyAnyOn;
+ } else if (obj3.isName("AnyOff")) {
+ policy = ocPolicyAnyOff;
+ } else if (obj3.isName("AllOff")) {
+ policy = ocPolicyAllOff;
}
- obj3.free();
- obj2.dictLookupNF("OCGs", &obj3);
- ocg = NULL;
- if (obj3.isRef()) {
- ref = obj3.getRef();
- ocg = findOCG(&ref);
+ }
+ obj3.free();
+ obj2.dictLookupNF("OCGs", &obj3);
+ ocg = NULL;
+ if (obj3.isRef()) {
+ ref = obj3.getRef();
+ ocg = findOCG(&ref);
+ }
+ if (ocg) {
+ *visible = (policy == ocPolicyAllOn || policy == ocPolicyAnyOn) ?
+ ocg->getState() : !ocg->getState();
+ } else {
+ *visible = policy == ocPolicyAllOn || policy == ocPolicyAllOff;
+ if (!obj3.fetch(xref, &obj4)->isArray()) {
+ obj4.free();
+ obj3.free();
+ obj2.free();
+ return gFalse;
}
- if (ocg) {
- *visible = (policy == ocPolicyAllOn || policy == ocPolicyAnyOn) ?
- ocg->getState() : !ocg->getState();
- } else {
- *visible = gTrue;
- if (obj3.fetch(xref, &obj4)->isArray()) {
- for (i = 0; i < obj4.arrayGetLength(); ++i) {
- obj4.arrayGetNF(i, &obj5);
- if (obj5.isRef()) {
- ref = obj5.getRef();
- if ((ocg = findOCG(&ref))) {
- switch (policy) {
- case ocPolicyAllOn:
- *visible = *visible && ocg->getState();
- break;
- case ocPolicyAnyOn:
- *visible = *visible || ocg->getState();
- break;
- case ocPolicyAnyOff:
- *visible = *visible || !ocg->getState();
- break;
- case ocPolicyAllOff:
- *visible = *visible && !ocg->getState();
- break;
- }
- }
- }
+ for (i = 0; i < obj4.arrayGetLength(); ++i) {
+ obj4.arrayGetNF(i, &obj5);
+ if (obj5.isRef()) {
+ ref = obj5.getRef();
+ if (!(ocg = findOCG(&ref))) {
obj5.free();
+ obj4.free();
+ obj3.free();
+ obj2.free();
+ return gFalse;
+ }
+ switch (policy) {
+ case ocPolicyAllOn:
+ *visible = *visible && ocg->getState();
+ break;
+ case ocPolicyAnyOn:
+ *visible = *visible || ocg->getState();
+ break;
+ case ocPolicyAnyOff:
+ *visible = *visible || !ocg->getState();
+ break;
+ case ocPolicyAllOff:
+ *visible = *visible && !ocg->getState();
+ break;
}
}
- obj4.free();
+ obj5.free();
}
- obj3.free();
+ obj4.free();
}
- obj2.free();
- return gTrue;
+ obj3.free();
}
obj2.free();
- return gFalse;
+ return gTrue;
}
GBool OptionalContent::evalOCVisibilityExpr(Object *expr, int recursion) {
@@ -279,12 +288,9 @@ GBool OptionalContent::evalOCVisibilityE
//------------------------------------------------------------------------
OptionalContentGroup *OptionalContentGroup::parse(Ref *refA, Object *obj) {
- Unicode *nameA;
- int nameLenA;
+ TextString *nameA;
Object obj1, obj2, obj3;
- GString *s;
OCUsageState viewStateA, printStateA;
- int i;
if (!obj->isDict()) {
return NULL;
@@ -294,22 +300,7 @@ OptionalContentGroup *OptionalContentGro
obj1.free();
return NULL;
}
- s = obj1.getString();
- if ((s->getChar(0) & 0xff) == 0xfe &&
- (s->getChar(1) & 0xff) == 0xff) {
- nameLenA = (s->getLength() - 2) / 2;
- nameA = (Unicode *)gmallocn(nameLenA, sizeof(Unicode));
- for (i = 0; i < nameLenA; ++i) {
- nameA[i] = ((s->getChar(2 + 2*i) & 0xff) << 8) |
- (s->getChar(3 + 2*i) & 0xff);
- }
- } else {
- nameLenA = s->getLength();
- nameA = (Unicode *)gmallocn(nameLenA, sizeof(Unicode));
- for (i = 0; i < nameLenA; ++i) {
- nameA[i] = pdfDocEncoding[s->getChar(i) & 0xff];
- }
- }
+ nameA = new TextString(obj1.getString());
obj1.free();
viewStateA = printStateA = ocUsageUnset;
@@ -339,30 +330,35 @@ OptionalContentGroup *OptionalContentGro
}
obj1.free();
- return new OptionalContentGroup(refA, nameA, nameLenA,
- viewStateA, printStateA);
+ return new OptionalContentGroup(refA, nameA, viewStateA, printStateA);
}
-OptionalContentGroup::OptionalContentGroup(Ref *refA, Unicode *nameA,
- int nameLenA,
+OptionalContentGroup::OptionalContentGroup(Ref *refA, TextString *nameA,
OCUsageState viewStateA,
OCUsageState printStateA) {
ref = *refA;
name = nameA;
- nameLen = nameLenA;
viewState = viewStateA;
printState = printStateA;
state = gTrue;
}
OptionalContentGroup::~OptionalContentGroup() {
- gfree(name);
+ delete name;
}
GBool OptionalContentGroup::matches(Ref *refA) {
return refA->num == ref.num && refA->gen == ref.gen;
}
+Unicode *OptionalContentGroup::getName() {
+ return name->getUnicode();
+}
+
+int OptionalContentGroup::getNameLength() {
+ return name->getLength();
+}
+
//------------------------------------------------------------------------
OCDisplayNode *OCDisplayNode::parse(Object *obj, OptionalContent *oc,
@@ -404,8 +400,10 @@ OCDisplayNode *OCDisplayNode::parse(Obje
obj2.arrayGetNF(i, &obj3);
if ((child = OCDisplayNode::parse(&obj3, oc, xref, recursion + 1))) {
if (!child->ocg && !child->name && node->getNumChildren() > 0) {
- node->getChild(node->getNumChildren() - 1)->
- addChildren(child->takeChildren());
+ if (child->getNumChildren() > 0) {
+ node->getChild(node->getNumChildren() - 1)->
+ addChildren(child->takeChildren());
+ }
delete child;
} else {
node->addChild(child);
@@ -418,42 +416,19 @@ OCDisplayNode *OCDisplayNode::parse(Obje
}
OCDisplayNode::OCDisplayNode() {
- name = NULL;
- nameLen = 0;
+ name = new TextString();
ocg = NULL;
children = NULL;
}
OCDisplayNode::OCDisplayNode(GString *nameA) {
- int i;
-
- if ((nameA->getChar(0) & 0xff) == 0xfe &&
- (nameA->getChar(1) & 0xff) == 0xff) {
- nameLen = (nameA->getLength() - 2) / 2;
- name = (Unicode *)gmallocn(nameLen, sizeof(Unicode));
- for (i = 0; i < nameLen; ++i) {
- name[i] = ((nameA->getChar(2 + 2*i) & 0xff) << 8) |
- (nameA->getChar(3 + 2*i) & 0xff);
- }
- } else {
- nameLen = nameA->getLength();
- name = (Unicode *)gmallocn(nameLen, sizeof(Unicode));
- for (i = 0; i < nameLen; ++i) {
- name[i] = pdfDocEncoding[nameA->getChar(i) & 0xff];
- }
- }
+ name = new TextString(nameA);
ocg = NULL;
children = NULL;
}
OCDisplayNode::OCDisplayNode(OptionalContentGroup *ocgA) {
- nameLen = ocgA->getNameLength();
- if (nameLen) {
- name = (Unicode *)gmallocn(nameLen, sizeof(Unicode));
- memcpy(name, ocgA->getName(), nameLen * sizeof(Unicode));
- } else {
- name = NULL;
- }
+ name = new TextString(ocgA->name);
ocg = ocgA;
children = NULL;
}
@@ -482,12 +457,20 @@ GList *OCDisplayNode::takeChildren() {
}
OCDisplayNode::~OCDisplayNode() {
- gfree(name);
+ delete name;
if (children) {
deleteGList(children, OCDisplayNode);
}
}
+Unicode *OCDisplayNode::getName() {
+ return name->getUnicode();
+}
+
+int OCDisplayNode::getNameLength() {
+ return name->getLength();
+}
+
int OCDisplayNode::getNumChildren() {
if (!children) {
return 0;
diff -uNrp 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 @@ public:
GBool matches(Ref *refA);
- Unicode *getName() { return name; }
- int getNameLength() { return nameLen; }
+ Unicode *getName();
+ int getNameLength();
OCUsageState getViewState() { return viewState; }
OCUsageState getPrintState() { return printState; }
GBool getState() { return state; }
@@ -87,15 +88,16 @@ public:
private:
- OptionalContentGroup(Ref *refA, Unicode *nameA, int nameLenA,
+ OptionalContentGroup(Ref *refA, TextString *nameA,
OCUsageState viewStateA, OCUsageState printStateA);
Ref ref;
- Unicode *name;
- int nameLen;
+ TextString *name;
OCUsageState viewState, // suggested state when viewing
printState; // suggested state when printing
GBool state; // current state (on/off)
+
+ friend class OCDisplayNode;
};
//------------------------------------------------------------------------
@@ -108,8 +110,8 @@ public:
OCDisplayNode();
~OCDisplayNode();
- Unicode *getName() { return name; }
- int getNameLength() { return nameLen; }
+ Unicode *getName();
+ int getNameLength();
OptionalContentGroup *getOCG() { return ocg; }
int getNumChildren();
OCDisplayNode *getChild(int idx);
@@ -122,8 +124,7 @@ private:
void addChildren(GList *childrenA);
GList *takeChildren();
- Unicode *name; // display name (may be NULL)
- int nameLen;
+ TextString *name; // display name
OptionalContentGroup *ocg; // NULL for display labels
GList *children; // NULL if there are no children
// [OCDisplayNode]
diff -uNrp 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 @@ Outline::Outline(Object *outlineObj, XRe
outlineObj->dictLookupNF("First", &first);
outlineObj->dictLookupNF("Last", &last);
if (first.isRef() && last.isRef()) {
- items = OutlineItem::readItemList(&first, &last, xref);
+ items = OutlineItem::readItemList(&first, &last, NULL, xref);
}
first.free();
last.free();
@@ -46,35 +46,18 @@ Outline::~Outline() {
//------------------------------------------------------------------------
-OutlineItem::OutlineItem(Dict *dict, XRef *xrefA) {
+OutlineItem::OutlineItem(Object *itemRefA, Dict *dict,
+ OutlineItem *parentA, XRef *xrefA) {
Object obj1;
- GString *s;
- int i;
xref = xrefA;
title = NULL;
action = NULL;
kids = NULL;
+ parent = parentA;
if (dict->lookup("Title", &obj1)->isString()) {
- s = obj1.getString();
- if ((s->getChar(0) & 0xff) == 0xfe &&
- (s->getChar(1) & 0xff) == 0xff) {
- titleLen = (s->getLength() - 2) / 2;
- title = (Unicode *)gmallocn(titleLen, sizeof(Unicode));
- for (i = 0; i < titleLen; ++i) {
- title[i] = ((s->getChar(2 + 2*i) & 0xff) << 8) |
- (s->getChar(3 + 2*i) & 0xff);
- }
- } else {
- titleLen = s->getLength();
- title = (Unicode *)gmallocn(titleLen, sizeof(Unicode));
- for (i = 0; i < titleLen; ++i) {
- title[i] = pdfDocEncoding[s->getChar(i) & 0xff];
- }
- }
- } else {
- titleLen = 0;
+ title = new TextString(obj1.getString());
}
obj1.free();
@@ -88,6 +71,7 @@ OutlineItem::OutlineItem(Dict *dict, XRe
}
obj1.free();
+ itemRefA->copy(&itemRef);
dict->lookupNF("First", &firstRef);
dict->lookupNF("Last", &lastRef);
dict->lookupNF("Next", &nextRef);
@@ -104,22 +88,24 @@ OutlineItem::OutlineItem(Dict *dict, XRe
OutlineItem::~OutlineItem() {
close();
if (title) {
- gfree(title);
+ delete title;
}
if (action) {
delete action;
}
+ itemRef.free();
firstRef.free();
lastRef.free();
nextRef.free();
}
GList *OutlineItem::readItemList(Object *firstItemRef, Object *lastItemRef,
- XRef *xrefA) {
+ OutlineItem *parentA, XRef *xrefA) {
GList *items;
- OutlineItem *item;
+ OutlineItem *item, *sibling;
Object obj;
- Object *p, *refObj;
+ Object *p;
+ OutlineItem *ancestor;
int i;
items = new GList();
@@ -132,8 +118,36 @@ GList *OutlineItem::readItemList(Object
obj.free();
break;
}
- item = new OutlineItem(obj.getDict(), xrefA);
+ item = new OutlineItem(p, obj.getDict(), parentA, xrefA);
obj.free();
+
+ // check for loops with parents
+ for (ancestor = parentA; ancestor; ancestor = ancestor->parent) {
+ if (p->getRefNum() == ancestor->itemRef.getRefNum() &&
+ p->getRefGen() == ancestor->itemRef.getRefGen()) {
+ error(errSyntaxError, -1, "Loop detected in outline");
+ break;
+ }
+ }
+ if (ancestor) {
+ delete item;
+ break;
+ }
+
+ // check for loops with siblings
+ for (i = 0; i < items->getLength(); ++i) {
+ sibling = (OutlineItem *)items->get(i);
+ if (p->getRefNum() == sibling->itemRef.getRefNum() &&
+ p->getRefGen() == sibling->itemRef.getRefGen()) {
+ error(errSyntaxError, -1, "Loop detected in outline");
+ break;
+ }
+ }
+ if (i < items->getLength()) {
+ delete item;
+ break;
+ }
+
items->append(item);
if (p->getRefNum() == lastItemRef->getRef().num &&
p->getRefGen() == lastItemRef->getRef().gen) {
@@ -143,23 +157,13 @@ GList *OutlineItem::readItemList(Object
if (!p->isRef()) {
break;
}
- for (i = 0; i < items->getLength(); ++i) {
- refObj = (i == 0) ? firstItemRef
- : &((OutlineItem *)items->get(i - 1))->nextRef;
- if (refObj->getRefNum() == p->getRefNum() &&
- refObj->getRefGen() == p->getRefGen()) {
- error(errSyntaxError, -1, "Loop detected in outline item list");
- p = NULL;
- break;
- }
- }
} while (p);
return items;
}
void OutlineItem::open() {
if (!kids) {
- kids = readItemList(&firstRef, &lastRef, xref);
+ kids = readItemList(&firstRef, &lastRef, this, xref);
}
}
@@ -169,3 +173,11 @@ void OutlineItem::close() {
kids = NULL;
}
}
+
+Unicode *OutlineItem::getTitle() {
+ return title ? title->getUnicode() : (Unicode *)NULL;
+}
+
+int OutlineItem::getTitleLength() {
+ return title ? title->getLength() : 0;
+}
diff -uNrp 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 GString;
class GList;
class XRef;
class LinkAction;
+class TextString;
//------------------------------------------------------------------------
@@ -44,17 +45,18 @@ private:
class OutlineItem {
public:
- OutlineItem(Dict *dict, XRef *xrefA);
+ OutlineItem(Object *itemRefA, Dict *dict, OutlineItem *parentA, XRef *xrefA);
~OutlineItem();
static GList *readItemList(Object *firstItemRef, Object *lastItemRef,
- XRef *xrefA);
+ OutlineItem *parentA, XRef *xrefA);
void open();
void close();
- Unicode *getTitle() { return title; }
- int getTitleLength() { return titleLen; }
+ Unicode *getTitle();
+ int getTitleLength();
+ TextString *getTitleTextString() { return title; }
LinkAction *getAction() { return action; }
GBool isOpen() { return startsOpen; }
GBool hasKids() { return firstRef.isRef(); }
@@ -63,14 +65,15 @@ public:
private:
XRef *xref;
- Unicode *title;
- int titleLen;
+ TextString *title; // may be NULL
LinkAction *action;
+ Object itemRef;
Object firstRef;
Object lastRef;
Object nextRef;
GBool startsOpen;
GList *kids; // NULL unless this item is open [OutlineItem]
+ OutlineItem *parent;
};
#endif
diff -uNrp 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 @@ void OutputDev::cvtDevToUser(double dx,
*uy = defICTM[1] * dx + defICTM[3] * dy + defICTM[5];
}
+void OutputDev::cvtUserToDev(double ux, double uy, double *dx, double *dy) {
+ *dx = defCTM[0] * ux + defCTM[2] * uy + defCTM[4];
+ *dy = defCTM[1] * ux + defCTM[3] * uy + defCTM[5];
+}
+
void OutputDev::cvtUserToDev(double ux, double uy, int *dx, int *dy) {
*dx = (int)(defCTM[0] * ux + defCTM[2] * uy + defCTM[4] + 0.5);
*dy = (int)(defCTM[1] * ux + defCTM[3] * uy + defCTM[5] + 0.5);
@@ -78,14 +83,10 @@ GBool OutputDev::beginType3Char(GfxState
void OutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
int width, int height, GBool invert,
- GBool inlineImg) {
- int i, j;
-
+ GBool inlineImg, GBool interpolate) {
if (inlineImg) {
str->reset();
- j = height * ((width + 7) / 8);
- for (i = 0; i < j; ++i)
- str->getChar();
+ str->discardChars(height * ((width + 7) / 8));
str->close();
}
}
@@ -93,21 +94,17 @@ void OutputDev::drawImageMask(GfxState *
void OutputDev::setSoftMaskFromImageMask(GfxState *state,
Object *ref, Stream *str,
int width, int height, GBool invert,
- GBool inlineImg) {
- drawImageMask(state, ref, str, width, height, invert, inlineImg);
+ GBool inlineImg, GBool interpolate) {
+ drawImageMask(state, ref, str, width, height, invert, inlineImg, interpolate);
}
void OutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
int width, int height, GfxImageColorMap *colorMap,
- int *maskColors, GBool inlineImg) {
- int i, j;
-
+ int *maskColors, GBool inlineImg, GBool interpolate) {
if (inlineImg) {
str->reset();
- j = height * ((width * colorMap->getNumPixelComps() *
- colorMap->getBits() + 7) / 8);
- for (i = 0; i < j; ++i)
- str->getChar();
+ str->discardChars(height * ((width * colorMap->getNumPixelComps() *
+ colorMap->getBits() + 7) / 8));
str->close();
}
}
@@ -117,8 +114,9 @@ void OutputDev::drawMaskedImage(GfxState
GfxImageColorMap *colorMap,
Stream *maskStr,
int maskWidth, int maskHeight,
- GBool maskInvert) {
- drawImage(state, ref, str, width, height, colorMap, NULL, gFalse);
+ GBool maskInvert, GBool interpolate) {
+ drawImage(state, ref, str, width, height, colorMap, NULL, gFalse,
+ interpolate);
}
void OutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
@@ -126,8 +124,10 @@ void OutputDev::drawSoftMaskedImage(GfxS
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 -uNrp 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 @@ public:
// Convert between device and user coordinates.
virtual void cvtDevToUser(double dx, double dy, double *ux, double *uy);
+ virtual void cvtUserToDev(double ux, double uy, double *dx, double *dy);
virtual void cvtUserToDev(double ux, double uy, int *dx, int *dy);
double *getDefCTM() { return defCTM; }
@@ -161,7 +162,7 @@ public:
virtual void stroke(GfxState *state) {}
virtual void fill(GfxState *state) {}
virtual void eoFill(GfxState *state) {}
- virtual void tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
+ virtual void tilingPatternFill(GfxState *state, Gfx *gfx, Object *strRef,
int paintType, Dict *resDict,
double *mat, double *bbox,
int x0, int y0, int x1, int y1,
@@ -201,25 +202,26 @@ public:
//----- image drawing
virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
int width, int height, GBool invert,
- GBool inlineImg);
+ GBool inlineImg, GBool interpolate);
virtual void setSoftMaskFromImageMask(GfxState *state,
Object *ref, Stream *str,
int width, int height, GBool invert,
- GBool inlineImg);
+ GBool inlineImg, GBool interpolate);
virtual void drawImage(GfxState *state, Object *ref, Stream *str,
int width, int height, GfxImageColorMap *colorMap,
- int *maskColors, GBool inlineImg);
+ int *maskColors, GBool inlineImg, GBool interpolate);
virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
Stream *maskStr, int maskWidth, int maskHeight,
- GBool maskInvert);
+ GBool maskInvert, GBool interpolate);
virtual void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
Stream *maskStr,
int maskWidth, int maskHeight,
- GfxImageColorMap *maskColorMap);
+ GfxImageColorMap *maskColorMap,
+ GBool interpolate);
#if OPI_SUPPORT
//----- OPI functions
diff -uNrp 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 @@ PageAttrs::PageAttrs(PageAttrs *attrs, D
dict->lookup("Metadata", &metadata);
dict->lookup("PieceInfo", &pieceInfo);
dict->lookup("SeparationInfo", &separationInfo);
+ if (dict->lookup("UserUnit", &obj1)->isNum()) {
+ userUnit = obj1.getNum();
+ if (userUnit < 1) {
+ userUnit = 1;
+ }
+ } else {
+ userUnit = 1;
+ }
+ obj1.free();
// resource dictionary
dict->lookup("Resources", &obj1);
@@ -312,7 +322,7 @@ void Page::displaySlice(OutputDev *out,
Gfx *gfx;
Object obj;
Annots *annotList;
- Dict *acroForm;
+ Form *form;
int i;
if (!out->checkPageSlice(this, hDPI, vDPI, rotate, useMediaBox, crop,
@@ -347,8 +357,10 @@ void Page::displaySlice(OutputDev *out,
contents.fetch(xref, &obj);
if (!obj.isNull()) {
gfx->saveState();
- gfx->display(&obj);
- gfx->restoreState();
+ gfx->display(&contents);
+ while (gfx->getState()->hasSaves()) {
+ gfx->restoreState();
+ }
} else {
// empty pages need to call dump to do any setup required by the
// OutputDev
@@ -356,20 +368,11 @@ void Page::displaySlice(OutputDev *out,
}
obj.free();
- // draw annotations
+ // draw (non-form) annotations
if (globalParams->getDrawAnnotations()) {
annotList = new Annots(doc, getAnnots(&obj));
obj.free();
- acroForm = doc->getCatalog()->getAcroForm()->isDict() ?
- doc->getCatalog()->getAcroForm()->getDict() : NULL;
- if (acroForm) {
- if (acroForm->lookup("NeedAppearances", &obj)) {
- if (obj.isBool() && obj.getBool()) {
- annotList->generateAppearances();
- }
- }
- obj.free();
- }
+ annotList->generateAnnotAppearances();
if (annotList->getNumAnnots() > 0) {
if (globalParams->getPrintCommands()) {
printf("***** Annotations\n");
@@ -382,6 +385,12 @@ void Page::displaySlice(OutputDev *out,
delete annotList;
}
+ // draw form fields
+ if ((form = doc->getCatalog()->getForm())) {
+ form->draw(num, gfx, printing);
+ out->dump();
+ }
+
delete gfx;
#endif
}
@@ -459,6 +468,7 @@ void Page::processLinks(OutputDev *out)
delete links;
}
+#ifndef PDF_PARSER_ONLY
void Page::getDefaultCTM(double *ctm, double hDPI, double vDPI,
int rotate, GBool useMediaBox, GBool upsideDown) {
GfxState *state;
@@ -478,3 +488,4 @@ void Page::getDefaultCTM(double *ctm, do
}
delete state;
}
+#endif
diff -uNrp 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 @@ public:
Dict *getSeparationInfo()
{ return separationInfo.isDict()
? separationInfo.getDict() : (Dict *)NULL; }
+ double getUserUnit() { return userUnit; }
Dict *getResourceDict()
{ return resources.isDict() ? resources.getDict() : (Dict *)NULL; }
@@ -100,6 +101,7 @@ private:
Object metadata;
Object pieceInfo;
Object separationInfo;
+ double userUnit;
Object resources;
};
@@ -146,6 +148,7 @@ public:
Stream *getMetadata() { return attrs->getMetadata(); }
Dict *getPieceInfo() { return attrs->getPieceInfo(); }
Dict *getSeparationInfo() { return attrs->getSeparationInfo(); }
+ double getUserUnit() { return attrs->getUserUnit(); }
// Get resource dictionary.
Dict *getResourceDict() { return attrs->getResourceDict(); }
diff -uNrp 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 @@ Stream *Parser::makeStream(Object *dict,
Object obj;
BaseStream *baseStr;
Stream *str;
- Guint pos, endPos, length;
+ GFileOffset pos, endPos, length;
// get stream start position
lexer->skipToNextLine();
@@ -162,20 +162,21 @@ Stream *Parser::makeStream(Object *dict,
}
pos = str->getPos();
- // get length
- dict->dictLookup("Length", &obj, recursion);
- if (obj.isInt()) {
- length = (Guint)obj.getInt();
- obj.free();
- } else {
- error(errSyntaxError, getPos(), "Bad 'Length' attribute in stream");
- obj.free();
- return NULL;
- }
-
// check for length in damaged file
if (xref && xref->getStreamEnd(pos, &endPos)) {
length = endPos - pos;
+
+ // get length from the stream object
+ } else {
+ dict->dictLookup("Length", &obj, recursion);
+ if (obj.isInt()) {
+ length = (GFileOffset)(Guint)obj.getInt();
+ obj.free();
+ } else {
+ error(errSyntaxError, getPos(), "Bad 'Length' attribute in stream");
+ obj.free();
+ return NULL;
+ }
}
// in badly damaged PDF files, we can run off the end of the input
@@ -210,7 +211,7 @@ Stream *Parser::makeStream(Object *dict,
}
// get filters
- str = str->addFilters(dict);
+ str = str->addFilters(dict, recursion);
return str;
}
diff -uNrp 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 @@ public:
Stream *getStream() { return lexer->getStream(); }
// Get current position in file.
- int getPos() { return lexer->getPos(); }
+ GFileOffset getPos() { return lexer->getPos(); }
private:
diff -uNrp 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 @@ PDFCore::PDFCore(SplashColorMode colorMo
selectULY = selectLRY = 0;
dragging = gFalse;
lastDragLeft = lastDragTop = gTrue;
- selectXorColor[0] = selectXorColor[1] = selectXorColor[2] = 0;
+ selectXorColor[0] = selectXorColor[1] = selectXorColor[2] =
+ reverseVideoA ? 0xff : 0x00;
splashColorXor(selectXorColor, paperColorA);
historyCur = pdfHistorySize - 1;
@@ -128,7 +129,11 @@ PDFCore::~PDFCore() {
}
for (i = 0; i < pdfHistorySize; ++i) {
if (history[i].fileName) {
+#ifdef _WIN32
+ delete[] history[i].fileName;
+#else
delete history[i].fileName;
+#endif
}
}
gfree(pageY);
@@ -423,6 +428,7 @@ void PDFCore::update(int topPageA, int s
// check for changes to the PDF file
if ((force || (!continuousMode && topPage != topPageA)) &&
+ doc->getFileName() &&
checkForNewFile()) {
if (loadFile(doc->getFileName()) == errNone) {
if (topPageA > doc->getNumPages()) {
@@ -758,13 +764,30 @@ void PDFCore::update(int topPageA, int s
}
hist = &history[historyCur];
if (hist->fileName) {
+#ifdef _WIN32
+ delete[] hist->fileName;
+#else
delete hist->fileName;
+#endif
+ }
+#ifdef _WIN32
+ if (doc->getFileNameU()) {
+ hist->fileName = (wchar_t *)gmallocn(MAX_PATH + 1, sizeof(wchar_t));
+ if (GetFullPathNameW(doc->getFileNameU(), MAX_PATH + 1,
+ hist->fileName, NULL) == 0) {
+ delete[] hist->fileName;
+ hist->fileName = NULL;
+ }
+ } else {
+ hist->fileName = NULL;
}
+#else
if (doc->getFileName()) {
hist->fileName = doc->getFileName()->copy();
} else {
hist->fileName = NULL;
}
+#endif
hist->page = topPage;
if (historyBLen < pdfHistorySize) {
++historyBLen;
@@ -807,6 +830,7 @@ void PDFCore::addPage(int pg, int rot) {
void PDFCore::needTile(PDFCorePage *page, int x, int y) {
PDFCoreTile *tile;
+ TextOutputControl textOutCtrl;
TextOutputDev *textOut;
int xDest, yDest, sliceW, sliceH;
int i;
@@ -893,7 +917,8 @@ void PDFCore::needTile(PDFCorePage *page
page->links = doc->getLinks(page->page);
}
if (!page->text) {
- if ((textOut = new TextOutputDev(NULL, gTrue, 0, gFalse, gFalse))) {
+ textOutCtrl.mode = textOutPhysLayout;
+ if ((textOut = new TextOutputDev(NULL, &textOutCtrl, gFalse))) {
doc->displayPage(textOut, page->page, dpi, dpi, rotate,
gFalse, gTrue, gFalse);
page->text = textOut->takeText();
@@ -977,11 +1002,27 @@ GBool PDFCore::goForward() {
}
--historyFLen;
++historyBLen;
- if (!doc || history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
+ if (!history[historyCur].fileName) {
+ return gFalse;
+ }
+#ifdef _WIN32
+ if (!doc ||
+ !doc->getFileNameU() ||
+ wcscmp(history[historyCur].fileName, doc->getFileNameU()) != 0) {
+ if (loadFile(history[historyCur].fileName,
+ wcslen(history[historyCur].fileName)) != errNone) {
+ return gFalse;
+ }
+ }
+#else
+ if (!doc ||
+ !doc->getFileName() ||
+ history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
if (loadFile(history[historyCur].fileName) != errNone) {
return gFalse;
}
}
+#endif
pg = history[historyCur].page;
update(pg, scrollX, continuousMode ? -1 : scrollY,
zoom, rotate, gFalse, gFalse, gTrue);
@@ -999,11 +1040,27 @@ GBool PDFCore::goBackward() {
}
--historyBLen;
++historyFLen;
- if (!doc || history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
+ if (!history[historyCur].fileName) {
+ return gFalse;
+ }
+#ifdef _WIN32
+ if (!doc ||
+ !doc->getFileNameU() ||
+ wcscmp(history[historyCur].fileName, doc->getFileNameU()) != 0) {
+ if (loadFile(history[historyCur].fileName,
+ wcslen(history[historyCur].fileName)) != errNone) {
+ return gFalse;
+ }
+ }
+#else
+ if (!doc ||
+ !doc->getFileName() ||
+ history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
if (loadFile(history[historyCur].fileName) != errNone) {
return gFalse;
}
}
+#endif
pg = history[historyCur].page;
update(pg, scrollX, continuousMode ? -1 : scrollY,
zoom, rotate, gFalse, gFalse, gTrue);
@@ -1615,6 +1672,7 @@ GBool PDFCore::getSelection(int *pg, dou
GString *PDFCore::extractText(int pg, double xMin, double yMin,
double xMax, double yMax) {
PDFCorePage *page;
+ TextOutputControl textOutCtrl;
TextOutputDev *textOut;
int x0, y0, x1, y1, t;
GString *s;
@@ -1633,7 +1691,8 @@ GString *PDFCore::extractText(int pg, do
}
s = page->text->getText(x0, y0, x1, y1);
} else {
- textOut = new TextOutputDev(NULL, gTrue, 0, gFalse, gFalse);
+ textOutCtrl.mode = textOutPhysLayout;
+ textOut = new TextOutputDev(NULL, &textOutCtrl, gFalse);
if (textOut->isOk()) {
doc->displayPage(textOut, pg, dpi, dpi, rotate, gFalse, gTrue, gFalse);
textOut->cvtUserToDev(xMin, yMin, &x0, &y0);
@@ -1675,10 +1734,10 @@ GBool PDFCore::find(char *s, GBool caseS
GBool PDFCore::findU(Unicode *u, int len, GBool caseSensitive,
GBool next, GBool backward, GBool wholeWord,
GBool onePageOnly) {
+ TextOutputControl textOutCtrl;
TextOutputDev *textOut;
double xMin, yMin, xMax, yMax;
PDFCorePage *page;
- PDFCoreTile *tile;
int pg;
GBool startAtTop, startAtLast, stopAtLast;
@@ -1721,7 +1780,8 @@ GBool PDFCore::findU(Unicode *u, int len
if (!onePageOnly) {
// search following/previous pages
- textOut = new TextOutputDev(NULL, gTrue, 0, gFalse, gFalse);
+ textOutCtrl.mode = textOutPhysLayout;
+ textOut = new TextOutputDev(NULL, &textOutCtrl, gFalse);
if (!textOut->isOk()) {
delete textOut;
goto notFound;
@@ -1791,7 +1851,6 @@ GBool PDFCore::findU(Unicode *u, int len
// 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 -uNrp 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 @@ public:
//------------------------------------------------------------------------
struct PDFHistory {
+#ifdef _WIN32
+ wchar_t *fileName;
+#else
GString *fileName;
+#endif
int page;
};
diff -uNrp 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 @@ int main(int argc, char *argv[]) {
} else if (saveAll) {
for (i = 0; i < nFiles; ++i) {
if (savePath[0]) {
- n = strlen(savePath);
+ n = (int)strlen(savePath);
if (n > (int)sizeof(path) - 2) {
n = sizeof(path) - 2;
}
diff -uNrp 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
@@ -231,6 +231,7 @@ GBool PDFDoc::setup(GString *ownerPasswo
// read the optional content info
optContent = new OptionalContent(this);
+
// done
return gTrue;
}
@@ -309,10 +310,8 @@ void PDFDoc::checkHeader() {
int i;
pdfVersion = 0;
- for (i = 0; i < headerSearchSize; ++i) {
- hdrBuf[i] = str->getChar();
- }
- hdrBuf[headerSearchSize] = '\0';
+ memset(hdrBuf, 0, headerSearchSize + 1);
+ str->getBlock(hdrBuf, headerSearchSize);
for (i = 0; i < headerSearchSize - 5; ++i) {
if (!strncmp(&hdrBuf[i], "%PDF-", 5)) {
break;
@@ -455,15 +454,16 @@ GBool PDFDoc::isLinearized() {
GBool PDFDoc::saveAs(GString *name) {
FILE *f;
- int c;
+ char buf[4096];
+ int n;
if (!(f = fopen(name->getCString(), "wb"))) {
error(errIO, -1, "Couldn't open file '{0:t}'", name);
return gFalse;
}
str->reset();
- while ((c = str->getChar()) != EOF) {
- fputc(c, f);
+ while ((n = str->getBlock(buf, sizeof(buf))) > 0) {
+ fwrite(buf, 1, n, f);
}
str->close();
fclose(f);
@@ -518,14 +518,15 @@ GBool PDFDoc::saveEmbeddedFile(int idx,
GBool PDFDoc::saveEmbeddedFile2(int idx, FILE *f) {
Object strObj;
- int c;
+ char buf[4096];
+ int n;
if (!catalog->getEmbeddedFileStreamObj(idx, &strObj)) {
return gFalse;
}
strObj.streamReset();
- while ((c = strObj.streamGetChar()) != EOF) {
- fputc(c, f);
+ while ((n = strObj.streamGetBlock(buf, sizeof(buf))) > 0) {
+ fwrite(buf, 1, n, f);
}
strObj.streamClose();
strObj.free();
@@ -535,24 +536,28 @@ GBool PDFDoc::saveEmbeddedFile2(int idx,
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 -uNrp 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 @@ static const char *fontTypeNames[] = {
"CID TrueType (OT)"
};
+static void scanFonts(Object *obj, PDFDoc *doc);
static void scanFonts(Dict *resDict, PDFDoc *doc);
static void scanFont(GfxFont *font, PDFDoc *doc);
static int firstPage = 1;
static int lastPage = 0;
+static GBool showFontLoc = gFalse;
+static GBool showFontLocPS = gFalse;
static char ownerPassword[33] = "\001";
static char userPassword[33] = "\001";
static char cfgFileName[256] = "";
@@ -57,6 +61,10 @@ static ArgDesc argDesc[] = {
"first page to examine"},
{"-l", argInt, &lastPage, 0,
"last page to examine"},
+ {"-loc", argFlag, &showFontLoc, 0,
+ "print extended info on font location"},
+ {"-locPS", argFlag, &showFontLocPS, 0,
+ "print extended info on font location for PostScript conversion"},
{"-opw", argString, ownerPassword, sizeof(ownerPassword),
"owner password (for encrypted files)"},
{"-upw", argString, userPassword, sizeof(userPassword),
@@ -80,6 +88,10 @@ static Ref *fonts;
static int fontsLen;
static int fontsSize;
+static Ref *seenObjs;
+static int seenObjsLen;
+static int seenObjsSize;
+
int main(int argc, char *argv[]) {
PDFDoc *doc;
GString *fileName;
@@ -88,8 +100,9 @@ int main(int argc, char *argv[]) {
Page *page;
Dict *resDict;
Annots *annots;
+ Form *form;
Object obj1, obj2;
- int pg, i;
+ int pg, i, j;
int exitCode;
exitCode = 99;
@@ -108,6 +121,7 @@ int main(int argc, char *argv[]) {
// read config file
globalParams = new GlobalParams(cfgFileName);
+ globalParams->setupBaseFonts(NULL);
// open PDF file
if (ownerPassword[0] != '\001') {
@@ -141,10 +155,17 @@ int main(int argc, char *argv[]) {
}
// scan the fonts
- printf("name type emb sub uni object ID\n");
- printf("------------------------------------ ----------------- --- --- --- ---------\n");
+ if (showFontLoc || showFontLocPS) {
+ printf("name type emb sub uni object ID location\n");
+ printf("------------------------------------ ----------------- --- --- --- --------- --------\n");
+ } else {
+ printf("name type emb sub uni object ID\n");
+ printf("------------------------------------ ----------------- --- --- --- ---------\n");
+ }
fonts = NULL;
fontsLen = fontsSize = 0;
+ seenObjs = NULL;
+ seenObjsLen = seenObjsSize = 0;
for (pg = firstPage; pg <= lastPage; ++pg) {
page = doc->getCatalog()->getPage(pg);
if ((resDict = page->getResourceDict())) {
@@ -154,21 +175,35 @@ int main(int argc, char *argv[]) {
obj1.free();
for (i = 0; i < annots->getNumAnnots(); ++i) {
if (annots->getAnnot(i)->getAppearance(&obj1)->isStream()) {
- obj1.streamGetDict()->lookup("Resources", &obj2);
- if (obj2.isDict()) {
- scanFonts(obj2.getDict(), doc);
- }
+ obj1.streamGetDict()->lookupNF("Resources", &obj2);
+ scanFonts(&obj2, doc);
obj2.free();
}
obj1.free();
}
delete annots;
}
+ if ((form = doc->getCatalog()->getForm())) {
+ for (i = 0; i < form->getNumFields(); ++i) {
+ form->getField(i)->getResources(&obj1);
+ if (obj1.isArray()) {
+ for (j = 0; j < obj1.arrayGetLength(); ++j) {
+ obj1.arrayGetNF(j, &obj2);
+ scanFonts(&obj2, doc);
+ obj2.free();
+ }
+ } else if (obj1.isDict()) {
+ scanFonts(obj1.getDict(), doc);
+ }
+ obj1.free();
+ }
+ }
exitCode = 0;
// clean up
gfree(fonts);
+ gfree(seenObjs);
err1:
delete doc;
delete globalParams;
@@ -181,8 +216,37 @@ int main(int argc, char *argv[]) {
return exitCode;
}
+static void scanFonts(Object *obj, PDFDoc *doc) {
+ Object obj2;
+ int i;
+
+ if (obj->isRef()) {
+ for (i = 0; i < seenObjsLen; ++i) {
+ if (obj->getRefNum() == seenObjs[i].num &&
+ obj->getRefGen() == seenObjs[i].gen) {
+ return;
+ }
+ }
+ if (seenObjsLen == seenObjsSize) {
+ if (seenObjsSize <= INT_MAX - 32) {
+ seenObjsSize += 32;
+ } else {
+ // let greallocn throw an exception
+ seenObjsSize = -1;
+ }
+ seenObjs = (Ref *)greallocn(seenObjs, seenObjsSize, sizeof(Ref));
+ }
+ seenObjs[seenObjsLen++] = obj->getRef();
+ }
+ if (obj->fetch(doc->getXRef(), &obj2)->isDict()) {
+ scanFonts(obj2.getDict(), doc);
+ }
+ obj2.free();
+}
+
static void scanFonts(Dict *resDict, PDFDoc *doc) {
- Object obj1, obj2, xObjDict, xObj, resObj;
+ Object obj1, obj2, xObjDict, xObj;
+ Object patternDict, pattern, gsDict, gs, smask, smaskGroup, resObj;
Ref r;
GfxFontDict *gfxFontDict;
GfxFont *font;
@@ -211,23 +275,58 @@ static void scanFonts(Dict *resDict, PDF
}
obj1.free();
- // recursively scan any resource dictionaries in objects in this
+ // recursively scan any resource dictionaries in XObjects in this
// resource dictionary
resDict->lookup("XObject", &xObjDict);
if (xObjDict.isDict()) {
for (i = 0; i < xObjDict.dictGetLength(); ++i) {
xObjDict.dictGetVal(i, &xObj);
if (xObj.isStream()) {
- xObj.streamGetDict()->lookup("Resources", &resObj);
- if (resObj.isDict()) {
- scanFonts(resObj.getDict(), doc);
- }
+ xObj.streamGetDict()->lookupNF("Resources", &resObj);
+ scanFonts(&resObj, doc);
resObj.free();
}
xObj.free();
}
}
xObjDict.free();
+
+ // recursively scan any resource dictionaries in Patterns in this
+ // resource dictionary
+ resDict->lookup("Pattern", &patternDict);
+ if (patternDict.isDict()) {
+ for (i = 0; i < patternDict.dictGetLength(); ++i) {
+ patternDict.dictGetVal(i, &pattern);
+ if (pattern.isStream()) {
+ pattern.streamGetDict()->lookupNF("Resources", &resObj);
+ scanFonts(&resObj, doc);
+ resObj.free();
+ }
+ pattern.free();
+ }
+ }
+ patternDict.free();
+
+ // recursively scan any resource dictionaries in ExtGStates in this
+ // resource dictionary
+ resDict->lookup("ExtGState", &gsDict);
+ if (gsDict.isDict()) {
+ for (i = 0; i < gsDict.dictGetLength(); ++i) {
+ if (gsDict.dictGetVal(i, &gs)->isDict()) {
+ if (gs.dictLookup("SMask", &smask)->isDict()) {
+ if (smask.dictLookup("G", &smaskGroup)->isStream()) {
+ smaskGroup.streamGetDict()->lookupNF("Resources", &resObj);
+ scanFonts(&resObj, doc);
+ resObj.free();
+ }
+ smaskGroup.free();
+ }
+ smask.free();
+ }
+ gs.free();
+ }
+ }
+ gsDict.free();
}
static void scanFont(GfxFont *font, PDFDoc *doc) {
@@ -235,6 +334,7 @@ static void scanFont(GfxFont *font, PDFD
Object fontObj, toUnicodeObj;
GString *name;
GBool emb, subset, hasToUnicode;
+ GfxFontLoc *loc;
int i;
fontRef = *font->getID();
@@ -284,10 +384,38 @@ static void scanFont(GfxFont *font, PDFD
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 -uNrp 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 @@ int main(int argc, char *argv[]) {
wISO /= sqrt(2.0);
}
}
+ printf(" (rotated %d degrees)", doc->getPageRotate(pg));
printf("\n");
}
@@ -275,16 +277,8 @@ int main(int argc, char *argv[]) {
f = fopen(fileName->getCString(), "rb");
#endif
if (f) {
-#if HAVE_FSEEKO
- fseeko(f, 0, SEEK_END);
- printf("File size: %u bytes\n", (Guint)ftello(f));
-#elif HAVE_FSEEK64
- fseek64(f, 0, SEEK_END);
- printf("File size: %u bytes\n", (Guint)ftell64(f));
-#else
- fseek(f, 0, SEEK_END);
- printf("File size: %d bytes\n", (int)ftell(f));
-#endif
+ gfseek(f, 0, SEEK_END);
+ printf("File size: %u bytes\n", (Guint)gftell(f));
fclose(f);
}
@@ -322,36 +316,21 @@ int main(int argc, char *argv[]) {
static void printInfoString(Dict *infoDict, const char *key, const char *text,
UnicodeMap *uMap) {
Object obj;
- GString *s1;
- GBool isUnicode;
- Unicode u;
+ TextString *s;
+ Unicode *u;
char buf[8];
int i, n;
if (infoDict->lookup(key, &obj)->isString()) {
fputs(text, stdout);
- s1 = obj.getString();
- if ((s1->getChar(0) & 0xff) == 0xfe &&
- (s1->getChar(1) & 0xff) == 0xff) {
- isUnicode = gTrue;
- i = 2;
- } else {
- isUnicode = gFalse;
- i = 0;
- }
- while (i < obj.getString()->getLength()) {
- if (isUnicode) {
- u = ((s1->getChar(i) & 0xff) << 8) |
- (s1->getChar(i+1) & 0xff);
- i += 2;
- } else {
- u = pdfDocEncoding[s1->getChar(i) & 0xff];
- ++i;
- }
- n = uMap->mapUnicode(u, buf, sizeof(buf));
+ s = new TextString(obj.getString());
+ u = s->getUnicode();
+ for (i = 0; i < s->getLength(); ++i) {
+ n = uMap->mapUnicode(u[i], buf, sizeof(buf));
fwrite(buf, 1, n, stdout);
}
fputc('\n', stdout);
+ delete s;
}
obj.free();
}
diff -uNrp 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
+#include
+#include
+#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", " ", 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, "\n");
+ fprintf(html, "\n");
+ for (pg = firstPage; pg <= lastPage; ++pg) {
+ fprintf(html, "page %d
\n", pg, pg);
+ }
+ fprintf(html, "\n");
+ fprintf(html, "\n");
+
+ fclose(html);
+
+ return gTrue;
+}
diff -uNrp 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
+#include
+#include
+#include
+#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", " ", 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 -uNrp 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
#include
+#ifdef _WIN32
+# include
+# include
+#endif
+#ifdef DEBUG_FP_LINUX
+# include
+# include
+#endif
#include "parseargs.h"
#include "gmem.h"
#include "GString.h"
@@ -24,7 +32,6 @@ static int lastPage = 0;
static int resolution = 150;
static GBool mono = gFalse;
static GBool gray = gFalse;
-static char enableT1libStr[16] = "";
static char enableFreeTypeStr[16] = "";
static char antialiasStr[16] = "";
static char vectorAntialiasStr[16] = "";
@@ -46,10 +53,6 @@ static ArgDesc argDesc[] = {
"generate a monochrome PBM file"},
{"-gray", argFlag, &gray, 0,
"generate a grayscale PGM file"},
-#if HAVE_T1LIB_H
- {"-t1lib", argString, enableT1libStr, sizeof(enableT1libStr),
- "enable t1lib font rasterizer: yes, no"},
-#endif
#if HAVE_FREETYPE_FREETYPE_H | HAVE_FREETYPE_H
{"-freetype", argString, enableFreeTypeStr, sizeof(enableFreeTypeStr),
"enable FreeType font rasterizer: yes, no"},
@@ -91,6 +94,21 @@ int main(int argc, char *argv[]) {
int exitCode;
int pg;
+#ifdef DEBUG_FP_LINUX
+ // enable exceptions on floating point div-by-zero
+ feenableexcept(FE_DIVBYZERO);
+ // force 64-bit rounding: this avoids changes in output when minor
+ // code changes result in spills of x87 registers; it also avoids
+ // differences in output with valgrind's 64-bit floating point
+ // emulation (yes, this is a kludge; but it's pretty much
+ // unavoidable given the x87 instruction set; see gcc bug 323 for
+ // more info)
+ fpu_control_t cw;
+ _FPU_GETCW(cw);
+ cw = (cw & ~_FPU_EXTENDED) | _FPU_DOUBLE;
+ _FPU_SETCW(cw);
+#endif
+
exitCode = 99;
// parse args
@@ -112,11 +130,6 @@ int main(int argc, char *argv[]) {
// read config file
globalParams = new GlobalParams(cfgFileName);
globalParams->setupBaseFonts(NULL);
- if (enableT1libStr[0]) {
- if (!globalParams->setEnableT1lib(enableT1libStr)) {
- fprintf(stderr, "Bad '-t1lib' value on command line\n");
- }
- }
if (enableFreeTypeStr[0]) {
if (!globalParams->setEnableFreeType(enableFreeTypeStr)) {
fprintf(stderr, "Bad '-freetype' value on command line\n");
@@ -182,6 +195,9 @@ int main(int argc, char *argv[]) {
doc->displayPage(splashOut, pg, resolution, resolution, 0,
gFalse, gTrue, gFalse);
if (!strcmp(ppmRoot, "-")) {
+#ifdef _WIN32
+ _setmode(_fileno(stdout), _O_BINARY);
+#endif
splashOut->getBitmap()->writePNMFile(stdout);
} else {
ppmFile = GString::format("{0:s}-{1:06d}.{2:s}",
diff -uNrp 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
#include
#include
+#ifdef DEBUG_FP_LINUX
+# include
+# include
+#endif
#include "parseargs.h"
#include "GString.h"
#include "gmem.h"
@@ -147,6 +151,21 @@ int main(int argc, char *argv[]) {
char *p;
int exitCode;
+#ifdef DEBUG_FP_LINUX
+ // enable exceptions on floating point div-by-zero
+ feenableexcept(FE_DIVBYZERO);
+ // force 64-bit rounding: this avoids changes in output when minor
+ // code changes result in spills of x87 registers; it also avoids
+ // differences in output with valgrind's 64-bit floating point
+ // emulation (yes, this is a kludge; but it's pretty much
+ // unavoidable given the x87 instruction set; see gcc bug 323 for
+ // more info)
+ fpu_control_t cw;
+ _FPU_GETCW(cw);
+ cw = (cw & ~_FPU_EXTENDED) | _FPU_DOUBLE;
+ _FPU_SETCW(cw);
+#endif
+
exitCode = 99;
// parse args
@@ -216,6 +235,9 @@ int main(int argc, char *argv[]) {
if (noCrop) {
globalParams->setPSCrop(gFalse);
}
+ if (pageCrop) {
+ globalParams->setPSUseCropBoxAsPage(gTrue);
+ }
if (expand) {
globalParams->setPSExpandSmaller(gTrue);
}
@@ -318,15 +340,18 @@ int main(int argc, char *argv[]) {
firstPage, lastPage, mode);
if (psOut->isOk()) {
doc->displayPages(psOut, firstPage, lastPage, 72, 72,
- 0, !pageCrop, globalParams->getPSCrop(), gTrue);
+ 0, !globalParams->getPSUseCropBoxAsPage(),
+ globalParams->getPSCrop(), gTrue);
} else {
delete psOut;
exitCode = 2;
goto err2;
}
- delete psOut;
-
exitCode = 0;
+ if (!psOut->checkIO()) {
+ exitCode = 2;
+ }
+ delete psOut;
// clean up
err2:
diff -uNrp 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
#include
#include
+#ifdef DEBUG_FP_LINUX
+# include
+# include
+#endif
#include "parseargs.h"
#include "GString.h"
#include "gmem.h"
@@ -26,21 +30,19 @@
#include "TextOutputDev.h"
#include "CharTypes.h"
#include "UnicodeMap.h"
+#include "TextString.h"
#include "Error.h"
#include "config.h"
-static void printInfoString(FILE *f, Dict *infoDict, const char *key,
- const char *text1, const char *text2,
- UnicodeMap *uMap);
-static void printInfoDate(FILE *f, Dict *infoDict, const char *key,
- const char *fmt);
-
static int firstPage = 1;
static int lastPage = 0;
static GBool physLayout = gFalse;
-static double fixedPitch = 0;
+static GBool tableLayout = gFalse;
+static GBool linePrinter = gFalse;
static GBool rawOrder = gFalse;
-static GBool htmlMeta = gFalse;
+static double fixedPitch = 0;
+static double fixedLineSpacing = 0;
+static GBool clipText = gFalse;
static char textEncName[128] = "";
static char textEOL[16] = "";
static GBool noPageBreaks = gFalse;
@@ -58,12 +60,18 @@ static ArgDesc argDesc[] = {
"last page to convert"},
{"-layout", argFlag, &physLayout, 0,
"maintain original physical layout"},
- {"-fixed", argFP, &fixedPitch, 0,
- "assume fixed-pitch (or tabular) text"},
+ {"-table", argFlag, &tableLayout, 0,
+ "similar to -layout, but optimized for tables"},
+ {"-lineprinter", argFlag, &linePrinter, 0,
+ "use strict fixed-pitch/height layout"},
{"-raw", argFlag, &rawOrder, 0,
"keep strings in content stream order"},
- {"-htmlmeta", argFlag, &htmlMeta, 0,
- "generate a simple HTML file, including the meta information"},
+ {"-fixed", argFP, &fixedPitch, 0,
+ "assume fixed-pitch (or tabular) text"},
+ {"-linespacing", argFP, &fixedLineSpacing, 0,
+ "fixed line spacing for LinePrinter mode"},
+ {"-clip", argFlag, &clipText, 0,
+ "separate clipped text"},
{"-enc", argString, textEncName, sizeof(textEncName),
"output text encoding name"},
{"-eol", argString, textEOL, sizeof(textEOL),
@@ -96,14 +104,29 @@ int main(int argc, char *argv[]) {
GString *fileName;
GString *textFileName;
GString *ownerPW, *userPW;
+ TextOutputControl textOutControl;
TextOutputDev *textOut;
- FILE *f;
UnicodeMap *uMap;
Object info;
GBool ok;
char *p;
int exitCode;
+#ifdef DEBUG_FP_LINUX
+ // enable exceptions on floating point div-by-zero
+ feenableexcept(FE_DIVBYZERO);
+ // force 64-bit rounding: this avoids changes in output when minor
+ // code changes result in spills of x87 registers; it also avoids
+ // differences in output with valgrind's 64-bit floating point
+ // emulation (yes, this is a kludge; but it's pretty much
+ // unavoidable given the x87 instruction set; see gcc bug 323 for
+ // more info)
+ fpu_control_t cw;
+ _FPU_GETCW(cw);
+ cw = (cw & ~_FPU_EXTENDED) | _FPU_DOUBLE;
+ _FPU_SETCW(cw);
+#endif
+
exitCode = 99;
// parse args
@@ -117,9 +140,6 @@ int main(int argc, char *argv[]) {
goto err0;
}
fileName = new GString(argv[1]);
- if (fixedPitch) {
- physLayout = gTrue;
- }
// read config file
globalParams = new GlobalParams(cfgFileName);
@@ -187,7 +207,7 @@ int main(int argc, char *argv[]) {
} else {
textFileName = fileName->copy();
}
- textFileName->append(htmlMeta ? ".html" : ".txt");
+ textFileName->append(".txt");
}
// get page range
@@ -198,50 +218,25 @@ int main(int argc, char *argv[]) {
lastPage = doc->getNumPages();
}
- // write HTML header
- if (htmlMeta) {
- if (!textFileName->cmp("-")) {
- f = stdout;
- } else {
- if (!(f = fopen(textFileName->getCString(), "wb"))) {
- error(errIO, -1, "Couldn't open text file '{0:t}'", textFileName);
- exitCode = 2;
- goto err3;
- }
- }
- fputs("\n", f);
- fputs("\n", f);
- doc->getDocInfo(&info);
- if (info.isDict()) {
- printInfoString(f, info.getDict(), "Title", "", " \n",
- uMap);
- printInfoString(f, info.getDict(), "Subject",
- "\n", uMap);
- printInfoString(f, info.getDict(), "Keywords",
- "\n", uMap);
- printInfoString(f, info.getDict(), "Author",
- "\n", uMap);
- printInfoString(f, info.getDict(), "Creator",
- "\n", uMap);
- printInfoString(f, info.getDict(), "Producer",
- "\n", uMap);
- printInfoDate(f, info.getDict(), "CreationDate",
- "\n");
- printInfoDate(f, info.getDict(), "LastModifiedDate",
- "\n");
- }
- info.free();
- fputs("\n", f);
- fputs("\n", f);
- fputs("\n", f);
- if (f != stdout) {
- fclose(f);
- }
- }
-
// write text file
- textOut = new TextOutputDev(textFileName->getCString(),
- physLayout, fixedPitch, rawOrder, htmlMeta);
+ if (tableLayout) {
+ textOutControl.mode = textOutTableLayout;
+ textOutControl.fixedPitch = fixedPitch;
+ } else if (physLayout) {
+ textOutControl.mode = textOutPhysLayout;
+ textOutControl.fixedPitch = fixedPitch;
+ } else if (linePrinter) {
+ textOutControl.mode = textOutLinePrinter;
+ textOutControl.fixedPitch = fixedPitch;
+ textOutControl.fixedLineSpacing = fixedLineSpacing;
+ } else if (rawOrder) {
+ textOutControl.mode = textOutRawOrder;
+ } else {
+ textOutControl.mode = textOutReadingOrder;
+ }
+ textOutControl.clipText = clipText;
+ textOut = new TextOutputDev(textFileName->getCString(), &textOutControl,
+ gFalse);
if (textOut->isOk()) {
doc->displayPages(textOut, firstPage, lastPage, 72, 72, 0,
gFalse, gTrue, gFalse);
@@ -252,25 +247,6 @@ int main(int argc, char *argv[]) {
}
delete textOut;
- // write end of HTML file
- if (htmlMeta) {
- if (!textFileName->cmp("-")) {
- f = stdout;
- } else {
- if (!(f = fopen(textFileName->getCString(), "ab"))) {
- error(errIO, -1, "Couldn't open text file '{0:t}'", textFileName);
- exitCode = 2;
- goto err3;
- }
- }
- fputs("
\n", f);
- fputs("\n", f);
- fputs("\n", f);
- if (f != stdout) {
- fclose(f);
- }
- }
-
exitCode = 0;
// clean up
@@ -289,56 +265,3 @@ int main(int argc, char *argv[]) {
return exitCode;
}
-
-static void printInfoString(FILE *f, Dict *infoDict, const char *key,
- const char *text1, const char *text2,
- UnicodeMap *uMap) {
- Object obj;
- GString *s1;
- GBool isUnicode;
- Unicode u;
- char buf[8];
- int i, n;
-
- if (infoDict->lookup(key, &obj)->isString()) {
- fputs(text1, f);
- s1 = obj.getString();
- if ((s1->getChar(0) & 0xff) == 0xfe &&
- (s1->getChar(1) & 0xff) == 0xff) {
- isUnicode = gTrue;
- i = 2;
- } else {
- isUnicode = gFalse;
- i = 0;
- }
- while (i < obj.getString()->getLength()) {
- if (isUnicode) {
- u = ((s1->getChar(i) & 0xff) << 8) |
- (s1->getChar(i+1) & 0xff);
- i += 2;
- } else {
- u = s1->getChar(i) & 0xff;
- ++i;
- }
- n = uMap->mapUnicode(u, buf, sizeof(buf));
- fwrite(buf, 1, n, f);
- }
- fputs(text2, f);
- }
- obj.free();
-}
-
-static void printInfoDate(FILE *f, Dict *infoDict, const char *key,
- const char *fmt) {
- Object obj;
- char *s;
-
- if (infoDict->lookup(key, &obj)->isString()) {
- s = obj.getString()->getCString();
- if (s[0] == 'D' && s[1] == ':') {
- s += 2;
- }
- fprintf(f, fmt, s);
- }
- obj.free();
-}
diff -uNrp 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::eoFill(GfxState *
}
void PreScanOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx,
- Object *str,
+ Object *strRef,
int paintType, Dict *resDict,
double *mat, double *bbox,
int x0, int y0, int x1, int y1,
double xStep, double yStep) {
if (paintType == 1) {
- gfx->drawForm(str, resDict, mat, bbox);
+ gfx->drawForm(strRef, resDict, mat, bbox);
} else {
check(state->getFillColorSpace(), state->getFillColor(),
state->getFillOpacity(), state->getBlendMode());
@@ -174,9 +174,7 @@ void PreScanOutputDev::endType3Char(GfxS
void PreScanOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
int width, int height, GBool invert,
- GBool inlineImg) {
- int i, j;
-
+ GBool inlineImg, GBool interpolate) {
check(state->getFillColorSpace(), state->getFillColor(),
state->getFillOpacity(), state->getBlendMode());
if (state->getFillColorSpace()->getMode() == csPattern) {
@@ -186,9 +184,7 @@ void PreScanOutputDev::drawImageMask(Gfx
if (inlineImg) {
str->reset();
- j = height * ((width + 7) / 8);
- for (i = 0; i < j; ++i)
- str->getChar();
+ str->discardChars(height * ((width + 7) / 8));
str->close();
}
}
@@ -196,9 +192,9 @@ void PreScanOutputDev::drawImageMask(Gfx
void PreScanOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
- int *maskColors, GBool inlineImg) {
+ int *maskColors, GBool inlineImg,
+ GBool interpolate) {
GfxColorSpace *colorSpace;
- int i, j;
colorSpace = colorMap->getColorSpace();
if (colorSpace->getMode() == csIndexed) {
@@ -221,10 +217,8 @@ void PreScanOutputDev::drawImage(GfxStat
if (inlineImg) {
str->reset();
- j = height * ((width * colorMap->getNumPixelComps() *
- colorMap->getBits() + 7) / 8);
- for (i = 0; i < j; ++i)
- str->getChar();
+ str->discardChars(height * ((width * colorMap->getNumPixelComps() *
+ colorMap->getBits() + 7) / 8));
str->close();
}
}
@@ -235,7 +229,7 @@ void PreScanOutputDev::drawMaskedImage(G
GfxImageColorMap *colorMap,
Stream *maskStr,
int maskWidth, int maskHeight,
- GBool maskInvert) {
+ GBool maskInvert, GBool interpolate) {
GfxColorSpace *colorSpace;
colorSpace = colorMap->getColorSpace();
@@ -264,7 +258,8 @@ void PreScanOutputDev::drawSoftMaskedIma
GfxImageColorMap *colorMap,
Stream *maskStr,
int maskWidth, int maskHeight,
- GfxImageColorMap *maskColorMap) {
+ GfxImageColorMap *maskColorMap,
+ GBool interpolate) {
GfxColorSpace *colorSpace;
colorSpace = colorMap->getColorSpace();
diff -uNrp 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 @@ public:
virtual void stroke(GfxState *state);
virtual void fill(GfxState *state);
virtual void eoFill(GfxState *state);
- virtual void tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
+ virtual void tilingPatternFill(GfxState *state, Gfx *gfx, Object *strRef,
int paintType, Dict *resDict,
double *mat, double *bbox,
int x0, int y0, int x1, int y1,
@@ -92,21 +92,22 @@ public:
//----- image drawing
virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
int width, int height, GBool invert,
- GBool inlineImg);
+ GBool inlineImg, GBool interpolate);
virtual void drawImage(GfxState *state, Object *ref, Stream *str,
int width, int height, GfxImageColorMap *colorMap,
- int *maskColors, GBool inlineImg);
+ int *maskColors, GBool inlineImg, GBool interpolate);
virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
Stream *maskStr, int maskWidth, int maskHeight,
- GBool maskInvert);
+ GBool maskInvert, GBool interpolate);
virtual void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
Stream *maskStr,
int maskWidth, int maskHeight,
- GfxImageColorMap *maskColorMap);
+ GfxImageColorMap *maskColorMap,
+ GBool interpolate);
//----- transparency groups and soft masks
virtual void beginTransparencyGroup(GfxState *state, double *bbox,
diff -uNrp 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 @@ static const char *prolog[] = {
" } for",
"~123sn",
"/pdfSetup {",
+ " /pdfDuplex exch def",
" /setpagedevice where {",
" pop 2 dict begin",
" /Policies 1 dict dup begin /PageSize 6 def end def",
- " { /Duplex true def } if",
+ " pdfDuplex { /Duplex true def } if",
" currentdict end setpagedevice",
- " } {",
- " pop",
- " } ifelse",
+ " } if",
+ " /pdfPageW 0 def",
+ " /pdfPageH 0 def",
"} def",
"/pdfSetupPaper {",
- " 2 array astore",
- " /setpagedevice where {",
- " pop 2 dict begin",
- " /PageSize exch def",
- " /ImagingBBox null def",
- " currentdict end setpagedevice",
+ " 2 copy pdfPageH ne exch pdfPageW ne or {",
+ " /pdfPageH exch def",
+ " /pdfPageW exch def",
+ " /setpagedevice where {",
+ " pop 3 dict begin",
+ " /PageSize [pdfPageW pdfPageH] def",
+ " pdfDuplex { /Duplex true def } if",
+ " /ImagingBBox null def",
+ " currentdict end setpagedevice",
+ " } if",
" } {",
- " pop",
+ " pop pop",
" } ifelse",
"} def",
"~1sn",
@@ -571,17 +573,14 @@ static const char *prolog[] = {
"} def",
"~23sn",
"/pdfImM { fCol imagemask skipEOD } def",
- "/pr { 2 index 2 index 3 2 roll putinterval 4 add } def",
- "/pdfImClip {",
- " gsave",
- " 0 2 4 index length 1 sub {",
- " dup 4 index exch 2 copy",
- " get 5 index div put",
- " 1 add 3 index exch 2 copy",
- " get 3 index div put",
- " } for",
- " pop pop rectclip",
+ "/pr {",
+ " 4 2 roll exch 5 index div exch 4 index div moveto",
+ " exch 3 index div dup 0 rlineto",
+ " exch 2 index div 0 exch rlineto",
+ " neg 0 rlineto",
+ " closepath",
"} def",
+ "/pdfImClip { gsave clip } def",
"/pdfImClipEnd { grestore } def",
"~23sn",
"% shading operators",
@@ -736,6 +735,19 @@ static const char *prolog[] = {
NULL
};
+static const char *minLineWidthProlog[] = {
+ "/pdfDist { dup dtransform dup mul exch dup mul add 0.5 mul sqrt } def",
+ "/pdfIDist { dup idtransform dup mul exch dup mul add 0.5 mul sqrt } def",
+ "/pdfMinLineDist pdfMinLineWidth pdfDist def",
+ "/setlinewidth {",
+ " dup pdfDist pdfMinLineDist lt {",
+ " pop pdfMinLineDist pdfIDist",
+ " } if",
+ " setlinewidth",
+ "} bind def",
+ NULL
+};
+
static const char *cmapProlog[] = {
"/CIDInit /ProcSet findresource begin",
"10 dict begin",
@@ -809,25 +821,68 @@ static PSSubstFont psBase14SubstFonts[14
{"ZapfDingbats", 0}
};
-// Mapping from Type 1/1C font file to PS font name.
-struct PST1FontName {
- Ref fontFileID;
- GString *psName; // PostScript font name used for this
- // embedded font file
-};
+class PSFontInfo {
+public:
+
+ PSFontInfo(Ref fontIDA)
+ { fontID = fontIDA; ff = NULL; }
-// Info for 8-bit fonts
-struct PSFont8Info {
Ref fontID;
- int *codeToGID; // code-to-GID mapping for TrueType fonts
+ PSFontFileInfo *ff; // pointer to font file info; NULL indicates
+ // font mapping failed
};
-// Encoding info for substitute 16-bit font
-struct PSFont16Enc {
- Ref fontID;
- GString *enc; // NULL means font wasn't correctly substituted
+enum PSFontFileLocation {
+ psFontFileResident,
+ psFontFileEmbedded,
+ psFontFileExternal
};
+class PSFontFileInfo {
+public:
+
+ PSFontFileInfo(GString *psNameA, GfxFontType typeA,
+ PSFontFileLocation locA);
+ ~PSFontFileInfo();
+
+ GString *psName; // name under which font is defined
+ GfxFontType type; // font type
+ PSFontFileLocation loc; // font location
+ Ref embFontID; // object ID for the embedded font file
+ // (for all embedded fonts)
+ GString *extFileName; // external font file path
+ // (for all external fonts)
+ GString *encoding; // encoding name (for resident CID fonts)
+ int *codeToGID; // mapping from code/CID to GID
+ // (for TrueType, OpenType-TrueType, and
+ // CID OpenType-CFF fonts)
+ int codeToGIDLen; // length of codeToGID array
+};
+
+PSFontFileInfo::PSFontFileInfo(GString *psNameA, GfxFontType typeA,
+ PSFontFileLocation locA) {
+ psName = psNameA;
+ type = typeA;
+ loc = locA;
+ embFontID.num = embFontID.gen = -1;
+ extFileName = NULL;
+ encoding = NULL;
+ codeToGID = NULL;
+}
+
+PSFontFileInfo::~PSFontFileInfo() {
+ delete psName;
+ if (extFileName) {
+ delete extFileName;
+ }
+ if (encoding) {
+ delete encoding;
+ }
+ if (codeToGID) {
+ gfree(codeToGID);
+ }
+}
+
//------------------------------------------------------------------------
// process colors
//------------------------------------------------------------------------
@@ -998,11 +1053,8 @@ PSOutputDev::PSOutputDev(char *fileName,
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;
@@ -1060,11 +1112,8 @@ PSOutputDev::PSOutputDev(PSOutputFunc ou
customCodeCbk = customCodeCbkA;
customCodeCbkData = customCodeCbkDataA;
- fontIDs = NULL;
- fontNames = new GHash(gTrue);
- t1FontNames = NULL;
- font8Info = NULL;
- font16Enc = NULL;
+ fontInfo = new GList();
+ fontFileInfo = new GHash();
imgIDs = NULL;
formIDs = NULL;
xobjStack = NULL;
@@ -1088,6 +1137,7 @@ void PSOutputDev::init(PSOutputFunc outp
Page *page;
PDFRectangle *box;
PSOutPaperSize *size;
+ PSFontFileInfo *ff;
GList *names;
int pg, w, h, i;
@@ -1118,8 +1168,13 @@ void PSOutputDev::init(PSOutputFunc outp
pg <= lastPage && pg <= catalog->getNumPages();
++pg) {
page = catalog->getPage(pg);
- w = (int)ceil(page->getMediaWidth());
- h = (int)ceil(page->getMediaHeight());
+ if (globalParams->getPSUseCropBoxAsPage()) {
+ w = (int)ceil(page->getCropWidth());
+ h = (int)ceil(page->getCropHeight());
+ } else {
+ w = (int)ceil(page->getMediaWidth());
+ h = (int)ceil(page->getMediaHeight());
+ }
for (i = 0; i < paperSizes->getLength(); ++i) {
size = (PSOutPaperSize *)paperSizes->get(i);
if (size->w == w && size->h == h) {
@@ -1160,25 +1215,21 @@ void PSOutputDev::init(PSOutputFunc outp
clipLLX0 = clipLLY0 = 0;
clipURX0 = clipURY0 = -1;
- // initialize fontIDs and fontNames lists
- fontIDSize = 64;
- fontIDLen = 0;
- fontIDs = (Ref *)gmallocn(fontIDSize, sizeof(Ref));
+ // initialize font lists, etc.
for (i = 0; i < 14; ++i) {
- fontNames->add(new GString(psBase14SubstFonts[i].psName), 1);
+ ff = new PSFontFileInfo(new GString(psBase14SubstFonts[i].psName),
+ fontType1, psFontFileResident);
+ fontFileInfo->add(ff->psName, ff);
}
names = globalParams->getPSResidentFonts();
for (i = 0; i < names->getLength(); ++i) {
- fontNames->add((GString *)names->get(i), 1);
+ if (!fontFileInfo->lookup((GString *)names->get(i))) {
+ ff = new PSFontFileInfo((GString *)names->get(i), fontType1,
+ psFontFileResident);
+ fontFileInfo->add(ff->psName, ff);
+ }
}
delete names;
- t1FontNameSize = 64;
- t1FontNameLen = 0;
- t1FontNames = (PST1FontName *)gmallocn(t1FontNameSize, sizeof(PST1FontName));
- font8InfoLen = 0;
- font8InfoSize = 0;
- font16EncLen = 0;
- font16EncSize = 0;
imgIDLen = 0;
imgIDSize = 0;
formIDLen = 0;
@@ -1224,7 +1275,6 @@ void PSOutputDev::init(PSOutputFunc outp
PSOutputDev::~PSOutputDev() {
PSOutCustomColor *cc;
- int i;
if (ok) {
if (!manualCtrl) {
@@ -1255,30 +1305,8 @@ PSOutputDev::~PSOutputDev() {
if (embFontList) {
delete embFontList;
}
- if (fontIDs) {
- gfree(fontIDs);
- }
- delete fontNames;
- if (t1FontNames) {
- for (i = 0; i < t1FontNameLen; ++i) {
- delete t1FontNames[i].psName;
- }
- gfree(t1FontNames);
- }
- if (font8Info) {
- for (i = 0; i < font8InfoLen; ++i) {
- gfree(font8Info[i].codeToGID);
- }
- gfree(font8Info);
- }
- if (font16Enc) {
- for (i = 0; i < font16EncLen; ++i) {
- if (font16Enc[i].enc) {
- delete font16Enc[i].enc;
- }
- }
- gfree(font16Enc);
- }
+ deleteGList(fontInfo, PSFontInfo);
+ deleteGHash(fontFileInfo, PSFontFileInfo);
gfree(imgIDs);
gfree(formIDs);
if (xobjStack) {
@@ -1291,6 +1319,16 @@ PSOutputDev::~PSOutputDev() {
}
}
+GBool PSOutputDev::checkIO() {
+ if (fileType == psFile || fileType == psPipe || fileType == psStdout) {
+ if (ferror((FILE *)outputStream)) {
+ error(errIO, -1, "Error writing to PostScript file");
+ return gFalse;
+ }
+ }
+ return gTrue;
+}
+
void PSOutputDev::writeHeader(int firstPage, int lastPage,
PDFRectangle *mediaBox, PDFRectangle *cropBox,
int pageRotate) {
@@ -1395,6 +1433,7 @@ void PSOutputDev::writeXpdfProcset() {
GBool lev1, lev2, lev3, sep, nonSep;
const char **p;
const char *q;
+ double w;
writePSFmt("%%BeginResource: procset xpdf {0:s} 0\n", xpdfVersion);
writePSFmt("%%Copyright: {0:s}\n", xpdfCopyright);
@@ -1420,6 +1459,12 @@ void PSOutputDev::writeXpdfProcset() {
writePSFmt("{0:s}\n", *p);
}
}
+ if ((w = globalParams->getPSMinLineWidth()) > 0) {
+ writePSFmt("/pdfMinLineWidth {0:.4g} def\n", w);
+ for (p = minLineWidthProlog; *p; ++p) {
+ writePSFmt("{0:s}\n", *p);
+ }
+ }
writePS("%%EndResource\n");
if (level >= psLevel3) {
@@ -1434,10 +1479,10 @@ void PSOutputDev::writeDocSetup(Catalog
Page *page;
Dict *resDict;
Annots *annots;
- Object *acroForm;
+ Form *form;
Object obj1, obj2, obj3;
GString *s;
- int pg, i;
+ int pg, i, j;
if (mode == psModeForm) {
// swap the form and xpdf dicts
@@ -1464,23 +1509,22 @@ void PSOutputDev::writeDocSetup(Catalog
}
delete annots;
}
- if ((acroForm = catalog->getAcroForm()) && acroForm->isDict()) {
- if (acroForm->dictLookup("DR", &obj1)->isDict()) {
- setupResources(obj1.getDict());
- }
- obj1.free();
- if (acroForm->dictLookup("Fields", &obj1)->isArray()) {
- for (i = 0; i < obj1.arrayGetLength(); ++i) {
- if (obj1.arrayGet(i, &obj2)->isDict()) {
- if (obj2.dictLookup("DR", &obj3)->isDict()) {
- setupResources(obj3.getDict());
+ if ((form = catalog->getForm())) {
+ for (i = 0; i < form->getNumFields(); ++i) {
+ form->getField(i)->getResources(&obj1);
+ if (obj1.isArray()) {
+ for (j = 0; j < obj1.arrayGetLength(); ++j) {
+ obj1.arrayGet(j, &obj2);
+ if (obj2.isDict()) {
+ setupResources(obj2.getDict());
}
- obj3.free();
+ obj2.free();
}
- obj2.free();
+ } else if (obj1.isDict()) {
+ setupResources(obj1.getDict());
}
+ obj1.free();
}
- obj1.free();
}
if (mode != psModeForm) {
if (mode != psModeEPS && !manualCtrl) {
@@ -1503,6 +1547,9 @@ void PSOutputDev::writeDocSetup(Catalog
delete s;
}
}
+ if (mode != psModeForm) {
+ writePS("end\n");
+ }
}
void PSOutputDev::writePageTrailer() {
@@ -1517,7 +1564,6 @@ void PSOutputDev::writeTrailer() {
if (mode == psModeForm) {
writePS("/Foo exch /Form defineresource pop\n");
} else {
- writePS("end\n");
writePS("%%DocumentSuppliedResources:\n");
writePS(embFontList->getCString());
if (level == psLevel1Sep || level == psLevel2Sep ||
@@ -1554,14 +1600,14 @@ void PSOutputDev::writeTrailer() {
}
void PSOutputDev::setupResources(Dict *resDict) {
- Object xObjDict, xObjRef, xObj, patDict, patRef, pat, resObj;
+ Object xObjDict, xObjRef, xObj, patDict, patRef, pat;
+ Object gsDict, gsRef, gs, smask, smaskGroup, resObj;
Ref ref0, ref1;
GBool skip;
int i, j;
setupFonts(resDict);
setupImages(resDict);
- setupForms(resDict);
//----- recursively scan XObjects
resDict->lookup("XObject", &xObjDict);
@@ -1648,6 +1694,55 @@ void PSOutputDev::setupResources(Dict *r
inType3Char = gFalse;
}
patDict.free();
+
+ //----- recursively scan SMask transparency groups in ExtGState dicts
+ resDict->lookup("ExtGState", &gsDict);
+ if (gsDict.isDict()) {
+ for (i = 0; i < gsDict.dictGetLength(); ++i) {
+
+ // avoid infinite recursion on ExtGStates
+ skip = gFalse;
+ if ((gsDict.dictGetValNF(i, &gsRef)->isRef())) {
+ ref0 = gsRef.getRef();
+ for (j = 0; j < xobjStack->getLength(); ++j) {
+ ref1 = *(Ref *)xobjStack->get(j);
+ if (ref1.num == ref0.num && ref1.gen == ref0.gen) {
+ skip = gTrue;
+ break;
+ }
+ }
+ if (!skip) {
+ xobjStack->append(&ref0);
+ }
+ }
+ if (!skip) {
+
+ // process the ExtGState's SMask's transparency group's resource dict
+ if (gsDict.dictGetVal(i, &gs)->isDict()) {
+ if (gs.dictLookup("SMask", &smask)->isDict()) {
+ if (smask.dictLookup("G", &smaskGroup)->isStream()) {
+ smaskGroup.streamGetDict()->lookup("Resources", &resObj);
+ if (resObj.isDict()) {
+ setupResources(resObj.getDict());
+ }
+ resObj.free();
+ }
+ smaskGroup.free();
+ }
+ smask.free();
+ }
+ gs.free();
+ }
+
+ if (gsRef.isRef() && !skip) {
+ xobjStack->del(xobjStack->getLength() - 1);
+ }
+ gsRef.free();
+ }
+ }
+ gsDict.free();
+
+ setupForms(resDict);
}
void PSOutputDev::setupFonts(Dict *resDict) {
@@ -1681,8 +1776,8 @@ void PSOutputDev::setupFonts(Dict *resDi
}
void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) {
+ PSFontInfo *fi;
GfxFontLoc *fontLoc;
- GString *psName;
GBool subst;
char buf[16];
UnicodeMap *uMap;
@@ -1693,123 +1788,101 @@ void PSOutputDev::setupFont(GfxFont *fon
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 @@ void PSOutputDev::setupFont(GfxFont *fon
if (fontLoc->locType == gfxFontLocResident &&
fontLoc->fontType >= fontCIDType0) {
subst = gTrue;
- if (font16EncLen >= font16EncSize) {
- font16EncSize += 16;
- font16Enc = (PSFont16Enc *)greallocn(font16Enc,
- font16EncSize,
- sizeof(PSFont16Enc));
- }
- font16Enc[font16EncLen].fontID = *font->getID();
if ((uMap = globalParams->getUnicodeMap(fontLoc->encoding))) {
- font16Enc[font16EncLen].enc = fontLoc->encoding->copy();
+ fi->ff->encoding = fontLoc->encoding->copy();
uMap->decRefCnt();
} else {
error(errSyntaxError, -1,
"Couldn't find Unicode map for 16-bit font encoding '{0:t}'",
fontLoc->encoding);
- font16Enc[font16EncLen].enc = NULL;
}
- ++font16EncLen;
}
delete fontLoc;
@@ -1869,16 +1933,16 @@ void PSOutputDev::setupFont(GfxFont *fon
if (font->isCIDFont()) {
if (level == psLevel3 || level == psLevel3Sep) {
writePSFmt("/F{0:d}_{1:d} /{2:t} {3:d} pdfMakeFont16L3\n",
- font->getID()->num, font->getID()->gen, psName,
+ font->getID()->num, font->getID()->gen, fi->ff->psName,
font->getWMode());
} else {
writePSFmt("/F{0:d}_{1:d} /{2:t} {3:d} pdfMakeFont16\n",
- font->getID()->num, font->getID()->gen, psName,
+ font->getID()->num, font->getID()->gen, fi->ff->psName,
font->getWMode());
}
} else {
writePSFmt("/F{0:d}_{1:d} /{2:t} {3:.6g} {4:.6g}\n",
- font->getID()->num, font->getID()->gen, psName, xs, ys);
+ font->getID()->num, font->getID()->gen, fi->ff->psName, xs, ys);
for (i = 0; i < 256; i += 8) {
writePS((char *)((i == 0) ? "[ " : " "));
for (j = 0; j < 8; ++j) {
@@ -1903,25 +1967,29 @@ void PSOutputDev::setupFont(GfxFont *fon
}
writePS("pdfMakeFont\n");
}
-
- delete psName;
}
-void PSOutputDev::setupEmbeddedType1Font(Ref *id, GString *psName) {
+PSFontFileInfo *PSOutputDev::setupEmbeddedType1Font(GfxFont *font, Ref *id) {
static char hexChar[17] = "0123456789abcdef";
- Object refObj, strObj, obj1, obj2, obj3;
+ GString *psName;
+ PSFontFileInfo *ff;
+ Object refObj, strObj, obj1, obj2;
Dict *dict;
- int length1, length2, length3;
+ int length1, length2;
int c;
- int start[4];
+ int start[6];
GBool binMode;
- int i;
+ int n, i;
// check if font is already embedded
- if (fontNames->lookupInt(psName)) {
- return;
+ if ((ff = (PSFontFileInfo *)
+ fontFileInfo->lookup(font->getEmbeddedFontName()))) {
+ return ff;
}
- fontNames->add(psName->copy(), 1);
+
+ // generate name
+ // (this assumes that the PS font name matches the PDF font name)
+ psName = font->getEmbeddedFontName()->copy();
// get the font stream and info
refObj.initRef(id->num, id->gen);
@@ -1938,21 +2006,17 @@ void PSOutputDev::setupEmbeddedType1Font
}
dict->lookup("Length1", &obj1);
dict->lookup("Length2", &obj2);
- dict->lookup("Length3", &obj3);
- if (!obj1.isInt() || !obj2.isInt() || !obj3.isInt()) {
+ if (!obj1.isInt() || !obj2.isInt()) {
error(errSyntaxError, -1,
"Missing length fields in embedded font stream dictionary");
obj1.free();
obj2.free();
- obj3.free();
goto err1;
}
length1 = obj1.getInt();
length2 = obj2.getInt();
- length3 = obj3.getInt();
obj1.free();
obj2.free();
- obj3.free();
// beginning comment
writePSFmt("%%BeginResource: font {0:t}\n", psName);
@@ -1960,91 +2024,164 @@ void PSOutputDev::setupEmbeddedType1Font
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 @@ void PSOutputDev::setupExternalType1Font
embFontList->append(psName->getCString());
embFontList->append("\n");
- // copy the font file
+ // open the font file
if (!(fontFile = fopen(fileName->getCString(), "rb"))) {
error(errIO, -1, "Couldn't open external font file");
- return;
+ return NULL;
}
- while ((c = fgetc(fontFile)) != EOF) {
- writePSChar(c);
+
+ // check for PFB format
+ buf[0] = fgetc(fontFile);
+ buf[1] = fgetc(fontFile);
+ if (buf[0] == 0x80 && buf[1] == 0x01) {
+ while (1) {
+ for (i = 2; i < 6; ++i) {
+ buf[i] = fgetc(fontFile);
+ }
+ if (buf[2] == EOF || buf[3] == EOF || buf[4] == EOF || buf[5] == EOF) {
+ break;
+ }
+ n = buf[2] + (buf[3] << 8) + (buf[4] << 16) + (buf[5] << 24);
+ if (buf[1] == 0x01) {
+ for (i = 0; i < n; ++i) {
+ if ((c = fgetc(fontFile)) == EOF) {
+ break;
+ }
+ writePSChar(c);
+ }
+ } else {
+ for (i = 0; i < n; ++i) {
+ if ((c = fgetc(fontFile)) == EOF) {
+ break;
+ }
+ writePSChar(hexChar[(c >> 4) & 0x0f]);
+ writePSChar(hexChar[c & 0x0f]);
+ if (i % 32 == 31) {
+ writePSChar('\n');
+ }
+ }
+ }
+ buf[0] = fgetc(fontFile);
+ buf[1] = fgetc(fontFile);
+ if (buf[0] == EOF || buf[1] == EOF ||
+ (buf[0] == 0x80 && buf[1] == 0x03)) {
+ break;
+ } else if (!(buf[0] == 0x80 &&
+ (buf[1] == 0x01 || buf[1] == 0x02))) {
+ error(errSyntaxError, -1,
+ "Invalid PFB header in external font file");
+ break;
+ }
+ }
+ writePSChar('\n');
+
+ // plain text (PFA) format
+ } else {
+ writePSChar(buf[0]);
+ writePSChar(buf[1]);
+ while ((c = fgetc(fontFile)) != EOF) {
+ writePSChar(c);
+ }
}
+
fclose(fontFile);
// ending comment
writePS("%%EndResource\n");
+
+ ff = new PSFontFileInfo(psName, font->getType(), psFontFileExternal);
+ ff->extFileName = fileName->copy();
+ fontFileInfo->add(ff->psName, ff);
+ return ff;
}
-void PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id,
- GString *psName) {
+PSFontFileInfo *PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id) {
+ GString *psName;
+ PSFontFileInfo *ff;
char *fontBuf;
int fontLen;
FoFiType1C *ffT1C;
- int i;
+ GHashIter *iter;
// check if font is already embedded
- for (i = 0; i < t1FontNameLen; ++i) {
- if (t1FontNames[i].fontFileID.num == id->num &&
- t1FontNames[i].fontFileID.gen == id->gen) {
- psName->clear();
- psName->insert(0, t1FontNames[i].psName);
- return;
+ fontFileInfo->startIter(&iter);
+ while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) {
+ if (ff->loc == psFontFileEmbedded &&
+ ff->embFontID.num == id->num &&
+ ff->embFontID.gen == id->gen) {
+ fontFileInfo->killIter(&iter);
+ return ff;
}
}
- if (t1FontNameLen == t1FontNameSize) {
- t1FontNameSize *= 2;
- t1FontNames = (PST1FontName *)greallocn(t1FontNames, t1FontNameSize,
- sizeof(PST1FontName));
- }
- t1FontNames[t1FontNameLen].fontFileID = *id;
- t1FontNames[t1FontNameLen].psName = psName->copy();
- ++t1FontNameLen;
+
+ // generate name
+ psName = makePSFontName(font, id);
// beginning comment
writePSFmt("%%BeginResource: font {0:t}\n", psName);
@@ -2109,32 +2300,35 @@ void PSOutputDev::setupEmbeddedType1CFon
// ending comment
writePS("%%EndResource\n");
+
+ ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded);
+ ff->embFontID = *id;
+ fontFileInfo->add(ff->psName, ff);
+ return ff;
}
-void PSOutputDev::setupEmbeddedOpenTypeT1CFont(GfxFont *font, Ref *id,
- GString *psName) {
+PSFontFileInfo *PSOutputDev::setupEmbeddedOpenTypeT1CFont(GfxFont *font,
+ Ref *id) {
+ GString *psName;
+ PSFontFileInfo *ff;
char *fontBuf;
int fontLen;
FoFiTrueType *ffTT;
- int i;
+ GHashIter *iter;
// check if font is already embedded
- for (i = 0; i < t1FontNameLen; ++i) {
- if (t1FontNames[i].fontFileID.num == id->num &&
- t1FontNames[i].fontFileID.gen == id->gen) {
- psName->clear();
- psName->insert(0, t1FontNames[i].psName);
- return;
+ fontFileInfo->startIter(&iter);
+ while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) {
+ if (ff->loc == psFontFileEmbedded &&
+ ff->embFontID.num == id->num &&
+ ff->embFontID.gen == id->gen) {
+ fontFileInfo->killIter(&iter);
+ return ff;
}
}
- if (t1FontNameLen == t1FontNameSize) {
- t1FontNameSize *= 2;
- t1FontNames = (PST1FontName *)greallocn(t1FontNames, t1FontNameSize,
- sizeof(PST1FontName));
- }
- t1FontNames[t1FontNameLen].fontFileID = *id;
- t1FontNames[t1FontNameLen].psName = psName->copy();
- ++t1FontNameLen;
+
+ // generate name
+ psName = makePSFontName(font, id);
// beginning comment
writePSFmt("%%BeginResource: font {0:t}\n", psName);
@@ -2144,7 +2338,7 @@ void PSOutputDev::setupEmbeddedOpenTypeT
// convert it to a Type 1 font
if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) {
- if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) {
+ if ((ffTT = FoFiTrueType::make(fontBuf, fontLen, 0))) {
if (ffTT->isOpenTypeCFF()) {
ffTT->convertToType1(psName->getCString(), NULL, gTrue,
outputFunc, outputStream);
@@ -2156,14 +2350,50 @@ void PSOutputDev::setupEmbeddedOpenTypeT
// ending comment
writePS("%%EndResource\n");
+
+ ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded);
+ ff->embFontID = *id;
+ fontFileInfo->add(ff->psName, ff);
+ return ff;
}
-void PSOutputDev::setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id,
- GString *psName) {
+PSFontFileInfo *PSOutputDev::setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id) {
+ GString *psName;
+ PSFontFileInfo *ff;
char *fontBuf;
int fontLen;
FoFiTrueType *ffTT;
int *codeToGID;
+ GHashIter *iter;
+
+ // get the code-to-GID mapping
+ if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
+ return NULL;
+ }
+ if (!(ffTT = FoFiTrueType::make(fontBuf, fontLen, 0))) {
+ gfree(fontBuf);
+ return NULL;
+ }
+ codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT);
+
+ // check if font is already embedded
+ fontFileInfo->startIter(&iter);
+ while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) {
+ if (ff->loc == psFontFileEmbedded &&
+ ff->embFontID.num == id->num &&
+ ff->embFontID.gen == id->gen &&
+ ff->codeToGIDLen == 256 &&
+ !memcmp(ff->codeToGID, codeToGID, 256 * sizeof(int))) {
+ fontFileInfo->killIter(&iter);
+ gfree(codeToGID);
+ delete ffTT;
+ gfree(fontBuf);
+ return ff;
+ }
+ }
+
+ // generate name
+ psName = makePSFontName(font, id);
// beginning comment
writePSFmt("%%BeginResource: font {0:t}\n", psName);
@@ -2172,38 +2402,57 @@ void PSOutputDev::setupEmbeddedTrueTypeF
embFontList->append("\n");
// convert it to a Type 42 font
- if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) {
- if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) {
- codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT);
- ffTT->convertToType42(psName->getCString(),
- ((Gfx8BitFont *)font)->getHasEncoding()
- ? ((Gfx8BitFont *)font)->getEncoding()
- : (char **)NULL,
- codeToGID, outputFunc, outputStream);
- if (codeToGID) {
- if (font8InfoLen >= font8InfoSize) {
- font8InfoSize += 16;
- font8Info = (PSFont8Info *)greallocn(font8Info,
- font8InfoSize,
- sizeof(PSFont8Info));
- }
- font8Info[font8InfoLen].fontID = *font->getID();
- font8Info[font8InfoLen].codeToGID = codeToGID;
- ++font8InfoLen;
- }
- delete ffTT;
- }
- gfree(fontBuf);
- }
+ ffTT->convertToType42(psName->getCString(),
+ ((Gfx8BitFont *)font)->getHasEncoding()
+ ? ((Gfx8BitFont *)font)->getEncoding()
+ : (char **)NULL,
+ codeToGID, outputFunc, outputStream);
+ delete ffTT;
+ gfree(fontBuf);
// ending comment
writePS("%%EndResource\n");
+
+ ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded);
+ ff->embFontID = *id;
+ ff->codeToGID = codeToGID;
+ ff->codeToGIDLen = 256;
+ fontFileInfo->add(ff->psName, ff);
+ return ff;
}
-void PSOutputDev::setupExternalTrueTypeFont(GfxFont *font, GString *fileName,
- GString *psName) {
+PSFontFileInfo *PSOutputDev::setupExternalTrueTypeFont(GfxFont *font,
+ GString *fileName,
+ int fontNum) {
+ GString *psName;
+ PSFontFileInfo *ff;
FoFiTrueType *ffTT;
int *codeToGID;
+ GHashIter *iter;
+
+ // get the code-to-GID mapping
+ if (!(ffTT = FoFiTrueType::load(fileName->getCString(), fontNum))) {
+ return NULL;
+ }
+ codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT);
+
+ // check if font is already embedded
+ fontFileInfo->startIter(&iter);
+ while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) {
+ if (ff->loc == psFontFileExternal &&
+ ff->type == font->getType() &&
+ !ff->extFileName->cmp(fileName) &&
+ ff->codeToGIDLen == 256 &&
+ !memcmp(ff->codeToGID, codeToGID, 256 * sizeof(int))) {
+ fontFileInfo->killIter(&iter);
+ gfree(codeToGID);
+ delete ffTT;
+ return ff;
+ }
+ }
+
+ // generate name
+ psName = makePSFontName(font, font->getID());
// beginning comment
writePSFmt("%%BeginResource: font {0:t}\n", psName);
@@ -2212,55 +2461,45 @@ void PSOutputDev::setupExternalTrueTypeF
embFontList->append("\n");
// convert it to a Type 42 font
- if ((ffTT = FoFiTrueType::load(fileName->getCString()))) {
- codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT);
- ffTT->convertToType42(psName->getCString(),
- ((Gfx8BitFont *)font)->getHasEncoding()
- ? ((Gfx8BitFont *)font)->getEncoding()
- : (char **)NULL,
- codeToGID, outputFunc, outputStream);
- if (codeToGID) {
- if (font8InfoLen >= font8InfoSize) {
- font8InfoSize += 16;
- font8Info = (PSFont8Info *)greallocn(font8Info,
- font8InfoSize,
- sizeof(PSFont8Info));
- }
- font8Info[font8InfoLen].fontID = *font->getID();
- font8Info[font8InfoLen].codeToGID = codeToGID;
- ++font8InfoLen;
- }
- delete ffTT;
- }
+ ffTT->convertToType42(psName->getCString(),
+ ((Gfx8BitFont *)font)->getHasEncoding()
+ ? ((Gfx8BitFont *)font)->getEncoding()
+ : (char **)NULL,
+ codeToGID, outputFunc, outputStream);
+ delete ffTT;
// ending comment
writePS("%%EndResource\n");
+
+ ff = new PSFontFileInfo(psName, font->getType(), psFontFileExternal);
+ ff->extFileName = fileName->copy();
+ ff->codeToGID = codeToGID;
+ ff->codeToGIDLen = 256;
+ fontFileInfo->add(ff->psName, ff);
+ return ff;
}
-void PSOutputDev::setupEmbeddedCIDType0Font(GfxFont *font, Ref *id,
- GString *psName) {
+PSFontFileInfo *PSOutputDev::setupEmbeddedCIDType0Font(GfxFont *font, Ref *id) {
+ GString *psName;
+ PSFontFileInfo *ff;
char *fontBuf;
int fontLen;
FoFiType1C *ffT1C;
- int i;
+ GHashIter *iter;
// check if font is already embedded
- for (i = 0; i < t1FontNameLen; ++i) {
- if (t1FontNames[i].fontFileID.num == id->num &&
- t1FontNames[i].fontFileID.gen == id->gen) {
- psName->clear();
- psName->insert(0, t1FontNames[i].psName);
- return;
+ fontFileInfo->startIter(&iter);
+ while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) {
+ if (ff->loc == psFontFileEmbedded &&
+ ff->embFontID.num == id->num &&
+ ff->embFontID.gen == id->gen) {
+ fontFileInfo->killIter(&iter);
+ return ff;
}
}
- if (t1FontNameLen == t1FontNameSize) {
- t1FontNameSize *= 2;
- t1FontNames = (PST1FontName *)greallocn(t1FontNames, t1FontNameSize,
- sizeof(PST1FontName));
- }
- t1FontNames[t1FontNameLen].fontFileID = *id;
- t1FontNames[t1FontNameLen].psName = psName->copy();
- ++t1FontNameLen;
+
+ // generate name
+ psName = makePSFontName(font, id);
// beginning comment
writePSFmt("%%BeginResource: font {0:t}\n", psName);
@@ -2287,14 +2526,46 @@ void PSOutputDev::setupEmbeddedCIDType0F
// ending comment
writePS("%%EndResource\n");
+
+ ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded);
+ ff->embFontID = *id;
+ fontFileInfo->add(ff->psName, ff);
+ return ff;
}
-void PSOutputDev::setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id,
- GString *psName,
- GBool needVerticalMetrics) {
+PSFontFileInfo *PSOutputDev::setupEmbeddedCIDTrueTypeFont(
+ GfxFont *font, Ref *id,
+ GBool needVerticalMetrics) {
+ GString *psName;
+ PSFontFileInfo *ff;
char *fontBuf;
int fontLen;
FoFiTrueType *ffTT;
+ int *codeToGID;
+ int codeToGIDLen;
+ GHashIter *iter;
+
+ // get the code-to-GID mapping
+ codeToGID = ((GfxCIDFont *)font)->getCIDToGID();
+ codeToGIDLen = ((GfxCIDFont *)font)->getCIDToGIDLen();
+
+ // check if font is already embedded
+ fontFileInfo->startIter(&iter);
+ while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) {
+ if (ff->loc == psFontFileEmbedded &&
+ ff->embFontID.num == id->num &&
+ ff->embFontID.gen == id->gen &&
+ ff->codeToGIDLen == codeToGIDLen &&
+ ((!ff->codeToGID && !codeToGID) ||
+ (ff->codeToGID && codeToGID &&
+ !memcmp(ff->codeToGID, codeToGID, codeToGIDLen * sizeof(int))))) {
+ fontFileInfo->killIter(&iter);
+ return ff;
+ }
+ }
+
+ // generate name
+ psName = makePSFontName(font, id);
// beginning comment
writePSFmt("%%BeginResource: font {0:t}\n", psName);
@@ -2304,19 +2575,17 @@ void PSOutputDev::setupEmbeddedCIDTrueTy
// convert it to a Type 0 font
if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) {
- if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) {
+ if ((ffTT = FoFiTrueType::make(fontBuf, fontLen, 0))) {
if (globalParams->getPSLevel() >= psLevel3) {
// Level 3: use a CID font
ffTT->convertToCIDType2(psName->getCString(),
- ((GfxCIDFont *)font)->getCIDToGID(),
- ((GfxCIDFont *)font)->getCIDToGIDLen(),
+ codeToGID, codeToGIDLen,
needVerticalMetrics,
outputFunc, outputStream);
} else {
// otherwise: use a non-CID composite font
ffTT->convertToType0(psName->getCString(),
- ((GfxCIDFont *)font)->getCIDToGID(),
- ((GfxCIDFont *)font)->getCIDToGIDLen(),
+ codeToGID, codeToGIDLen,
needVerticalMetrics,
outputFunc, outputStream);
}
@@ -2327,18 +2596,104 @@ void PSOutputDev::setupEmbeddedCIDTrueTy
// 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 @@ void PSOutputDev::setupExternalCIDTrueTy
// convert it to a Type 0 font
//~ this should use fontNum to load the correct font
- if ((ffTT = FoFiTrueType::load(fileName->getCString()))) {
-
- // check for embedding permission
- if (ffTT->getEmbeddingRights() >= 1) {
-
- // create a CID-to-GID mapping, via Unicode
- if ((ctu = ((GfxCIDFont *)font)->getToUnicode())) {
- // look for a Unicode cmap
- for (cmap = 0; cmap < ffTT->getNumCmaps(); ++cmap) {
- if ((ffTT->getCmapPlatform(cmap) == 3 &&
- ffTT->getCmapEncoding(cmap) == 1) ||
- ffTT->getCmapPlatform(cmap) == 0) {
- break;
- }
- }
- if (cmap < ffTT->getNumCmaps()) {
- // map CID -> Unicode -> GID
- codeToGIDLen = ctu->getLength();
- codeToGID = (int *)gmallocn(codeToGIDLen, sizeof(int));
- for (code = 0; code < codeToGIDLen; ++code) {
- if (ctu->mapToUnicode(code, uBuf, 8) > 0) {
- codeToGID[code] = ffTT->mapCodeToGID(cmap, uBuf[0]);
- } else {
- codeToGID[code] = 0;
- }
- }
- if (globalParams->getPSLevel() >= psLevel3) {
- // Level 3: use a CID font
- ffTT->convertToCIDType2(psName->getCString(),
- codeToGID, codeToGIDLen,
- needVerticalMetrics,
- outputFunc, outputStream);
- } else {
- // otherwise: use a non-CID composite font
- ffTT->convertToType0(psName->getCString(),
- codeToGID, codeToGIDLen,
- needVerticalMetrics,
- outputFunc, outputStream);
- }
- gfree(codeToGID);
- }
- ctu->decRefCnt();
- } else {
- error(errSyntaxError, -1,
- "Couldn't find a mapping to Unicode for font '{0:s}'",
- font->getName() ? font->getName()->getCString() : "(unnamed)");
- }
- } else {
- error(errSyntaxError, -1,
- "TrueType font '%s' does not allow embedding",
- font->getName() ? font->getName()->getCString() : "(unnamed)");
-
- }
- delete ffTT;
+ if (globalParams->getPSLevel() >= psLevel3) {
+ // Level 3: use a CID font
+ ffTT->convertToCIDType2(psName->getCString(),
+ codeToGID, codeToGIDLen,
+ needVerticalMetrics,
+ outputFunc, outputStream);
+ } else {
+ // otherwise: use a non-CID composite font
+ ffTT->convertToType0(psName->getCString(),
+ codeToGID, codeToGIDLen,
+ needVerticalMetrics,
+ outputFunc, outputStream);
}
+ delete ffTT;
// ending comment
writePS("%%EndResource\n");
+
+ ff = new PSFontFileInfo(psName, font->getType(), psFontFileExternal);
+ ff->extFileName = fileName->copy();
+ ff->codeToGID = codeToGID;
+ ff->codeToGIDLen = codeToGIDLen;
+ fontFileInfo->add(ff->psName, ff);
+ return ff;
}
-void PSOutputDev::setupEmbeddedOpenTypeCFFFont(GfxFont *font, Ref *id,
- GString *psName) {
+PSFontFileInfo *PSOutputDev::setupEmbeddedOpenTypeCFFFont(GfxFont *font,
+ Ref *id) {
+ GString *psName;
+ PSFontFileInfo *ff;
char *fontBuf;
int fontLen;
FoFiTrueType *ffTT;
- int i;
+ GHashIter *iter;
+ int n;
// check if font is already embedded
- for (i = 0; i < t1FontNameLen; ++i) {
- if (t1FontNames[i].fontFileID.num == id->num &&
- t1FontNames[i].fontFileID.gen == id->gen) {
- psName->clear();
- psName->insert(0, t1FontNames[i].psName);
- return;
+ fontFileInfo->startIter(&iter);
+ while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) {
+ if (ff->loc == psFontFileEmbedded &&
+ ff->embFontID.num == id->num &&
+ ff->embFontID.gen == id->gen) {
+ fontFileInfo->killIter(&iter);
+ return ff;
}
}
- if (t1FontNameLen == t1FontNameSize) {
- t1FontNameSize *= 2;
- t1FontNames = (PST1FontName *)greallocn(t1FontNames, t1FontNameSize,
- sizeof(PST1FontName));
- }
- t1FontNames[t1FontNameLen].fontFileID = *id;
- t1FontNames[t1FontNameLen].psName = psName->copy();
- ++t1FontNameLen;
+
+ // generate name
+ psName = makePSFontName(font, id);
// beginning comment
writePSFmt("%%BeginResource: font {0:t}\n", psName);
@@ -2441,7 +2761,7 @@ void PSOutputDev::setupEmbeddedOpenTypeC
// convert it to a Type 0 font
if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) {
- if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) {
+ if ((ffTT = FoFiTrueType::make(fontBuf, fontLen, 0))) {
if (ffTT->isOpenTypeCFF()) {
if (globalParams->getPSLevel() >= psLevel3) {
// Level 3: use a CID font
@@ -2464,10 +2784,22 @@ void PSOutputDev::setupEmbeddedOpenTypeC
// ending comment
writePS("%%EndResource\n");
+
+ ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded);
+ ff->embFontID = *id;
+ if ((n = ((GfxCIDFont *)font)->getCIDToGIDLen())) {
+ ff->codeToGID = (int *)gmallocn(n, sizeof(int));
+ memcpy(ff->codeToGID, ((GfxCIDFont *)font)->getCIDToGID(), n * sizeof(int));
+ ff->codeToGIDLen = n;
+ }
+ fontFileInfo->add(ff->psName, ff);
+ return ff;
}
-void PSOutputDev::setupType3Font(GfxFont *font, GString *psName,
- Dict *parentResDict) {
+PSFontFileInfo *PSOutputDev::setupType3Font(GfxFont *font,
+ Dict *parentResDict) {
+ PSFontFileInfo *ff;
+ GString *psName;
Dict *resDict;
Dict *charProcs;
Object charProc;
@@ -2477,6 +2809,10 @@ void PSOutputDev::setupType3Font(GfxFont
GString *buf;
int i;
+ // generate name
+ psName = GString::format("T3_{0:d}_{1:d}",
+ font->getID()->num, font->getID()->gen);
+
// set up resources used by font
if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
inType3Char = gTrue;
@@ -2528,7 +2864,7 @@ void PSOutputDev::setupType3Font(GfxFont
writePS("/");
writePSName(charProcs->getKey(i));
writePS(" {\n");
- gfx->display(charProcs->getVal(i, &charProc));
+ gfx->display(charProcs->getValNF(i, &charProc));
charProc.free();
if (t3String) {
if (t3Cacheable) {
@@ -2558,6 +2894,10 @@ void PSOutputDev::setupType3Font(GfxFont
// ending comment
writePS("%%EndResource\n");
+
+ ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded);
+ fontFileInfo->add(ff->psName, ff);
+ return ff;
}
// Make a unique PS font name, based on the names given in the PDF
@@ -2567,16 +2907,14 @@ GString *PSOutputDev::makePSFontName(Gfx
if ((s = font->getEmbeddedFontName())) {
psName = filterPSName(s);
- if (!fontNames->lookupInt(psName)) {
- fontNames->add(psName->copy(), 1);
+ if (!fontFileInfo->lookup(psName)) {
return psName;
}
delete psName;
}
if ((s = font->getName())) {
psName = filterPSName(s);
- if (!fontNames->lookupInt(psName)) {
- fontNames->add(psName->copy(), 1);
+ if (!fontFileInfo->lookup(psName)) {
return psName;
}
delete psName;
@@ -2591,7 +2929,6 @@ GString *PSOutputDev::makePSFontName(Gfx
psName->append('_')->append(s);
delete s;
}
- fontNames->add(psName->copy(), 1);
return psName;
}
@@ -2651,7 +2988,7 @@ void PSOutputDev::setupImages(Dict *resD
}
void PSOutputDev::setupImage(Ref id, Stream *str, GBool mask) {
- GBool useRLE, useCompressed, useASCIIHex;
+ GBool useLZW, useRLE, useCompressed, useASCIIHex;
GString *s;
int c;
int size, line, col, i;
@@ -2660,21 +2997,27 @@ void PSOutputDev::setupImage(Ref id, Str
//~ this does not correctly handle the DeviceN color space
//~ -- need to use DeviceNRecoder
if (level < psLevel2) {
- useRLE = gFalse;
+ useLZW = useRLE = gFalse;
useCompressed = gFalse;
useASCIIHex = gTrue;
} else {
if (globalParams->getPSUncompressPreloadedImages()) {
- useRLE = gFalse;
+ useLZW = useRLE = gFalse;
useCompressed = gFalse;
} else {
s = str->getPSFilter(level < psLevel3 ? 2 : 3, "");
if (s) {
- useRLE = gFalse;
+ useLZW = useRLE = gFalse;
useCompressed = gTrue;
delete s;
} else {
- useRLE = gTrue;
+ if (globalParams->getPSLZW()) {
+ useLZW = gTrue;
+ useRLE = gFalse;
+ } else {
+ useRLE = gTrue;
+ useLZW = gFalse;
+ }
useCompressed = gFalse;
}
}
@@ -2683,7 +3026,9 @@ void PSOutputDev::setupImage(Ref id, Str
if (useCompressed) {
str = str->getUndecodedStream();
}
- if (useRLE) {
+ if (useLZW) {
+ str = new LZWEncoder(str);
+ } else if (useRLE) {
str = new RunLengthEncoder(str);
}
if (useASCIIHex) {
@@ -2722,9 +3067,9 @@ void PSOutputDev::setupImage(Ref id, Str
}
} while (c != (useASCIIHex ? '>' : '~') && c != EOF);
// add one entry for the final line of data; add another entry
- // because the RunLengthDecode filter may read past the end
+ // because the LZWDecode/RunLengthDecode filter may read past the end
++size;
- if (useRLE) {
+ if (useLZW || useRLE) {
++size;
}
writePSFmt("{0:d} array dup /{1:s}Data_{2:d}_{3:d} exch def\n",
@@ -2771,7 +3116,7 @@ void PSOutputDev::setupImage(Ref id, Str
}
} while (c != (useASCIIHex ? '>' : '~') && c != EOF);
writePS((char *)(useASCIIHex ? "> put\n" : "~> put\n"));
- if (useRLE) {
+ if (useLZW || useRLE) {
++line;
writePSFmt("{0:d} <> put\n", line);
} else {
@@ -2799,7 +3144,7 @@ void PSOutputDev::setupForms(Dict *resDi
xObj.streamGetDict()->lookup("Subtype", &subtypeObj);
if (subtypeObj.isName("Form")) {
if (xObjRef.isRef()) {
- setupForm(xObjRef.getRef(), &xObj);
+ setupForm(&xObjRef, &xObj);
} else {
error(errSyntaxError, -1,
"Form in resource dict is not an indirect reference");
@@ -2814,7 +3159,7 @@ void PSOutputDev::setupForms(Dict *resDi
xObjDict.free();
}
-void PSOutputDev::setupForm(Ref id, Object *strObj) {
+void PSOutputDev::setupForm(Object *strRef, Object *strObj) {
Dict *dict, *resDict;
Object matrixObj, bboxObj, resObj, obj1;
double m[6], bbox[4];
@@ -2824,7 +3169,8 @@ void PSOutputDev::setupForm(Ref id, Obje
// check if form is already defined
for (i = 0; i < formIDLen; ++i) {
- if (formIDs[i].num == id.num && formIDs[i].gen == id.gen) {
+ if (formIDs[i].num == strRef->getRefNum() &&
+ formIDs[i].gen == strRef->getRefGen()) {
return;
}
}
@@ -2838,7 +3184,7 @@ void PSOutputDev::setupForm(Ref id, Obje
}
formIDs = (Ref *)greallocn(formIDs, formIDSize, sizeof(Ref));
}
- formIDs[formIDLen++] = id;
+ formIDs[formIDLen++] = strRef->getRef();
dict = strObj->streamGetDict();
@@ -2875,7 +3221,7 @@ void PSOutputDev::setupForm(Ref id, Obje
dict->lookup("Resources", &resObj);
resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
- writePSFmt("/f_{0:d}_{1:d} {{\n", id.num, id.gen);
+ writePSFmt("/f_{0:d}_{1:d} {{\n", strRef->getRefNum(), strRef->getRefGen());
writePS("q\n");
writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] cm\n",
m[0], m[1], m[2], m[3], m[4], m[5]);
@@ -2885,7 +3231,7 @@ void PSOutputDev::setupForm(Ref id, Obje
box.x2 = bbox[2];
box.y2 = bbox[3];
gfx = new Gfx(doc, this, resDict, &box, &box);
- gfx->display(strObj);
+ gfx->display(strRef);
delete gfx;
writePS("Q\n");
@@ -2905,6 +3251,7 @@ GBool PSOutputDev::checkPageSlice(Page *
GBool rasterize;
#if HAVE_SPLASH
GBool mono;
+ GBool useLZW;
double dpi;
SplashOutputDev *splashOut;
SplashColor paperColor;
@@ -2915,10 +3262,11 @@ GBool PSOutputDev::checkPageSlice(Page *
Object obj;
Guchar *p;
Guchar col[4];
+ char buf[4096];
double hDPI2, vDPI2;
double m0, m1, m2, m3, m4, m5;
int nStripes, stripeH, stripeY;
- int c, w, h, x, y, comp, i;
+ int w, h, x, y, comp, i, n;
#endif
if (globalParams->getPSAlwaysRasterize()) {
@@ -2939,6 +3287,7 @@ GBool PSOutputDev::checkPageSlice(Page *
// get the rasterization parameters
dpi = globalParams->getPSRasterResolution();
mono = globalParams->getPSRasterMono();
+ useLZW = globalParams->getPSLZW();
// start the PS page
page->makeBox(dpi, dpi, rotateA, useMediaBox, gFalse,
@@ -2987,8 +3336,8 @@ GBool PSOutputDev::checkPageSlice(Page *
sliceW = (int)((box.x2 - box.x1) * hDPI2 / 72.0);
sliceH = (int)((box.y2 - box.y1) * vDPI2 / 72.0);
}
- nStripes = (int)ceil((double)(sliceW * sliceH) /
- (double)rasterizationSliceSize);
+ nStripes = (int)ceil(((double)sliceW * (double)sliceH) /
+ (double)globalParams->getPSRasterSliceSize());
stripeH = (sliceH + nStripes - 1) / nStripes;
// render the stripes
@@ -3094,21 +3443,29 @@ GBool PSOutputDev::checkPageSlice(Page *
} else {
writePS(" /ASCII85Decode filter\n");
}
- writePS(" /RunLengthDecode filter\n");
+ if (useLZW) {
+ writePS(" /LZWDecode filter\n");
+ } else {
+ writePS(" /RunLengthDecode filter\n");
+ }
writePS(">>\n");
writePS("image\n");
obj.initNull();
p = bitmap->getDataPtr() + (h - 1) * bitmap->getRowSize();
str0 = new MemStream((char *)p, 0, w * h * (mono ? 1 : 3), &obj);
- str = new RunLengthEncoder(str0);
+ if (useLZW) {
+ str = new LZWEncoder(str0);
+ } else {
+ str = new RunLengthEncoder(str0);
+ }
if (globalParams->getPSASCIIHex()) {
str = new ASCIIHexEncoder(str);
} else {
str = new ASCII85Encoder(str);
}
str->reset();
- while ((c = str->getChar()) != EOF) {
- writePSChar(c);
+ while ((n = str->getBlock(buf, sizeof(buf))) > 0) {
+ writePSBlock(buf, n);
}
str->close();
delete str;
@@ -3148,8 +3505,13 @@ void PSOutputDev::startPage(int pageNum,
if (paperMatch) {
page = doc->getCatalog()->getPage(pageNum);
imgLLX = imgLLY = 0;
- imgURX = (int)ceil(page->getMediaWidth());
- imgURY = (int)ceil(page->getMediaHeight());
+ if (globalParams->getPSUseCropBoxAsPage()) {
+ imgURX = (int)ceil(page->getCropWidth());
+ imgURY = (int)ceil(page->getCropHeight());
+ } else {
+ imgURX = (int)ceil(page->getMediaWidth());
+ imgURY = (int)ceil(page->getMediaHeight());
+ }
if (state->getRotate() == 90 || state->getRotate() == 270) {
t = imgURX;
imgURX = imgURY;
@@ -3160,6 +3522,9 @@ void PSOutputDev::startPage(int pageNum,
}
writePS("%%BeginPageSetup\n");
}
+ if (mode != psModeForm) {
+ writePS("xpdf begin\n");
+ }
// underlays
if (underlayCbk) {
@@ -3356,6 +3721,7 @@ void PSOutputDev::endPage() {
}
writePS("%%PageTrailer\n");
writePageTrailer();
+ writePS("end\n");
}
}
@@ -3371,8 +3737,13 @@ void PSOutputDev::restoreState(GfxState
void PSOutputDev::updateCTM(GfxState *state, double m11, double m12,
double m21, double m22, double m31, double m32) {
- writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] cm\n",
- m11, m12, m21, m22, m31, m32);
+ if (fabs(m11 * m22 - m12 * m21) < 0.00001) {
+ // avoid a singular (or close-to-singular) matrix
+ writePSFmt("[0.00001 0 0 0.00001 {0:.6g} {1:.6g}] Tm\n", m31, m32);
+ } else {
+ writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] cm\n",
+ m11, m12, m21, m22, m31, m32);
+ }
}
void PSOutputDev::updateLineDash(GfxState *state) {
@@ -3737,7 +4108,7 @@ void PSOutputDev::eoFill(GfxState *state
writePS("f*\n");
}
-void PSOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
+void PSOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *strRef,
int paintType, Dict *resDict,
double *mat, double *bbox,
int x0, int y0, int x1, int y1,
@@ -3770,6 +4141,7 @@ void PSOutputDev::tilingPatternFill(GfxS
box.x2 = bbox[2];
box.y2 = bbox[3];
gfx2 = new Gfx(doc, this, resDict, &box, NULL);
+ gfx2->takeContentStreamStack(gfx);
writePS("/x {\n");
if (paintType == 2) {
writePSFmt("{0:.6g} 0 {1:.6g} {2:.6g} {3:.6g} {4:.6g} setcachedevice\n",
@@ -3785,7 +4157,7 @@ void PSOutputDev::tilingPatternFill(GfxS
}
inType3Char = gTrue;
++numTilingPatterns;
- gfx2->display(str);
+ gfx2->display(strRef);
--numTilingPatterns;
inType3Char = gFalse;
writePS("} def\n");
@@ -3944,7 +4316,7 @@ GBool PSOutputDev::radialShadedFill(GfxS
double xMin, yMin, xMax, yMax;
double x0, y0, r0, x1, y1, r1, t0, t1;
double xa, ya, ra;
- double sz, sMin, sMax, h, ta;
+ double sMin, sMax, h, ta;
double sLeft, sRight, sTop, sBottom, sZero, sDiag;
GBool haveSLeft, haveSRight, haveSTop, haveSBottom, haveSZero;
GBool haveSMin, haveSMax;
@@ -3970,18 +4342,14 @@ GBool PSOutputDev::radialShadedFill(GfxS
if (h == 0) {
enclosed = gTrue;
theta = 0; // make gcc happy
- sz = 0; // make gcc happy
} else if (r1 - r0 == 0) {
enclosed = gFalse;
theta = 0;
- sz = 0; // make gcc happy
} else if (fabs(r1 - r0) >= h) {
enclosed = gTrue;
theta = 0; // make gcc happy
- sz = 0; // make gcc happy
} else {
enclosed = gFalse;
- sz = -r0 / (r1 - r0);
theta = asin((r1 - r0) / h);
}
if (enclosed) {
@@ -4267,8 +4635,9 @@ void PSOutputDev::drawString(GfxState *s
int wMode;
int *codeToGID;
GString *s2;
- double dx, dy, originX, originY;
+ double dx, dy, originX, originY, originX0, originY0, tOriginX0, tOriginY0;
char *p;
+ PSFontInfo *fi;
UnicodeMap *uMap;
CharCode code;
Unicode u[8];
@@ -4292,30 +4661,32 @@ void PSOutputDev::drawString(GfxState *s
}
wMode = font->getWMode();
+ fi = NULL;
+ for (i = 0; i < fontInfo->getLength(); ++i) {
+ fi = (PSFontInfo *)fontInfo->get(i);
+ if (fi->fontID.num == font->getID()->num &&
+ fi->fontID.gen == font->getID()->gen) {
+ break;
+ }
+ fi = NULL;
+ }
+
// check for a subtitute 16-bit font
uMap = NULL;
codeToGID = NULL;
if (font->isCIDFont()) {
- for (i = 0; i < font16EncLen; ++i) {
- if (font->getID()->num == font16Enc[i].fontID.num &&
- font->getID()->gen == font16Enc[i].fontID.gen) {
- if (!font16Enc[i].enc) {
- // font substitution failed, so don't output any text
- return;
- }
- uMap = globalParams->getUnicodeMap(font16Enc[i].enc);
- break;
- }
+ if (!(fi && fi->ff)) {
+ // font substitution failed, so don't output any text
+ return;
+ }
+ if (fi->ff->encoding) {
+ uMap = globalParams->getUnicodeMap(fi->ff->encoding);
}
- // check for a code-to-GID map
+ // check for an 8-bit code-to-GID map
} else {
- for (i = 0; i < font8InfoLen; ++i) {
- if (font->getID()->num == font8Info[i].fontID.num &&
- font->getID()->gen == font8Info[i].fontID.gen) {
- codeToGID = font8Info[i].codeToGID;
- break;
- }
+ if (fi && fi->ff) {
+ codeToGID = fi->ff->codeToGID;
}
}
@@ -4326,10 +4697,18 @@ void PSOutputDev::drawString(GfxState *s
s2 = new GString();
dxdySize = font->isCIDFont() ? 8 : s->getLength();
dxdy = (double *)gmallocn(2 * dxdySize, sizeof(double));
+ originX0 = originY0 = 0; // make gcc happy
while (len > 0) {
n = font->getNextChar(p, len, &code,
u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
&dx, &dy, &originX, &originY);
+ //~ this doesn't handle the case where the origin offset changes
+ //~ within a string of characters -- which could be fixed by
+ //~ modifying dx,dy as needed for each character
+ if (p == s->getCString()) {
+ originX0 = originX;
+ originY0 = originY;
+ }
dx *= state->getFontSize();
dy *= state->getFontSize();
if (wMode) {
@@ -4389,8 +4768,14 @@ void PSOutputDev::drawString(GfxState *s
if (uMap) {
uMap->decRefCnt();
}
+ originX0 *= state->getFontSize();
+ originY0 *= state->getFontSize();
+ state->textTransformDelta(originX0, originY0, &tOriginX0, &tOriginY0);
if (nChars > 0) {
+ if (wMode) {
+ writePSFmt("{0:.6g} {1:.6g} rmoveto\n", -tOriginX0, -tOriginY0);
+ }
writePSString(s2);
writePS("\n[");
for (i = 0; i < 2 * nChars; ++i) {
@@ -4400,6 +4785,9 @@ void PSOutputDev::drawString(GfxState *s
writePSFmt("{0:.6g}", dxdy[i]);
}
writePS("] Tj\n");
+ if (wMode) {
+ writePSFmt("{0:.6g} {1:.6g} rmoveto\n", tOriginX0, tOriginY0);
+ }
}
gfree(dxdy);
delete s2;
@@ -4418,7 +4806,7 @@ void PSOutputDev::endTextObject(GfxState
void PSOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
int width, int height, GBool invert,
- GBool inlineImg) {
+ GBool inlineImg, GBool interpolate) {
int len;
len = height * ((width + 7) / 8);
@@ -4442,7 +4830,8 @@ void PSOutputDev::drawImageMask(GfxState
void PSOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
int width, int height, GfxImageColorMap *colorMap,
- int *maskColors, GBool inlineImg) {
+ int *maskColors, GBool inlineImg,
+ GBool interpolate) {
int len;
len = height * ((width * colorMap->getNumPixelComps() *
@@ -4474,7 +4863,7 @@ void PSOutputDev::drawMaskedImage(GfxSta
GfxImageColorMap *colorMap,
Stream *maskStr,
int maskWidth, int maskHeight,
- GBool maskInvert) {
+ GBool maskInvert, GBool interpolate) {
int len;
len = height * ((width * colorMap->getNumPixelComps() *
@@ -4684,10 +5073,11 @@ void PSOutputDev::doImageL2(Object *ref,
GBool emitRect, addRect, extendRect;
GString *s;
int n, numComps;
- GBool useRLE, useASCII, useASCIIHex, useCompressed;
+ GBool useLZW, useRLE, useASCII, useASCIIHex, useCompressed;
GfxSeparationColorSpace *sepCS;
GfxColor color;
GfxCMYK cmyk;
+ char buf[4096];
int c;
int col, i, j, x0, x1, y, maskXor;
@@ -4821,14 +5211,14 @@ void PSOutputDev::doImageL2(Object *ref,
rectsOut[rectsOutLen].y1 = height - rects0[i].y0 - 1;
++rectsOutLen;
}
- writePSFmt("{0:d} array 0\n", rectsOutLen * 4);
+ writePSFmt("{0:d} {1:d}\n", maskWidth, maskHeight);
for (i = 0; i < rectsOutLen; ++i) {
- writePSFmt("[{0:d} {1:d} {2:d} {3:d}] pr\n",
+ writePSFmt("{0:d} {1:d} {2:d} {3:d} pr\n",
rectsOut[i].x0, rectsOut[i].y0,
rectsOut[i].x1 - rectsOut[i].x0,
rectsOut[i].y1 - rectsOut[i].y0);
}
- writePSFmt("pop {0:d} {1:d} pdfImClip\n", width, height);
+ writePS("pop pop pdfImClip\n");
gfree(rectsOut);
gfree(rects0);
gfree(rects1);
@@ -4923,14 +5313,14 @@ void PSOutputDev::doImageL2(Object *ref,
rectsOut[rectsOutLen].y1 = maskHeight - rects0[i].y0 - 1;
++rectsOutLen;
}
- writePSFmt("{0:d} array 0\n", rectsOutLen * 4);
+ writePSFmt("{0:d} {1:d}\n", maskWidth, maskHeight);
for (i = 0; i < rectsOutLen; ++i) {
- writePSFmt("[{0:d} {1:d} {2:d} {3:d}] pr\n",
+ writePSFmt("{0:d} {1:d} {2:d} {3:d} pr\n",
rectsOut[i].x0, rectsOut[i].y0,
rectsOut[i].x1 - rectsOut[i].x0,
rectsOut[i].y1 - rectsOut[i].y0);
}
- writePSFmt("pop {0:d} {1:d} pdfImClip\n", maskWidth, maskHeight);
+ writePS("pop pop pdfImClip\n");
gfree(rectsOut);
gfree(rects0);
gfree(rects1);
@@ -4951,7 +5341,11 @@ void PSOutputDev::doImageL2(Object *ref,
if (inlineImg) {
// create an array
str2 = new FixedLengthEncoder(str, len);
- str2 = new RunLengthEncoder(str2);
+ if (globalParams->getPSLZW()) {
+ str2 = new LZWEncoder(str2);
+ } else {
+ str2 = new RunLengthEncoder(str2);
+ }
if (useASCIIHex) {
str2 = new ASCIIHexEncoder(str2);
} else {
@@ -4994,8 +5388,8 @@ void PSOutputDev::doImageL2(Object *ref,
}
} while (c != (useASCIIHex ? '>' : '~') && c != EOF);
writePS((char *)(useASCIIHex ? ">\n" : "~>\n"));
- // add an extra entry because the RunLengthDecode filter may
- // read past the end
+ // add an extra entry because the LZWDecode/RunLengthDecode
+ // filter may read past the end
writePS("<>]\n");
writePS("0\n");
str2->close();
@@ -5065,7 +5459,7 @@ void PSOutputDev::doImageL2(Object *ref,
if ((mode == psModeForm || inType3Char || preload) &&
globalParams->getPSUncompressPreloadedImages()) {
s = NULL;
- useRLE = gFalse;
+ useLZW = useRLE = gFalse;
useCompressed = gFalse;
useASCII = gFalse;
} else {
@@ -5073,11 +5467,17 @@ void PSOutputDev::doImageL2(Object *ref,
" ");
if ((colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) ||
inlineImg || !s) {
- useRLE = gTrue;
+ if (globalParams->getPSLZW()) {
+ useLZW = gTrue;
+ useRLE = gFalse;
+ } else {
+ useRLE = gTrue;
+ useLZW = gFalse;
+ }
useASCII = !(mode == psModeForm || inType3Char || preload);
useCompressed = gFalse;
} else {
- useRLE = gFalse;
+ useLZW = useRLE = gFalse;
useASCII = str->isBinary() &&
!(mode == psModeForm || inType3Char || preload);
useCompressed = gTrue;
@@ -5087,7 +5487,9 @@ void PSOutputDev::doImageL2(Object *ref,
writePSFmt(" /ASCII{0:s}Decode filter\n",
useASCIIHex ? "Hex" : "85");
}
- if (useRLE) {
+ if (useLZW) {
+ writePS(" /LZWDecode filter\n");
+ } else if (useRLE) {
writePS(" /RunLengthDecode filter\n");
}
if (useCompressed) {
@@ -5119,8 +5521,10 @@ void PSOutputDev::doImageL2(Object *ref,
str = new DeviceNRecoder(str, width, height, colorMap);
}
- // add RunLengthEncode and ASCIIHex/85 encode filters
- if (useRLE) {
+ // add LZWEncode/RunLengthEncode and ASCIIHex/85 encode filters
+ if (useLZW) {
+ str = new LZWEncoder(str);
+ } else if (useRLE) {
str = new RunLengthEncoder(str);
}
if (useASCII) {
@@ -5141,12 +5545,14 @@ void PSOutputDev::doImageL2(Object *ref,
n = 0;
} else {
// need to read the stream to count characters -- the length
- // is data-dependent (because of ASCII and RLE filters)
+ // is data-dependent (because of ASCII and LZW/RunLength
+ // filters)
str->reset();
n = 0;
- while ((c = str->getChar()) != EOF) {
- ++n;
- }
+ do {
+ i = str->discardChars(4096);
+ n += i;
+ } while (i == 4096);
str->close();
}
// +6/7 for "pdfIm\n" / "pdfImM\n"
@@ -5170,8 +5576,8 @@ void PSOutputDev::doImageL2(Object *ref,
// copy the stream data
str->reset();
- while ((c = str->getChar()) != EOF) {
- writePSChar(c);
+ while ((n = str->getBlock(buf, sizeof(buf))) > 0) {
+ writePSBlock(buf, n);
}
str->close();
@@ -5185,7 +5591,7 @@ void PSOutputDev::doImageL2(Object *ref,
#endif
// delete encoders
- if (useRLE || useASCII || inlineImg) {
+ if (useLZW || useRLE || useASCII || inlineImg) {
delete str;
}
}
@@ -5204,18 +5610,20 @@ void PSOutputDev::doImageL3(Object *ref,
Stream *str2;
GString *s;
int n, numComps;
- GBool useRLE, useASCII, useASCIIHex, useCompressed;
- GBool maskUseRLE, maskUseASCII, maskUseCompressed;
+ GBool useLZW, useRLE, useASCII, useASCIIHex, useCompressed;
+ GBool maskUseLZW, maskUseRLE, maskUseASCII, maskUseCompressed;
GString *maskFilters;
GfxSeparationColorSpace *sepCS;
GfxColor color;
GfxCMYK cmyk;
+ char buf[4096];
int c;
int col, i;
useASCIIHex = globalParams->getPSASCIIHex();
- useRLE = useASCII = useCompressed = gFalse; // make gcc happy
- maskUseRLE = maskUseASCII = maskUseCompressed = gFalse; // make gcc happy
+ useLZW = useRLE = useASCII = useCompressed = gFalse; // make gcc happy
+ maskUseLZW = maskUseRLE = maskUseASCII = gFalse; // make gcc happy
+ maskUseCompressed = gFalse; // make gcc happy
maskFilters = NULL; // make gcc happy
// explicit masking
@@ -5225,17 +5633,23 @@ void PSOutputDev::doImageL3(Object *ref,
if ((mode == psModeForm || inType3Char || preload) &&
globalParams->getPSUncompressPreloadedImages()) {
s = NULL;
- maskUseRLE = gFalse;
+ maskUseLZW = maskUseRLE = gFalse;
maskUseCompressed = gFalse;
maskUseASCII = gFalse;
} else {
s = maskStr->getPSFilter(3, " ");
if (!s) {
- maskUseRLE = gTrue;
+ if (globalParams->getPSLZW()) {
+ maskUseLZW = gTrue;
+ maskUseRLE = gFalse;
+ } else {
+ maskUseRLE = gTrue;
+ maskUseLZW = gFalse;
+ }
maskUseASCII = !(mode == psModeForm || inType3Char || preload);
maskUseCompressed = gFalse;
} else {
- maskUseRLE = gFalse;
+ maskUseLZW = maskUseRLE = gFalse;
maskUseASCII = maskStr->isBinary() &&
!(mode == psModeForm || inType3Char || preload);
maskUseCompressed = gTrue;
@@ -5246,7 +5660,9 @@ void PSOutputDev::doImageL3(Object *ref,
maskFilters->appendf(" /ASCII{0:s}Decode filter\n",
useASCIIHex ? "Hex" : "85");
}
- if (maskUseRLE) {
+ if (maskUseLZW) {
+ maskFilters->append(" /LZWDecode filter\n");
+ } else if (maskUseRLE) {
maskFilters->append(" /RunLengthDecode filter\n");
}
if (maskUseCompressed) {
@@ -5263,11 +5679,13 @@ void PSOutputDev::doImageL3(Object *ref,
writePS(maskFilters->getCString());
writePS("pdfMask\n");
- // add RunLengthEncode and ASCIIHex/85 encode filters
+ // add LZWEncode/RunLengthEncode and ASCIIHex/85 encode filters
if (maskUseCompressed) {
maskStr = maskStr->getUndecodedStream();
}
- if (maskUseRLE) {
+ if (maskUseLZW) {
+ maskStr = new LZWEncoder(maskStr);
+ } else if (maskUseRLE) {
maskStr = new RunLengthEncoder(maskStr);
}
if (maskUseASCII) {
@@ -5280,15 +5698,15 @@ void PSOutputDev::doImageL3(Object *ref,
// copy the stream data
maskStr->reset();
- while ((c = maskStr->getChar()) != EOF) {
- writePSChar(c);
+ while ((n = maskStr->getBlock(buf, sizeof(buf))) > 0) {
+ writePSBlock(buf, n);
}
maskStr->close();
writePSChar('\n');
writePS("%-EOD-\n");
// delete encoders
- if (maskUseRLE || maskUseASCII) {
+ if (maskUseLZW || maskUseRLE || maskUseASCII) {
delete maskStr;
}
}
@@ -5305,7 +5723,11 @@ void PSOutputDev::doImageL3(Object *ref,
if (inlineImg) {
// create an array
str2 = new FixedLengthEncoder(str, len);
- str2 = new RunLengthEncoder(str2);
+ if (globalParams->getPSLZW()) {
+ str2 = new LZWEncoder(str2);
+ } else {
+ str2 = new RunLengthEncoder(str2);
+ }
if (useASCIIHex) {
str2 = new ASCIIHexEncoder(str2);
} else {
@@ -5348,8 +5770,8 @@ void PSOutputDev::doImageL3(Object *ref,
}
} while (c != (useASCIIHex ? '>' : '~') && c != EOF);
writePS((char *)(useASCIIHex ? ">\n" : "~>\n"));
- // add an extra entry because the RunLengthDecode filter may
- // read past the end
+ // add an extra entry because the LZWDecode/RunLengthDecode
+ // filter may read past the end
writePS("<>]\n");
writePS("0\n");
str2->close();
@@ -5436,7 +5858,7 @@ void PSOutputDev::doImageL3(Object *ref,
if ((mode == psModeForm || inType3Char || preload) &&
globalParams->getPSUncompressPreloadedImages()) {
s = NULL;
- useRLE = gFalse;
+ useLZW = useRLE = gFalse;
useCompressed = gFalse;
useASCII = gFalse;
} else {
@@ -5444,11 +5866,17 @@ void PSOutputDev::doImageL3(Object *ref,
" ");
if ((colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) ||
inlineImg || !s) {
- useRLE = gTrue;
+ if (globalParams->getPSLZW()) {
+ useLZW = gTrue;
+ useRLE = gFalse;
+ } else {
+ useRLE = gTrue;
+ useLZW = gFalse;
+ }
useASCII = !(mode == psModeForm || inType3Char || preload);
useCompressed = gFalse;
} else {
- useRLE = gFalse;
+ useLZW = useRLE = gFalse;
useASCII = str->isBinary() &&
!(mode == psModeForm || inType3Char || preload);
useCompressed = gTrue;
@@ -5458,7 +5886,9 @@ void PSOutputDev::doImageL3(Object *ref,
writePSFmt(" /ASCII{0:s}Decode filter\n",
useASCIIHex ? "Hex" : "85");
}
- if (useRLE) {
+ if (useLZW) {
+ writePS(" /LZWDecode filter\n");
+ } else if (useRLE) {
writePS(" /RunLengthDecode filter\n");
}
if (useCompressed) {
@@ -5538,8 +5968,10 @@ void PSOutputDev::doImageL3(Object *ref,
str = new DeviceNRecoder(str, width, height, colorMap);
}
- // add RunLengthEncode and ASCIIHex/85 encode filters
- if (useRLE) {
+ // add LZWEncode/RunLengthEncode and ASCIIHex/85 encode filters
+ if (useLZW) {
+ str = new LZWEncoder(str);
+ } else if (useRLE) {
str = new RunLengthEncoder(str);
}
if (useASCII) {
@@ -5552,8 +5984,8 @@ void PSOutputDev::doImageL3(Object *ref,
// copy the stream data
str->reset();
- while ((c = str->getChar()) != EOF) {
- writePSChar(c);
+ while ((n = str->getBlock(buf, sizeof(buf))) > 0) {
+ writePSBlock(buf, n);
}
str->close();
@@ -5562,7 +5994,7 @@ void PSOutputDev::doImageL3(Object *ref,
writePS("%-EOD-\n");
// delete encoders
- if (useRLE || useASCII || inlineImg) {
+ if (useLZW || useRLE || useASCII || inlineImg) {
delete str;
}
}
@@ -6267,6 +6699,10 @@ void PSOutputDev::type3D0(GfxState *stat
void PSOutputDev::type3D1(GfxState *state, double wx, double wy,
double llx, double lly, double urx, double ury) {
+ if (t3String) {
+ error(errSyntaxError, -1, "Multiple 'd1' operators in Type 3 CharProc");
+ return;
+ }
t3WX = wx;
t3WY = wy;
t3LLX = llx;
@@ -6286,7 +6722,8 @@ void PSOutputDev::drawForm(Ref id) {
void PSOutputDev::psXObject(Stream *psStream, Stream *level1Stream) {
Stream *str;
- int c;
+ char buf[4096];
+ int n;
if ((level == psLevel1 || level == psLevel1Sep) && level1Stream) {
str = level1Stream;
@@ -6294,8 +6731,8 @@ void PSOutputDev::psXObject(Stream *psSt
str = psStream;
}
str->reset();
- while ((c = str->getChar()) != EOF) {
- writePSChar(c);
+ while ((n = str->getBlock(buf, sizeof(buf))) > 0) {
+ writePSBlock(buf, n);
}
str->close();
}
@@ -6462,6 +6899,14 @@ void PSOutputDev::writePSChar(char c) {
}
}
+void PSOutputDev::writePSBlock(char *s, int len) {
+ if (t3String) {
+ t3String->append(s, len);
+ } else {
+ (*outputFunc)(outputStream, s, len);
+ }
+}
+
void PSOutputDev::writePS(const char *s) {
if (t3String) {
t3String->append(s);
@@ -6564,7 +7009,9 @@ GString *PSOutputDev::filterPSName(GStri
// Write a DSC-compliant .
void PSOutputDev::writePSTextLine(GString *s) {
- int i, j, step;
+ TextString *ts;
+ Unicode *u;
+ int i, j;
int c;
// - DSC comments must be printable ASCII; control chars and
@@ -6574,17 +7021,10 @@ void PSOutputDev::writePSTextLine(GStrin
// for the keyword, which was emitted by the caller)
// - lines that start with a left paren are treated as
// instead of , so we escape a leading paren
- if (s->getLength() >= 2 &&
- (s->getChar(0) & 0xff) == 0xfe &&
- (s->getChar(1) & 0xff) == 0xff) {
- i = 3;
- step = 2;
- } else {
- i = 0;
- step = 1;
- }
- for (j = 0; i < s->getLength() && j < 200; i += step) {
- c = s->getChar(i) & 0xff;
+ ts = new TextString(s);
+ u = ts->getUnicode();
+ for (i = 0, j = 0; i < ts->getLength() && j < 200; ++i) {
+ c = u[i] & 0xff;
if (c == '\\') {
writePS("\\\\");
j += 2;
@@ -6597,4 +7037,5 @@ void PSOutputDev::writePSTextLine(GStrin
}
}
writePS("\n");
+ delete ts;
}
diff -uNrp 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 GfxFont;
class GfxColorSpace;
class GfxSeparationColorSpace;
class PDFRectangle;
-struct PST1FontName;
-struct PSFont8Info;
-struct PSFont16Enc;
class PSOutCustomColor;
class PSOutputDev;
+class PSFontFileInfo;
//------------------------------------------------------------------------
// PSOutputDev
@@ -92,6 +90,9 @@ public:
// Check if file was successfully created.
virtual GBool isOk() { return ok; }
+ // Returns false if there have been any errors on the output stream.
+ GBool checkIO();
+
//---- get info about output device
// Does this device use upside-down coordinates?
@@ -196,7 +197,7 @@ public:
virtual void stroke(GfxState *state);
virtual void fill(GfxState *state);
virtual void eoFill(GfxState *state);
- virtual void tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
+ virtual void tilingPatternFill(GfxState *state, Gfx *gfx, Object *strRef,
int paintType, Dict *resDict,
double *mat, double *bbox,
int x0, int y0, int x1, int y1,
@@ -218,15 +219,15 @@ public:
//----- image drawing
virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
int width, int height, GBool invert,
- GBool inlineImg);
+ GBool inlineImg, GBool interpolate);
virtual void drawImage(GfxState *state, Object *ref, Stream *str,
int width, int height, GfxImageColorMap *colorMap,
- int *maskColors, GBool inlineImg);
+ int *maskColors, GBool inlineImg, GBool interpolate);
virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
Stream *maskStr, int maskWidth, int maskHeight,
- GBool maskInvert);
+ GBool maskInvert, GBool interpolate);
#if OPI_SUPPORT
//----- OPI functions
@@ -262,6 +263,7 @@ public:
{ overlayCbk = cbk; overlayCbkData = data; }
void writePSChar(char c);
+ void writePSBlock(char *s, int len);
void writePS(const char *s);
void writePSFmt(const char *fmt, ...);
void writePSString(GString *s);
@@ -277,27 +279,27 @@ private:
void setupResources(Dict *resDict);
void setupFonts(Dict *resDict);
void setupFont(GfxFont *font, Dict *parentResDict);
- void setupEmbeddedType1Font(Ref *id, GString *psName);
- void setupExternalType1Font(GString *fileName, GString *psName);
- void setupEmbeddedType1CFont(GfxFont *font, Ref *id, GString *psName);
- void setupEmbeddedOpenTypeT1CFont(GfxFont *font, Ref *id, GString *psName);
- void setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id, GString *psName);
- void setupExternalTrueTypeFont(GfxFont *font, GString *fileName,
- GString *psName);
- void setupEmbeddedCIDType0Font(GfxFont *font, Ref *id, GString *psName);
- void setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id, GString *psName,
- GBool needVerticalMetrics);
- void setupExternalCIDTrueTypeFont(GfxFont *font,
- GString *fileName,
- GString *psName,
- GBool needVerticalMetrics);
- void setupEmbeddedOpenTypeCFFFont(GfxFont *font, Ref *id, GString *psName);
- void setupType3Font(GfxFont *font, GString *psName, Dict *parentResDict);
+ PSFontFileInfo *setupEmbeddedType1Font(GfxFont *font, Ref *id);
+ PSFontFileInfo *setupExternalType1Font(GfxFont *font, GString *fileName);
+ PSFontFileInfo *setupEmbeddedType1CFont(GfxFont *font, Ref *id);
+ PSFontFileInfo *setupEmbeddedOpenTypeT1CFont(GfxFont *font, Ref *id);
+ PSFontFileInfo *setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id);
+ PSFontFileInfo *setupExternalTrueTypeFont(GfxFont *font, GString *fileName,
+ int fontNum);
+ PSFontFileInfo *setupEmbeddedCIDType0Font(GfxFont *font, Ref *id);
+ PSFontFileInfo *setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id,
+ GBool needVerticalMetrics);
+ PSFontFileInfo *setupExternalCIDTrueTypeFont(GfxFont *font,
+ GString *fileName,
+ int fontNum,
+ GBool needVerticalMetrics);
+ PSFontFileInfo *setupEmbeddedOpenTypeCFFFont(GfxFont *font, Ref *id);
+ PSFontFileInfo *setupType3Font(GfxFont *font, Dict *parentResDict);
GString *makePSFontName(GfxFont *font, Ref *id);
void setupImages(Dict *resDict);
void setupImage(Ref id, Stream *str, GBool mask);
void setupForms(Dict *resDict);
- void setupForm(Ref id, Object *strObj);
+ void setupForm(Object *strRef, Object *strObj);
void addProcessColor(double c, double m, double y, double k);
void addCustomColor(GfxSeparationColorSpace *sepCS);
void doPath(GfxPath *path);
@@ -358,19 +360,8 @@ private:
PDFDoc *doc;
XRef *xref; // the xref table for this PDF file
- Ref *fontIDs; // list of object IDs of all used fonts
- int fontIDLen; // number of entries in fontIDs array
- int fontIDSize; // size of fontIDs array
- GHash *fontNames; // all used font names
- PST1FontName *t1FontNames; // font names for Type 1/1C fonts
- int t1FontNameLen; // number of entries in t1FontNames array
- int t1FontNameSize; // size of t1FontNames array
- PSFont8Info *font8Info; // info for 8-bit fonts
- int font8InfoLen; // number of entries in font8Info array
- int font8InfoSize; // size of font8Info array
- PSFont16Enc *font16Enc; // encodings for substitute 16-bit fonts
- int font16EncLen; // number of entries in font16Enc array
- int font16EncSize; // size of font16Enc array
+ GList *fontInfo; // info for each font [PSFontInfo]
+ GHash *fontFileInfo; // info for each font file [PSFontFileInfo]
Ref *imgIDs; // list of image IDs for in-memory images
int imgIDLen; // number of entries in imgIDs array
int imgIDSize; // size of imgIDs array
diff -uNrp 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 @@ StandardSecurityHandler::StandardSecurit
if ((encRevision <= 4 &&
ownerKeyObj.getString()->getLength() == 32 &&
userKeyObj.getString()->getLength() == 32) ||
- (encRevision == 5 &&
+ ((encRevision == 5 || encRevision == 6) &&
// the spec says 48 bytes, but Acrobat pads them out longer
ownerKeyObj.getString()->getLength() >= 48 &&
userKeyObj.getString()->getLength() >= 48 &&
@@ -180,7 +180,7 @@ StandardSecurityHandler::StandardSecurit
//~ doesn't handle the case where StmF, StrF, and EFF are not all the
//~ same)
if ((encVersion == 4 || encVersion == 5) &&
- (encRevision == 4 || encRevision == 5)) {
+ (encRevision == 4 || encRevision == 5 || encRevision == 6)) {
encryptDictA->dictLookup("CF", &cryptFiltersObj);
encryptDictA->dictLookup("StmF", &streamFilterObj);
encryptDictA->dictLookup("StrF", &stringFilterObj);
@@ -216,7 +216,9 @@ StandardSecurityHandler::StandardSecurit
cfLengthObj.free();
} else if (cfmObj.isName("AESV3")) {
encVersion = 5;
- encRevision = 5;
+ if (encRevision != 5 && encRevision != 6) {
+ encRevision = 6;
+ }
encAlgorithm = cryptAES256;
if (cryptFilterObj.dictLookup("Length",
&cfLengthObj)->isInt()) {
@@ -254,15 +256,15 @@ StandardSecurityHandler::StandardSecurit
} 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 -uNrp 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 @@ static void splashOutBlendColorDodge(Spl
int i, x;
for (i = 0; i < splashColorModeNComps[cm]; ++i) {
- if (src[i] == 255) {
+ if (dest[i] == 0) {
+ blend[i] = 0;
+ } else if (src[i] == 255) {
blend[i] = 255;
} else {
x = (dest[i] * 255) / (255 - src[i]);
@@ -132,7 +135,9 @@ static void splashOutBlendColorBurn(Spla
int i, x;
for (i = 0; i < splashColorModeNComps[cm]; ++i) {
- if (src[i] == 0) {
+ if (dest[i] == 255) {
+ blend[i] = 255;
+ } else if (src[i] == 0) {
blend[i] = 0;
} else {
x = ((255 - dest[i]) * 255) / src[i];
@@ -284,7 +289,10 @@ static void setSat(Guchar rIn, Guchar gI
static void splashOutBlendHue(SplashColorPtr src, SplashColorPtr dest,
SplashColorPtr blend, SplashColorMode cm) {
- Guchar r0, g0, b0, r1, g1, b1;
+ Guchar r0, g0, b0;
+#if SPLASH_CMYK
+ Guchar r1, g1, b1;
+#endif
switch (cm) {
case splashModeMono1:
@@ -317,7 +325,10 @@ static void splashOutBlendHue(SplashColo
static void splashOutBlendSaturation(SplashColorPtr src, SplashColorPtr dest,
SplashColorPtr blend,
SplashColorMode cm) {
- Guchar r0, g0, b0, r1, g1, b1;
+ Guchar r0, g0, b0;
+#if SPLASH_CMYK
+ Guchar r1, g1, b1;
+#endif
switch (cm) {
case splashModeMono1:
@@ -435,7 +446,11 @@ SplashBlendFunc splashOutBlendFuncs[] =
class SplashOutFontFileID: public SplashFontFileID {
public:
- SplashOutFontFileID(Ref *rA) { r = *rA; substIdx = -1; }
+ SplashOutFontFileID(Ref *rA) {
+ r = *rA;
+ substIdx = -1;
+ oblique = 0;
+ }
~SplashOutFontFileID() {}
@@ -444,12 +459,15 @@ public:
((SplashOutFontFileID *)id)->r.gen == r.gen;
}
+ void setOblique(double obliqueA) { oblique = obliqueA; }
+ double getOblique() { return oblique; }
void setSubstIdx(int substIdxA) { substIdx = substIdxA; }
int getSubstIdx() { return substIdx; }
private:
Ref r;
+ double oblique;
int substIdx;
};
@@ -536,6 +554,10 @@ T3FontCache::~T3FontCache() {
struct T3GlyphStack {
Gushort code; // character code
+ GBool haveDx; // set after seeing a d0/d1 operator
+ GBool doNotCache; // set if we see a gsave/grestore before
+ // the d0/d1
+
//----- cache info
T3FontCache *cache; // font cache for the current font
T3FontCacheTag *cacheTag; // pointer to cache tag for the glyph
@@ -580,6 +602,7 @@ SplashOutputDev::SplashOutputDev(SplashC
bitmapRowPad = bitmapRowPadA;
bitmapTopDown = bitmapTopDownA;
bitmapUpsideDown = gFalse;
+ noComposite = gFalse;
allowAntialias = allowAntialiasA;
vectorAntialias = allowAntialias &&
globalParams->getVectorAntialias() &&
@@ -596,6 +619,7 @@ SplashOutputDev::SplashOutputDev(SplashC
colorMode != splashModeMono1, bitmapTopDown);
splash = new Splash(bitmap, vectorAntialias, &screenParams);
splash->setMinLineWidth(globalParams->getMinLineWidth());
+ splash->setStrokeAdjust(globalParams->getStrokeAdjust());
splash->clear(paperColor, 0);
fontEngine = NULL;
@@ -688,9 +712,6 @@ void SplashOutputDev::startDoc(XRef *xre
delete fontEngine;
}
fontEngine = new SplashFontEngine(
-#if HAVE_T1LIB_H
- globalParams->getEnableT1lib(),
-#endif
#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
globalParams->getEnableFreeType(),
globalParams->getDisableFreeTypeHinting()
@@ -777,18 +798,28 @@ void SplashOutputDev::startPage(int page
}
void SplashOutputDev::endPage() {
- if (colorMode != splashModeMono1) {
+ if (colorMode != splashModeMono1 && !noComposite) {
splash->compositeBackground(paperColor);
}
}
void SplashOutputDev::saveState(GfxState *state) {
splash->saveState();
+ if (t3GlyphStack && !t3GlyphStack->haveDx) {
+ t3GlyphStack->doNotCache = gTrue;
+ error(errSyntaxWarning, -1,
+ "Save (q) operator before d0/d1 in Type 3 glyph");
+ }
}
void SplashOutputDev::restoreState(GfxState *state) {
splash->restoreState();
needFontUpdate = gTrue;
+ if (t3GlyphStack && !t3GlyphStack->haveDx) {
+ t3GlyphStack->doNotCache = gTrue;
+ error(errSyntaxWarning, -1,
+ "Restore (Q) operator before d0/d1 in Type 3 glyph");
+ }
}
void SplashOutputDev::updateAll(GfxState *state) {
@@ -976,10 +1007,19 @@ void SplashOutputDev::setOverprintMask(G
if (overprintFlag && globalParams->getOverprintPreview()) {
mask = colorSpace->getOverprintMask();
+ // The OPM (overprintMode) setting is only relevant when the color
+ // space is DeviceCMYK or is "implicitly converted to DeviceCMYK".
+ // Per the PDF spec, this happens with ICCBased color spaces only
+ // if the profile matches the output device -- Acrobat's output
+ // preview mode does NOT honor OPM=1 for ICCBased CMYK color
+ // spaces. To change the behavior here, use:
+ // if (singleColor && overprintMode &&
+ // (colorSpace->getMode() == csDeviceCMYK ||
+ // (colorSpace->getMode() == csICCBased &&
+ // colorSpace->getNComps() == 4 &&
+ // <...the profile matches...>)))
if (singleColor && overprintMode &&
- (colorSpace->getMode() == csDeviceCMYK ||
- (colorSpace->getMode() == csICCBased &&
- colorSpace->getNComps() == 4))) {
+ colorSpace->getMode() == csDeviceCMYK) {
colorSpace->getCMYK(singleColor, &cmyk);
if (cmyk.c == 0) {
mask &= ~1;
@@ -1072,22 +1112,33 @@ void SplashOutputDev::doUpdateFont(GfxSt
FoFiTrueType *ff;
Ref embRef;
Object refObj, strObj;
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf;
+ FILE *extFontFile;
+#else
GString *tmpFileName, *fileName;
FILE *tmpFile;
+#endif
+ char blk[4096];
int *codeToGID;
CharCodeToUnicode *ctu;
double *textMat;
- double m11, m12, m21, m22, fontSize;
- double w, fontScaleMin, fontScaleAvg, fontScale;
+ double m11, m12, m21, m22, fontSize, oblique;
+ double fsx, fsy, w, fontScaleMin, fontScaleAvg, fontScale;
Gushort ww;
SplashCoord mat[4];
char *name;
Unicode uBuf[8];
- int c, substIdx, n, code, cmap, i;
+ int substIdx, n, code, cmap, i;
needFontUpdate = gFalse;
font = NULL;
+#if LOAD_FONTS_FROM_MEM
+ fontBuf = NULL;
+#else
tmpFileName = NULL;
+ fileName = NULL;
+#endif
substIdx = -1;
if (!(gfxFont = state->getFont())) {
@@ -1098,10 +1149,13 @@ void SplashOutputDev::doUpdateFont(GfxSt
goto err1;
}
- // sanity-check the font size - skip anything larger than 10 inches
+ // sanity-check the font size - skip anything larger than 20 inches
// (this avoids problems allocating memory for the font cache)
- if (state->getTransformedFontSize()
- > 10 * (state->getHDPI() + state->getVDPI())) {
+ state->textTransformDelta(state->getFontSize(), state->getFontSize(),
+ &fsx, &fsy);
+ state->transformDelta(fsx, fsy, &fsx, &fsy);
+ if (fabs(fsx) > 20 * state->getHDPI() ||
+ fabs(fsy) > 20 * state->getVDPI()) {
goto err1;
}
@@ -1112,7 +1166,6 @@ void SplashOutputDev::doUpdateFont(GfxSt
} else {
- fileName = NULL;
fontNum = 0;
if (!(fontLoc = gfxFont->locateFont(xref, gFalse))) {
@@ -1125,6 +1178,24 @@ void SplashOutputDev::doUpdateFont(GfxSt
// embedded font
if (fontLoc->locType == gfxFontLocEmbedded) {
gfxFont->getEmbeddedFontID(&embRef);
+#if LOAD_FONTS_FROM_MEM
+ fontBuf = new GString();
+ refObj.initRef(embRef.num, embRef.gen);
+ refObj.fetch(xref, &strObj);
+ refObj.free();
+ if (!strObj.isStream()) {
+ error(errSyntaxError, -1, "Embedded font object is wrong type");
+ strObj.free();
+ delete fontLoc;
+ goto err2;
+ }
+ strObj.streamReset();
+ while ((n = strObj.streamGetBlock(blk, sizeof(blk))) > 0) {
+ fontBuf->append(blk, n);
+ }
+ strObj.streamClose();
+ strObj.free();
+#else
if (!openTempFile(&tmpFileName, &tmpFile, "wb", NULL)) {
error(errIO, -1, "Couldn't create temporary font file");
delete fontLoc;
@@ -1141,21 +1212,39 @@ void SplashOutputDev::doUpdateFont(GfxSt
goto err2;
}
strObj.streamReset();
- while ((c = strObj.streamGetChar()) != EOF) {
- fputc(c, tmpFile);
+ while ((n = strObj.streamGetBlock(blk, sizeof(blk))) > 0) {
+ fwrite(blk, 1, n, tmpFile);
}
strObj.streamClose();
strObj.free();
fclose(tmpFile);
fileName = tmpFileName;
+#endif
// external font
} else { // gfxFontLocExternal
+#if LOAD_FONTS_FROM_MEM
+ if (!(extFontFile = fopen(fontLoc->path->getCString(), "rb"))) {
+ error(errSyntaxError, -1, "Couldn't open external font file '{0:t}'",
+ fontLoc->path);
+ delete fontLoc;
+ goto err2;
+ }
+ fontBuf = new GString();
+ while ((n = fread(blk, 1, sizeof(blk), extFontFile)) > 0) {
+ fontBuf->append(blk, n);
+ }
+ fclose(extFontFile);
+#else
fileName = fontLoc->path;
+#endif
fontNum = fontLoc->fontNum;
if (fontLoc->substIdx >= 0) {
id->setSubstIdx(fontLoc->substIdx);
}
+ if (fontLoc->oblique != 0) {
+ id->setOblique(fontLoc->oblique);
+ }
}
// load the font file
@@ -1163,8 +1252,12 @@ void SplashOutputDev::doUpdateFont(GfxSt
case fontType1:
if (!(fontFile = fontEngine->loadType1Font(
id,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
fileName->getCString(),
fileName == tmpFileName,
+#endif
(const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) {
error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
gfxFont->getName() ? gfxFont->getName()->getCString()
@@ -1176,8 +1269,12 @@ void SplashOutputDev::doUpdateFont(GfxSt
case fontType1C:
if (!(fontFile = fontEngine->loadType1CFont(
id,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
fileName->getCString(),
fileName == tmpFileName,
+#endif
(const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) {
error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
gfxFont->getName() ? gfxFont->getName()->getCString()
@@ -1189,8 +1286,12 @@ void SplashOutputDev::doUpdateFont(GfxSt
case fontType1COT:
if (!(fontFile = fontEngine->loadOpenTypeT1CFont(
id,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
fileName->getCString(),
fileName == tmpFileName,
+#endif
(const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) {
error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
gfxFont->getName() ? gfxFont->getName()->getCString()
@@ -1201,7 +1302,12 @@ void SplashOutputDev::doUpdateFont(GfxSt
break;
case fontTrueType:
case fontTrueTypeOT:
- if ((ff = FoFiTrueType::load(fileName->getCString()))) {
+#if LOAD_FONTS_FROM_MEM
+ if ((ff = FoFiTrueType::make(fontBuf->getCString(), fontBuf->getLength(),
+ fontNum))) {
+#else
+ if ((ff = FoFiTrueType::load(fileName->getCString(), fontNum))) {
+#endif
codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff);
n = 256;
delete ff;
@@ -1222,9 +1328,13 @@ void SplashOutputDev::doUpdateFont(GfxSt
}
if (!(fontFile = fontEngine->loadTrueTypeFont(
id,
- fileName->getCString(), fontNum,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
+ fileName->getCString(),
fileName == tmpFileName,
- codeToGID, n,
+#endif
+ fontNum, codeToGID, n,
gfxFont->getEmbeddedFontName()
? gfxFont->getEmbeddedFontName()->getCString()
: (char *)NULL))) {
@@ -1239,8 +1349,14 @@ void SplashOutputDev::doUpdateFont(GfxSt
case fontCIDType0C:
if (!(fontFile = fontEngine->loadCIDFont(
id,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf
+#else
fileName->getCString(),
- fileName == tmpFileName))) {
+ fileName == tmpFileName
+#endif
+ ))) {
+
error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
gfxFont->getName() ? gfxFont->getName()->getCString()
: "(unnamed)");
@@ -1260,8 +1376,12 @@ void SplashOutputDev::doUpdateFont(GfxSt
}
if (!(fontFile = fontEngine->loadOpenTypeCFFFont(
id,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
fileName->getCString(),
fileName == tmpFileName,
+#endif
codeToGID, n))) {
error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
gfxFont->getName() ? gfxFont->getName()->getCString()
@@ -1281,11 +1401,18 @@ void SplashOutputDev::doUpdateFont(GfxSt
memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(),
n * sizeof(int));
}
+ } else if (!globalParams->getMapExtTrueTypeFontsViaUnicode()) {
+ codeToGID = NULL;
+ n = 0;
} else {
// create a CID-to-GID mapping, via Unicode
if ((ctu = ((GfxCIDFont *)gfxFont)->getToUnicode())) {
- //~ this should use fontNum to load the correct font
- if ((ff = FoFiTrueType::load(fileName->getCString()))) {
+#if LOAD_FONTS_FROM_MEM
+ if ((ff = FoFiTrueType::make(fontBuf->getCString(),
+ fontBuf->getLength(), fontNum))) {
+#else
+ if ((ff = FoFiTrueType::load(fileName->getCString(), fontNum))) {
+#endif
// look for a Unicode cmap
for (cmap = 0; cmap < ff->getNumCmaps(); ++cmap) {
if ((ff->getCmapPlatform(cmap) == 3 &&
@@ -1296,7 +1423,11 @@ void SplashOutputDev::doUpdateFont(GfxSt
}
if (cmap < ff->getNumCmaps()) {
// map CID -> Unicode -> GID
- n = ctu->getLength();
+ if (ctu->isIdentity()) {
+ n = 65536;
+ } else {
+ n = ctu->getLength();
+ }
codeToGID = (int *)gmallocn(n, sizeof(int));
for (code = 0; code < n; ++code) {
if (ctu->mapToUnicode(code, uBuf, 8) > 0) {
@@ -1318,9 +1449,13 @@ void SplashOutputDev::doUpdateFont(GfxSt
}
if (!(fontFile = fontEngine->loadTrueTypeFont(
id,
- fileName->getCString(), fontNum,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
+ fileName->getCString(),
fileName == tmpFileName,
- codeToGID, n,
+#endif
+ fontNum, codeToGID, n,
gfxFont->getEmbeddedFontName()
? gfxFont->getEmbeddedFontName()->getCString()
: (char *)NULL))) {
@@ -1342,10 +1477,15 @@ void SplashOutputDev::doUpdateFont(GfxSt
// get the font matrix
textMat = state->getTextMat();
fontSize = state->getFontSize();
- m11 = textMat[0] * fontSize * state->getHorizScaling();
- m12 = textMat[1] * fontSize * state->getHorizScaling();
- m21 = textMat[2] * fontSize;
- m22 = textMat[3] * fontSize;
+ oblique = ((SplashOutFontFileID *)fontFile->getID())->getOblique();
+ m11 = state->getHorizScaling() * textMat[0];
+ m12 = state->getHorizScaling() * textMat[1];
+ m21 = oblique * m11 + textMat[2];
+ m22 = oblique * m12 + textMat[3];
+ m11 *= fontSize;
+ m12 *= fontSize;
+ m21 *= fontSize;
+ m22 *= fontSize;
// for substituted fonts: adjust the font matrix -- compare the
// widths of letters and digits (A-Z, a-z, 0-9) in the original font
@@ -1393,18 +1533,26 @@ void SplashOutputDev::doUpdateFont(GfxSt
mat[2] = m21; mat[3] = m22;
font = fontEngine->getFont(fontFile, mat, splash->getMatrix());
+#if !LOAD_FONTS_FROM_MEM
if (tmpFileName) {
delete tmpFileName;
}
+#endif
return;
err2:
delete id;
err1:
+#if LOAD_FONTS_FROM_MEM
+ if (fontBuf) {
+ delete fontBuf;
+ }
+#else
if (tmpFileName) {
unlink(tmpFileName->getCString());
delete tmpFileName;
}
+#endif
return;
}
@@ -1447,7 +1595,8 @@ void SplashOutputDev::eoFill(GfxState *s
delete path;
}
-void SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
+void SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx,
+ Object *strRef,
int paintType, Dict *resDict,
double *mat, double *bbox,
int x0, int y0, int x1, int y1,
@@ -1529,7 +1678,7 @@ void SplashOutputDev::tilingPatternFill(
ya = y * yStep;
mat1[4] = xa * mat[0] + ya * mat[2] + mat[4];
mat1[5] = xa * mat[1] + ya * mat[3] + mat[5];
- gfx->drawForm(str, resDict, mat1, bbox);
+ gfx->drawForm(strRef, resDict, mat1, bbox);
}
}
return;
@@ -1542,6 +1691,7 @@ void SplashOutputDev::tilingPatternFill(
colorMode, gTrue, bitmapTopDown);
splash = new Splash(bitmap, vectorAntialias, origSplash->getScreen());
splash->setMinLineWidth(globalParams->getMinLineWidth());
+ splash->setStrokeAdjust(globalParams->getStrokeAdjust());
for (i = 0; i < splashMaxColorComps; ++i) {
color[i] = 0;
}
@@ -1556,7 +1706,7 @@ void SplashOutputDev::tilingPatternFill(
// render the tile
state->shiftCTM(-tileX0, -tileY0);
updateCTM(state, 0, 0, 0, 0, 0, 0);
- gfx->drawForm(str, resDict, mat, bbox);
+ gfx->drawForm(strRef, resDict, mat, bbox);
state->shiftCTM(tileX0, tileY0);
updateCTM(state, 0, 0, 0, 0, 0, 0);
@@ -1889,8 +2039,8 @@ GBool SplashOutputDev::beginType3Char(Gf
t3GlyphStack->cache = t3Font;
t3GlyphStack->cacheTag = NULL;
t3GlyphStack->cacheData = NULL;
-
- haveT3Dx = gFalse;
+ t3GlyphStack->haveDx = gFalse;
+ t3GlyphStack->doNotCache = gFalse;
return gFalse;
}
@@ -1920,7 +2070,7 @@ void SplashOutputDev::endType3Char(GfxSt
}
void SplashOutputDev::type3D0(GfxState *state, double wx, double wy) {
- haveT3Dx = gTrue;
+ t3GlyphStack->haveDx = gTrue;
}
void SplashOutputDev::type3D1(GfxState *state, double wx, double wy,
@@ -1932,10 +2082,14 @@ void SplashOutputDev::type3D1(GfxState *
int i, j;
// ignore multiple d0/d1 operators
- if (haveT3Dx) {
+ if (t3GlyphStack->haveDx) {
+ return;
+ }
+ t3GlyphStack->haveDx = gTrue;
+ // don't cache if we got a gsave/grestore before the d1
+ if (t3GlyphStack->doNotCache) {
return;
}
- haveT3Dx = gTrue;
t3Font = t3GlyphStack->cache;
@@ -2026,6 +2180,7 @@ void SplashOutputDev::type3D1(GfxState *
color[0] = 0xff;
}
splash->setMinLineWidth(globalParams->getMinLineWidth());
+ splash->setStrokeAdjust(t3GlyphStack->origSplash->getStrokeAdjust());
splash->setFillPattern(new SplashSolidColor(color));
splash->setStrokePattern(new SplashSolidColor(color));
//~ this should copy other state from t3GlyphStack->origSplash?
@@ -2072,10 +2227,9 @@ GBool SplashOutputDev::imageMaskSrc(void
SplashColorPtr q;
int x;
- if (imgMaskData->y == imgMaskData->height) {
- return gFalse;
- }
- if (!(p = imgMaskData->imgStr->getLine())) {
+ if (imgMaskData->y == imgMaskData->height ||
+ !(p = imgMaskData->imgStr->getLine())) {
+ memset(line, 0, imgMaskData->width);
return gFalse;
}
for (x = 0, q = line; x < imgMaskData->width; ++x) {
@@ -2087,7 +2241,7 @@ GBool SplashOutputDev::imageMaskSrc(void
void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
int width, int height, GBool invert,
- GBool inlineImg) {
+ GBool inlineImg, GBool interpolate) {
double *ctm;
SplashCoord mat[6];
SplashOutImageMaskData imgMaskData;
@@ -2106,6 +2260,8 @@ void SplashOutputDev::drawImageMask(GfxS
mat[4] = ctm[2] + ctm[4];
mat[5] = ctm[3] + ctm[5];
+ reduceImageResolution(str, ctm, &width, &height);
+
imgMaskData.imgStr = new ImageStream(str, width, 1, 1);
imgMaskData.imgStr->reset();
imgMaskData.invert = invert ? 0 : 1;
@@ -2114,7 +2270,7 @@ void SplashOutputDev::drawImageMask(GfxS
imgMaskData.y = 0;
splash->fillImageMask(&imageMaskSrc, &imgMaskData, width, height, mat,
- t3GlyphStack != NULL);
+ t3GlyphStack != NULL, interpolate);
if (inlineImg) {
while (imgMaskData.y < height) {
imgMaskData.imgStr->getLine();
@@ -2130,7 +2286,8 @@ void SplashOutputDev::setSoftMaskFromIma
Object *ref, Stream *str,
int width, int height,
GBool invert,
- GBool inlineImg) {
+ GBool inlineImg,
+ GBool interpolate) {
double *ctm;
SplashCoord mat[6];
SplashOutImageMaskData imgMaskData;
@@ -2145,6 +2302,7 @@ void SplashOutputDev::setSoftMaskFromIma
mat[3] = -ctm[3];
mat[4] = ctm[2] + ctm[4];
mat[5] = ctm[3] + ctm[5];
+ reduceImageResolution(str, ctm, &width, &height);
imgMaskData.imgStr = new ImageStream(str, width, 1, 1);
imgMaskData.imgStr->reset();
imgMaskData.invert = invert ? 0 : 1;
@@ -2154,12 +2312,12 @@ void SplashOutputDev::setSoftMaskFromIma
maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(),
1, splashModeMono8, gFalse);
maskSplash = new Splash(maskBitmap, gTrue);
- maskColor[0] = 0;
- maskSplash->clear(maskColor);
+ maskSplash->setStrokeAdjust(globalParams->getStrokeAdjust());
+ clearMaskRegion(state, maskSplash, 0, 0, 1, 1);
maskColor[0] = 0xff;
maskSplash->setFillPattern(new SplashSolidColor(maskColor));
maskSplash->fillImageMask(&imageMaskSrc, &imgMaskData,
- width, height, mat, gFalse);
+ width, height, mat, gFalse, interpolate);
delete imgMaskData.imgStr;
str->close();
delete maskSplash;
@@ -2180,17 +2338,12 @@ GBool SplashOutputDev::imageSrc(void *da
SplashOutImageData *imgData = (SplashOutImageData *)data;
Guchar *p;
SplashColorPtr q, col;
- GfxRGB rgb;
- GfxGray gray;
-#if SPLASH_CMYK
- GfxCMYK cmyk;
-#endif
int nComps, x;
- if (imgData->y == imgData->height) {
- return gFalse;
- }
- if (!(p = imgData->imgStr->getLine())) {
+ if (imgData->y == imgData->height ||
+ !(p = imgData->imgStr->getLine())) {
+ memset(colorLine, 0,
+ imgData->width * splashColorModeNComps[imgData->colorMode]);
return gFalse;
}
@@ -2229,29 +2382,15 @@ GBool SplashOutputDev::imageSrc(void *da
switch (imgData->colorMode) {
case splashModeMono1:
case splashModeMono8:
- for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) {
- imgData->colorMap->getGray(p, &gray);
- *q++ = colToByte(gray);
- }
+ imgData->colorMap->getGrayByteLine(p, colorLine, imgData->width);
break;
case splashModeRGB8:
case splashModeBGR8:
- for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) {
- imgData->colorMap->getRGB(p, &rgb);
- *q++ = colToByte(rgb.r);
- *q++ = colToByte(rgb.g);
- *q++ = colToByte(rgb.b);
- }
+ imgData->colorMap->getRGBByteLine(p, colorLine, imgData->width);
break;
#if SPLASH_CMYK
case splashModeCMYK8:
- for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) {
- imgData->colorMap->getCMYK(p, &cmyk);
- *q++ = colToByte(cmyk.c);
- *q++ = colToByte(cmyk.m);
- *q++ = colToByte(cmyk.y);
- *q++ = colToByte(cmyk.k);
- }
+ imgData->colorMap->getCMYKByteLine(p, colorLine, imgData->width);
break;
#endif
}
@@ -2274,10 +2413,11 @@ GBool SplashOutputDev::alphaImageSrc(voi
Guchar alpha;
int nComps, x, i;
- if (imgData->y == imgData->height) {
- return gFalse;
- }
- if (!(p = imgData->imgStr->getLine())) {
+ if (imgData->y == imgData->height ||
+ !(p = imgData->imgStr->getLine())) {
+ memset(colorLine, 0,
+ imgData->width * splashColorModeNComps[imgData->colorMode]);
+ memset(alphaLine, 0, imgData->width);
return gFalse;
}
@@ -2353,7 +2493,8 @@ GBool SplashOutputDev::alphaImageSrc(voi
void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
- int *maskColors, GBool inlineImg) {
+ int *maskColors, GBool inlineImg,
+ GBool interpolate) {
double *ctm;
SplashCoord mat[6];
SplashOutImageData imgData;
@@ -2378,6 +2519,8 @@ void SplashOutputDev::drawImage(GfxState
mat[4] = ctm[2] + ctm[4];
mat[5] = ctm[3] + ctm[5];
+ reduceImageResolution(str, ctm, &width, &height);
+
imgData.imgStr = new ImageStream(str, width,
colorMap->getNumPixelComps(),
colorMap->getBits());
@@ -2433,12 +2576,14 @@ void SplashOutputDev::drawImage(GfxState
if (colorMode == splashModeMono1) {
srcMode = splashModeMono8;
+ } else if (colorMode == splashModeBGR8) {
+ srcMode = splashModeRGB8;
} else {
srcMode = colorMode;
}
src = maskColors ? &alphaImageSrc : &imageSrc;
splash->drawImage(src, &imgData, srcMode, maskColors ? gTrue : gFalse,
- width, height, mat);
+ width, height, mat, interpolate);
if (inlineImg) {
while (imgData.y < height) {
imgData.imgStr->getLine();
@@ -2470,15 +2615,17 @@ GBool SplashOutputDev::maskedImageSrc(vo
#if SPLASH_CMYK
GfxCMYK cmyk;
#endif
+ static Guchar bitToByte[2] = {0x00, 0xff};
Guchar alpha;
Guchar *maskPtr;
- int maskBit;
+ int maskShift;
int nComps, x;
- if (imgData->y == imgData->height) {
- return gFalse;
- }
- if (!(p = imgData->imgStr->getLine())) {
+ if (imgData->y == imgData->height ||
+ !(p = imgData->imgStr->getLine())) {
+ memset(colorLine, 0,
+ imgData->width * splashColorModeNComps[imgData->colorMode]);
+ memset(alphaLine, 0, imgData->width);
return gFalse;
}
@@ -2486,15 +2633,13 @@ GBool SplashOutputDev::maskedImageSrc(vo
maskPtr = imgData->mask->getDataPtr() +
imgData->y * imgData->mask->getRowSize();
- maskBit = 0x80;
+ maskShift = 7;
for (x = 0, q = colorLine, aq = alphaLine;
x < imgData->width;
++x, p += nComps) {
- alpha = (*maskPtr & maskBit) ? 0xff : 0x00;
- if (!(maskBit >>= 1)) {
- ++maskPtr;
- maskBit = 0x80;
- }
+ alpha = bitToByte[(*maskPtr >> maskShift) & 1];
+ maskPtr += (8 - maskShift) >> 3;
+ maskShift = (maskShift - 1) & 7;
if (imgData->lookup) {
switch (imgData->colorMode) {
case splashModeMono1:
@@ -2555,7 +2700,8 @@ void SplashOutputDev::drawMaskedImage(Gf
Stream *str, int width, int height,
GfxImageColorMap *colorMap,
Stream *maskStr, int maskWidth,
- int maskHeight, GBool maskInvert) {
+ int maskHeight, GBool maskInvert,
+ GBool interpolate) {
GfxImageColorMap *maskColorMap;
Object maskDecode, decodeLow, decodeHigh;
double *ctm;
@@ -2577,6 +2723,10 @@ void SplashOutputDev::drawMaskedImage(Gf
setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(),
state->getOverprintMode(), NULL);
+ ctm = state->getCTM();
+ reduceImageResolution(str, ctm, &width, &height);
+ reduceImageResolution(maskStr, ctm, &maskWidth, &maskHeight);
+
// If the mask is higher resolution than the image, use
// drawSoftMaskedImage() instead.
if (maskWidth > width || maskHeight > height) {
@@ -2589,7 +2739,8 @@ void SplashOutputDev::drawMaskedImage(Gf
new GfxDeviceGrayColorSpace());
maskDecode.free();
drawSoftMaskedImage(state, ref, str, width, height, colorMap,
- maskStr, maskWidth, maskHeight, maskColorMap);
+ maskStr, maskWidth, maskHeight, maskColorMap,
+ interpolate);
delete maskColorMap;
} else {
@@ -2610,19 +2761,20 @@ void SplashOutputDev::drawMaskedImage(Gf
imgMaskData.y = 0;
maskBitmap = new SplashBitmap(width, height, 1, splashModeMono1, gFalse);
maskSplash = new Splash(maskBitmap, gFalse);
+ maskSplash->setStrokeAdjust(globalParams->getStrokeAdjust());
maskColor[0] = 0;
maskSplash->clear(maskColor);
maskColor[0] = 0xff;
maskSplash->setFillPattern(new SplashSolidColor(maskColor));
+ // use "glyph mode" here to get the correct scaled size
maskSplash->fillImageMask(&imageMaskSrc, &imgMaskData,
- maskWidth, maskHeight, mat, gFalse);
+ maskWidth, maskHeight, mat, gTrue, interpolate);
delete imgMaskData.imgStr;
maskStr->close();
delete maskSplash;
//----- draw the source image
- ctm = state->getCTM();
mat[0] = ctm[0];
mat[1] = ctm[1];
mat[2] = -ctm[2];
@@ -2685,11 +2837,13 @@ void SplashOutputDev::drawMaskedImage(Gf
if (colorMode == splashModeMono1) {
srcMode = splashModeMono8;
+ } else if (colorMode == splashModeBGR8) {
+ srcMode = splashModeRGB8;
} else {
srcMode = colorMode;
}
splash->drawImage(&maskedImageSrc, &imgData, srcMode, gTrue,
- width, height, mat);
+ width, height, mat, interpolate);
delete maskBitmap;
gfree(imgData.lookup);
@@ -2703,7 +2857,8 @@ void SplashOutputDev::drawSoftMaskedImag
GfxImageColorMap *colorMap,
Stream *maskStr,
int maskWidth, int maskHeight,
- GfxImageColorMap *maskColorMap) {
+ GfxImageColorMap *maskColorMap,
+ GBool interpolate) {
double *ctm;
SplashCoord mat[6];
SplashOutImageData imgData;
@@ -2711,7 +2866,6 @@ void SplashOutputDev::drawSoftMaskedImag
SplashColorMode srcMode;
SplashBitmap *maskBitmap;
Splash *maskSplash;
- SplashColor maskColor;
GfxGray gray;
GfxRGB rgb;
#if SPLASH_CMYK
@@ -2731,6 +2885,9 @@ void SplashOutputDev::drawSoftMaskedImag
mat[4] = ctm[2] + ctm[4];
mat[5] = ctm[3] + ctm[5];
+ reduceImageResolution(str, ctm, &width, &height);
+ reduceImageResolution(maskStr, ctm, &maskWidth, &maskHeight);
+
//----- set up the soft mask
imgMaskData.imgStr = new ImageStream(maskStr, maskWidth,
@@ -2753,10 +2910,10 @@ void SplashOutputDev::drawSoftMaskedImag
maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(),
1, splashModeMono8, gFalse);
maskSplash = new Splash(maskBitmap, vectorAntialias);
- maskColor[0] = 0;
- maskSplash->clear(maskColor);
+ maskSplash->setStrokeAdjust(globalParams->getStrokeAdjust());
+ clearMaskRegion(state, maskSplash, 0, 0, 1, 1);
maskSplash->drawImage(&imageSrc, &imgMaskData, splashModeMono8, gFalse,
- maskWidth, maskHeight, mat);
+ maskWidth, maskHeight, mat, interpolate);
delete imgMaskData.imgStr;
maskStr->close();
gfree(imgMaskData.lookup);
@@ -2820,10 +2977,13 @@ void SplashOutputDev::drawSoftMaskedImag
if (colorMode == splashModeMono1) {
srcMode = splashModeMono8;
+ } else if (colorMode == splashModeBGR8) {
+ srcMode = splashModeRGB8;
} else {
srcMode = colorMode;
}
- splash->drawImage(&imageSrc, &imgData, srcMode, gFalse, width, height, mat);
+ splash->drawImage(&imageSrc, &imgData, srcMode, gFalse, width, height, mat,
+ interpolate);
splash->setSoftMask(NULL);
gfree(imgData.lookup);
@@ -2831,6 +2991,98 @@ void SplashOutputDev::drawSoftMaskedImag
str->close();
}
+void SplashOutputDev::reduceImageResolution(Stream *str, double *ctm,
+ int *width, int *height) {
+ double sw, sh;
+ int reduction;
+
+ if (str->getKind() == strJPX &&
+ *width * *height > 10000000) {
+ sw = (double)*width / (fabs(ctm[2]) + fabs(ctm[3]));
+ sh = (double)*height / (fabs(ctm[0]) + fabs(ctm[1]));
+ if (sw > 8 && sh > 8) {
+ reduction = 3;
+ } else if (sw > 4 && sh > 4) {
+ reduction = 2;
+ } else if (sw > 2 && sh > 2) {
+ reduction = 1;
+ } else {
+ reduction = 0;
+ }
+ if (reduction > 0) {
+ ((JPXStream *)str)->reduceResolution(reduction);
+ *width >>= reduction;
+ *height >>= reduction;
+ }
+ }
+}
+
+void SplashOutputDev::clearMaskRegion(GfxState *state,
+ Splash *maskSplash,
+ double xMin, double yMin,
+ double xMax, double yMax) {
+ SplashBitmap *maskBitmap;
+ double xxMin, yyMin, xxMax, yyMax, xx, yy;
+ int xxMinI, yyMinI, xxMaxI, yyMaxI, y, n;
+ Guchar *p;
+
+ maskBitmap = maskSplash->getBitmap();
+ xxMin = maskBitmap->getWidth();
+ xxMax = 0;
+ yyMin = maskBitmap->getHeight();
+ yyMax = 0;
+ state->transform(xMin, yMin, &xx, &yy);
+ if (xx < xxMin) { xxMin = xx; }
+ if (xx > xxMax) { xxMax = xx; }
+ if (yy < yyMin) { yyMin = yy; }
+ if (yy > yyMax) { yyMax = yy; }
+ state->transform(xMin, yMax, &xx, &yy);
+ if (xx < xxMin) { xxMin = xx; }
+ if (xx > xxMax) { xxMax = xx; }
+ if (yy < yyMin) { yyMin = yy; }
+ if (yy > yyMax) { yyMax = yy; }
+ state->transform(xMax, yMin, &xx, &yy);
+ if (xx < xxMin) { xxMin = xx; }
+ if (xx > xxMax) { xxMax = xx; }
+ if (yy < yyMin) { yyMin = yy; }
+ if (yy > yyMax) { yyMax = yy; }
+ state->transform(xMax, yMax, &xx, &yy);
+ if (xx < xxMin) { xxMin = xx; }
+ if (xx > xxMax) { xxMax = xx; }
+ if (yy < yyMin) { yyMin = yy; }
+ if (yy > yyMax) { yyMax = yy; }
+ xxMinI = (int)floor(xxMin);
+ if (xxMinI < 0) {
+ xxMinI = 0;
+ }
+ xxMaxI = (int)ceil(xxMax);
+ if (xxMaxI > maskBitmap->getWidth()) {
+ xxMaxI = maskBitmap->getWidth();
+ }
+ yyMinI = (int)floor(yyMin);
+ if (yyMinI < 0) {
+ yyMinI = 0;
+ }
+ yyMaxI = (int)ceil(yyMax);
+ if (yyMaxI > maskBitmap->getHeight()) {
+ yyMaxI = maskBitmap->getHeight();
+ }
+ p = maskBitmap->getDataPtr() + yyMinI * maskBitmap->getRowSize();
+ if (maskBitmap->getMode() == splashModeMono1) {
+ n = (xxMaxI + 7) / 8 - xxMinI / 8;
+ p += xxMinI / 8;
+ } else {
+ n = xxMaxI - xxMinI;
+ p += xxMinI;
+ }
+ if (xxMaxI > xxMinI) {
+ for (y = yyMinI; y < yyMaxI; ++y) {
+ memset(p, 0, n);
+ p += maskBitmap->getRowSize();
+ }
+ }
+}
+
void SplashOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
GfxColorSpace *blendingColorSpace,
GBool isolated, GBool knockout,
@@ -2921,7 +3173,7 @@ void SplashOutputDev::beginTransparencyG
//~ not yet for transparency groups
// switch to the blending color space
- if (forSoftMask && isolated && blendingColorSpace) {
+ if (forSoftMask && isolated && !knockout && blendingColorSpace) {
if (blendingColorSpace->getMode() == csDeviceGray ||
blendingColorSpace->getMode() == csCalGray ||
(blendingColorSpace->getMode() == csICCBased &&
@@ -2948,6 +3200,7 @@ void SplashOutputDev::beginTransparencyG
splash = new Splash(bitmap, vectorAntialias,
transpGroup->origSplash->getScreen());
splash->setMinLineWidth(globalParams->getMinLineWidth());
+ splash->setStrokeAdjust(globalParams->getStrokeAdjust());
//~ Acrobat apparently copies at least the fill and stroke colors, and
//~ maybe other state(?) -- but not the clipping path (and not sure
//~ what else)
@@ -2962,8 +3215,9 @@ void SplashOutputDev::beginTransparencyG
splash->clear(color, 0);
} else {
splash->blitTransparent(transpGroup->origBitmap, tx, ty, 0, 0, w, h);
- splash->setInNonIsolatedGroup(transpGroup->origBitmap, tx, ty);
}
+ splash->setInTransparencyGroup(transpGroup->origBitmap, tx, ty,
+ !isolated, knockout);
transpGroup->tBitmap = bitmap;
state->shiftCTM(-tx, -ty);
updateCTM(state, 0, 0, 0, 0, 0, 0);
@@ -3022,7 +3276,7 @@ void SplashOutputDev::setSoftMask(GfxSta
#if SPLASH_CMYK
GfxCMYK cmyk;
#endif
- double lum, lum2;
+ double backdrop, backdrop2, lum, lum2;
int tx, ty, x, y;
tx = transpGroupStack->tx;
@@ -3030,11 +3284,13 @@ void SplashOutputDev::setSoftMask(GfxSta
tBitmap = transpGroupStack->tBitmap;
// composite with backdrop color
+ backdrop = 0;
if (!alpha && tBitmap->getMode() != splashModeMono1) {
//~ need to correctly handle the case where no blending color
//~ space is given
tSplash = new Splash(tBitmap, vectorAntialias,
transpGroupStack->origSplash->getScreen());
+ tSplash->setStrokeAdjust(globalParams->getStrokeAdjust());
if (transpGroupStack->blendingColorSpace) {
switch (tBitmap->getMode()) {
case splashModeMono1:
@@ -3042,12 +3298,16 @@ void SplashOutputDev::setSoftMask(GfxSta
break;
case splashModeMono8:
transpGroupStack->blendingColorSpace->getGray(backdropColor, &gray);
+ backdrop = colToDbl(gray);
color[0] = colToByte(gray);
tSplash->compositeBackground(color);
break;
case splashModeRGB8:
case splashModeBGR8:
transpGroupStack->blendingColorSpace->getRGB(backdropColor, &rgb);
+ backdrop = 0.3 * colToDbl(rgb.r) +
+ 0.59 * colToDbl(rgb.g) +
+ 0.11 * colToDbl(rgb.b);
color[0] = colToByte(rgb.r);
color[1] = colToByte(rgb.g);
color[2] = colToByte(rgb.b);
@@ -3056,6 +3316,13 @@ void SplashOutputDev::setSoftMask(GfxSta
#if SPLASH_CMYK
case splashModeCMYK8:
transpGroupStack->blendingColorSpace->getCMYK(backdropColor, &cmyk);
+ backdrop = (1 - colToDbl(cmyk.k))
+ - 0.3 * colToDbl(cmyk.c)
+ - 0.59 * colToDbl(cmyk.m)
+ - 0.11 * colToDbl(cmyk.y);
+ if (backdrop < 0) {
+ backdrop = 0;
+ }
color[0] = colToByte(cmyk.c);
color[1] = colToByte(cmyk.m);
color[2] = colToByte(cmyk.y);
@@ -3067,10 +3334,15 @@ void SplashOutputDev::setSoftMask(GfxSta
delete tSplash;
}
}
+ if (transferFunc) {
+ transferFunc->transform(&backdrop, &backdrop2);
+ } else {
+ backdrop2 = backdrop;
+ }
softMask = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(),
1, splashModeMono8, gFalse);
- memset(softMask->getDataPtr(), 0,
+ memset(softMask->getDataPtr(), (int)(backdrop2 * 255.0 + 0.5),
softMask->getRowSize() * softMask->getHeight());
if (tx < softMask->getWidth() && ty < softMask->getHeight()) {
p = softMask->getDataPtr() + ty * softMask->getRowSize() + tx;
@@ -3198,12 +3470,19 @@ SplashFont *SplashOutputDev::getFont(GSt
Ref ref;
SplashOutFontFileID *id;
GfxFontLoc *fontLoc;
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf;
+ FILE *extFontFile;
+ char blk[4096];
+ int n;
+#endif
SplashFontFile *fontFile;
SplashFont *fontObj;
FoFiTrueType *ff;
int *codeToGID;
Unicode u;
SplashCoord textMat[4];
+ SplashCoord oblique;
int cmap, i;
for (i = 0; i < nBuiltinFonts; ++i) {
@@ -3227,11 +3506,40 @@ SplashFont *SplashOutputDev::getFont(GSt
if (!(fontLoc = GfxFont::locateBase14Font(name))) {
return NULL;
}
+#if LOAD_FONTS_FROM_MEM
+ fontBuf = NULL;
+ if (fontLoc->fontType == fontType1 ||
+ fontLoc->fontType == fontTrueType) {
+ if (!(extFontFile = fopen(fontLoc->path->getCString(), "rb"))) {
+ delete fontLoc;
+ delete id;
+ return NULL;
+ }
+ fontBuf = new GString();
+ while ((n = fread(blk, 1, sizeof(blk), extFontFile)) > 0) {
+ fontBuf->append(blk, n);
+ }
+ fclose(extFontFile);
+ }
+#endif
if (fontLoc->fontType == fontType1) {
- fontFile = fontEngine->loadType1Font(id, fontLoc->path->getCString(),
- gFalse, winAnsiEncoding);
+ fontFile = fontEngine->loadType1Font(id,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
+ fontLoc->path->getCString(),
+ gFalse,
+#endif
+ winAnsiEncoding);
} else if (fontLoc->fontType == fontTrueType) {
- if (!(ff = FoFiTrueType::load(fontLoc->path->getCString()))) {
+#if LOAD_FONTS_FROM_MEM
+ if (!(ff = FoFiTrueType::make(fontBuf->getCString(),
+ fontBuf->getLength(),
+ fontLoc->fontNum))) {
+#else
+ if (!(ff = FoFiTrueType::load(fontLoc->path->getCString(),
+ fontLoc->fontNum))) {
+#endif
delete fontLoc;
delete id;
return NULL;
@@ -3259,9 +3567,14 @@ SplashFont *SplashOutputDev::getFont(GSt
}
delete ff;
fontFile = fontEngine->loadTrueTypeFont(id,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
fontLoc->path->getCString(),
+ gFalse,
+#endif
fontLoc->fontNum,
- gFalse, codeToGID, 256, NULL);
+ codeToGID, 256, NULL);
} else {
delete fontLoc;
delete id;
@@ -3274,10 +3587,12 @@ SplashFont *SplashOutputDev::getFont(GSt
}
// 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 -uNrp 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 @@ public:
virtual void stroke(GfxState *state);
virtual void fill(GfxState *state);
virtual void eoFill(GfxState *state);
- virtual void tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
+ virtual void tilingPatternFill(GfxState *state, Gfx *gfx, Object *strRef,
int paintType, Dict *resDict,
double *mat, double *bbox,
int x0, int y0, int x1, int y1,
@@ -135,25 +135,26 @@ public:
//----- image drawing
virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
int width, int height, GBool invert,
- GBool inlineImg);
+ GBool inlineImg, GBool interpolate);
virtual void setSoftMaskFromImageMask(GfxState *state,
Object *ref, Stream *str,
int width, int height, GBool invert,
- GBool inlineImg);
+ GBool inlineImg, GBool interpolate);
virtual void drawImage(GfxState *state, Object *ref, Stream *str,
int width, int height, GfxImageColorMap *colorMap,
- int *maskColors, GBool inlineImg);
+ int *maskColors, GBool inlineImg, GBool interpolate);
virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
Stream *maskStr, int maskWidth, int maskHeight,
- GBool maskInvert);
+ GBool maskInvert, GBool interpolate);
virtual void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
Stream *maskStr,
int maskWidth, int maskHeight,
- GfxImageColorMap *maskColorMap);
+ GfxImageColorMap *maskColorMap,
+ GBool interpolate);
//----- Type 3 font operators
virtual void type3D0(GfxState *state, double wx, double wy);
@@ -194,6 +195,10 @@ public:
// for Windows BMP files).
void setBitmapUpsideDown(GBool f) { bitmapUpsideDown = f; }
+ // Setting this to true disables the final composite (with the
+ // opaque paper color), resulting in transparent output.
+ void setNoComposite(GBool f) { noComposite = f; }
+
// Get the Splash object.
Splash *getSplash() { return splash; }
@@ -245,11 +250,18 @@ private:
Guchar *alphaLine);
static GBool maskedImageSrc(void *data, SplashColorPtr line,
Guchar *alphaLine);
+ void reduceImageResolution(Stream *str, double *mat,
+ int *width, int *height);
+ void clearMaskRegion(GfxState *state,
+ Splash *maskSplash,
+ double xMin, double yMin,
+ double xMax, double yMax);
SplashColorMode colorMode;
int bitmapRowPad;
GBool bitmapTopDown;
GBool bitmapUpsideDown;
+ GBool noComposite;
GBool allowAntialias;
GBool vectorAntialias;
GBool reverseVideo; // reverse video mode
@@ -268,7 +280,6 @@ private:
t3FontCache[splashOutT3FontCacheSize];
int nT3Fonts; // number of valid entries in t3FontCache
T3GlyphStack *t3GlyphStack; // Type 3 glyph context stack
- GBool haveT3Dx; // set after seeing a d0/d1 operator
SplashFont *font; // current font
GBool needFontUpdate; // set when the font needs to be updated
diff -uNrp 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
@@ -98,11 +98,29 @@ char *Stream::getLine(char *buf, int siz
return buf;
}
+Guint Stream::discardChars(Guint n) {
+ char buf[4096];
+ Guint count, i, j;
+
+ count = 0;
+ while (count < n) {
+ if ((i = n - count) > sizeof(buf)) {
+ i = (Guint)sizeof(buf);
+ }
+ j = (Guint)getBlock(buf, (int)i);
+ count += j;
+ if (j != i) {
+ break;
+ }
+ }
+ return count;
+}
+
GString *Stream::getPSFilter(int psLevel, const char *indent) {
return new GString();
}
-Stream *Stream::addFilters(Object *dict) {
+Stream *Stream::addFilters(Object *dict, int recursion) {
Object obj, obj2;
Object params, params2;
Stream *str;
@@ -120,7 +138,7 @@ Stream *Stream::addFilters(Object *dict)
dict->dictLookup("DP", ¶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 @@ Stream *Stream::addFilters(Object *dict)
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 @@ Stream *Stream::addFilters(Object *dict)
return str;
}
-Stream *Stream::makeFilter(char *name, Stream *str, Object *params) {
+Stream *Stream::makeFilter(char *name, Stream *str, Object *params,
+ int recursion) {
int pred; // parameters
int colors;
int bits;
@@ -168,23 +187,23 @@ Stream *Stream::makeFilter(char *name, S
bits = 8;
early = 1;
if (params->isDict()) {
- params->dictLookup("Predictor", &obj);
+ params->dictLookup("Predictor", &obj, recursion);
if (obj.isInt())
pred = obj.getInt();
obj.free();
- params->dictLookup("Columns", &obj);
+ params->dictLookup("Columns", &obj, recursion);
if (obj.isInt())
columns = obj.getInt();
obj.free();
- params->dictLookup("Colors", &obj);
+ params->dictLookup("Colors", &obj, recursion);
if (obj.isInt())
colors = obj.getInt();
obj.free();
- params->dictLookup("BitsPerComponent", &obj);
+ params->dictLookup("BitsPerComponent", &obj, recursion);
if (obj.isInt())
bits = obj.getInt();
obj.free();
- params->dictLookup("EarlyChange", &obj);
+ params->dictLookup("EarlyChange", &obj, recursion);
if (obj.isInt())
early = obj.getInt();
obj.free();
@@ -201,37 +220,37 @@ Stream *Stream::makeFilter(char *name, S
endOfBlock = gTrue;
black = gFalse;
if (params->isDict()) {
- params->dictLookup("K", &obj);
+ params->dictLookup("K", &obj, recursion);
if (obj.isInt()) {
encoding = obj.getInt();
}
obj.free();
- params->dictLookup("EndOfLine", &obj);
+ params->dictLookup("EndOfLine", &obj, recursion);
if (obj.isBool()) {
endOfLine = obj.getBool();
}
obj.free();
- params->dictLookup("EncodedByteAlign", &obj);
+ params->dictLookup("EncodedByteAlign", &obj, recursion);
if (obj.isBool()) {
byteAlign = obj.getBool();
}
obj.free();
- params->dictLookup("Columns", &obj);
+ params->dictLookup("Columns", &obj, recursion);
if (obj.isInt()) {
columns = obj.getInt();
}
obj.free();
- params->dictLookup("Rows", &obj);
+ params->dictLookup("Rows", &obj, recursion);
if (obj.isInt()) {
rows = obj.getInt();
}
obj.free();
- params->dictLookup("EndOfBlock", &obj);
+ params->dictLookup("EndOfBlock", &obj, recursion);
if (obj.isBool()) {
endOfBlock = obj.getBool();
}
obj.free();
- params->dictLookup("BlackIs1", &obj);
+ params->dictLookup("BlackIs1", &obj, recursion);
if (obj.isBool()) {
black = obj.getBool();
}
@@ -242,7 +261,7 @@ Stream *Stream::makeFilter(char *name, S
} else if (!strcmp(name, "DCTDecode") || !strcmp(name, "DCT")) {
colorXform = -1;
if (params->isDict()) {
- if (params->dictLookup("ColorTransform", &obj)->isInt()) {
+ if (params->dictLookup("ColorTransform", &obj, recursion)->isInt()) {
colorXform = obj.getInt();
}
obj.free();
@@ -254,19 +273,19 @@ Stream *Stream::makeFilter(char *name, S
colors = 1;
bits = 8;
if (params->isDict()) {
- params->dictLookup("Predictor", &obj);
+ params->dictLookup("Predictor", &obj, recursion);
if (obj.isInt())
pred = obj.getInt();
obj.free();
- params->dictLookup("Columns", &obj);
+ params->dictLookup("Columns", &obj, recursion);
if (obj.isInt())
columns = obj.getInt();
obj.free();
- params->dictLookup("Colors", &obj);
+ params->dictLookup("Colors", &obj, recursion);
if (obj.isInt())
colors = obj.getInt();
obj.free();
- params->dictLookup("BitsPerComponent", &obj);
+ params->dictLookup("BitsPerComponent", &obj, recursion);
if (obj.isInt())
bits = obj.getInt();
obj.free();
@@ -274,7 +293,7 @@ Stream *Stream::makeFilter(char *name, S
str = new FlateStream(str, pred, columns, colors, bits);
} else if (!strcmp(name, "JBIG2Decode")) {
if (params->isDict()) {
- params->dictLookup("JBIG2Globals", &globals);
+ params->dictLookup("JBIG2Globals", &globals, recursion);
}
str = new JBIG2Stream(str, &globals);
globals.free();
@@ -314,7 +333,7 @@ void FilterStream::close() {
str->close();
}
-void FilterStream::setPos(Guint pos, int dir) {
+void FilterStream::setPos(GFileOffset pos, int dir) {
error(errInternal, -1, "Called setPos() on FilterStream");
}
@@ -365,6 +384,10 @@ void ImageStream::reset() {
str->reset();
}
+void ImageStream::close() {
+ str->close();
+}
+
GBool ImageStream::getPixel(Guchar *pix) {
int i;
@@ -405,6 +428,10 @@ Guchar *ImageStream::getLine() {
}
} else if (nBits == 8) {
// special case: imgLine == inputLine
+ } else if (nBits == 16) {
+ for (i = 0; i < nVals; ++i) {
+ imgLine[i] = (Guchar)inputLine[2*i];
+ }
} else {
bitMask = (1 << nBits) - 1;
buf = 0;
@@ -451,8 +478,8 @@ StreamPredictor::StreamPredictor(Stream
return;
}
predLine = (Guchar *)gmalloc(rowBytes);
- memset(predLine, 0, rowBytes);
- predIdx = rowBytes;
+
+ reset();
ok = gTrue;
}
@@ -461,6 +488,11 @@ StreamPredictor::~StreamPredictor() {
gfree(predLine);
}
+void StreamPredictor::reset() {
+ memset(predLine, 0, rowBytes);
+ predIdx = rowBytes;
+}
+
int StreamPredictor::lookChar() {
if (predIdx >= rowBytes) {
if (!getNextLine()) {
@@ -573,19 +605,19 @@ GBool StreamPredictor::getNextLine() {
// apply TIFF (component) predictor
if (predictor == 2) {
- if (nBits == 1) {
- inBuf = predLine[pixBytes - 1];
- for (i = pixBytes; i < rowBytes; i += 8) {
- // 1-bit add is just xor
- inBuf = (inBuf << 8) | predLine[i];
- predLine[i] ^= inBuf >> nComps;
- }
- } else if (nBits == 8) {
+ if (nBits == 8) {
for (i = pixBytes; i < rowBytes; ++i) {
predLine[i] += predLine[i - nComps];
}
+ } else if (nBits == 16) {
+ for (i = pixBytes; i < rowBytes; i += 2) {
+ c = ((predLine[i] + predLine[i - 2*nComps]) << 8) +
+ predLine[i + 1] + predLine[i + 1 - 2*nComps];
+ predLine[i] = (Guchar)(c >> 8);
+ predLine[i+1] = (Guchar)(c & 0xff);
+ }
} else {
- memset(upLeftBuf, 0, nComps + 1);
+ memset(upLeftBuf, 0, nComps);
bitMask = (1 << nBits) - 1;
inBuf = outBuf = 0;
inBits = outBits = 0;
@@ -624,8 +656,8 @@ GBool StreamPredictor::getNextLine() {
// FileStream
//------------------------------------------------------------------------
-FileStream::FileStream(FILE *fA, Guint startA, GBool limitedA,
- Guint lengthA, Object *dictA):
+FileStream::FileStream(FILE *fA, GFileOffset startA, GBool limitedA,
+ GFileOffset lengthA, Object *dictA):
BaseStream(dictA) {
f = fA;
start = startA;
@@ -641,22 +673,14 @@ FileStream::~FileStream() {
close();
}
-Stream *FileStream::makeSubStream(Guint startA, GBool limitedA,
- Guint lengthA, Object *dictA) {
+Stream *FileStream::makeSubStream(GFileOffset startA, GBool limitedA,
+ GFileOffset lengthA, Object *dictA) {
return new FileStream(f, startA, limitedA, lengthA, dictA);
}
void FileStream::reset() {
-#if HAVE_FSEEKO
- savePos = (Guint)ftello(f);
- fseeko(f, start, SEEK_SET);
-#elif HAVE_FSEEK64
- savePos = (Guint)ftell64(f);
- fseek64(f, start, SEEK_SET);
-#else
- savePos = (Guint)ftell(f);
- fseek(f, start, SEEK_SET);
-#endif
+ savePos = gftell(f);
+ gfseek(f, start, SEEK_SET);
saved = gTrue;
bufPtr = bufEnd = buf;
bufPos = start;
@@ -664,13 +688,7 @@ void FileStream::reset() {
void FileStream::close() {
if (saved) {
-#if HAVE_FSEEKO
- fseeko(f, savePos, SEEK_SET);
-#elif HAVE_FSEEK64
- fseek64(f, savePos, SEEK_SET);
-#else
- fseek(f, savePos, SEEK_SET);
-#endif
+ gfseek(f, savePos, SEEK_SET);
saved = gFalse;
}
}
@@ -705,7 +723,7 @@ GBool FileStream::fillBuf() {
return gFalse;
}
if (limited && bufPos + fileStreamBufSize > start + length) {
- n = start + length - bufPos;
+ n = (int)(start + length - bufPos);
} else {
n = fileStreamBufSize;
}
@@ -717,41 +735,20 @@ GBool FileStream::fillBuf() {
return gTrue;
}
-void FileStream::setPos(Guint pos, int dir) {
- Guint size;
+void FileStream::setPos(GFileOffset pos, int dir) {
+ GFileOffset size;
if (dir >= 0) {
-#if HAVE_FSEEKO
- fseeko(f, pos, SEEK_SET);
-#elif HAVE_FSEEK64
- fseek64(f, pos, SEEK_SET);
-#else
- fseek(f, pos, SEEK_SET);
-#endif
+ gfseek(f, pos, SEEK_SET);
bufPos = pos;
} else {
-#if HAVE_FSEEKO
- fseeko(f, 0, SEEK_END);
- size = (Guint)ftello(f);
-#elif HAVE_FSEEK64
- fseek64(f, 0, SEEK_END);
- size = (Guint)ftell64(f);
-#else
- fseek(f, 0, SEEK_END);
- size = (Guint)ftell(f);
-#endif
- if (pos > size)
- pos = (Guint)size;
-#if HAVE_FSEEKO
- fseeko(f, -(int)pos, SEEK_END);
- bufPos = (Guint)ftello(f);
-#elif HAVE_FSEEK64
- fseek64(f, -(int)pos, SEEK_END);
- bufPos = (Guint)ftell64(f);
-#else
- fseek(f, -(int)pos, SEEK_END);
- bufPos = (Guint)ftell(f);
-#endif
+ gfseek(f, 0, SEEK_END);
+ size = gftell(f);
+ if (pos > size) {
+ pos = size;
+ }
+ gfseek(f, -pos, SEEK_END);
+ bufPos = gftell(f);
}
bufPtr = bufEnd = buf;
}
@@ -782,17 +779,24 @@ MemStream::~MemStream() {
}
}
-Stream *MemStream::makeSubStream(Guint startA, GBool limited,
- Guint lengthA, Object *dictA) {
+Stream *MemStream::makeSubStream(GFileOffset startA, GBool limited,
+ GFileOffset lengthA, Object *dictA) {
MemStream *subStr;
- Guint newLength;
+ Guint newStart, newLength;
- if (!limited || startA + lengthA > start + length) {
- newLength = start + length - startA;
+ if (startA < start) {
+ newStart = start;
+ } else if (startA > start + length) {
+ newStart = start + (int)length;
} else {
- 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 @@ int MemStream::getBlock(char *blk, int s
return n;
}
-void MemStream::setPos(Guint pos, int dir) {
+void MemStream::setPos(GFileOffset pos, int dir) {
Guint i;
if (dir >= 0) {
- i = pos;
+ i = (Guint)pos;
} else {
- i = start + length - pos;
+ i = (Guint)(start + length - pos);
}
if (i < start) {
i = start;
@@ -846,7 +850,7 @@ void MemStream::moveStart(int delta) {
//------------------------------------------------------------------------
EmbedStream::EmbedStream(Stream *strA, Object *dictA,
- GBool limitedA, Guint lengthA):
+ GBool limitedA, GFileOffset lengthA):
BaseStream(dictA) {
str = strA;
limited = limitedA;
@@ -856,8 +860,8 @@ EmbedStream::EmbedStream(Stream *strA, O
EmbedStream::~EmbedStream() {
}
-Stream *EmbedStream::makeSubStream(Guint start, GBool limitedA,
- Guint lengthA, Object *dictA) {
+Stream *EmbedStream::makeSubStream(GFileOffset start, GBool limitedA,
+ GFileOffset lengthA, Object *dictA) {
error(errInternal, -1, "Called makeSubStream() on EmbedStream");
return NULL;
}
@@ -887,11 +891,11 @@ int EmbedStream::getBlock(char *blk, int
return str->getBlock(blk, size);
}
-void EmbedStream::setPos(Guint pos, int dir) {
+void EmbedStream::setPos(GFileOffset pos, int dir) {
error(errInternal, -1, "Called setPos() on EmbedStream");
}
-Guint EmbedStream::getStart() {
+GFileOffset EmbedStream::getStart() {
error(errInternal, -1, "Called getStart() on EmbedStream");
return 0;
}
@@ -1173,6 +1177,9 @@ int LZWStream::getBlock(char *blk, int s
void LZWStream::reset() {
str->reset();
+ if (pred) {
+ pred->reset();
+ }
eof = gFalse;
inputBits = 0;
clearTable();
@@ -2068,14 +2075,16 @@ GBool CCITTFaxStream::isBinary(GBool las
//------------------------------------------------------------------------
// IDCT constants (20.12 fixed point format)
-#define dctCos1 4017 // cos(pi/16)
-#define dctSin1 799 // sin(pi/16)
-#define dctCos3 3406 // cos(3*pi/16)
-#define dctSin3 2276 // sin(3*pi/16)
-#define dctCos6 1567 // cos(6*pi/16)
-#define dctSin6 3784 // sin(6*pi/16)
-#define dctSqrt2 5793 // sqrt(2)
-#define dctSqrt1d2 2896 // sqrt(2) / 2
+#define dctSqrt2 5793 // sqrt(2)
+#define dctSqrt2Cos6 2217 // sqrt(2) * cos(6*pi/16)
+#define dctSqrt2Cos6PSin6 7568 // sqrt(2) * (cos(6*pi/16) + sin(6*pi/16))
+#define dctSqrt2Sin6MCos6 3135 // sqrt(2) * (sin(6*pi/16) - cos(6*pi/16))
+#define dctCos3 3406 // cos(3*pi/16)
+#define dctCos3PSin3 5681 // cos(3*pi/16) + sin(3*pi/16)
+#define dctSin3MCos3 -1130 // sin(3*pi/16) - cos(3*pi/16)
+#define dctCos1 4017 // cos(pi/16)
+#define dctCos1PSin1 4816 // cos(pi/16) + sin(pi/16)
+#define dctSin1MCos1 -3218 // sin(pi/16) - cos(pi/16)
// color conversion parameters (16.16 fixed point format)
#define dctCrToR 91881 // 1.4020
@@ -2083,10 +2092,47 @@ GBool CCITTFaxStream::isBinary(GBool las
#define dctCrToG -46802 // -0.71413636
#define dctCbToB 116130 // 1.772
-// clip [-256,511] --> [0,255]
-#define dctClipOffset 256
-static Guchar dctClip[768];
-static int dctClipInit = 0;
+// The dctClip function clips signed integers to the [0,255] range.
+// To handle valid DCT inputs, this must support an input range of at
+// least [-256,511]. Invalid DCT inputs (e.g., from damaged PDF
+// files) can result in arbitrary values, so we want to mask those
+// out. We round the input range size up to a power of 2 (so we can
+// use a bit mask), which gives us an input range of [-384,639]. The
+// end result is:
+// input output
+// ---------- ------
+// <-384 X invalid inputs -> output is "don't care"
+// -384..-257 0 invalid inputs, clipped
+// -256..-1 0 valid inputs, need to be clipped
+// 0..255 0..255
+// 256..511 255 valid inputs, need to be clipped
+// 512..639 255 invalid inputs, clipped
+// >=512 X invalid inputs -> output is "don't care"
+
+#define dctClipOffset 384
+#define dctClipMask 1023
+static Guchar dctClipData[1024];
+
+static inline void dctClipInit() {
+ static int initDone = 0;
+ int i;
+ if (!initDone) {
+ for (i = -384; i < 0; ++i) {
+ dctClipData[dctClipOffset + i] = 0;
+ }
+ for (i = 0; i < 256; ++i) {
+ dctClipData[dctClipOffset + i] = i;
+ }
+ for (i = 256; i < 639; ++i) {
+ dctClipData[dctClipOffset + i] = 255;
+ }
+ initDone = 1;
+ }
+}
+
+static inline int dctClip(int x) {
+ return dctClipData[(dctClipOffset + x) & dctClipMask];
+}
// zig zag decode map
static int dctZigZag[64] = {
@@ -2109,7 +2155,7 @@ static int dctZigZag[64] = {
DCTStream::DCTStream(Stream *strA, GBool colorXformA):
FilterStream(strA) {
- int i, j;
+ int i;
colorXform = colorXformA;
progressive = interleaved = gFalse;
@@ -2117,23 +2163,15 @@ DCTStream::DCTStream(Stream *strA, GBool
mcuWidth = mcuHeight = 0;
numComps = 0;
comp = 0;
- x = y = dy = 0;
+ x = y = 0;
for (i = 0; i < 4; ++i) {
- for (j = 0; j < 32; ++j) {
- rowBuf[i][j] = NULL;
- }
frameBuf[i] = NULL;
}
+ rowBuf = NULL;
+ memset(dcHuffTables, 0, sizeof(dcHuffTables));
+ memset(acHuffTables, 0, sizeof(acHuffTables));
- if (!dctClipInit) {
- for (i = -256; i < 0; ++i)
- dctClip[dctClipOffset + i] = 0;
- for (i = 0; i < 256; ++i)
- dctClip[dctClipOffset + i] = i;
- for (i = 256; i < 512; ++i)
- dctClip[dctClipOffset + i] = 255;
- dctClipInit = 1;
- }
+ dctClipInit();
}
DCTStream::~DCTStream() {
@@ -2142,7 +2180,7 @@ DCTStream::~DCTStream() {
}
void DCTStream::reset() {
- int i, j;
+ int i;
str->reset();
@@ -2157,6 +2195,8 @@ void DCTStream::reset() {
restartInterval = 0;
if (!readHeader()) {
+ // force an EOF condition
+ progressive = gTrue;
y = height;
return;
}
@@ -2229,17 +2269,11 @@ void DCTStream::reset() {
// allocate a buffer for one row of MCUs
bufWidth = ((width + mcuWidth - 1) / mcuWidth) * mcuWidth;
- for (i = 0; i < numComps; ++i) {
- for (j = 0; j < mcuHeight; ++j) {
- rowBuf[i][j] = (Guchar *)gmallocn(bufWidth, sizeof(Guchar));
- }
- }
+ rowBuf = (Guchar *)gmallocn(numComps * mcuHeight, bufWidth);
+ rowBufPtr = rowBufEnd = rowBuf;
// initialize counters
- comp = 0;
- x = 0;
- y = 0;
- dy = mcuHeight;
+ y = -mcuHeight;
restartMarker = 0xd0;
restart();
@@ -2247,26 +2281,24 @@ void DCTStream::reset() {
}
void DCTStream::close() {
- int i, j;
+ int i;
for (i = 0; i < 4; ++i) {
- for (j = 0; j < 32; ++j) {
- gfree(rowBuf[i][j]);
- rowBuf[i][j] = NULL;
- }
gfree(frameBuf[i]);
frameBuf[i] = NULL;
}
+ gfree(rowBuf);
+ rowBuf = NULL;
FilterStream::close();
}
int DCTStream::getChar() {
int c;
- if (y >= height) {
- return EOF;
- }
if (progressive || !interleaved) {
+ if (y >= height) {
+ return EOF;
+ }
c = frameBuf[comp][y * bufWidth + x];
if (++comp == numComps) {
comp = 0;
@@ -2276,48 +2308,38 @@ int DCTStream::getChar() {
}
}
} else {
- if (dy >= mcuHeight) {
+ if (rowBufPtr == rowBufEnd) {
+ if (y + mcuHeight >= height) {
+ return EOF;
+ }
+ y += mcuHeight;
if (!readMCURow()) {
y = height;
return EOF;
}
- comp = 0;
- x = 0;
- dy = 0;
- }
- c = rowBuf[comp][dy][x];
- if (++comp == numComps) {
- comp = 0;
- if (++x == width) {
- x = 0;
- ++y;
- ++dy;
- if (y == height) {
- readTrailer();
- }
- }
}
+ c = *rowBufPtr++;
}
return c;
}
int DCTStream::lookChar() {
- if (y >= height) {
- return EOF;
- }
if (progressive || !interleaved) {
+ if (y >= height) {
+ return EOF;
+ }
return frameBuf[comp][y * bufWidth + x];
} else {
- if (dy >= mcuHeight) {
+ if (rowBufPtr == rowBufEnd) {
+ if (y + mcuHeight >= height) {
+ return EOF;
+ }
if (!readMCURow()) {
y = height;
return EOF;
}
- comp = 0;
- x = 0;
- dy = 0;
}
- return rowBuf[comp][dy][x];
+ return *rowBufPtr;
}
}
@@ -2375,38 +2397,49 @@ GBool DCTStream::readMCURow() {
}
transformDataUnit(quantTables[compInfo[cc].quantTable],
data1, data2);
- if (hSub == 1 && vSub == 1) {
+ if (hSub == 1 && vSub == 1 && x1+x2+8 <= width) {
for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) {
- p1 = &rowBuf[cc][y2+y3][x1+x2];
- p1[0] = data2[i];
- p1[1] = data2[i+1];
- p1[2] = data2[i+2];
- p1[3] = data2[i+3];
- p1[4] = data2[i+4];
- p1[5] = data2[i+5];
- p1[6] = data2[i+6];
- p1[7] = data2[i+7];
+ p1 = &rowBuf[((y2+y3) * width + (x1+x2)) * numComps + cc];
+ p1[0] = data2[i];
+ p1[ numComps] = data2[i+1];
+ p1[2*numComps] = data2[i+2];
+ p1[3*numComps] = data2[i+3];
+ p1[4*numComps] = data2[i+4];
+ p1[5*numComps] = data2[i+5];
+ p1[6*numComps] = data2[i+6];
+ p1[7*numComps] = data2[i+7];
}
- } else if (hSub == 2 && vSub == 2) {
+ } else if (hSub == 2 && vSub == 2 && x1+x2+16 <= width) {
for (y3 = 0, i = 0; y3 < 16; y3 += 2, i += 8) {
- p1 = &rowBuf[cc][y2+y3][x1+x2];
- p2 = &rowBuf[cc][y2+y3+1][x1+x2];
- p1[0] = p1[1] = p2[0] = p2[1] = data2[i];
- p1[2] = p1[3] = p2[2] = p2[3] = data2[i+1];
- p1[4] = p1[5] = p2[4] = p2[5] = data2[i+2];
- p1[6] = p1[7] = p2[6] = p2[7] = data2[i+3];
- p1[8] = p1[9] = p2[8] = p2[9] = data2[i+4];
- p1[10] = p1[11] = p2[10] = p2[11] = data2[i+5];
- p1[12] = p1[13] = p2[12] = p2[13] = data2[i+6];
- p1[14] = p1[15] = p2[14] = p2[15] = data2[i+7];
+ p1 = &rowBuf[((y2+y3) * width + (x1+x2)) * numComps + cc];
+ p2 = p1 + width * numComps;
+ p1[0] = p1[numComps] =
+ p2[0] = p2[numComps] = data2[i];
+ p1[2*numComps] = p1[3*numComps] =
+ p2[2*numComps] = p2[3*numComps] = data2[i+1];
+ p1[4*numComps] = p1[5*numComps] =
+ p2[4*numComps] = p2[5*numComps] = data2[i+2];
+ p1[6*numComps] = p1[7*numComps] =
+ p2[6*numComps] = p2[7*numComps] = data2[i+3];
+ p1[8*numComps] = p1[9*numComps] =
+ p2[8*numComps] = p2[9*numComps] = data2[i+4];
+ p1[10*numComps] = p1[11*numComps] =
+ p2[10*numComps] = p2[11*numComps] = data2[i+5];
+ p1[12*numComps] = p1[13*numComps] =
+ p2[12*numComps] = p2[13*numComps] = data2[i+6];
+ p1[14*numComps] = p1[15*numComps] =
+ p2[14*numComps] = p2[15*numComps] = data2[i+7];
}
} else {
+ p1 = &rowBuf[(y2 * width + (x1+x2)) * numComps + cc];
i = 0;
for (y3 = 0, y4 = 0; y3 < 8; ++y3, y4 += vSub) {
for (x3 = 0, x4 = 0; x3 < 8; ++x3, x4 += hSub) {
- for (y5 = 0; y5 < vSub; ++y5)
- for (x5 = 0; x5 < hSub; ++x5)
- rowBuf[cc][y2+y4+y5][x1+x2+x4+x5] = data2[i];
+ for (y5 = 0; y5 < vSub; ++y5) {
+ for (x5 = 0; x5 < hSub && x1+x2+x4+x5 < width; ++x5) {
+ p1[((y4+y5) * width + (x4+x5)) * numComps] = data2[i];
+ }
+ }
++i;
}
}
@@ -2415,42 +2448,46 @@ GBool DCTStream::readMCURow() {
}
}
--restartCtr;
+ }
- // color space conversion
- if (colorXform) {
- // convert YCbCr to RGB
- if (numComps == 3) {
- for (y2 = 0; y2 < mcuHeight; ++y2) {
- for (x2 = 0; x2 < mcuWidth; ++x2) {
- pY = rowBuf[0][y2][x1+x2];
- pCb = rowBuf[1][y2][x1+x2] - 128;
- pCr = rowBuf[2][y2][x1+x2] - 128;
- pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16;
- rowBuf[0][y2][x1+x2] = dctClip[dctClipOffset + pR];
- pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32768) >> 16;
- rowBuf[1][y2][x1+x2] = dctClip[dctClipOffset + pG];
- pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16;
- rowBuf[2][y2][x1+x2] = dctClip[dctClipOffset + pB];
- }
- }
- // convert YCbCrK to CMYK (K is passed through unchanged)
- } else if (numComps == 4) {
- for (y2 = 0; y2 < mcuHeight; ++y2) {
- for (x2 = 0; x2 < mcuWidth; ++x2) {
- pY = rowBuf[0][y2][x1+x2];
- pCb = rowBuf[1][y2][x1+x2] - 128;
- pCr = rowBuf[2][y2][x1+x2] - 128;
- pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16;
- rowBuf[0][y2][x1+x2] = 255 - dctClip[dctClipOffset + pR];
- pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32768) >> 16;
- rowBuf[1][y2][x1+x2] = 255 - dctClip[dctClipOffset + pG];
- pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16;
- rowBuf[2][y2][x1+x2] = 255 - dctClip[dctClipOffset + pB];
- }
- }
+ // color space conversion
+ if (colorXform) {
+ // convert YCbCr to RGB
+ if (numComps == 3) {
+ for (i = 0, p1 = rowBuf; i < width * mcuHeight; ++i, p1 += 3) {
+ pY = p1[0];
+ pCb = p1[1] - 128;
+ pCr = p1[2] - 128;
+ pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16;
+ p1[0] = dctClip(pR);
+ pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32768) >> 16;
+ p1[1] = dctClip(pG);
+ pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16;
+ p1[2] = dctClip(pB);
+ }
+ // convert YCbCrK to CMYK (K is passed through unchanged)
+ } else if (numComps == 4) {
+ for (i = 0, p1 = rowBuf; i < width * mcuHeight; ++i, p1 += 4) {
+ pY = p1[0];
+ pCb = p1[1] - 128;
+ pCr = p1[2] - 128;
+ pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16;
+ p1[0] = 255 - dctClip(pR);
+ pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32768) >> 16;
+ p1[1] = 255 - dctClip(pG);
+ pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16;
+ p1[2] = 255 - dctClip(pB);
}
}
}
+
+ rowBufPtr = rowBuf;
+ if (y + mcuHeight <= height) {
+ rowBufEnd = rowBuf + numComps * width * mcuHeight;
+ } else {
+ rowBufEnd = rowBuf + numComps * width * (height - y);
+ }
+
return gTrue;
}
@@ -2609,7 +2646,7 @@ GBool DCTStream::readDataUnit(DCTHuffTab
return gTrue;
}
-// Read one data unit from a sequential JPEG stream.
+// Read one data unit from a progressive JPEG stream.
GBool DCTStream::readProgressiveDataUnit(DCTHuffTable *dcHuffTable,
DCTHuffTable *acHuffTable,
int *prevDC, int data[64]) {
@@ -2635,7 +2672,13 @@ GBool DCTStream::readProgressiveDataUnit
if ((bit = readBit()) == 9999) {
return gFalse;
}
- data[0] += bit << scanInfo.al;
+ if (bit) {
+ if (data[0] >= 0) {
+ data[0] += 1 << scanInfo.al;
+ } else {
+ data[0] -= 1 << scanInfo.al;
+ }
+ }
}
++i;
}
@@ -2652,7 +2695,11 @@ GBool DCTStream::readProgressiveDataUnit
return gFalse;
}
if (bit) {
- data[j] += 1 << scanInfo.al;
+ if (data[j] >= 0) {
+ data[j] += 1 << scanInfo.al;
+ } else {
+ data[j] -= 1 << scanInfo.al;
+ }
}
}
}
@@ -2678,7 +2725,11 @@ GBool DCTStream::readProgressiveDataUnit
return gFalse;
}
if (bit) {
- data[j] += 1 << scanInfo.al;
+ if (data[j] >= 0) {
+ data[j] += 1 << scanInfo.al;
+ } else {
+ data[j] -= 1 << scanInfo.al;
+ }
}
}
}
@@ -2701,7 +2752,11 @@ GBool DCTStream::readProgressiveDataUnit
return gFalse;
}
if (bit) {
- data[j] += 1 << scanInfo.al;
+ if (data[j] >= 0) {
+ data[j] += 1 << scanInfo.al;
+ } else {
+ data[j] -= 1 << scanInfo.al;
+ }
}
}
}
@@ -2723,7 +2778,11 @@ GBool DCTStream::readProgressiveDataUnit
return gFalse;
}
if (bit) {
- data[j] += 1 << scanInfo.al;
+ if (data[j] >= 0) {
+ data[j] += 1 << scanInfo.al;
+ } else {
+ data[j] -= 1 << scanInfo.al;
+ }
}
j = dctZigZag[i++];
}
@@ -2837,12 +2896,12 @@ void DCTStream::decodeImage() {
pCb = *p1 - 128;
pCr = *p2 - 128;
pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16;
- *p0++ = dctClip[dctClipOffset + pR];
+ *p0++ = dctClip(pR);
pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr +
32768) >> 16;
- *p1++ = dctClip[dctClipOffset + pG];
+ *p1++ = dctClip(pG);
pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16;
- *p2++ = dctClip[dctClipOffset + pB];
+ *p2++ = dctClip(pB);
}
}
// convert YCbCrK to CMYK (K is passed through unchanged)
@@ -2856,12 +2915,12 @@ void DCTStream::decodeImage() {
pCb = *p1 - 128;
pCr = *p2 - 128;
pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16;
- *p0++ = 255 - dctClip[dctClipOffset + pR];
+ *p0++ = 255 - dctClip(pR);
pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr +
32768) >> 16;
- *p1++ = 255 - dctClip[dctClipOffset + pG];
+ *p1++ = 255 - dctClip(pG);
pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16;
- *p2++ = 255 - dctClip[dctClipOffset + pB];
+ *p2++ = 255 - dctClip(pB);
}
}
}
@@ -2880,71 +2939,76 @@ void DCTStream::decodeImage() {
// paper.
void DCTStream::transformDataUnit(Gushort *quantTable,
int dataIn[64], Guchar dataOut[64]) {
- int v0, v1, v2, v3, v4, v5, v6, v7, t;
+ int v0, v1, v2, v3, v4, v5, v6, v7, t0, t1, t2;
int *p;
+ Gushort *q;
int i;
- // dequant
- for (i = 0; i < 64; ++i) {
- dataIn[i] *= quantTable[i];
- }
-
- // inverse DCT on rows
+ // dequant; inverse DCT on rows
for (i = 0; i < 64; i += 8) {
p = dataIn + i;
+ q = quantTable + i;
// check for all-zero AC coefficients
if (p[1] == 0 && p[2] == 0 && p[3] == 0 &&
p[4] == 0 && p[5] == 0 && p[6] == 0 && p[7] == 0) {
- t = (dctSqrt2 * p[0] + 512) >> 10;
- p[0] = t;
- p[1] = t;
- p[2] = t;
- p[3] = t;
- p[4] = t;
- p[5] = t;
- p[6] = t;
- p[7] = t;
+ t0 = p[0] * q[0];
+ p[0] = t0;
+ p[1] = t0;
+ p[2] = t0;
+ p[3] = t0;
+ p[4] = t0;
+ p[5] = t0;
+ p[6] = t0;
+ p[7] = t0;
continue;
}
// stage 4
- v0 = (dctSqrt2 * p[0] + 128) >> 8;
- v1 = (dctSqrt2 * p[4] + 128) >> 8;
- v2 = p[2];
- v3 = p[6];
- v4 = (dctSqrt1d2 * (p[1] - p[7]) + 128) >> 8;
- v7 = (dctSqrt1d2 * (p[1] + p[7]) + 128) >> 8;
- v5 = p[3] << 4;
- v6 = p[5] << 4;
+ v0 = p[0] * q[0];
+ v1 = p[4] * q[4];
+ v2 = p[2] * q[2];
+ v3 = p[6] * q[6];
+ t0 = p[1] * q[1];
+ t1 = p[7] * q[7];
+ v4 = t0 - t1;
+ v7 = t0 + t1;
+ v5 = (dctSqrt2 * p[3] * q[3]) >> 12;
+ v6 = (dctSqrt2 * p[5] * q[5]) >> 12;
// stage 3
- t = (v0 - v1+ 1) >> 1;
- v0 = (v0 + v1 + 1) >> 1;
- v1 = t;
- t = (v2 * dctSin6 + v3 * dctCos6 + 128) >> 8;
- v2 = (v2 * dctCos6 - v3 * dctSin6 + 128) >> 8;
- v3 = t;
- t = (v4 - v6 + 1) >> 1;
- v4 = (v4 + v6 + 1) >> 1;
- v6 = t;
- t = (v7 + v5 + 1) >> 1;
- v5 = (v7 - v5 + 1) >> 1;
- v7 = t;
+ t0 = v0 - v1;
+ v0 = v0 + v1;
+ v1 = t0;
+ t0 = dctSqrt2Cos6 * (v2 + v3);
+ t1 = dctSqrt2Cos6PSin6 * v3;
+ t2 = dctSqrt2Sin6MCos6 * v2;
+ v2 = (t0 - t1) >> 12;
+ v3 = (t0 + t2) >> 12;
+ t0 = v4 - v6;
+ v4 = v4 + v6;
+ v6 = t0;
+ t0 = v7 + v5;
+ v5 = v7 - v5;
+ v7 = t0;
// stage 2
- t = (v0 - v3 + 1) >> 1;
- v0 = (v0 + v3 + 1) >> 1;
- v3 = t;
- t = (v1 - v2 + 1) >> 1;
- v1 = (v1 + v2 + 1) >> 1;
- v2 = t;
- t = (v4 * dctSin3 + v7 * dctCos3 + 2048) >> 12;
- v4 = (v4 * dctCos3 - v7 * dctSin3 + 2048) >> 12;
- v7 = t;
- t = (v5 * dctSin1 + v6 * dctCos1 + 2048) >> 12;
- v5 = (v5 * dctCos1 - v6 * dctSin1 + 2048) >> 12;
- v6 = t;
+ t0 = v0 - v3;
+ v0 = v0 + v3;
+ v3 = t0;
+ t0 = v1 - v2;
+ v1 = v1 + v2;
+ v2 = t0;
+ t0 = dctCos3 * (v4 + v7);
+ t1 = dctCos3PSin3 * v7;
+ t2 = dctSin3MCos3 * v4;
+ v4 = (t0 - t1) >> 12;
+ v7 = (t0 + t2) >> 12;
+ t0 = dctCos1 * (v5 + v6);
+ t1 = dctCos1PSin1 * v6;
+ t2 = dctSin1MCos1 * v5;
+ v5 = (t0 - t1) >> 12;
+ v6 = (t0 + t2) >> 12;
// stage 1
p[0] = v0 + v7;
@@ -2964,55 +3028,60 @@ void DCTStream::transformDataUnit(Gushor
// check for all-zero AC coefficients
if (p[1*8] == 0 && p[2*8] == 0 && p[3*8] == 0 &&
p[4*8] == 0 && p[5*8] == 0 && p[6*8] == 0 && p[7*8] == 0) {
- t = (dctSqrt2 * dataIn[i+0] + 8192) >> 14;
- p[0*8] = t;
- p[1*8] = t;
- p[2*8] = t;
- p[3*8] = t;
- p[4*8] = t;
- p[5*8] = t;
- p[6*8] = t;
- p[7*8] = t;
+ t0 = p[0*8];
+ p[1*8] = t0;
+ p[2*8] = t0;
+ p[3*8] = t0;
+ p[4*8] = t0;
+ p[5*8] = t0;
+ p[6*8] = t0;
+ p[7*8] = t0;
continue;
}
// stage 4
- v0 = (dctSqrt2 * p[0*8] + 2048) >> 12;
- v1 = (dctSqrt2 * p[4*8] + 2048) >> 12;
+ v0 = p[0*8];
+ v1 = p[4*8];
v2 = p[2*8];
v3 = p[6*8];
- v4 = (dctSqrt1d2 * (p[1*8] - p[7*8]) + 2048) >> 12;
- v7 = (dctSqrt1d2 * (p[1*8] + p[7*8]) + 2048) >> 12;
- v5 = p[3*8];
- v6 = p[5*8];
+ v4 = p[1*8] - p[7*8];
+ v7 = p[1*8] + p[7*8];
+ v5 = (dctSqrt2 * p[3*8]) >> 12;
+ v6 = (dctSqrt2 * p[5*8]) >> 12;
// stage 3
- t = (v0 - v1 + 1) >> 1;
- v0 = (v0 + v1 + 1) >> 1;
- v1 = t;
- t = (v2 * dctSin6 + v3 * dctCos6 + 2048) >> 12;
- v2 = (v2 * dctCos6 - v3 * dctSin6 + 2048) >> 12;
- v3 = t;
- t = (v4 - v6 + 1) >> 1;
- v4 = (v4 + v6 + 1) >> 1;
- v6 = t;
- t = (v7 + v5 + 1) >> 1;
- v5 = (v7 - v5 + 1) >> 1;
- v7 = t;
+ t0 = v0 - v1;
+ v0 = v0 + v1;
+ v1 = t0;
+ t0 = dctSqrt2Cos6 * (v2 + v3);
+ t1 = dctSqrt2Cos6PSin6 * v3;
+ t2 = dctSqrt2Sin6MCos6 * v2;
+ v2 = (t0 - t1) >> 12;
+ v3 = (t0 + t2) >> 12;
+ t0 = v4 - v6;
+ v4 = v4 + v6;
+ v6 = t0;
+ t0 = v7 + v5;
+ v5 = v7 - v5;
+ v7 = t0;
// stage 2
- t = (v0 - v3 + 1) >> 1;
- v0 = (v0 + v3 + 1) >> 1;
- v3 = t;
- t = (v1 - v2 + 1) >> 1;
- v1 = (v1 + v2 + 1) >> 1;
- v2 = t;
- t = (v4 * dctSin3 + v7 * dctCos3 + 2048) >> 12;
- v4 = (v4 * dctCos3 - v7 * dctSin3 + 2048) >> 12;
- v7 = t;
- t = (v5 * dctSin1 + v6 * dctCos1 + 2048) >> 12;
- v5 = (v5 * dctCos1 - v6 * dctSin1 + 2048) >> 12;
- v6 = t;
+ t0 = v0 - v3;
+ v0 = v0 + v3;
+ v3 = t0;
+ t0 = v1 - v2;
+ v1 = v1 + v2;
+ v2 = t0;
+ t0 = dctCos3 * (v4 + v7);
+ t1 = dctCos3PSin3 * v7;
+ t2 = dctSin3MCos3 * v4;
+ v4 = (t0 - t1) >> 12;
+ v7 = (t0 + t2) >> 12;
+ t0 = dctCos1 * (v5 + v6);
+ t1 = dctCos1PSin1 * v6;
+ t2 = dctSin1MCos1 * v5;
+ v5 = (t0 - t1) >> 12;
+ v6 = (t0 + t2) >> 12;
// stage 1
p[0*8] = v0 + v7;
@@ -3027,7 +3096,7 @@ void DCTStream::transformDataUnit(Gushor
// convert to 8-bit integers
for (i = 0; i < 64; ++i) {
- dataOut[i] = dctClip[dctClipOffset + 128 + ((dataIn[i] + 8) >> 4)];
+ dataOut[i] = dctClip(128 + (dataIn[i] >> 3));
}
}
@@ -3103,7 +3172,6 @@ GBool DCTStream::readHeader() {
GBool doScan;
int n;
int c = 0;
- int i;
// read headers
doScan = gFalse;
@@ -3163,9 +3231,7 @@ GBool DCTStream::readHeader() {
// skip APPn / COM / etc.
if (c >= 0xe0) {
n = read16() - 2;
- for (i = 0; i < n; ++i) {
- str->getChar();
- }
+ str->discardChars(n);
} else {
error(errSyntaxError, getPos(), "Unknown DCT marker <{0:02x}>", c);
return gFalse;
@@ -3178,12 +3244,11 @@ GBool DCTStream::readHeader() {
}
GBool DCTStream::readBaselineSOF() {
- int length;
int prec;
int i;
int c;
- length = read16();
+ read16(); // length
prec = str->getChar();
height = read16();
width = read16();
@@ -3218,12 +3283,11 @@ GBool DCTStream::readBaselineSOF() {
}
GBool DCTStream::readProgressiveSOF() {
- int length;
int prec;
int i;
int c;
- length = read16();
+ read16(); // length
prec = str->getChar();
height = read16();
width = read16();
@@ -4194,6 +4258,9 @@ void FlateStream::reset() {
eof = gTrue;
str->reset();
+ if (pred) {
+ pred->reset();
+ }
// read header
//~ need to look at window size?
@@ -4274,10 +4341,10 @@ int FlateStream::getBlock(char *blk, int
n = 0;
while (n < size) {
- if (endOfBlock && eof) {
- break;
- }
if (remain == 0) {
+ if (endOfBlock && eof) {
+ break;
+ }
readSome();
}
while (remain && n < size) {
@@ -4969,3 +5036,149 @@ GBool RunLengthEncoder::fillBuf() {
bufPtr = buf;
return gTrue;
}
+
+//------------------------------------------------------------------------
+// LZWEncoder
+//------------------------------------------------------------------------
+
+LZWEncoder::LZWEncoder(Stream *strA):
+ FilterStream(strA)
+{
+ inBufLen = 0;
+ outBufLen = 0;
+}
+
+LZWEncoder::~LZWEncoder() {
+ if (str->isEncoder()) {
+ delete str;
+ }
+}
+
+void LZWEncoder::reset() {
+ int i;
+
+ str->reset();
+
+ // initialize code table
+ for (i = 0; i < 256; ++i) {
+ table[i].byte = i;
+ table[i].next = NULL;
+ table[i].children = NULL;
+ }
+ nextSeq = 258;
+ codeLen = 9;
+
+ // initialize input buffer
+ inBufLen = str->getBlock((char *)inBuf, sizeof(inBuf));
+
+ // initialize output buffer with a clear-table code
+ outBuf = 256;
+ outBufLen = 9;
+ needEOD = gFalse;
+}
+
+int LZWEncoder::getChar() {
+ int ret;
+
+ if (inBufLen == 0 && !needEOD && outBufLen == 0) {
+ return EOF;
+ }
+ if (outBufLen < 8 && (inBufLen > 0 || needEOD)) {
+ fillBuf();
+ }
+ if (outBufLen >= 8) {
+ ret = (outBuf >> (outBufLen - 8)) & 0xff;
+ outBufLen -= 8;
+ } else {
+ ret = (outBuf << (8 - outBufLen)) & 0xff;
+ outBufLen = 0;
+ }
+ return ret;
+}
+
+int LZWEncoder::lookChar() {
+ if (inBufLen == 0 && !needEOD && outBufLen == 0) {
+ return EOF;
+ }
+ if (outBufLen < 8 && (inBufLen > 0 || needEOD)) {
+ fillBuf();
+ }
+ if (outBufLen >= 8) {
+ return (outBuf >> (outBufLen - 8)) & 0xff;
+ } else {
+ return (outBuf << (8 - outBufLen)) & 0xff;
+ }
+}
+
+// On input, outBufLen < 8.
+// This function generates, at most, 2 12-bit codes
+// --> outBufLen < 8 + 12 + 12 = 32
+void LZWEncoder::fillBuf() {
+ LZWEncoderNode *p0, *p1;
+ int seqLen, code, i;
+
+ if (needEOD) {
+ outBuf = (outBuf << codeLen) | 257;
+ outBufLen += codeLen;
+ needEOD = gFalse;
+ return;
+ }
+
+ // find longest matching sequence (if any)
+ p0 = table + inBuf[0];
+ seqLen = 1;
+ while (inBufLen > seqLen) {
+ for (p1 = p0->children; p1; p1 = p1->next) {
+ if (p1->byte == inBuf[seqLen]) {
+ break;
+ }
+ }
+ if (!p1) {
+ break;
+ }
+ p0 = p1;
+ ++seqLen;
+ }
+ code = (int)(p0 - table);
+
+ // generate an output code
+ outBuf = (outBuf << codeLen) | code;
+ outBufLen += codeLen;
+
+ // update the table
+ table[nextSeq].byte = seqLen < inBufLen ? inBuf[seqLen] : 0;
+ table[nextSeq].children = NULL;
+ if (table[code].children) {
+ table[nextSeq].next = table[code].children;
+ } else {
+ table[nextSeq].next = NULL;
+ }
+ table[code].children = table + nextSeq;
+ ++nextSeq;
+
+ // update the input buffer
+ memmove(inBuf, inBuf + seqLen, inBufLen - seqLen);
+ inBufLen -= seqLen;
+ inBufLen += str->getBlock((char *)inBuf + inBufLen,
+ sizeof(inBuf) - inBufLen);
+
+ // increment codeLen; generate clear-table code
+ if (nextSeq == (1 << codeLen)) {
+ ++codeLen;
+ if (codeLen == 13) {
+ outBuf = (outBuf << 12) | 256;
+ outBufLen += 12;
+ for (i = 0; i < 256; ++i) {
+ table[i].next = NULL;
+ table[i].children = NULL;
+ }
+ nextSeq = 258;
+ codeLen = 9;
+ }
+ }
+
+ // generate EOD next time
+ if (inBufLen == 0) {
+ needEOD = gTrue;
+ }
+}
diff -uNrp 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
#include "gtypes.h"
+#include "gfile.h"
#include "Object.h"
class BaseStream;
@@ -97,13 +98,18 @@ public:
// Get next line from stream.
virtual char *getLine(char *buf, int size);
+ // Discard the next bytes from stream. Returns the number of
+ // bytes discarded, which will be less than 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 is negative, the
// position is from the end of the file; otherwise the position is
// from the start of the file.
- virtual void setPos(Guint pos, int dir = 0) = 0;
+ virtual void setPos(GFileOffset pos, int dir = 0) = 0;
// Get PostScript command for the filter(s).
virtual GString *getPSFilter(int psLevel, const char *indent);
@@ -133,11 +139,11 @@ public:
// Add filters to this stream according to the parameters in .
// Returns the new stream.
- Stream *addFilters(Object *dict);
+ Stream *addFilters(Object *dict, int recursion = 0);
private:
- Stream *makeFilter(char *name, Stream *str, Object *params);
+ Stream *makeFilter(char *name, Stream *str, Object *params, int recursion);
int ref; // reference count
};
@@ -153,9 +159,9 @@ public:
BaseStream(Object *dictA);
virtual ~BaseStream();
- virtual Stream *makeSubStream(Guint start, GBool limited,
- Guint length, Object *dict) = 0;
- virtual void setPos(Guint pos, int dir = 0) = 0;
+ virtual Stream *makeSubStream(GFileOffset start, GBool limited,
+ GFileOffset length, Object *dict) = 0;
+ virtual void setPos(GFileOffset pos, int dir = 0) = 0;
virtual GBool isBinary(GBool last = gTrue) { return last; }
virtual BaseStream *getBaseStream() { return this; }
virtual Stream *getUndecodedStream() { return this; }
@@ -163,7 +169,7 @@ public:
virtual GString *getFileName() { return NULL; }
// Get/set position of first byte of stream within the file.
- virtual Guint getStart() = 0;
+ virtual GFileOffset getStart() = 0;
virtual void moveStart(int delta) = 0;
private:
@@ -183,8 +189,8 @@ public:
FilterStream(Stream *strA);
virtual ~FilterStream();
virtual void close();
- virtual int getPos() { return str->getPos(); }
- virtual void setPos(Guint pos, int dir = 0);
+ virtual GFileOffset getPos() { return str->getPos(); }
+ virtual void setPos(GFileOffset pos, int dir = 0);
virtual BaseStream *getBaseStream() { return str->getBaseStream(); }
virtual Stream *getUndecodedStream() { return str->getUndecodedStream(); }
virtual Dict *getDict() { return str->getDict(); }
@@ -212,6 +218,9 @@ public:
// Reset the stream.
void reset();
+ // Close down the stream.
+ void close();
+
// Gets the next pixel from the stream. should be able to hold
// at least nComps elements. Returns false at end of file.
GBool getPixel(Guchar *pix);
@@ -252,6 +261,8 @@ public:
GBool isOk() { return ok; }
+ void reset();
+
int lookChar();
int getChar();
int getBlock(char *blk, int size);
@@ -282,11 +293,11 @@ private:
class FileStream: public BaseStream {
public:
- FileStream(FILE *fA, Guint startA, GBool limitedA,
- Guint lengthA, Object *dictA);
+ FileStream(FILE *fA, GFileOffset startA, GBool limitedA,
+ GFileOffset lengthA, Object *dictA);
virtual ~FileStream();
- virtual Stream *makeSubStream(Guint startA, GBool limitedA,
- Guint lengthA, Object *dictA);
+ virtual Stream *makeSubStream(GFileOffset startA, GBool limitedA,
+ GFileOffset lengthA, Object *dictA);
virtual StreamKind getKind() { return strFile; }
virtual void reset();
virtual void close();
@@ -295,9 +306,9 @@ public:
virtual int lookChar()
{ return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); }
virtual int getBlock(char *blk, int size);
- virtual int getPos() { return bufPos + (int)(bufPtr - buf); }
- virtual void setPos(Guint pos, int dir = 0);
- virtual Guint getStart() { return start; }
+ virtual GFileOffset getPos() { return bufPos + (int)(bufPtr - buf); }
+ virtual void setPos(GFileOffset pos, int dir = 0);
+ virtual GFileOffset getStart() { return start; }
virtual void moveStart(int delta);
private:
@@ -305,14 +316,14 @@ private:
GBool fillBuf();
FILE *f;
- Guint start;
+ GFileOffset start;
GBool limited;
- Guint length;
+ GFileOffset length;
char buf[fileStreamBufSize];
char *bufPtr;
char *bufEnd;
- Guint bufPos;
- int savePos;
+ GFileOffset bufPos;
+ GFileOffset savePos;
GBool saved;
};
@@ -325,8 +336,8 @@ public:
MemStream(char *bufA, Guint startA, Guint lengthA, Object *dictA);
virtual ~MemStream();
- virtual Stream *makeSubStream(Guint start, GBool limited,
- Guint lengthA, Object *dictA);
+ virtual Stream *makeSubStream(GFileOffset start, GBool limited,
+ GFileOffset lengthA, Object *dictA);
virtual StreamKind getKind() { return strWeird; }
virtual void reset();
virtual void close();
@@ -335,9 +346,9 @@ public:
virtual int lookChar()
{ return (bufPtr < bufEnd) ? (*bufPtr & 0xff) : EOF; }
virtual int getBlock(char *blk, int size);
- virtual int getPos() { return (int)(bufPtr - buf); }
- virtual void setPos(Guint pos, int dir = 0);
- virtual Guint getStart() { return start; }
+ virtual GFileOffset getPos() { return (GFileOffset)(bufPtr - buf); }
+ virtual void setPos(GFileOffset pos, int dir = 0);
+ virtual GFileOffset getStart() { return start; }
virtual void moveStart(int delta);
private:
@@ -363,25 +374,25 @@ private:
class EmbedStream: public BaseStream {
public:
- EmbedStream(Stream *strA, Object *dictA, GBool limitedA, Guint lengthA);
+ EmbedStream(Stream *strA, Object *dictA, GBool limitedA, GFileOffset lengthA);
virtual ~EmbedStream();
- virtual Stream *makeSubStream(Guint start, GBool limitedA,
- Guint lengthA, Object *dictA);
+ virtual Stream *makeSubStream(GFileOffset start, GBool limitedA,
+ GFileOffset lengthA, Object *dictA);
virtual StreamKind getKind() { return str->getKind(); }
virtual void reset() {}
virtual int getChar();
virtual int lookChar();
virtual int getBlock(char *blk, int size);
- virtual int getPos() { return str->getPos(); }
- virtual void setPos(Guint pos, int dir = 0);
- virtual Guint getStart();
+ virtual GFileOffset getPos() { return str->getPos(); }
+ virtual void setPos(GFileOffset pos, int dir = 0);
+ virtual GFileOffset getStart();
virtual void moveStart(int delta);
private:
Stream *str;
GBool limited;
- Guint length;
+ GFileOffset length;
};
//------------------------------------------------------------------------
@@ -623,9 +634,11 @@ private:
DCTHuffTable acHuffTables[4]; // AC Huffman tables
int numDCHuffTables; // number of DC Huffman tables
int numACHuffTables; // number of AC Huffman tables
- Guchar *rowBuf[4][32]; // buffer for one MCU (non-progressive mode)
+ Guchar *rowBuf;
+ Guchar *rowBufPtr; // current position within rowBuf
+ Guchar *rowBufEnd; // end of valid data in rowBuf
int *frameBuf[4]; // buffer for frame (progressive mode)
- int comp, x, y, dy; // current position within image/MCU
+ int comp, x, y; // current position within image/MCU
int restartCtr; // MCUs left until restart
int restartMarker; // next restart marker
int eobRun; // number of EOBs left in the current run
@@ -902,4 +915,42 @@ private:
GBool fillBuf();
};
+//------------------------------------------------------------------------
+// LZWEncoder
+//------------------------------------------------------------------------
+
+struct LZWEncoderNode {
+ int byte;
+ LZWEncoderNode *next; // next sibling
+ LZWEncoderNode *children; // first child
+};
+
+class LZWEncoder: public FilterStream {
+public:
+
+ LZWEncoder(Stream *strA);
+ virtual ~LZWEncoder();
+ virtual StreamKind getKind() { return strWeird; }
+ virtual void reset();
+ virtual int getChar();
+ virtual int lookChar();
+ virtual GString *getPSFilter(int psLevel, const char *indent)
+ { return NULL; }
+ virtual GBool isBinary(GBool last = gTrue) { return gTrue; }
+ virtual GBool isEncoder() { return gTrue; }
+
+private:
+
+ LZWEncoderNode table[4096];
+ int nextSeq;
+ int codeLen;
+ Guchar inBuf[4096];
+ int inBufLen;
+ int outBuf;
+ int outBufLen;
+ GBool needEOD;
+
+ void fillBuf();
+};
+
#endif
diff -uNrp 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
@@ -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 @@ public:
class TextLink {
public:
- TextLink(int xMinA, int yMinA, int xMaxA, int yMaxA, Link *linkA)
- { xMin = xMinA; yMin = yMinA; xMax = xMaxA; yMax = yMaxA; link = linkA; }
- ~TextLink() {}
+ TextLink(double xMinA, double yMinA, double xMaxA, double yMaxA,
+ GString *uriA)
+ { xMin = xMinA; yMin = yMinA; xMax = xMaxA; yMax = yMaxA; uri = uriA; }
+ ~TextLink();
- int xMin, yMin, xMax, yMax;
- Link *link;
+ double xMin, yMin, xMax, yMax;
+ GString *uri;
};
+TextLink::~TextLink() {
+ if (uri) {
+ delete uri;
+ }
+}
+
+//------------------------------------------------------------------------
+// TextOutputControl
+//------------------------------------------------------------------------
+
+TextOutputControl::TextOutputControl() {
+ mode = textOutReadingOrder;
+ fixedPitch = 0;
+ fixedLineSpacing = 0;
+ html = gFalse;
+ clipText = gFalse;
+}
+
+
//------------------------------------------------------------------------
// TextFontInfo
//------------------------------------------------------------------------
TextFontInfo::TextFontInfo(GfxState *state) {
+ GfxFont *gfxFont;
+
gfxFont = state->getFont();
-#if TEXTOUT_WORD_LIST
+ if (gfxFont) {
+ fontID = *gfxFont->getID();
+ ascent = gfxFont->getAscent();
+ descent = gfxFont->getDescent();
+ // "odd" ascent/descent values cause trouble more often than not
+ // (in theory these could be legitimate values for oddly designed
+ // fonts -- but they are more often due to buggy PDF generators)
+ // (values that are too small are a different issue -- those seem
+ // to be more commonly legitimate)
+ if (ascent > 1) {
+ ascent = 0.75;
+ }
+ if (descent < -0.5) {
+ descent = -0.25;
+ }
+ } else {
+ fontID.num = -1;
+ fontID.gen = -1;
+ ascent = 0.75;
+ descent = -0.25;
+ }
fontName = (gfxFont && gfxFont->getName()) ? gfxFont->getName()->copy()
: (GString *)NULL;
flags = gfxFont ? gfxFont->getFlags() : 0;
-#endif
+ mWidth = 0;
+ if (gfxFont && !gfxFont->isCIDFont()) {
+ char *name;
+ int code;
+ for (code = 0; code < 256; ++code) {
+ if ((name = ((Gfx8BitFont *)gfxFont)->getCharName(code)) &&
+ name[0] == 'm' && name[1] == '\0') {
+ mWidth = ((Gfx8BitFont *)gfxFont)->getWidth(code);
+ break;
+ }
+ }
+ }
}
TextFontInfo::~TextFontInfo() {
-#if TEXTOUT_WORD_LIST
if (fontName) {
delete fontName;
}
-#endif
}
GBool TextFontInfo::matches(GfxState *state) {
- return state->getFont() == gfxFont;
+ Ref *id;
+
+ if (!state->getFont()) {
+ return gFalse;
+ }
+ id = state->getFont()->getID();
+ return id->num == fontID.num && id->gen == fontID.gen;
}
//------------------------------------------------------------------------
// TextWord
//------------------------------------------------------------------------
-TextWord::TextWord(GfxState *state, int rotA, double x0, double y0,
- TextFontInfo *fontA, double fontSizeA) {
- GfxFont *gfxFont;
- double x, y, ascent, descent;
- int wMode;
+// Build a TextWord object, using chars[start .. start+len-1].
+// (If rot >= 2, the chars list is in reverse order.)
+TextWord::TextWord(GList *chars, int start, int lenA,
+ int rotA, GBool spaceAfterA) {
+ TextChar *ch;
+ int i;
rot = rotA;
- font = fontA;
- fontSize = fontSizeA;
- state->transform(x0, y0, &x, &y);
- if ((gfxFont = font->gfxFont)) {
- ascent = gfxFont->getAscent() * fontSize;
- descent = gfxFont->getDescent() * fontSize;
- wMode = gfxFont->getWMode();
- } else {
- // this means that the PDF file draws text without a current font,
- // which should never happen
- ascent = 0.95 * fontSize;
- descent = -0.35 * fontSize;
- wMode = 0;
- }
- if (wMode) { // vertical writing mode
- // NB: the rotation value has been incremented by 1 (in
- // TextPage::beginWord()) for vertical writing mode
- switch (rot) {
- case 0:
- yMin = y - fontSize;
- yMax = y;
- base = y;
- break;
- case 1:
- xMin = x;
- xMax = x + fontSize;
- base = x;
- break;
- case 2:
- yMin = y;
- yMax = y + fontSize;
- base = y;
- break;
- case 3:
- xMin = x - fontSize;
- xMax = x;
- base = x;
- break;
+ len = lenA;
+ text = (Unicode *)gmallocn(len, sizeof(Unicode));
+ edge = (double *)gmallocn(len + 1, sizeof(double));
+ charPos = (int *)gmallocn(len + 1, sizeof(int));
+ switch (rot) {
+ case 0:
+ default:
+ ch = (TextChar *)chars->get(start);
+ xMin = ch->xMin;
+ yMin = ch->yMin;
+ yMax = ch->yMax;
+ ch = (TextChar *)chars->get(start + len - 1);
+ xMax = ch->xMax;
+ break;
+ case 1:
+ ch = (TextChar *)chars->get(start);
+ xMin = ch->xMin;
+ xMax = ch->xMax;
+ yMin = ch->yMin;
+ ch = (TextChar *)chars->get(start + len - 1);
+ yMax = ch->yMax;
+ break;
+ case 2:
+ ch = (TextChar *)chars->get(start);
+ xMax = ch->xMax;
+ yMin = ch->yMin;
+ yMax = ch->yMax;
+ ch = (TextChar *)chars->get(start + len - 1);
+ xMin = ch->xMin;
+ break;
+ case 3:
+ ch = (TextChar *)chars->get(start);
+ xMin = ch->xMin;
+ xMax = ch->xMax;
+ yMax = ch->yMax;
+ ch = (TextChar *)chars->get(start + len - 1);
+ yMin = ch->yMin;
+ break;
+ }
+ for (i = 0; i < len; ++i) {
+ ch = (TextChar *)chars->get(rot >= 2 ? start + len - 1 - i : start + i);
+ text[i] = ch->c;
+ charPos[i] = ch->charPos;
+ if (i == len - 1) {
+ charPos[len] = ch->charPos + ch->charLen;
}
- } else { // horizontal writing mode
switch (rot) {
case 0:
- yMin = y - ascent;
- yMax = y - descent;
- if (yMin == yMax) {
- // this is a sanity check for a case that shouldn't happen -- but
- // if it does happen, we want to avoid dividing by zero later
- yMin = y;
- yMax = y + 1;
+ default:
+ edge[i] = ch->xMin;
+ if (i == len - 1) {
+ edge[len] = ch->xMax;
}
- base = y;
break;
case 1:
- xMin = x + descent;
- xMax = x + ascent;
- if (xMin == xMax) {
- // this is a sanity check for a case that shouldn't happen -- but
- // if it does happen, we want to avoid dividing by zero later
- xMin = x;
- xMax = x + 1;
+ edge[i] = ch->yMin;
+ if (i == len - 1) {
+ edge[len] = ch->yMax;
}
- base = x;
break;
case 2:
- yMin = y + descent;
- yMax = y + ascent;
- if (yMin == yMax) {
- // this is a sanity check for a case that shouldn't happen -- but
- // if it does happen, we want to avoid dividing by zero later
- yMin = y;
- yMax = y + 1;
+ edge[i] = ch->xMax;
+ if (i == len - 1) {
+ edge[len] = ch->xMin;
}
- base = y;
break;
case 3:
- xMin = x - ascent;
- xMax = x - descent;
- if (xMin == xMax) {
- // this is a sanity check for a case that shouldn't happen -- but
- // if it does happen, we want to avoid dividing by zero later
- xMin = x;
- xMax = x + 1;
+ edge[i] = ch->yMax;
+ if (i == len - 1) {
+ edge[len] = ch->yMin;
}
- base = x;
break;
}
}
- text = NULL;
- edge = NULL;
- charPos = NULL;
- len = size = 0;
- spaceAfter = gFalse;
- next = NULL;
-
-#if TEXTOUT_WORD_LIST
- GfxRGB rgb;
-
- if ((state->getRender() & 3) == 1) {
- state->getStrokeRGB(&rgb);
- } else {
- state->getFillRGB(&rgb);
- }
- colorR = colToDbl(rgb.r);
- colorG = colToDbl(rgb.g);
- colorB = colToDbl(rgb.b);
-#endif
-
+ ch = (TextChar *)chars->get(start);
+ font = ch->font;
+ fontSize = ch->fontSize;
+ spaceAfter = spaceAfterA;
underlined = gFalse;
link = NULL;
+ colorR = ch->colorR;
+ colorG = ch->colorG;
+ colorB = ch->colorB;
+ invisible = ch->invisible;
+}
+
+TextWord::TextWord(TextWord *word) {
+ *this = *word;
+ text = (Unicode *)gmallocn(len, sizeof(Unicode));
+ memcpy(text, word->text, len * sizeof(Unicode));
+ edge = (double *)gmallocn(len + 1, sizeof(double));
+ memcpy(edge, word->edge, (len + 1) * sizeof(double));
+ charPos = (int *)gmallocn(len + 1, sizeof(int));
+ memcpy(charPos, word->charPos, (len + 1) * sizeof(int));
}
TextWord::~TextWord() {
@@ -318,175 +596,65 @@ TextWord::~TextWord() {
gfree(charPos);
}
-void TextWord::addChar(GfxState *state, double x, double y,
- double dx, double dy, int charPosA, int charLen,
- Unicode u) {
- int wMode;
-
- if (len == size) {
- size += 16;
- text = (Unicode *)greallocn(text, size, sizeof(Unicode));
- edge = (double *)greallocn(edge, size + 1, sizeof(double));
- charPos = (int *)greallocn(charPos, size + 1, sizeof(int));
- }
- text[len] = u;
- charPos[len] = charPosA;
- charPos[len + 1] = charPosA + charLen;
- wMode = font->gfxFont ? font->gfxFont->getWMode() : 0;
- if (wMode) { // vertical writing mode
- // NB: the rotation value has been incremented by 1 (in
- // TextPage::beginWord()) for vertical writing mode
- switch (rot) {
- case 0:
- if (len == 0) {
- xMin = x - fontSize;
- }
- edge[len] = x - fontSize;
- xMax = edge[len+1] = x;
- break;
- case 1:
- if (len == 0) {
- yMin = y - fontSize;
- }
- edge[len] = y - fontSize;
- yMax = edge[len+1] = y;
- break;
- case 2:
- if (len == 0) {
- xMax = x + fontSize;
- }
- edge[len] = x + fontSize;
- xMin = edge[len+1] = x;
- break;
- case 3:
- if (len == 0) {
- yMax = y + fontSize;
- }
- edge[len] = y + fontSize;
- yMin = edge[len+1] = y;
- break;
- }
- } else { // horizontal writing mode
- switch (rot) {
- case 0:
- if (len == 0) {
- xMin = x;
- }
- edge[len] = x;
- xMax = edge[len+1] = x + dx;
- break;
- case 1:
- if (len == 0) {
- yMin = y;
- }
- edge[len] = y;
- yMax = edge[len+1] = y + dy;
- break;
- case 2:
- if (len == 0) {
- xMax = x;
- }
- edge[len] = x;
- xMin = edge[len+1] = x + dx;
- break;
- case 3:
- if (len == 0) {
- yMax = y;
- }
- edge[len] = y;
- yMin = edge[len+1] = y + dy;
- break;
- }
- }
- ++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 @@ void TextWord::getCharBBox(int charIdx,
}
}
-#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 where large char 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 to which clipped char 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 TextPage::findText(Unicode *s, int
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 @@ GBool TextPage::findText(Unicode *s, int
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 @@ void TextPage::dump(void *outputStream,
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 @@ static void outputToFile(void *stream, c
fwrite(text, 1, len, (FILE *)stream);
}
-TextOutputDev::TextOutputDev(char *fileName, GBool physLayoutA,
- double fixedPitchA, GBool rawOrderA,
+TextOutputDev::TextOutputDev(char *fileName, TextOutputControl *controlA,
GBool append) {
text = NULL;
- physLayout = physLayoutA;
- fixedPitch = physLayout ? fixedPitchA : 0;
- rawOrder = rawOrderA;
- doHTML = gFalse;
+ control = *controlA;
ok = gTrue;
// open file
@@ -4070,28 +4214,21 @@ TextOutputDev::TextOutputDev(char *fileN
}
// set up text object
- text = new TextPage(rawOrderA);
+ text = new TextPage(&control);
}
TextOutputDev::TextOutputDev(TextOutputFunc func, void *stream,
- GBool physLayoutA, double fixedPitchA,
- GBool rawOrderA) {
+ TextOutputControl *controlA) {
outputFunc = func;
outputStream = stream;
needClose = gFalse;
- physLayout = physLayoutA;
- fixedPitch = physLayout ? fixedPitchA : 0;
- rawOrder = rawOrderA;
- doHTML = gFalse;
- text = new TextPage(rawOrderA);
+ control = *controlA;
+ text = new TextPage(&control);
ok = gTrue;
}
TextOutputDev::~TextOutputDev() {
if (needClose) {
-#ifdef MACOS
- ICS_MapRefNumAndAssign((short)((FILE *)outputStream)->handle);
-#endif
fclose((FILE *)outputStream);
}
if (text) {
@@ -4104,10 +4241,8 @@ void TextOutputDev::startPage(int pageNu
}
void TextOutputDev::endPage() {
- text->endPage();
- text->coalesce(physLayout, fixedPitch, doHTML);
if (outputStream) {
- text->dump(outputStream, outputFunc, physLayout);
+ text->write(outputStream, outputFunc);
}
}
@@ -4129,7 +4264,7 @@ void TextOutputDev::drawChar(GfxState *s
double dx, double dy,
double originX, double originY,
CharCode c, int nBytes, Unicode *u, int uLen) {
- text->addChar(state, x - originX, y - originY, dx, dy, c, nBytes, u, uLen);
+ text->addChar(state, x, y, dx, dy, c, nBytes, u, uLen);
}
void TextOutputDev::incCharCount(int nChars) {
@@ -4149,7 +4284,7 @@ void TextOutputDev::stroke(GfxState *sta
GfxSubpath *subpath;
double x[2], y[2];
- if (!doHTML) {
+ if (!control.html) {
return;
}
path = state->getPath();
@@ -4176,7 +4311,7 @@ void TextOutputDev::fill(GfxState *state
double rx0, ry0, rx1, ry1, t;
int i;
- if (!doHTML) {
+ if (!control.html) {
return;
}
path = state->getPath();
@@ -4238,7 +4373,7 @@ void TextOutputDev::fill(GfxState *state
}
void TextOutputDev::eoFill(GfxState *state) {
- if (!doHTML) {
+ if (!control.html) {
return;
}
fill(state);
@@ -4248,7 +4383,7 @@ void TextOutputDev::processLink(Link *li
double x1, y1, x2, y2;
int xMin, yMin, xMax, yMax, x, y;
- if (!doHTML) {
+ if (!control.html) {
return;
}
link->getRect(&x1, &y1, &x2, &y2);
@@ -4315,16 +4450,14 @@ GBool TextOutputDev::findCharRange(int p
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 -uNrp 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 @@ class TextPage;
typedef void (*TextOutputFunc)(void *stream, const char *text, int len);
//------------------------------------------------------------------------
+// TextOutputControl
+//------------------------------------------------------------------------
+
+enum TextOutputMode {
+ textOutReadingOrder, // format into reading order
+ textOutPhysLayout, // maintain original physical layout
+ textOutTableLayout, // similar to PhysLayout, but optimized
+ // for tables
+ textOutLinePrinter, // strict fixed-pitch/height layout
+ textOutRawOrder // keep text in content stream order
+};
+
+class TextOutputControl {
+public:
+
+ TextOutputControl();
+ ~TextOutputControl() {}
+
+ TextOutputMode mode; // formatting mode
+ double fixedPitch; // if this is non-zero, assume fixed-pitch
+ // characters with this width
+ // (only relevant for PhysLayout, Table,
+ // and LinePrinter modes)
+ double fixedLineSpacing; // fixed line spacing (only relevant for
+ // LinePrinter mode)
+ GBool html; // enable extra processing for HTML
+ GBool clipText; // separate clipped text and add it back
+ // in after forming columns
+};
+
+//------------------------------------------------------------------------
// TextFontInfo
//------------------------------------------------------------------------
@@ -52,7 +75,6 @@ public:
GBool matches(GfxState *state);
-#if TEXTOUT_WORD_LIST
// Get the font name (which may be NULL).
GString *getFontName() { return fontName; }
@@ -62,18 +84,21 @@ public:
GBool isSymbolic() { return flags & fontSymbolic; }
GBool isItalic() { return flags & fontItalic; }
GBool isBold() { return flags & fontBold; }
-#endif
+
+ // Get the width of the 'm' character, if available.
+ double getMWidth() { return mWidth; }
private:
- GfxFont *gfxFont;
-#if TEXTOUT_WORD_LIST
+ Ref fontID;
GString *fontName;
int flags;
-#endif
+ double mWidth;
+ double ascent, descent;
- friend class TextWord;
+ friend class TextLine;
friend class TextPage;
+ friend class TextWord;
};
//------------------------------------------------------------------------
@@ -83,44 +108,21 @@ private:
class TextWord {
public:
- // Constructor.
- TextWord(GfxState *state, int rotA, double x0, double y0,
- TextFontInfo *fontA, double fontSize);
-
- // Destructor.
+ TextWord(GList *chars, int start, int lenA,
+ int rotA, GBool spaceAfterA);
~TextWord();
-
- // Add a character to the word.
- void addChar(GfxState *state, double x, double y,
- double dx, double dy, int charPosA, int charLen,
- Unicode u);
-
- // Merge onto the end of .
- void merge(TextWord *word);
-
- // Compares to , 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 and
- // .
- double primaryDelta(TextWord *word);
-
- static int cmpYX(const void *p1, const void *p2);
+ TextWord *copy() { return new TextWord(this); }
// Get the TextFontInfo object associated with this word.
TextFontInfo *getFontInfo() { return font; }
- // Get the next TextWord on the linked list.
- TextWord *getNext() { return next; }
-
-#if TEXTOUT_WORD_LIST
int getLength() { return len; }
Unicode getChar(int idx) { return text[idx]; }
GString *getText();
GString *getFontName() { return font->fontName; }
void getColor(double *r, double *g, double *b)
{ *r = colorR; *g = colorG; *b = colorB; }
+ GBool isInvisible() { return invisible; }
void getBBox(double *xMinA, double *yMinA, double *xMaxA, double *yMaxA)
{ *xMinA = xMin; *yMinA = yMin; *xMaxA = xMax; *yMaxA = yMax; }
void getCharBBox(int charIdx, double *xMinA, double *yMinA,
@@ -130,76 +132,43 @@ public:
int getCharPos() { return charPos[0]; }
int getCharLen() { return charPos[len] - charPos[0]; }
GBool getSpaceAfter() { return spaceAfter; }
-#endif
-
+ double getBaseline();
GBool isUnderlined() { return underlined; }
- Link *getLink() { return link; }
+ GString *getLinkURI();
private:
+ TextWord(TextWord *word);
+ void appendChar(TextChar *ch);
+ static int cmpYX(const void *p1, const void *p2);
+ static int cmpCharPos(const void *p1, const void *p2);
+
int rot; // rotation, multiple of 90 degrees
// (0, 1, 2, or 3)
double xMin, xMax; // bounding box x coordinates
double yMin, yMax; // bounding box y coordinates
- double base; // baseline x or y coordinate
Unicode *text; // the text
- double *edge; // "near" edge x or y coord of each char
- // (plus one extra entry for the last char)
int *charPos; // character position (within content stream)
// of each char (plus one extra entry for
// the last char)
- int len; // length of text/edge/charPos arrays
- int size; // size of text/edge/charPos arrays
+ double *edge; // "near" edge x or y coord of each char
+ // (plus one extra entry for the last char)
+ int len; // number of characters
TextFontInfo *font; // font information
double fontSize; // font size
GBool spaceAfter; // set if there is a space between this
// word and the next word on the line
- TextWord *next; // next word in line
-#if TEXTOUT_WORD_LIST
+ GBool underlined;
+ TextLink *link;
+
double colorR, // word color
colorG,
colorB;
-#endif
-
- GBool underlined;
- Link *link;
-
- friend class TextPool;
- friend class TextLine;
- friend class TextBlock;
- friend class TextFlow;
- friend class TextWordList;
- friend class TextPage;
-};
-
-//------------------------------------------------------------------------
-// TextPool
-//------------------------------------------------------------------------
-
-class TextPool {
-public:
-
- TextPool();
- ~TextPool();
-
- TextWord *getPool(int baseIdx) { return pool[baseIdx - minBaseIdx]; }
- void setPool(int baseIdx, TextWord *p) { pool[baseIdx - minBaseIdx] = p; }
-
- int getBaseIdx(double base);
-
- void addWord(TextWord *word);
-
-private:
-
- int minBaseIdx; // min baseline bucket index
- int maxBaseIdx; // max baseline bucket index
- TextWord **pool; // array of linked lists, one for each
- // baseline value (multiple of 4 pts)
- TextWord *cursor; // pointer to last-accessed word
- int cursorBaseIdx; // baseline bucket index of last-accessed word
+ GBool invisible; // set for invisible text (render mode 3)
friend class TextBlock;
+ friend class TextLine;
friend class TextPage;
};
@@ -210,168 +179,92 @@ private:
class TextLine {
public:
- TextLine(TextBlock *blkA, int rotA, double baseA);
+ TextLine(GList *wordsA, double xMinA, double yMinA,
+ double xMaxA, double yMaxA, double fontSizeA);
~TextLine();
- void addWord(TextWord *word);
-
- // Return the distance along the primary axis between and
- // .
- double primaryDelta(TextLine *line);
-
- // Compares to , returning -1 (<), 0 (=), or +1 (>),
- // based on a primary-axis comparison, e.g., x ordering if rot=0.
- int primaryCmp(TextLine *line);
-
- // Compares to , 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 .
- 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 is below , 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 fits below 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 @@ private:
class TextWordList {
public:
- // Build a flat word list, in content stream order (if
- // text->rawOrder is true), physical layout order (if
- // is true and text->rawOrder is false), or reading order (if both
- // flags are false).
- TextWordList(TextPage *text, GBool physLayout);
+ TextWordList(GList *wordsA);
~TextWordList();
@@ -398,8 +287,6 @@ private:
GList *words; // [TextWord]
};
-#endif // TEXTOUT_WORD_LIST
-
//------------------------------------------------------------------------
// TextPage
//------------------------------------------------------------------------
@@ -407,52 +294,11 @@ private:
class TextPage {
public:
- // Constructor.
- TextPage(GBool rawOrderA);
-
- // Destructor.
+ TextPage(TextOutputControl *controlA);
~TextPage();
- // Start a new page.
- void startPage(GfxState *state);
-
- // End the current page.
- void endPage();
-
- // Update the current font.
- void updateFont(GfxState *state);
-
- // Begin a new word.
- void beginWord(GfxState *state, double x0, double y0);
-
- // Add a character to the current word.
- void addChar(GfxState *state, double x, double y,
- double dx, double dy,
- CharCode c, int nBytes, Unicode *u, int uLen);
-
- // Add 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 is true, starts looking at the
// top of the page; else if is true, starts looking
@@ -480,39 +326,106 @@ public:
double *xMin, double *yMin,
double *xMax, double *yMax);
- // Dump contents of page to a file.
- void dump(void *outputStream, TextOutputFunc outputFunc,
- GBool physLayout);
+ // Create and return a list of TextColumn objects.
+ GList *makeColumns();
- // Get the head of the linked list of TextFlows.
- TextFlow *getFlows() { return flows; }
+ // Get the list of all TextFontInfo objects used on this page.
+ GList *getFonts() { return fonts; }
-#if TEXTOUT_WORD_LIST
- // Build a flat word list, in content stream order (if
- // this->rawOrder is true), physical layout order (if
- // 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 @@ private:
actualTextY1;
int actualTextNBytes;
- TextPool *pools[4]; // a "pool" of TextWords for each rotation
- TextFlow *flows; // linked list of flows
- TextBlock **blocks; // array of blocks, in yx order
- int nBlocks; // number of blocks
- int primaryRot; // primary rotation
- GBool primaryLR; // primary direction (true means L-to-R,
- // false means R-to-L)
- TextWord *rawWords; // list of words, in raw order (only if
- // rawOrder is set)
- TextWord *rawLastWord; // last word on rawWords list
-
+ GList *chars; // [TextChar]
GList *fonts; // all font info objects used on this
// page [TextFontInfo]
+ GList *underlines; // [TextUnderline]
+ GList *links; // [TextLink]
+
+ GList *findCols; // text used by the findText function
+ // [TextColumn]
+ GBool findLR; // primary text direction, used by the
+ // findText function
double lastFindXMin, // coordinates of the last "find" result
lastFindYMin;
GBool haveLastFind;
- GList *underlines; // [TextUnderline]
- GList *links; // [TextLink]
-
- friend class TextLine;
- friend class TextLineFrag;
- friend class TextBlock;
- friend class TextFlow;
- friend class TextWordList;
+ friend class TextOutputDev;
};
//------------------------------------------------------------------------
@@ -561,8 +464,7 @@ public:
// is true, the original physical layout of the text
// is maintained. If is true, the text is kept in
// content stream order.
- TextOutputDev(char *fileName, GBool physLayoutA,
- double fixedPitchA, GBool rawOrderA,
+ TextOutputDev(char *fileName, TextOutputControl *controlA,
GBool append);
// Create a TextOutputDev which will write to a generic stream. If
@@ -570,8 +472,7 @@ public:
// is maintained. If is true, the text is kept in
// content stream order.
TextOutputDev(TextOutputFunc func, void *stream,
- GBool physLayoutA, double fixedPitchA,
- GBool rawOrderA);
+ TextOutputControl *controlA);
// Destructor.
virtual ~TextOutputDev();
@@ -660,20 +561,18 @@ public:
double *xMin, double *yMin,
double *xMax, double *yMax);
-#if TEXTOUT_WORD_LIST
// Build a flat word list, in content stream order (if
// this->rawOrder is true), physical layout order (if
// this->physLayout is true and this->rawOrder is false), or reading
// order (if both flags are false).
TextWordList *makeWordList();
-#endif
// Returns the TextPage object for the last rasterized page,
// transferring ownership to the caller.
TextPage *takeText();
// Turn extra processing for HTML conversion on or off.
- void enableHTMLExtras(GBool doHTMLA) { doHTML = doHTMLA; }
+ void enableHTMLExtras(GBool html) { control.html = html; }
private:
@@ -682,13 +581,7 @@ private:
GBool needClose; // need to close the output file?
// (only if outputStream is a FILE*)
TextPage *text; // text for the current page
- GBool physLayout; // maintain original physical layout when
- // dumping text
- double fixedPitch; // if physLayout is true and this is non-zero,
- // assume fixed-pitch characters with this
- // width
- GBool rawOrder; // keep text in content stream order
- GBool doHTML; // extra processing for HTML conversion
+ TextOutputControl control; // formatting parameters
GBool ok; // set up ok?
};
diff -uNrp 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
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include
+#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 -uNrp 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
+
+#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 -uNrp 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 @@ struct UnicodeCaseTableVector {
};
static UnicodeMapTableEntry typeTable[256] = {
- { "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN###NNNNN################NNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN#N####NNNNLNNNNN####NLNNN#LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLL", 'X' },
+ { "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN...NNNNN.....##########.NNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN.N....NNNNLNNNNN..##NLNNN#LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLL", 'X' },
{ NULL, 'L' },
{ "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLNNNNNNNNNNNNNNLLNNNNNNNNNNNNNNLLLLLNNNNNNNNNLNNNNNNNNNNNNNNNNN", 'X' },
{ "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLNNNNNNNNNNNLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLL", 'X' },
{ "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
{ "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNRNRNNRNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR", 'X' },
- { "RRRR#########RNNNNNNNNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNNNNNNNNNNNNN####################RRRNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNNNNNNRNNNNNNNRRNNNNNNNRR##########RRRRRR", 'X' },
+ { "RRRR.........RNNNNNNNNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNNNNNNNNNNNNN#################.##RRRNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNNNNNNRNNNNNNNRRNNNNNNNRR##########RRRRRR", 'X' },
{ "RRRRRRRRRRRRRRNNRNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNNNNNNNNNNNNNNNNNNNNNNNNNNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNNNNNNNNNNRNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },
{ NULL, 'N' },
- { "NNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNNNNNLLLLNLLLNNNNLLLLLLLLLLLLLNNLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNLLLLLLLLNLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLL##LLLLLLLNNNNN", 'X' },
- { "NNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNNNNNLLLLNLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLL##NNNNNNNNNNNNNN", 'X' },
- { "NNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLNLNNNLLLLLLLLLNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNN#NLLLLL", 'X' },
+ { "NNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNNNNNLLLLNLLLNNNNLLLLLLLLLLLLLNNLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNLLLLLLLLNLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLL..LLLLLLLNNNNN", 'X' },
+ { "NNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNNNNNLLLLNLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLL..NNNNNNNNNNNNNN", 'X' },
+ { "NNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLNLNNNLLLLLLLLLNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNN.NLLLLL", 'X' },
{ "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNLLLLNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
{ "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNLLLLLLLNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
- { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLNNNNNNN#####LLLLLLLNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLNNNNNNNNNLLLLLLLLLLNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
+ { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLNNNNNNN.....LLLLLLLNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLNNNNNNNNNLLLLLLLLLLNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
{ "LLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLNLNLNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNLNNNNNLNNLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
{ "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNLNNNNNNLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
{ NULL, 'L' },
@@ -43,7 +43,7 @@ static UnicodeMapTableEntry typeTable[25
{ NULL, 'L' },
{ NULL, 'L' },
{ "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
- { "LLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNLLLLLLLLNLLNNNNNNNNNNNLLLLLLL#LNLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNN", 'X' },
+ { "LLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNLLLLLLLLNLLNNNNNNNNNNNLLLLLLL.LNLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNN", 'X' },
{ "NNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
{ "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLNNNNNLLLLLLNLLLLLLNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },
{ NULL, 'L' },
@@ -52,9 +52,9 @@ static UnicodeMapTableEntry typeTable[25
{ NULL, 'L' },
{ NULL, 'L' },
{ "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLNNNLLLLLLLLLLLNNNLLLLLLLLLLLLNNNNLLLLLLLLLLLLLNNNLLLLLLLLLLLLLNNN", 'X' },
- { "NNNNNNNNNNNNNNLRNNNNNNNNNNNNNNNNNNNNNNNNNNLRNLRN#####NNNNNNNNNNNNNNN#NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN#L##########NNNL############NNN###################################NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },
- { "NNLNNNNLNNLLLLLLLLLLNLNNNLLLLLNNNNNNLNLNLNLLLL#LLLNLLLLLLLNNLLLLNNNNNLLLLLNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },
- { "NNNNNNNNNNNNNNNNNN##NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },
+ { "NNNNNNNNNNNNNNLRNNNNNNNNNNNNNNNNNNNNNNNNNNLRNLRN.....NNNNNNNNNNNNNNN.NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN#L########..NNNL##########..NNN...................................NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },
+ { "NNLNNNNLNNLLLLLLLLLLNLNNNLLLLLNNNNNNLNLNLNLLLL.LLLNLLLLLLLNNLLLLNNNNNLLLLLNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },
+ { "NNNNNNNNNNNNNNNNNN..NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },
{ "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },
{ "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN####################LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNN", 'X' },
{ NULL, 'N' },
@@ -271,11 +271,11 @@ static UnicodeMapTableEntry typeTable[25
{ NULL, 'L' },
{ NULL, 'L' },
{ NULL, 'L' },
- { "LLLLLLLLLLLLLLLLLLLLLLLLRRRRRRNRRRRRRRRRR#RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR", 'X' },
+ { "LLLLLLLLLLLLLLLLLLLLLLLLRRRRRRNRRRRRRRRRR.RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR", 'X' },
{ NULL, 'R' },
{ "RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNN", 'X' },
- { "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN#N#NN#NNNNNNNNN#NN##NNNNN##NRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNN", 'X' },
- { "NNN###NNNNN################NNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL#####NNN##NNNNNNNNNNNNNNNNNNNNNNNLL", 'X' }
+ { "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN.N.NN.NNNNNNNNN.NN..NNNNN..NRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNN", 'X' },
+ { "NNN...NNNNN.....##########.NNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL.....NNN..NNNNNNNNNNNNNNNNNNNNNNNLL", 'X' }
};
static UnicodeCaseTableVector caseTable00 = {{
@@ -935,13 +935,23 @@ GBool unicodeTypeR(Unicode c) {
}
GBool unicodeTypeNum(Unicode c) {
- return getType(c) == '#';
+ char t;
+
+ t = getType(c);
+ return t == '#' || t == '.';
}
GBool unicodeTypeAlphaNum(Unicode c) {
char t;
t = getType(c);
+ return t == 'L' || t == 'R' || t == '#' || t == '.';
+}
+
+GBool unicodeTypeWord(Unicode c) {
+ char t;
+
+ t = getType(c);
return t == 'L' || t == 'R' || t == '#';
}
diff -uNrp 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 unicodeTypeNum(Unicode c);
extern GBool unicodeTypeAlphaNum(Unicode c);
+extern GBool unicodeTypeWord(Unicode c);
+
extern Unicode unicodeToUpper(Unicode c);
#endif
diff -uNrp 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
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include
+#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 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 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 for a font matching(, ,
+// ).
+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 -uNrp 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
+
+#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 -uNrp 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 @@ static ArgDesc argDesc[] = {
"initial zoom level (percent, 'page', 'width')"},
{"-cont", argFlag, &contView, 0,
"start in continuous view mode" },
-#if HAVE_T1LIB_H
- {"-t1lib", argString, enableT1libStr, sizeof(enableT1libStr),
- "enable t1lib font rasterizer: yes, no"},
-#endif
#if HAVE_FREETYPE_FREETYPE_H | HAVE_FREETYPE_H
{"-freetype", argString, enableFreeTypeStr, sizeof(enableFreeTypeStr),
"enable FreeType font rasterizer: yes, no"},
@@ -185,11 +180,6 @@ int main(int argc, char *argv[]) {
fprintf(stderr, "Bad '-eol' value on command line\n");
}
}
- if (enableT1libStr[0]) {
- if (!globalParams->setEnableT1lib(enableT1libStr)) {
- fprintf(stderr, "Bad '-t1lib' value on command line\n");
- }
- }
if (enableFreeTypeStr[0]) {
if (!globalParams->setEnableFreeType(enableFreeTypeStr)) {
fprintf(stderr, "Bad '-freetype' value on command line\n");
diff -uNrp 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 @@ void XPDFCore::doAction(LinkAction *acti
}
s = ((LinkGoToR *)action)->getFileName()->getCString();
//~ translate path name for VMS (deal with '/')
- if (isAbsolutePath(s)) {
+ if (isAbsolutePath(s) || !doc->getFileName()) {
fileName = new GString(s);
} else {
fileName = appendToPath(grabPath(doc->getFileName()->getCString()), s);
@@ -531,7 +531,7 @@ void XPDFCore::doAction(LinkAction *acti
if (!strcmp(s + fileName->getLength() - 4, ".pdf") ||
!strcmp(s + fileName->getLength() - 4, ".PDF")) {
//~ translate path name for VMS (deal with '/')
- if (isAbsolutePath(s)) {
+ if (isAbsolutePath(s) || !doc->getFileName()) {
fileName = fileName->copy();
} else {
fileName = appendToPath(grabPath(doc->getFileName()->getCString()), s);
@@ -640,7 +640,8 @@ void XPDFCore::doAction(LinkAction *acti
if (movieAnnot.dictLookup("Movie", &obj1)->isDict()) {
if (obj1.dictLookup("F", &obj2)) {
if ((fileName = LinkAction::getFileSpecName(&obj2))) {
- if (!isAbsolutePath(fileName->getCString())) {
+ if (!isAbsolutePath(fileName->getCString()) &&
+ doc->getFileName()) {
fileName2 = appendToPath(
grabPath(doc->getFileName()->getCString()),
fileName->getCString());
@@ -658,6 +659,13 @@ void XPDFCore::doAction(LinkAction *acti
movieAnnot.free();
break;
+ // unsupported action types
+ case actionJavaScript:
+ case actionSubmitForm:
+ case actionHide:
+ error(errSyntaxError, -1, "Unsupported link action type");
+ break;
+
// unknown action type
case actionUnknown:
error(errSyntaxError, -1, "Unknown link action type: '{0:t}'",
@@ -1124,6 +1132,9 @@ void XPDFCore::inputCbk(Widget widget, X
case actionMovie:
s = "[movie]";
break;
+ case actionJavaScript:
+ case actionSubmitForm:
+ case actionHide:
case actionUnknown:
s = "[unknown link]";
break;
@@ -1356,6 +1367,8 @@ void XPDFCore::redrawRect(PDFCoreTile *t
XFillRectangle(display, drawAreaWin, drawAreaGC,
xDest, yDest, width, height);
}
+
+ XFlush(display);
}
void XPDFCore::updateScrollbars() {
diff -uNrp 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 @@ XPDFViewerCmd XPDFViewer::cmdTab[] = {
{ "about", 0, gFalse, gFalse, &XPDFViewer::cmdAbout },
{ "closeOutline", 0, gFalse, gFalse, &XPDFViewer::cmdCloseOutline },
{ "closeWindow", 0, gFalse, gFalse, &XPDFViewer::cmdCloseWindow },
+ { "closeWindowOrQuit", 0, gFalse, gFalse, &XPDFViewer::cmdCloseWindowOrQuit },
{ "continuousMode", 0, gFalse, gFalse, &XPDFViewer::cmdContinuousMode },
{ "endPan", 0, gTrue, gTrue, &XPDFViewer::cmdEndPan },
{ "endSelection", 0, gTrue, gTrue, &XPDFViewer::cmdEndSelection },
@@ -384,7 +385,9 @@ void XPDFViewer::open(GString *fileName,
int pg;
double z;
- if (!core->getDoc() || fileName->cmp(core->getDoc()->getFileName())) {
+ if (!core->getDoc() ||
+ !core->getDoc()->getFileName() ||
+ fileName->cmp(core->getDoc()->getFileName())) {
if (!loadFile(fileName, NULL, NULL)) {
return;
}
@@ -445,7 +448,7 @@ GBool XPDFViewer::loadFile(GString *file
void XPDFViewer::reloadFile() {
int pg;
- if (!core->getDoc()) {
+ if (!core->getDoc() || !core->getDoc()->getFileName()) {
return;
}
pg = core->getPageNum();
@@ -808,6 +811,11 @@ void XPDFViewer::cmdCloseWindow(GString
app->close(this, gFalse);
}
+void XPDFViewer::cmdCloseWindowOrQuit(GString *args[], int nArgs,
+ XEvent *event) {
+ app->close(this, gTrue);
+}
+
void XPDFViewer::cmdContinuousMode(GString *args[], int nArgs,
XEvent *event) {
Widget btn;
@@ -1803,7 +1811,7 @@ void XPDFViewer::initToolbar(Widget pare
menuPane = XmCreatePulldownMenu(toolBar, "zoomMenuPane", args, n);
for (i = 0; i < nZoomMenuItems; ++i) {
n = 0;
- s = XmStringCreateLocalized(zoomMenuInfo[i].label);
+ s = XmStringCreateLocalized((char *)zoomMenuInfo[i].label);
XtSetArg(args[n], XmNlabelString, s); ++n;
XtSetArg(args[n], XmNuserData, (XtPointer)i); ++n;
sprintf(buf, "zoom%d", i);
@@ -3422,17 +3430,18 @@ void XPDFViewer::setupPrintDialog() {
doc = core->getDoc();
psFileName = globalParams->getPSFile();
if (!psFileName || psFileName->getChar(0) == '|') {
- pdfFileName = doc->getFileName();
- p = pdfFileName->getCString() + pdfFileName->getLength() - 4;
- if (!strcmp(p, ".pdf") || !strcmp(p, ".PDF")) {
- psFileName2 = new GString(pdfFileName->getCString(),
- pdfFileName->getLength() - 4);
- } else {
- psFileName2 = pdfFileName->copy();
+ if ((pdfFileName = doc->getFileName())) {
+ p = pdfFileName->getCString() + pdfFileName->getLength() - 4;
+ if (!strcmp(p, ".pdf") || !strcmp(p, ".PDF")) {
+ psFileName2 = new GString(pdfFileName->getCString(),
+ pdfFileName->getLength() - 4);
+ } else {
+ psFileName2 = pdfFileName->copy();
+ }
+ psFileName2->append(".ps");
+ XmTextFieldSetString(printFileText, psFileName2->getCString());
+ delete psFileName2;
}
- psFileName2->append(".ps");
- XmTextFieldSetString(printFileText, psFileName2->getCString());
- delete psFileName2;
}
if (psFileName && psFileName->getChar(0) == '|') {
XmToggleButtonSetState(printWithCmdBtn, True, False);
diff -uNrp 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 @@ private:
void cmdAbout(GString *args[], int nArgs, XEvent *event);
void cmdCloseOutline(GString *args[], int nArgs, XEvent *event);
void cmdCloseWindow(GString *args[], int nArgs, XEvent *event);
+ void cmdCloseWindowOrQuit(GString *args[], int nArgs, XEvent *event);
void cmdContinuousMode(GString *args[], int nArgs, XEvent *event);
void cmdEndPan(GString *args[], int nArgs, XEvent *event);
void cmdEndSelection(GString *args[], int nArgs, XEvent *event);
diff -uNrp 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
#include
#include "gmem.h"
+#include "gfile.h"
#include "Object.h"
#include "Stream.h"
#include "Lexer.h"
@@ -43,6 +44,84 @@
#define defPermFlags 0xfffc
//------------------------------------------------------------------------
+// XRefPosSet
+//------------------------------------------------------------------------
+
+class XRefPosSet {
+public:
+
+ XRefPosSet();
+ ~XRefPosSet();
+ void add(GFileOffset pos);
+ GBool check(GFileOffset pos);
+
+private:
+
+ int find(GFileOffset pos);
+
+ GFileOffset *tab;
+ int size;
+ int len;
+};
+
+XRefPosSet::XRefPosSet() {
+ size = 16;
+ len = 0;
+ tab = (GFileOffset *)gmallocn(size, sizeof(GFileOffset));
+}
+
+XRefPosSet::~XRefPosSet() {
+ gfree(tab);
+}
+
+void XRefPosSet::add(GFileOffset pos) {
+ int i;
+
+ i = find(pos);
+ if (i < len && tab[i] == pos) {
+ return;
+ }
+ if (len == size) {
+ if (size > INT_MAX / 2) {
+ gMemError("Integer overflow in XRefPosSet::add()");
+ }
+ size *= 2;
+ tab = (GFileOffset *)greallocn(tab, size, sizeof(GFileOffset));
+ }
+ if (i < len) {
+ memmove(&tab[i + 1], &tab[i], (len - i) * sizeof(GFileOffset));
+ }
+ tab[i] = pos;
+ ++len;
+}
+
+GBool XRefPosSet::check(GFileOffset pos) {
+ int i;
+
+ i = find(pos);
+ return i < len && tab[i] == pos;
+}
+
+int XRefPosSet::find(GFileOffset pos) {
+ int a, b, m;
+
+ a = - 1;
+ b = len;
+ // invariant: tab[a] < pos < tab[b]
+ while (b - a > 1) {
+ m = (a + b) / 2;
+ if (tab[m] < pos) {
+ a = m;
+ } else if (tab[m] > pos) {
+ b = m;
+ } else {
+ return m;
+ }
+ }
+ return b;
+}
+
+//------------------------------------------------------------------------
// ObjectStream
//------------------------------------------------------------------------
@@ -134,7 +213,7 @@ ObjectStream::ObjectStream(XRef *xref, i
obj2.free();
delete parser;
gfree(offsets);
- goto err1;
+ goto err2;
}
objNums[i] = obj1.getInt();
offsets[i] = obj2.getInt();
@@ -144,7 +223,7 @@ ObjectStream::ObjectStream(XRef *xref, i
(i > 0 && offsets[i] < offsets[i-1])) {
delete parser;
gfree(offsets);
- goto err1;
+ goto err2;
}
}
while (str->getChar() != EOF) ;
@@ -153,8 +232,8 @@ ObjectStream::ObjectStream(XRef *xref, i
// skip to the first object - this shouldn't be necessary because
// the First key is supposed to be equal to offsets[0], but just in
// case...
- for (i = first; i < offsets[0]; ++i) {
- objStr.getStream()->getChar();
+ if (i < offsets[0]) {
+ objStr.getStream()->discardChars(offsets[0] - i);
}
// parse the objects
@@ -175,6 +254,8 @@ ObjectStream::ObjectStream(XRef *xref, i
gfree(offsets);
ok = gTrue;
+ err2:
+ objStr.streamClose();
err1:
objStr.free();
}
@@ -203,8 +284,10 @@ Object *ObjectStream::getObject(int objI
//------------------------------------------------------------------------
XRef::XRef(BaseStream *strA, GBool repair) {
- Guint pos;
+ GFileOffset pos;
Object obj;
+ XRefPosSet *posSet;
+ int i;
ok = gTrue;
errCode = errNone;
@@ -213,12 +296,18 @@ XRef::XRef(BaseStream *strA, GBool repai
entries = NULL;
streamEnds = NULL;
streamEndsLen = 0;
- objStr = NULL;
+ for (i = 0; i < objStrCacheSize; ++i) {
+ objStrs[i] = NULL;
+ }
encrypted = gFalse;
permFlags = defPermFlags;
ownerPasswordOk = gFalse;
+ for (i = 0; i < xrefCacheSize; ++i) {
+ cache[i].num = -1;
+ }
+
str = strA;
start = str->getStart();
@@ -241,7 +330,9 @@ XRef::XRef(BaseStream *strA, GBool repai
}
// read the xref table
- while (readXRef(&pos)) ;
+ posSet = new XRefPosSet();
+ while (readXRef(&pos, posSet)) ;
+ delete posSet;
if (!ok) {
errCode = errDamaged;
return;
@@ -268,30 +359,34 @@ XRef::XRef(BaseStream *strA, GBool repai
}
XRef::~XRef() {
+ int i;
+
+ for (i = 0; i < xrefCacheSize; ++i) {
+ if (cache[i].num >= 0) {
+ cache[i].obj.free();
+ }
+ }
gfree(entries);
trailerDict.free();
if (streamEnds) {
gfree(streamEnds);
}
- if (objStr) {
- delete objStr;
+ for (i = 0; i < objStrCacheSize; ++i) {
+ if (objStrs[i]) {
+ delete objStrs[i];
+ }
}
}
// Read the 'startxref' position.
-Guint XRef::getStartXref() {
+GFileOffset XRef::getStartXref() {
char buf[xrefSearchSize+1];
char *p;
- int c, n, i;
+ int n, i;
// read last xrefSearchSize bytes
str->setPos(xrefSearchSize, -1);
- for (n = 0; n < xrefSearchSize; ++n) {
- if ((c = str->getChar()) == EOF) {
- break;
- }
- buf[n] = c;
- }
+ n = str->getBlock(buf, xrefSearchSize);
buf[n] = '\0';
// find startxref
@@ -304,86 +399,123 @@ Guint XRef::getStartXref() {
return 0;
}
for (p = &buf[i+9]; isspace(*p & 0xff); ++p) ;
- lastXRefPos = strToUnsigned(p);
+ lastXRefPos = strToFileOffset(p);
return lastXRefPos;
}
// Read one xref table section. Also reads the associated trailer
// dictionary, and returns the prev pointer (if any).
-GBool XRef::readXRef(Guint *pos) {
+GBool XRef::readXRef(GFileOffset *pos, XRefPosSet *posSet) {
Parser *parser;
Object obj;
GBool more;
+ char buf[100];
+ int n, i;
- // start up a parser, parse one token
- obj.initNull();
- parser = new Parser(NULL,
- new Lexer(NULL,
- str->makeSubStream(start + *pos, gFalse, 0, &obj)),
- gTrue);
- parser->getObj(&obj, gTrue);
+ // the xref data should either be "xref ..." (for an xref table) or
+ // "nn gg obj << ... >> stream ..." (for an xref stream); possibly
+ // preceded by whitespace
+ str->setPos(start + *pos);
+ n = str->getBlock(buf, 100);
+ for (i = 0; i < n && Lexer::isSpace(buf[i]); ++i) ;
// parse an old-style xref table
- if (obj.isCmd("xref")) {
- obj.free();
- more = readXRefTable(parser, pos);
+ if (i + 4 < n &&
+ buf[i] == 'x' && buf[i+1] == 'r' && buf[i+2] == 'e' && buf[i+3] == 'f' &&
+ Lexer::isSpace(buf[i+4])) {
+ more = readXRefTable(pos, i + 5, posSet);
// parse an xref stream
- } else if (obj.isInt()) {
+ } else if (i < n && buf[i] >= '0' && buf[i] <= '9') {
+ obj.initNull();
+ parser = new Parser(NULL,
+ new Lexer(NULL,
+ str->makeSubStream(start + *pos, gFalse, 0, &obj)),
+ gTrue);
+ if (!parser->getObj(&obj, gTrue)->isInt()) {
+ goto err2;
+ }
obj.free();
if (!parser->getObj(&obj, gTrue)->isInt()) {
- goto err1;
+ goto err2;
}
obj.free();
if (!parser->getObj(&obj, gTrue)->isCmd("obj")) {
- goto err1;
+ goto err2;
}
obj.free();
if (!parser->getObj(&obj)->isStream()) {
- goto err1;
+ goto err2;
}
more = readXRefStream(obj.getStream(), pos);
obj.free();
+ delete parser;
} else {
goto err1;
}
- delete parser;
return more;
- err1:
+ err2:
obj.free();
delete parser;
+ err1:
ok = gFalse;
return gFalse;
}
-GBool XRef::readXRefTable(Parser *parser, Guint *pos) {
+GBool XRef::readXRefTable(GFileOffset *pos, int offset, XRefPosSet *posSet) {
XRefEntry entry;
- GBool more;
+ Parser *parser;
Object obj, obj2;
- Guint pos2;
- int first, n, newSize, i;
+ char buf[6];
+ GFileOffset off, pos2;
+ GBool more;
+ int first, n, newSize, gen, i, c;
+
+ if (posSet->check(*pos)) {
+ error(errSyntaxWarning, -1, "Infinite loop in xref table");
+ return gFalse;
+ }
+ posSet->add(*pos);
+
+ str->setPos(start + *pos + offset);
while (1) {
- parser->getObj(&obj, gTrue);
- if (obj.isCmd("trailer")) {
- obj.free();
+ do {
+ c = str->getChar();
+ } while (Lexer::isSpace(c));
+ if (c == 't') {
+ if (str->getBlock(buf, 6) != 6 || memcmp(buf, "railer", 6)) {
+ goto err1;
+ }
break;
}
- if (!obj.isInt()) {
+ if (c < '0' || c > '9') {
goto err1;
}
- first = obj.getInt();
- obj.free();
- if (!parser->getObj(&obj, gTrue)->isInt()) {
+ first = 0;
+ do {
+ first = (first * 10) + (c - '0');
+ c = str->getChar();
+ } while (c >= '0' && c <= '9');
+ if (!Lexer::isSpace(c)) {
goto err1;
}
- n = obj.getInt();
- obj.free();
- if (first < 0 || n < 0 || first + n < 0) {
+ do {
+ c = str->getChar();
+ } while (Lexer::isSpace(c));
+ n = 0;
+ do {
+ n = (n * 10) + (c - '0');
+ c = str->getChar();
+ } while (c >= '0' && c <= '9');
+ if (!Lexer::isSpace(c)) {
+ goto err1;
+ }
+ if (first < 0 || n < 0 || first > INT_MAX - n) {
goto err1;
}
if (first + n > size) {
@@ -395,32 +527,51 @@ GBool XRef::readXRefTable(Parser *parser
}
entries = (XRefEntry *)greallocn(entries, newSize, sizeof(XRefEntry));
for (i = size; i < newSize; ++i) {
- entries[i].offset = 0xffffffff;
+ entries[i].offset = (GFileOffset)-1;
entries[i].type = xrefEntryFree;
}
size = newSize;
}
for (i = first; i < first + n; ++i) {
- if (!parser->getObj(&obj, gTrue)->isInt()) {
+ do {
+ c = str->getChar();
+ } while (Lexer::isSpace(c));
+ off = 0;
+ do {
+ off = (off * 10) + (c - '0');
+ c = str->getChar();
+ } while (c >= '0' && c <= '9');
+ if (!Lexer::isSpace(c)) {
goto err1;
}
- entry.offset = (Guint)obj.getInt();
- obj.free();
- if (!parser->getObj(&obj, gTrue)->isInt()) {
+ entry.offset = off;
+ do {
+ c = str->getChar();
+ } while (Lexer::isSpace(c));
+ gen = 0;
+ do {
+ gen = (gen * 10) + (c - '0');
+ c = str->getChar();
+ } while (c >= '0' && c <= '9');
+ if (!Lexer::isSpace(c)) {
goto err1;
}
- entry.gen = obj.getInt();
- obj.free();
- parser->getObj(&obj, gTrue);
- if (obj.isCmd("n")) {
+ entry.gen = gen;
+ do {
+ c = str->getChar();
+ } while (Lexer::isSpace(c));
+ if (c == 'n') {
entry.type = xrefEntryUncompressed;
- } else if (obj.isCmd("f")) {
+ } else if (c == 'f') {
entry.type = xrefEntryFree;
} else {
goto err1;
}
- obj.free();
- if (entries[i].offset == 0xffffffff) {
+ c = str->getChar();
+ if (!Lexer::isSpace(c)) {
+ goto err1;
+ }
+ if (entries[i].offset == (GFileOffset)-1) {
entries[i] = entry;
// PDF files of patents from the IBM Intellectual Property
// Network have a bug: the xref table claims to start at 1
@@ -430,7 +581,7 @@ GBool XRef::readXRefTable(Parser *parser
entries[1].type == xrefEntryFree) {
i = first = 0;
entries[0] = entries[1];
- entries[1].offset = 0xffffffff;
+ entries[1].offset = (GFileOffset)-1;
}
if (i > last) {
last = i;
@@ -440,32 +591,29 @@ GBool XRef::readXRefTable(Parser *parser
}
// read the trailer dictionary
- if (!parser->getObj(&obj)->isDict()) {
+ obj.initNull();
+ parser = new Parser(NULL,
+ new Lexer(NULL,
+ str->makeSubStream(str->getPos(), gFalse, 0, &obj)),
+ gTrue);
+ parser->getObj(&obj);
+ delete parser;
+ if (!obj.isDict()) {
+ obj.free();
goto err1;
}
// get the 'Prev' pointer
+ //~ this can be a 64-bit int (?)
obj.getDict()->lookupNF("Prev", &obj2);
if (obj2.isInt()) {
- pos2 = (Guint)obj2.getInt();
- if (pos2 != *pos) {
- *pos = pos2;
- more = gTrue;
- } else {
- error(errSyntaxWarning, -1, "Infinite loop in xref table");
- more = gFalse;
- }
+ *pos = (GFileOffset)(Guint)obj2.getInt();
+ more = gTrue;
} else if (obj2.isRef()) {
// certain buggy PDF generators generate "/Prev NNN 0 R" instead
// of "/Prev NNN"
- pos2 = (Guint)obj2.getRefNum();
- if (pos2 != *pos) {
- *pos = pos2;
- more = gTrue;
- } else {
- error(errSyntaxWarning, -1, "Infinite loop in xref table");
- more = gFalse;
- }
+ *pos = (GFileOffset)(Guint)obj2.getRefNum();
+ more = gTrue;
} else {
more = gFalse;
}
@@ -477,9 +625,10 @@ GBool XRef::readXRefTable(Parser *parser
}
// check for an 'XRefStm' key
+ //~ this can be a 64-bit int (?)
if (obj.getDict()->lookup("XRefStm", &obj2)->isInt()) {
- pos2 = (Guint)obj2.getInt();
- readXRef(&pos2);
+ pos2 = (GFileOffset)(Guint)obj2.getInt();
+ readXRef(&pos2, posSet);
if (!ok) {
obj2.free();
goto err1;
@@ -491,12 +640,11 @@ GBool XRef::readXRefTable(Parser *parser
return more;
err1:
- obj.free();
ok = gFalse;
return gFalse;
}
-GBool XRef::readXRefStream(Stream *xrefStr, Guint *pos) {
+GBool XRef::readXRefStream(Stream *xrefStr, GFileOffset *pos) {
Dict *dict;
int w[3];
GBool more;
@@ -516,7 +664,7 @@ GBool XRef::readXRefStream(Stream *xrefS
if (newSize > size) {
entries = (XRefEntry *)greallocn(entries, newSize, sizeof(XRefEntry));
for (i = size; i < newSize; ++i) {
- entries[i].offset = 0xffffffff;
+ entries[i].offset = (GFileOffset)-1;
entries[i].type = xrefEntryFree;
}
size = newSize;
@@ -533,11 +681,13 @@ GBool XRef::readXRefStream(Stream *xrefS
}
w[i] = obj2.getInt();
obj2.free();
- if (w[i] < 0 || w[i] > 4) {
- goto err1;
- }
}
obj.free();
+ if (w[0] < 0 || w[0] > 4 ||
+ w[1] < 0 || w[1] > (int)sizeof(GFileOffset) ||
+ w[2] < 0 || w[2] > 4) {
+ goto err0;
+ }
xrefStr->reset();
dict->lookupNF("Index", &idx);
@@ -569,9 +719,10 @@ GBool XRef::readXRefStream(Stream *xrefS
}
idx.free();
+ //~ this can be a 64-bit int (?)
dict->lookupNF("Prev", &obj);
if (obj.isInt()) {
- *pos = (Guint)obj.getInt();
+ *pos = (GFileOffset)(Guint)obj.getInt();
more = gTrue;
} else {
more = gFalse;
@@ -591,7 +742,7 @@ GBool XRef::readXRefStream(Stream *xrefS
}
GBool XRef::readXRefStreamSection(Stream *xrefStr, int *w, int first, int n) {
- Guint offset;
+ GFileOffset offset;
int type, gen, c, newSize, i, j;
if (first + n < 0) {
@@ -606,7 +757,7 @@ GBool XRef::readXRefStreamSection(Stream
}
entries = (XRefEntry *)greallocn(entries, newSize, sizeof(XRefEntry));
for (i = size; i < newSize; ++i) {
- entries[i].offset = 0xffffffff;
+ entries[i].offset = (GFileOffset)-1;
entries[i].type = xrefEntryFree;
}
size = newSize;
@@ -634,7 +785,7 @@ GBool XRef::readXRefStreamSection(Stream
}
gen = (gen << 8) + c;
}
- if (entries[i].offset == 0xffffffff) {
+ if (entries[i].offset == (GFileOffset)-1) {
switch (type) {
case 0:
entries[i].offset = offset;
@@ -668,7 +819,7 @@ GBool XRef::constructXRef() {
Parser *parser;
Object newTrailerDict, obj;
char buf[256];
- Guint pos;
+ GFileOffset pos;
int num, gen;
int newSize;
int streamEndsSize;
@@ -748,7 +899,7 @@ GBool XRef::constructXRef() {
entries = (XRefEntry *)
greallocn(entries, newSize, sizeof(XRefEntry));
for (i = size; i < newSize; ++i) {
- entries[i].offset = 0xffffffff;
+ entries[i].offset = (GFileOffset)-1;
entries[i].type = xrefEntryFree;
}
size = newSize;
@@ -771,15 +922,16 @@ GBool XRef::constructXRef() {
} else if (!strncmp(p, "endstream", 9)) {
if (streamEndsLen == streamEndsSize) {
streamEndsSize += 64;
- streamEnds = (Guint *)greallocn(streamEnds,
- streamEndsSize, sizeof(Guint));
+ streamEnds = (GFileOffset *)greallocn(streamEnds, streamEndsSize,
+ sizeof(GFileOffset));
}
streamEnds[streamEndsLen++] = pos;
}
}
- if (gotRoot)
+ if (gotRoot) {
return gTrue;
+ }
error(errSyntaxError, -1, "Couldn't find trailer dictionary");
return gFalse;
@@ -824,13 +976,31 @@ GBool XRef::okToAddNotes(GBool ignoreOwn
Object *XRef::fetch(int num, int gen, Object *obj, int recursion) {
XRefEntry *e;
Parser *parser;
+ ObjectStream *objStr;
Object obj1, obj2, obj3;
+ XRefCacheEntry tmp;
+ int i, j;
// check for bogus ref - this can happen in corrupted PDF files
if (num < 0 || num >= size) {
goto err;
}
+ // check the cache
+ if (cache[0].num == num && cache[0].gen == gen) {
+ return cache[0].obj.copy(obj);
+ }
+ for (i = 1; i < xrefCacheSize; ++i) {
+ if (cache[i].num == num && cache[i].gen == gen) {
+ tmp = cache[i];
+ for (j = i; j > 0; --j) {
+ cache[j] = cache[j - 1];
+ }
+ cache[0] = tmp;
+ return cache[0].obj.copy(obj);
+ }
+ }
+
e = &entries[num];
switch (e->type) {
@@ -869,21 +1039,13 @@ Object *XRef::fetch(int num, int gen, Ob
goto err;
}
#endif
- if (e->offset >= (Guint)size ||
+ if (e->offset >= (GFileOffset)size ||
entries[e->offset].type != xrefEntryUncompressed) {
error(errSyntaxError, -1, "Invalid object stream");
goto err;
}
- if (!objStr || objStr->getObjStrNum() != (int)e->offset) {
- if (objStr) {
- delete objStr;
- }
- objStr = new ObjectStream(this, e->offset);
- if (!objStr->isOk()) {
- delete objStr;
- objStr = NULL;
- goto err;
- }
+ if (!(objStr = getObjectStream((int)e->offset))) {
+ goto err;
}
objStr->getObject(e->gen, num, obj);
break;
@@ -892,12 +1054,61 @@ Object *XRef::fetch(int num, int gen, Ob
goto err;
}
+ // put the new object in the cache, throwing away the oldest object
+ // currently in the cache
+ if (cache[xrefCacheSize - 1].num >= 0) {
+ cache[xrefCacheSize - 1].obj.free();
+ }
+ for (i = xrefCacheSize - 1; i > 0; --i) {
+ cache[i] = cache[i - 1];
+ }
+ cache[0].num = num;
+ cache[0].gen = gen;
+ obj->copy(&cache[0].obj);
+
return obj;
err:
return obj->initNull();
}
+ObjectStream *XRef::getObjectStream(int objStrNum) {
+ ObjectStream *objStr;
+ int i, j;
+
+ // check the MRU entry in the cache
+ if (objStrs[0] && objStrs[0]->getObjStrNum() == objStrNum) {
+ return objStrs[0];
+ }
+
+ // check the rest of the cache
+ for (i = 1; i < objStrCacheSize; ++i) {
+ if (objStrs[i] && objStrs[i]->getObjStrNum() == objStrNum) {
+ objStr = objStrs[i];
+ for (j = i; j > 0; --j) {
+ objStrs[j] = objStrs[j - 1];
+ }
+ objStrs[0] = objStr;
+ return objStr;
+ }
+ }
+
+ // load a new ObjectStream
+ objStr = new ObjectStream(this, objStrNum);
+ if (!objStr->isOk()) {
+ delete objStr;
+ return NULL;
+ }
+ if (objStrs[objStrCacheSize - 1]) {
+ delete objStrs[objStrCacheSize - 1];
+ }
+ for (j = objStrCacheSize - 1; j > 0; --j) {
+ objStrs[j] = objStrs[j - 1];
+ }
+ objStrs[0] = objStr;
+ return objStr;
+}
+
Object *XRef::getDocInfo(Object *obj) {
return trailerDict.dictLookup("Info", obj);
}
@@ -907,7 +1118,7 @@ Object *XRef::getDocInfoNF(Object *obj)
return trailerDict.dictLookupNF("Info", obj);
}
-GBool XRef::getStreamEnd(Guint streamStart, Guint *streamEnd) {
+GBool XRef::getStreamEnd(GFileOffset streamStart, GFileOffset *streamEnd) {
int a, b, m;
if (streamEndsLen == 0 ||
@@ -930,14 +1141,14 @@ GBool XRef::getStreamEnd(Guint streamSta
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 -uNrp 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 @@ enum XRefEntryType {
};
struct XRefEntry {
- Guint offset;
+ GFileOffset offset;
int gen;
XRefEntryType type;
};
+struct XRefCacheEntry {
+ int num;
+ int gen;
+ Object obj;
+};
+
+#define xrefCacheSize 16
+
+#define objStrCacheSize 4
+
class XRef {
public:
@@ -83,7 +95,7 @@ public:
int getNumObjects() { return last + 1; }
// Return the offset of the last xref table.
- Guint getLastXRefPos() { return lastXRefPos; }
+ GFileOffset getLastXRefPos() { return lastXRefPos; }
// Return the catalog object reference.
int getRootNum() { return rootNum; }
@@ -91,7 +103,7 @@ public:
// Get end position for a stream in a damaged file.
// Returns false if unknown or file is not damaged.
- GBool getStreamEnd(Guint streamStart, Guint *streamEnd);
+ GBool getStreamEnd(GFileOffset streamStart, GFileOffset *streamEnd);
// Direct access.
int getSize() { return size; }
@@ -101,7 +113,7 @@ public:
private:
BaseStream *str; // input stream
- Guint start; // offset in file (to allow for garbage
+ GFileOffset start; // offset in file (to allow for garbage
// at beginning of file)
XRefEntry *entries; // xref entries
int size; // size of array
@@ -110,11 +122,12 @@ private:
GBool ok; // true if xref table is valid
int errCode; // error code (if is false)
Object trailerDict; // trailer dictionary
- Guint lastXRefPos; // offset of last xref table
- Guint *streamEnds; // 'endstream' positions - only used in
+ GFileOffset lastXRefPos; // offset of last xref table
+ GFileOffset *streamEnds; // 'endstream' positions - only used in
// damaged files
int streamEndsLen; // number of valid entries in streamEnds
- ObjectStream *objStr; // cached object stream
+ ObjectStream * // cached object streams
+ objStrs[objStrCacheSize];
GBool encrypted; // true if file is encrypted
int permFlags; // permission bits
GBool ownerPasswordOk; // true if owner password is correct
@@ -122,14 +135,17 @@ private:
int keyLength; // length of key, in bytes
int encVersion; // encryption version
CryptAlgorithm encAlgorithm; // encryption algorithm
+ XRefCacheEntry // cache of recently accessed objects
+ cache[xrefCacheSize];
- Guint getStartXref();
- GBool readXRef(Guint *pos);
- GBool readXRefTable(Parser *parser, Guint *pos);
+ GFileOffset getStartXref();
+ GBool readXRef(GFileOffset *pos, XRefPosSet *posSet);
+ GBool readXRefTable(GFileOffset *pos, int offset, XRefPosSet *posSet);
GBool readXRefStreamSection(Stream *xrefStr, int *w, int first, int n);
- GBool readXRefStream(Stream *xrefStr, Guint *pos);
+ GBool readXRefStream(Stream *xrefStr, GFileOffset *pos);
GBool constructXRef();
- Guint strToUnsigned(char *s);
+ ObjectStream *getObjectStream(int objStrNum);
+ GFileOffset strToFileOffset(char *s);
};
#endif
diff -uNrp 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
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include
+#include
+#include
+#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("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("') {
+ 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("= '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("", 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("", 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 -uNrp 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
+
+#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